Quellcode durchsuchen

add 添加 docker等相关文件

YueYunyun vor 1 Monat
Ursprung
Commit
aa27ff4ca8

+ 13 - 0
SourceCode/DataMiddleware/.script/cmd/Build_App.run.xml

@@ -0,0 +1,13 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Build_App" type="docker-deploy" factoryName="dockerfile" server-name="104">
+    <deployment type="dockerfile">
+      <settings>
+        <option name="imageTag" value="y_tielu-data-middleware-app:1.0.0" />
+        <option name="buildOnly" value="true" />
+        <option name="contextFolderPath" value="." />
+        <option name="sourceFilePath" value="docker/Dockerfile" />
+      </settings>
+    </deployment>
+    <method v="2" />
+  </configuration>
+</component>

+ 12 - 0
SourceCode/DataMiddleware/.script/cmd/Run_App.run.xml

@@ -0,0 +1,12 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Run_App" type="docker-deploy" factoryName="docker-compose.yml" server-name="104">
+    <deployment type="docker-compose.yml">
+      <settings>
+        <option name="composeProjectName" value="tielu_dm" />
+        <option name="envFilePath" value="" />
+        <option name="sourceFilePath" value="docker/docker-compose.yml" />
+      </settings>
+    </deployment>
+    <method v="2" />
+  </configuration>
+</component>

+ 0 - 2
SourceCode/DataMiddleware/app/config.yml

@@ -1,5 +1,3 @@
-app:
-  port: 5123
 mysql:
   host: 192.168.0.81
   port: 3307

+ 3 - 0
SourceCode/DataMiddleware/app/data_process/pre_process.py

@@ -209,12 +209,14 @@ class PreProcess:
             """
             api_key= utils.get_config_value("fastgpt.api_key_pre_process")
             data = self._ai.call_ai_with_file(file_path+file, prompt,api_key)
+            # data = utils.AiHelper().call_openai_with_file(file_path+file,"", user_prompt=prompt,api_model="qwen-max")
             if isinstance(data, str):
                 import json
                 data= json.loads(data)
             res_data = self.format_data(project.id, data)
             if len(res_data)<=0:
                 self._logger.error(f"项目:{project.project_no} 文件处理失败: {data}")
+                self._store.update_project_status(project.id, 11)
                 return False
             project.items = res_data
             self._store.re_insert_project(project)
@@ -224,6 +226,7 @@ class PreProcess:
             return True
         except Exception as e:
             self._logger.error(f"项目:{project.project_no} 文件处理失败: {e}")
+            self._store.update_project_status(project.id,11)
             return False
 
 

+ 55 - 4
SourceCode/DataMiddleware/app/data_process/process.py

@@ -1,3 +1,5 @@
+from click import prompt
+
 import utils,json
 
 from stores.mysql_store import MysqlStore
@@ -17,9 +19,14 @@ class Process:
             self._logger.info(f"开始处理数据:{project.project_no}")
             self._store.update_project_status(project.id,22)
             project_items = self._store.query_project_items_by_project(project.id)
-            text = self.prompt_template(project,project_items)
-            items = self.call_ai_process(text)
-            self._store.update_project_item_standard_no_batch(items)
+            for project_item in project_items:
+                data = self.call_ai(project_item)
+                project_item.standard_no = data.standard_no
+                self._store.update_project_item(project_item)
+            # text = self.prompt_template(project,project_items)
+            # items = self.call_ai_process(text)
+            #
+            # self._store.update_project_item_standard_no_batch(items)
             self._store.update_project_status(project.id,12)
             self._logger.info(f"处理数据完成:{project.project_no}")
             return True
@@ -28,6 +35,48 @@ class Process:
             self._store.update_project_status(project.id,12)
             return False
 
+    def call_ai(self, project_item: ProjectItemModel) ->ProjectItemModel:
+        try:
+            self._logger.info(f"开始处理数据:{project_item.id}")
+            msg = """
+            请分析提供的json数据,要求:
+            1. 根据工作内容、类型、项目名称、规格型号、数量、单位查找计算出定额编号
+            2. 工程的工作内容及类型:{project.work_content}"""
+            msg += """
+            3. 提供的数据结构化信息:```typescript
+            export interface item { 
+            i: // ID
+            n: string; //物料名称
+            m: string; //型号规格
+            u:string; //单位
+            c: float; //数量
+            }
+            ```
+            4. 需返回的结构体:```typescript
+            export interface item { 
+            i: int; // ID 与提供的ID保持一致
+            s: string; // 定额编号
+            }
+            ```
+            5. 返回结构体item的json字符串,压缩成一行。
+            6. 数据如下:
+                """
+            msg += project_item.to_ai_json()
+            api_key = utils.get_config_value("fastgpt.api_key_process")
+            self._logger.info(f"开始调用AI:\n {msg}")
+            json_data = self._ai.call_ai(msg, api_key)
+            self._logger.info(f"AI返回结果:{json_data}")
+            if not json_data:
+                raise Exception("AI返回结果为空")
+            data = ProjectItemModel(
+                item_id=json_data["i"],
+                standard_no=json_data["s"]
+            )
+            return data
+        except Exception as e:
+            self._logger.error(f"AI调用失败:{e}")
+            raise Exception(f"AI调用失败:{e}")
+
     @staticmethod
     def prompt_template(project, items) ->str:
         text = f"""
@@ -63,6 +112,8 @@ class Process:
             self._logger.info(f"开始调用AI:\n {message}")
             json_data = self._ai.call_ai(message,api_key)
             self._logger.info(f"AI返回结果:{json_data}")
+            if not json_data:
+                raise  Exception("AI返回结果为空")
             data=[]
             for item in json_data:
                 data.append(ProjectItemModel(
@@ -72,6 +123,6 @@ class Process:
             return data
         except Exception as e:
             self._logger.error(f"AI调用失败:{e}")
-            return []
+            raise Exception(f"AI调用失败:{e}")
 
 

+ 2 - 2
SourceCode/DataMiddleware/app/init.sql

@@ -1,6 +1,6 @@
 -- 创建数据库
-CREATE DATABASE IF NOT EXISTS iwb_data_middleware_dev CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-USE iwb_data_middleware_dev;
+CREATE DATABASE IF NOT EXISTS iwb_data_middleware_v1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
+USE iwb_data_middleware_v1;
 
 -- 创建 SourceData 表
 CREATE TABLE IF NOT EXISTS project (

+ 3 - 8
SourceCode/DataMiddleware/app/main.py

@@ -5,15 +5,10 @@ logger = utils.get_logger()
 
 def main():
     logger.info("程序启动中...")
-    thread = threading.Thread(target=debug_test)
-    thread.start()
+    # thread = threading.Thread(target=debug_test)
+    # thread.start()
     app = ui.create_app()
-    port = utils.get_config_int("app.port", 5000)
-    app.run(port=port)  # 指定HTTP端口为5000
-
-
-
-
+    app.run(host='0.0.0.0',port=5123)  # 指定HTTP端口为5123
 
 def debug_test():
     logger.info("调试程序开始")

+ 1 - 1
SourceCode/DataMiddleware/app/stores/mysql_store.py

@@ -287,7 +287,7 @@ class MysqlStore:
         with self._db_helper:
             self._db_helper.execute_non_query(query, params)
             return True
-    def update_project_data(self, project_item: ProjectItemModel) -> bool:
+    def update_project_item(self, project_item: ProjectItemModel) -> bool:
         query = "SElECT standard_no FROM project_item WHERE id = %s"
         params = (project_item.id,)
         u_query = "UPDATE project_item SET device_name= %s,device_model= %s,device_unit= %s,device_count= %s,standard_no = %s,update_time = %s WHERE id = %s"

+ 1 - 1
SourceCode/DataMiddleware/app/ui/project_services.py

@@ -133,7 +133,7 @@ class SourceDataService:
             project_item.device_unit = item.device_unit
             project_item.device_count = item.device_count
             project_item.standard_no = item.standard_no
-            self._store.update_project_data(project_item)
+            self._store.update_project_item(project_item)
             #TODO 更新标准数据库
 
             #TODO 更新远程数据

+ 1 - 2
SourceCode/DataMiddleware/app/ui/project_views.py

@@ -12,8 +12,7 @@ project = Blueprint('project', __name__, template_folder='templates/project')
 
 @project.route('/')
 def index():
-    return "Hello, World!"
-
+    return redirect(url_for('project.project_list'))
 @project.route('/project_list')
 def project_list():
     keyword = request.args.get('k', '', type=str)

+ 10 - 8
SourceCode/DataMiddleware/app/ui/static/project_list.js

@@ -29,9 +29,9 @@ function updateProject(row, id) {
 	const checkboxDiv = document.createElement('div')
 	checkboxDiv.innerHTML = `<label for="delete_old_data">删除原数据:</label><input type="checkbox" id="delete_old_data" class="form-control" style="width: auto;flex: 0;">`
 	file.parentNode.insertBefore(checkboxDiv, file.nextSibling)
-	document.getElementById('new_work_catalog').value = row.querySelector('.work_catalog .form-control').value
+	// document.getElementById('new_work_catalog').value = row.querySelector('.work_catalog .form-control').value
 	document.getElementById('new_work_content').value = row.querySelector('.work_content .form-control').value
-	document.getElementById('new_standard_version').value = row.querySelector('.standard_version .form-control').value
+	// document.getElementById('new_standard_version').value = row.querySelector('.standard_version .form-control').value
 	const editBox = document.querySelector('div.edit-box')
 	editBox.classList.add('show')
 }
@@ -40,9 +40,11 @@ function saveProject() {
 	const no = document.getElementById('new_project_no').value
 	const name = document.getElementById('new_project_name').value
 	const files = document.getElementById('new_project_file').files
-	const catalog = document.getElementById('new_work_catalog').value
+	// const catalog = document.getElementById('new_work_catalog').value
+	// const version = document.getElementById('new_standard_version').value
+	const catalog = " "
+	const version = "2"
 	const content = document.getElementById('new_work_content').value
-	const version = document.getElementById('new_standard_version').value
 
 	if (no === '') {
 		alert('项目编号不能为空')
@@ -56,10 +58,10 @@ function saveProject() {
 		alert('请选择项目数据文件')
 		return
 	}
-	if (catalog === '') {
-		alert('工作目录不能为空')
-		return
-	}
+	// if (catalog === '') {
+	// 	alert('工作目录不能为空')
+	// 	return
+	// }
 	if (content === '') {
 		alert('工作内容不能为空')
 		return

+ 2 - 2
SourceCode/DataMiddleware/app/ui/templates/project/project_item_list.html

@@ -40,7 +40,7 @@
 						<dt>项目名称:</dt>
 						<dd>{{ project.project_name }}</dd>
 					</dl>
-					<dl>
+					<!--<dl>
 						<dt>标准版本:</dt>
 						<dd>
 							{% if project.standard_version == '1' %}
@@ -49,7 +49,7 @@
 							<span class="label label-info">新版</span>
 							{% endif %}
 						</dd>
-					</dl>
+					</dl>-->
 				</div>
 			</div>
 			<div class="box_header">

+ 36 - 36
SourceCode/DataMiddleware/app/ui/templates/project/project_list.html

@@ -63,21 +63,21 @@
                     <input type="file" id="new_project_file" class="form-control" accept=".xlsx,.xls,.csv" multiple>
                 </div>
                 <div></div>
-                <div>
-                    <label for="new_work_catalog">工作目录:</label>
-                    <textarea id="new_work_catalog" class="form-control" placeholder="工作目录"></textarea>
-                </div>
+<!--                <div>-->
+<!--                    <label for="new_work_catalog">工作目录:</label>-->
+<!--                    <textarea id="new_work_catalog" class="form-control" placeholder="工作目录"></textarea>-->
+<!--                </div>-->
                 <div>
                     <label for="new_work_content">工作内容:</label>
                     <textarea id="new_work_content" class="form-control large" placeholder="工作内容"></textarea>
                 </div>
-                <div>
-                    <label for="new_standard_version">标准版本:</label>
-                    <select id="new_standard_version" class="form-control">
-                        <option value="1">旧版</option>
-                        <option value="2">新版</option>
-                    </select>
-                </div>
+<!--                <div>-->
+<!--                    <label for="new_standard_version">标准版本:</label>-->
+<!--                    <select id="new_standard_version" class="form-control">-->
+<!--                        <option value="1">旧版</option>-->
+<!--                        <option value="2">新版</option>-->
+<!--                    </select>-->
+<!--                </div>-->
                 <div class="button-group">
                     <button type="button" class="btn btn-success btn-large" onclick="saveProject()">保存</button>
                     <button type="button" class="btn btn-warning btn-large" onclick="cancelAdd()">关闭</button>
@@ -89,12 +89,12 @@
                 <thead>
                     <tr>
                         <th width="200px">项目编号</th>
-                        <th width="280px">项目名称</th>
-                        <th width="15%">工作目录</th>
+                        <th width="330px">项目名称</th>
+<!--                        <th width="15%">工作目录</th>-->
                         <th width="">工作内容</th>
-                        <th width="120px">标准版本</th>
+<!--                        <th width="120px">标准版本</th>-->
                         <th width="120px">状态</th>
-                        <th width="260px">操作</th>
+                        <th width="280px">操作</th>
                     </tr>
                 </thead>
                 <tbody>
@@ -113,33 +113,33 @@
                                     <input type="text" class="form-control" title="项目名称" name="project_name" value="{{ project.project_name if project.project_name else '' }}">
                                 </span>
                             </td>
-                            <td class="editable work_catalog">
-                                <span class="show">{{ project.work_catalog if project.work_catalog else '-' }}</span>
-                                <span class="edit">
-                                    <input type="text" class="form-control" title="工作目录" name="work_catalog" value="{{ project.work_catalog if project.work_catalog else '' }}">
-                                </span>
-                            </td>
+<!--                            <td class="editable work_catalog">-->
+<!--                                <span class="show">{{ project.work_catalog if project.work_catalog else '-' }}</span>-->
+<!--                                <span class="edit">-->
+<!--                                    <input type="text" class="form-control" title="工作目录" name="work_catalog" value="{{ project.work_catalog if project.work_catalog else '' }}">-->
+<!--                                </span>-->
+<!--                            </td>-->
                             <td class="editable work_content">
                                 <span class="show">{{ project.work_content if project.work_content else '-' }}</span>
                                 <span class="edit">
                                     <input type="text" class="form-control" title="工作内容" value="{{ project.work_content if project.work_content else '' }}">
                                 </span>
                             </td>
-                            <td class="editable standard_version">
-                                <span class="show">
-                                    {% if project.standard_version == '1' %}
-                                    <span class="label label-warning">旧版</span>
-                                    {% elif project.standard_version == '2' %}
-                                    <span class="label label-info">新版</span>
-                                    {% endif %}
-                                </span>
-                                <span class="edit">
-                                    <select class="form-control" title="标准版本">
-                                        <option value="1" {% if project.standard_version == '1' %}selected{% endif %}>旧版</option>
-                                        <option value="2" {% if project.standard_version == '2' %}selected{% endif %}>新版</option>
-                                    </select>
-                                </span>
-                            </td>
+<!--                            <td class="editable standard_version">-->
+<!--                                <span class="show">-->
+<!--                                    {% if project.standard_version == '1' %}-->
+<!--                                    <span class="label label-warning">旧版</span>-->
+<!--                                    {% elif project.standard_version == '2' %}-->
+<!--                                    <span class="label label-info">新版</span>-->
+<!--                                    {% endif %}-->
+<!--                                </span>-->
+<!--                                <span class="edit">-->
+<!--                                    <select class="form-control" title="标准版本">-->
+<!--                                        <option value="1" {% if project.standard_version == '1' %}selected{% endif %}>旧版</option>-->
+<!--                                        <option value="2" {% if project.standard_version == '2' %}selected{% endif %}>新版</option>-->
+<!--                                    </select>-->
+<!--                                </span>-->
+<!--                            </td>-->
                             <td>
                                 <span class="status">
                                     {% if project.status == 0 %}

+ 64 - 14
SourceCode/DataMiddleware/app/utils/ai_helper.py

@@ -3,7 +3,7 @@ import re
 
 import utils
 from openai import OpenAI
-
+from pathlib import Path
 
 class AiHelper:
 
@@ -20,19 +20,7 @@ class AiHelper:
             self._ai_max_tokens = int(max_tokens)
 
     def call_openai(self, system_prompt: str, user_prompt: str,api_url: str=None,api_key: str=None,api_model: str=None) -> json:
-        if api_url:
-            self._ai_api_url = api_url
-        if api_key:
-            self._ai_api_key = api_key
-        if api_model:
-            self._api_model = api_model
-        if self._ai_api_key is None:
-            raise Exception("AI API key 没有配置")
-        if self._ai_api_url is None:
-            raise Exception("AI API url 没有配置")
-        if self._api_model is None:
-            raise Exception("AI API model 没有配置")
-
+        self.check_api(api_key, api_model, api_url)
         utils.get_logger().info(f"调用AI API ==> Url:{self._ai_api_url},Model:{self._api_model}")
 
         client = OpenAI(api_key=self._ai_api_key, base_url=self._ai_api_url)
@@ -72,6 +60,20 @@ class AiHelper:
         except Exception as e:
             raise Exception(f"解析 AI 响应错误: {e}")
 
+    def check_api(self, api_key, api_model, api_url):
+        if api_url:
+            self._ai_api_url = api_url
+        if api_key:
+            self._ai_api_key = api_key
+        if api_model:
+            self._api_model = api_model
+        if self._ai_api_key is None:
+            raise Exception("AI API key 没有配置")
+        if self._ai_api_url is None:
+            raise Exception("AI API url 没有配置")
+        if self._api_model is None:
+            raise Exception("AI API model 没有配置")
+
     @staticmethod
     def _extract_message_content(response_json: dict) -> str:
         utils.get_logger().info(f"AI Response JSON: {response_json}")
@@ -118,3 +120,51 @@ class AiHelper:
                 return self._parse_response(message_content, False)
             else:
                 raise Exception(f"解析 AI 响应错误: {response} {e}")
+
+
+    def call_openai_with_image(self, image_path,system_prompt: str, user_prompt: str, api_url: str=None,api_key: str=None,api_model: str=None) -> json:
+        pass
+
+    def call_openai_with_file(self, file_path,system_prompt: str, user_prompt: str, api_url: str=None,api_key: str=None,api_model: str=None)->json:
+        self.check_api(api_key, api_model, api_url)
+        utils.get_logger().info(f"调用AI API File==> Url:{self._ai_api_url},Model:{self._api_model}")
+
+        client = OpenAI(api_key=self._ai_api_key, base_url=self._ai_api_url)
+        file_object = client.files.create(    file=Path(file_path),purpose='file-extract',)
+        completion = client.chat.completions.create(
+            model=self._api_model,
+            messages=[
+                {
+                    "role": "system",
+                    # "content": system_prompt,
+                    'content': f'fileid://{file_object.id}'
+                },
+                {
+                    "role": "user",
+                    "content": user_prompt,
+                },
+            ],
+            stream=False,
+            temperature=0.7,
+            response_format={"type": "json_object"},
+            # max_tokens=self._ai_max_tokens,
+        )
+        try:
+            response = completion.model_dump_json()
+            result = {}
+            response_json = json.loads(response)
+            res_str = self._extract_message_content(response_json)
+            result_data = self._parse_response(res_str, True)
+            if result_data:
+                result["data"] = result_data
+                usage = response_json["usage"]
+                result["completion_tokens"] = usage.get("completion_tokens", 0)
+                result["prompt_tokens"] = usage.get("prompt_tokens", 0)
+                result["total_tokens"] = usage.get("total_tokens", 0)
+                utils.get_logger().info(f"AI Process JSON: {result}")
+            else:
+                utils.get_logger().info(f"AI Response: {response}")
+            return result
+        except Exception as e:
+            raise Exception(f"解析 AI 响应错误: {e}")
+        pass

+ 5 - 0
SourceCode/DataMiddleware/docker/.env

@@ -0,0 +1,5 @@
+MYSQL_ROOT_PASSWORD=123456qwertyu
+MYSQL_DATABASE=iwb_data_middleware_v1
+MYSQL_USER=iwb_data
+MYSQL_PASSWORD=123456iwb
+MYSQL_PORT=3536

+ 35 - 0
SourceCode/DataMiddleware/docker/Dockerfile

@@ -0,0 +1,35 @@
+# 第一阶段:构建
+# 使用官方的 Python 基础镜像
+FROM python:3.13-slim AS builder
+
+RUN mkdir /app
+
+WORKDIR /app
+# 明确指定 requirements.txt 的路径
+COPY requirements.txt .
+# 安装项目依赖
+RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
+# 在 builder 阶段添加调试命令
+# RUN pip freeze > installed-packages.txt
+
+# 复制项目文件到工作目录
+COPY app/ /app
+
+# 将/etc/localtime链接到上海时区文件
+RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+
+# 第二阶段:运行
+FROM python:3.13-slim
+
+WORKDIR /app
+COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
+COPY --from=builder /app /app
+
+# 暴露端口(如果有需要)
+EXPOSE 5123
+
+# 设置环境变量(如果有需要)
+# ENV MY_VARIABLE=value
+
+# 运行项目
+CMD ["python", "main.py"]

+ 57 - 0
SourceCode/DataMiddleware/docker/docker-compose.yml

@@ -0,0 +1,57 @@
+version: '3.8'
+
+services:
+  tl-mysql:
+    image: mysql:8.0.39
+    container_name: y_tielu-data-middleware-mysql
+    environment:
+      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
+      - MYSQL_DATABASE=${MYSQL_DATABASE}
+      - MYSQL_USER=${MYSQL_USER}
+      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
+      - TZ=Asia/Shanghai
+      # - MYSQL_DEFAULT_AUTHENTICATION_PLUGIN=mysql_native_password
+    volumes:
+      - /home/docker/teilu_data_middleware_v1/mysql/log:/var/log/mysql
+      - /home/docker/teilu_data_middleware_v1/mysql/data:/var/lib/mysql
+      - /etc/localtime:/etc/localtime:ro
+      - /home/docker/teilu_data_middleware_v1/app/init.sql:/docker-entrypoint-initdb.d/init.sql # 挂载 init.sql 文件
+      # - ./.dev/mysql5.7/log:/var/log/mysql
+      # - ./.dev/mysql5.7/data:/var/lib/mysql
+      # - ./.dev/mysql8.0.39/log:/var/log/mysql
+      # - ./.dev/mysql8.0.39/data:/var/lib/mysql
+      # - ./init.sql:/docker-entrypoint-initdb.d/init.sql
+    ports:
+      - '${MYSQL_PORT}:3306'
+    networks:
+      - tielu-data-middleware-net
+    restart: always
+
+  tl-app:
+    build:
+      context: ../
+      dockerfile: .
+    image: y_tielu-data-middleware-app:1.0.0
+    container_name: y_tielu-data-middleware-app
+    depends_on:
+      - tl-mysql
+    environment:
+      - TZ=Asia/Shanghai
+      - APP_MYSQL__HOST=y_tielu-data-middleware-mysql
+      - APP_MYSQL__PORT=3306
+      - APP_MYSQL__DB=${MYSQL_DATABASE}
+      - APP_MYSQL__USER=${MYSQL_USER}
+      - APP_MYSQL__PASSWORD=${MYSQL_PASSWORD}
+    volumes:
+      - /home/docker/teilu_data_middleware_v1/app/config.yml:/app/config.yml
+      - /home/docker/teilu_data_middleware_v1/app/logs:/app/logs
+      - /home/docker/teilu_data_middleware_v1/app/temp_files:/app/temp_files
+    networks:
+      - tielu-data-middleware-net
+    ports:
+       - "7010:5123"
+    restart: always
+
+networks:
+  tielu-data-middleware-net:
+    driver: bridge

+ 4 - 0
SourceCode/DataMiddleware/requirements.txt

@@ -8,3 +8,7 @@ PyPDF2~=3.0.1
 pillow~=11.1.0
 pathlib~=1.0.1
 PyMuPDF~=1.25.3
+cryptography==41.0.4
+openpyxl== 3.1.5
+tabulate==0.9.0
+

+ 32 - 31
SourceCode/DataMiddleware/tools/stores/mysql_store.py

@@ -15,7 +15,7 @@ class MysqlStore:
     def insert_standard(self, data: StandardModel) -> bool:
         try:
             sql = """
-                REPLACE INTO standard_data (book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material, created_at)
+                INSERT INTO standard_data (book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material, created_at)
                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
             """
             with self._db_helper as db:
@@ -41,22 +41,38 @@ class MysqlStore:
     def insert_standard_batch(self, data_list: list[StandardModel]) -> bool:
         try:
             sql = """
-                REPLACE INTO standard_data (book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material, created_at)
+                INSERT INTO standard_data (book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material, created_at)
                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
             """
-            insert_params = [(data.book_number, data.quota_number, data.quota_name, data.work_content, data.unit, data.basic_quota, data.base_price, data.unit_weight, data.labor_cost, data.material_cost, data.machine_cost, data.main_material, datetime.now()) for data in data_list]
-            with self._db_helper as db:
-                db.execute_many(sql, insert_params)
-            return True
+            insert_params = [(
+                data.book_number,
+                data.quota_number,
+                data.quota_name,
+                data.work_content,
+                data.unit,
+                data.basic_quota,
+                data.base_price,
+                data.unit_weight,
+                data.labor_cost,
+                data.material_cost,
+                data.machine_cost,
+                data.main_material,
+                datetime.now()
+            ) for data in data_list]
+            self._db_helper.execute_many(sql, insert_params)
         except Exception as e:
             self._logger.error(f"Error inserting standard batch: {str(e)}")
             return False
-    def query_standard_group_by_book(self) -> dict:
+    def query_standard_group_by_book(self):
         sql = """
-            SELECT book_number
-            FROM standard_data
-            GROUP BY book_number
-            ORDER BY book_number
+            WITH grouped_data AS (
+                SELECT DISTINCT book_number
+                FROM standard_data
+            )
+            SELECT id, book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material
+            FROM standard_data sd
+            INNER JOIN grouped_data gd ON sd.book_number = gd.book_number
+            ORDER BY sd.book_number, sd.quota_number
         """
         with self._db_helper:
             result = self._db_helper.execute_query(sql)
@@ -69,24 +85,7 @@ class MysqlStore:
                 book_number = row['book_number']
                 if book_number not in grouped_data:
                     grouped_data[book_number] = []
-            
-                # 由于只按book_number分组,其他字段无法从这里获取,需要重新查询
-                standards = self.query_standards_by_book_number(book_number)
-                grouped_data[book_number].extend(standards)
-            
-            return grouped_data
-
-    def query_standards_by_book_number(self, book_number: str) -> list[StandardModel]:
-        sql = """
-            SELECT book_number, quota_number, quota_name, work_content, unit, basic_quota, base_price, unit_weight, labor_cost, material_cost, machine_cost, main_material
-            FROM standard_data
-            WHERE book_number = %s
-            ORDER BY quota_number
-        """
-        with self._db_helper:
-            result = self._db_helper.execute_query(sql, (book_number,))
-            standards = []
-            for row in result:
+                
                 standard = StandardModel(
                     book_number=row['book_number'],
                     quota_number=row['quota_number'],
@@ -100,6 +99,8 @@ class MysqlStore:
                     material_cost=row['material_cost'],
                     machine_cost=row['machine_cost'],
                     main_material=row['main_material'],
+                    data_id=row['id']
                 )
-                standards.append(standard)
-            return standards
+                grouped_data[book_number].append(standard)
+            
+            return grouped_data