Browse Source

business_domain优化及bug修复。 cursor自动执行任务脚本优化。

maxiaolong 5 days ago
parent
commit
3fe3c045ef

+ 12 - 12
app/api/business_domain/routes.py

@@ -115,7 +115,7 @@ def bd_list():
         }))
         }))
     except Exception as e:
     except Exception as e:
         logger.error(f"获取业务领域列表失败: {str(e)}")
         logger.error(f"获取业务领域列表失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("获取业务领域列表失败", error=str(e)))
 
 
 
 
 @bp.route('/detail', methods=['POST'])
 @bp.route('/detail', methods=['POST'])
@@ -156,7 +156,7 @@ def bd_detail():
         return jsonify(success(domain_data))
         return jsonify(success(domain_data))
     except Exception as e:
     except Exception as e:
         logger.error(f"获取业务领域详情失败: {str(e)}")
         logger.error(f"获取业务领域详情失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("获取业务领域详情失败", error=str(e)))
 
 
 
 
 @bp.route('/delete', methods=['POST'])
 @bp.route('/delete', methods=['POST'])
@@ -190,7 +190,7 @@ def bd_delete():
             return jsonify(failed("业务领域删除失败"))
             return jsonify(failed("业务领域删除失败"))
     except Exception as e:
     except Exception as e:
         logger.error(f"删除业务领域失败: {str(e)}")
         logger.error(f"删除业务领域失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("删除业务领域失败", error=str(e)))
 
 
 
 
 @bp.route('/save', methods=['POST'])
 @bp.route('/save', methods=['POST'])
@@ -232,7 +232,7 @@ def bd_save():
         return jsonify(success(saved_data))
         return jsonify(success(saved_data))
     except Exception as e:
     except Exception as e:
         logger.error(f"保存业务领域失败: {str(e)}")
         logger.error(f"保存业务领域失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("保存业务领域失败", error=str(e)))
 
 
 
 
 @bp.route('/update', methods=['POST'])
 @bp.route('/update', methods=['POST'])
@@ -267,7 +267,7 @@ def bd_update():
         return jsonify(success(updated_data))
         return jsonify(success(updated_data))
     except Exception as e:
     except Exception as e:
         logger.error(f"更新业务领域失败: {str(e)}")
         logger.error(f"更新业务领域失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("更新业务领域失败", error=str(e)))
 
 
 
 
 @bp.route('/upload', methods=['POST'])
 @bp.route('/upload', methods=['POST'])
@@ -344,7 +344,7 @@ def bd_upload():
         }))
         }))
     except Exception as e:
     except Exception as e:
         logger.error(f"文件上传失败: {str(e)}")
         logger.error(f"文件上传失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("文件上传失败", error=str(e)))
 
 
 
 
 @bp.route('/download', methods=['GET'])
 @bp.route('/download', methods=['GET'])
@@ -400,7 +400,7 @@ def bd_download():
         )
         )
     except Exception as e:
     except Exception as e:
         logger.error(f"文件下载失败: {str(e)}")
         logger.error(f"文件下载失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("文件下载失败", error=str(e)))
     finally:
     finally:
         if response:
         if response:
             response.close()
             response.close()
@@ -448,7 +448,7 @@ def bd_graph_all():
         return jsonify(success(graph_data))
         return jsonify(success(graph_data))
     except Exception as e:
     except Exception as e:
         logger.error(f"获取业务领域图谱失败: {str(e)}")
         logger.error(f"获取业务领域图谱失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("获取业务领域图谱失败", error=str(e)))
 
 
 
 
 @bp.route('/ddlparse', methods=['POST'])
 @bp.route('/ddlparse', methods=['POST'])
@@ -598,7 +598,7 @@ def bd_ddl_parse():
     except Exception as e:
     except Exception as e:
         logger.error(f"解析DDL语句失败: {str(e)}")
         logger.error(f"解析DDL语句失败: {str(e)}")
         logger.error(traceback.format_exc())
         logger.error(traceback.format_exc())
-        return jsonify(failed(str(e)))
+        return jsonify(failed("解析DDL语句失败", error=str(e)))
 
 
 
 
 @bp.route('/search', methods=['POST'])
 @bp.route('/search', methods=['POST'])
@@ -672,7 +672,7 @@ def bd_search():
         }))
         }))
     except Exception as e:
     except Exception as e:
         logger.error(f"业务领域关联元数据搜索失败: {str(e)}")
         logger.error(f"业务领域关联元数据搜索失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("业务领域关联元数据搜索失败", error=str(e)))
 
 
 
 
 @bp.route('/compose', methods=['POST'])
 @bp.route('/compose', methods=['POST'])
@@ -721,7 +721,7 @@ def bd_compose():
         return jsonify(success(response_data))
         return jsonify(success(response_data))
     except Exception as e:
     except Exception as e:
         logger.error(f"组合创建业务领域失败: {str(e)}")
         logger.error(f"组合创建业务领域失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("组合创建业务领域失败", error=str(e)))
 
 
 
 
 @bp.route('/labellist', methods=['POST'])
 @bp.route('/labellist', methods=['POST'])
@@ -777,4 +777,4 @@ def bd_label_list():
         }))
         }))
     except Exception as e:
     except Exception as e:
         logger.error(f"获取标签列表失败: {str(e)}")
         logger.error(f"获取标签列表失败: {str(e)}")
-        return jsonify(failed(str(e)))
+        return jsonify(failed("获取标签列表失败", error=str(e)))

+ 1 - 1
app/api/meta_data/__init__.py

@@ -2,4 +2,4 @@ from flask import Blueprint
 
 
 bp = Blueprint('meta_data', __name__)
 bp = Blueprint('meta_data', __name__)
 
 
-from app.api.meta_data import routes 
+from app.api.meta_data import routes  # noqa: F401, E402

+ 113 - 9
app/core/business_domain/business_domain.py

@@ -305,7 +305,7 @@ def get_business_domain_by_id(domain_id):
                     }
                     }
                 }
                 }
                 parsed_data.append(meta_data)
                 parsed_data.append(meta_data)
-            
+
             domain_data["parsed_data"] = parsed_data
             domain_data["parsed_data"] = parsed_data
             
             
             # 确保所有必需字段都有默认值
             # 确保所有必需字段都有默认值
@@ -317,6 +317,7 @@ def get_business_domain_by_id(domain_id):
                 "data_sensitivity": "",
                 "data_sensitivity": "",
                 "storage_location": "/",
                 "storage_location": "/",
                 "create_time": "",
                 "create_time": "",
+                "update_time": "",
                 "type": "",
                 "type": "",
                 "category": "",
                 "category": "",
                 "url": "",
                 "url": "",
@@ -415,10 +416,11 @@ def save_business_domain(data):
                 "update_time": get_formatted_time()
                 "update_time": get_formatted_time()
             }
             }
 
 
-            # 添加可选字段
+            # 添加可选字段(不包含 parsed_data,它通过关系处理)
             optional_fields = [
             optional_fields = [
                 "describe", "type", "category", "leader",
                 "describe", "type", "category", "leader",
-                "organization", "status", "keywords"
+                "organization", "status", "keywords",
+                "data_sensitivity", "frequency", "url", "storage_location"
             ]
             ]
             for field in optional_fields:
             for field in optional_fields:
                 if data.get(field) is not None:
                 if data.get(field) is not None:
@@ -439,6 +441,49 @@ def save_business_domain(data):
             domain_id = created_node["n"].id
             domain_id = created_node["n"].id
             logger.info(f"成功创建业务领域节点,ID: {domain_id}")
             logger.info(f"成功创建业务领域节点,ID: {domain_id}")
 
 
+            # 处理 parsed_data - 创建 DataMeta 节点和 INCLUDES 关系
+            parsed_data = data.get("parsed_data")
+            if parsed_data and isinstance(parsed_data, list):
+                for item in parsed_data:
+                    if not item.get("name_zh"):
+                        continue
+                    # 创建或获取 DataMeta 节点
+                    meta_cypher = """
+                    MERGE (m:DataMeta {name_zh: $name_zh})
+                    ON CREATE SET m.name_en = $name_en,
+                                m.create_time = $create_time,
+                                m.data_type = $data_type,
+                                m.status = true
+                    ON MATCH SET m.data_type = $data_type,
+                                m.status = true
+                    RETURN m
+                    """
+                    meta_result = session.run(meta_cypher, {
+                        'name_zh': item.get('name_zh', ''),
+                        'name_en': item.get('name_en', ''),
+                        'create_time': get_formatted_time(),
+                        'data_type': item.get('data_type', 'varchar(255)')
+                    })
+                    meta_record = meta_result.single()
+
+                    if meta_record and meta_record["m"]:
+                        meta_id = meta_record["m"].id
+                        # 创建 INCLUDES 关系
+                        rel_cypher = """
+                        MATCH (n:BusinessDomain), (m:DataMeta)
+                        WHERE id(n) = $domain_id AND id(m) = $meta_id
+                        MERGE (n)-[r:INCLUDES]->(m)
+                        RETURN r
+                        """
+                        session.run(rel_cypher, {
+                            'domain_id': domain_id,
+                            'meta_id': meta_id
+                        })
+                        logger.info(
+                            f"创建 BusinessDomain 与 DataMeta 关系: "
+                            f"{domain_id} -> {meta_id}"
+                        )
+
             # 处理标签关系
             # 处理标签关系
             tag_id = data.get("tag")
             tag_id = data.get("tag")
             if tag_id:
             if tag_id:
@@ -525,13 +570,14 @@ def update_business_domain(data):
             raise ValueError(f"业务领域ID不是有效的整数: {domain_id}")
             raise ValueError(f"业务领域ID不是有效的整数: {domain_id}")
         
         
         with neo4j_driver.get_session() as session:
         with neo4j_driver.get_session() as session:
-            # 构建更新字段(过滤掉 id、parsed_data 和 None 值)
+            # 构建更新字段(过滤掉特殊字段和 None 值)
+            # parsed_data 通过 INCLUDES 关系处理,不存储为节点属性
             update_fields = {}
             update_fields = {}
+            excluded = ("id", "tag", "data_source", "parsed_data")
             for key, value in data.items():
             for key, value in data.items():
-                excluded = ("id", "parsed_data", "tag", "data_source")
                 if key not in excluded and value is not None:
                 if key not in excluded and value is not None:
                     update_fields[key] = value
                     update_fields[key] = value
-            
+
             # 添加更新时间
             # 添加更新时间
             update_fields["update_time"] = get_formatted_time()
             update_fields["update_time"] = get_formatted_time()
             
             
@@ -629,11 +675,66 @@ def update_business_domain(data):
                         )
                         )
                     except (ValueError, TypeError):
                     except (ValueError, TypeError):
                         logger.warning(f"数据源ID不是有效的整数: {data_source_id}")
                         logger.warning(f"数据源ID不是有效的整数: {data_source_id}")
-            
+
+            # 处理 parsed_data - 更新 DataMeta 节点和 INCLUDES 关系
+            parsed_data = data.get("parsed_data")
+            if parsed_data is not None:
+                # 先删除旧的 INCLUDES 关系
+                delete_includes_cypher = """
+                MATCH (n:BusinessDomain)-[r:INCLUDES]->(m:DataMeta)
+                WHERE id(n) = $domain_id
+                DELETE r
+                """
+                session.run(
+                    delete_includes_cypher, {'domain_id': domain_id_int}
+                )
+
+                # 创建新的关系
+                if isinstance(parsed_data, list):
+                    for item in parsed_data:
+                        if not item.get("name_zh"):
+                            continue
+                        # 创建或获取 DataMeta 节点
+                        meta_cypher = """
+                        MERGE (m:DataMeta {name_zh: $name_zh})
+                        ON CREATE SET m.name_en = $name_en,
+                                    m.create_time = $create_time,
+                                    m.data_type = $data_type,
+                                    m.status = true
+                        ON MATCH SET m.data_type = $data_type,
+                                    m.status = true
+                        RETURN m
+                        """
+                        meta_result = session.run(meta_cypher, {
+                            'name_zh': item.get('name_zh', ''),
+                            'name_en': item.get('name_en', ''),
+                            'create_time': get_formatted_time(),
+                            'data_type': item.get('data_type', 'varchar(255)')
+                        })
+                        meta_record = meta_result.single()
+
+                        if meta_record and meta_record["m"]:
+                            meta_id = meta_record["m"].id
+                            # 创建 INCLUDES 关系
+                            rel_cypher = """
+                            MATCH (n:BusinessDomain), (m:DataMeta)
+                            WHERE id(n) = $domain_id AND id(m) = $meta_id
+                            MERGE (n)-[r:INCLUDES]->(m)
+                            RETURN r
+                            """
+                            session.run(rel_cypher, {
+                                'domain_id': domain_id_int,
+                                'meta_id': meta_id
+                            })
+                            logger.info(
+                                f"更新 BusinessDomain 与 DataMeta 关系: "
+                                f"{domain_id_int} -> {meta_id}"
+                            )
+
             # 构建返回数据
             # 构建返回数据
             node_data = serialize_node_properties(updated_node["n"])
             node_data = serialize_node_properties(updated_node["n"])
             node_data["id"] = updated_node["n"].id
             node_data["id"] = updated_node["n"].id
-            
+
             logger.info(f"成功更新业务领域,ID: {domain_id_int}")
             logger.info(f"成功更新业务领域,ID: {domain_id_int}")
             return node_data
             return node_data
 
 
@@ -874,12 +975,15 @@ def business_domain_compose(data):
             # 添加可选字段
             # 添加可选字段
             optional_fields = [
             optional_fields = [
                 "describe", "type", "category", "leader",
                 "describe", "type", "category", "leader",
-                "organization", "status", "keywords"
+                "organization", "status", "keywords",
+                "data_sensitivity", "frequency", "url", "storage_location"
             ]
             ]
             for field in optional_fields:
             for field in optional_fields:
                 if data.get(field) is not None:
                 if data.get(field) is not None:
                     node_props[field] = data[field]
                     node_props[field] = data[field]
 
 
+            # 注意: parsed_data 通过 INCLUDES 关系处理,不存储为节点属性
+
             # 构建CREATE语句
             # 构建CREATE语句
             props_str = ", ".join([f"{k}: ${k}" for k in node_props.keys()])
             props_str = ", ".join([f"{k}: ${k}" for k in node_props.keys()])
             cypher = f"""
             cypher = f"""

+ 27 - 16
app/models/result.py

@@ -1,14 +1,15 @@
 from flask import jsonify, make_response
 from flask import jsonify, make_response
 
 
+
 def success(data=None, message="操作成功", code=200):
 def success(data=None, message="操作成功", code=200):
     """
     """
     Return a standardized success response
     Return a standardized success response
-    
+
     Args:
     Args:
         data: The data to return
         data: The data to return
         message: A success message
         message: A success message
         code: HTTP status code
         code: HTTP status code
-        
+
     Returns:
     Returns:
         dict: A standardized success response
         dict: A standardized success response
     """
     """
@@ -18,33 +19,38 @@ def success(data=None, message="操作成功", code=200):
         "data": data
         "data": data
     }
     }
 
 
-def failed(message="操作失败", code=500, data=None):
+
+def failed(message="操作失败", code=500, data=None, error=None):
     """
     """
     Return a standardized error response
     Return a standardized error response
-    
+
     Args:
     Args:
         message: An error message
         message: An error message
         code: HTTP status code
         code: HTTP status code
         data: Optional data to return
         data: Optional data to return
-        
+        error: Detailed error information
+
     Returns:
     Returns:
         dict: A standardized error response
         dict: A standardized error response
     """
     """
-    return {
+    result = {
         "code": code,
         "code": code,
         "message": message,
         "message": message,
-        "success": False,
         "data": data
         "data": data
     }
     }
+    if error is not None:
+        result["error"] = error
+    return result
+
 
 
 def json_response(data, status_code=200):
 def json_response(data, status_code=200):
     """
     """
     Create a JSON response with proper headers
     Create a JSON response with proper headers
-    
+
     Args:
     Args:
         data: The data to return (will be passed to jsonify)
         data: The data to return (will be passed to jsonify)
         status_code: HTTP status code
         status_code: HTTP status code
-        
+
     Returns:
     Returns:
         Flask Response object with proper JSON headers
         Flask Response object with proper JSON headers
     """
     """
@@ -52,30 +58,35 @@ def json_response(data, status_code=200):
     response.headers['Content-Type'] = 'application/json; charset=utf-8'
     response.headers['Content-Type'] = 'application/json; charset=utf-8'
     return response
     return response
 
 
+
 def success_response(data=None, message="操作成功", status_code=200):
 def success_response(data=None, message="操作成功", status_code=200):
     """
     """
     Return a standardized success response with proper headers
     Return a standardized success response with proper headers
-    
+
     Args:
     Args:
         data: The data to return
         data: The data to return
-        message: A success message  
+        message: A success message
         status_code: HTTP status code
         status_code: HTTP status code
-        
+
     Returns:
     Returns:
         Flask Response object with proper JSON headers
         Flask Response object with proper JSON headers
     """
     """
     return json_response(success(data, message, status_code), status_code)
     return json_response(success(data, message, status_code), status_code)
 
 
-def failed_response(message="操作失败", status_code=500, data=None):
+
+def failed_response(message="操作失败", status_code=500, data=None, error=None):
     """
     """
     Return a standardized error response with proper headers
     Return a standardized error response with proper headers
-    
+
     Args:
     Args:
         message: An error message
         message: An error message
         status_code: HTTP status code
         status_code: HTTP status code
         data: Optional data to return
         data: Optional data to return
-        
+        error: Detailed error information
+
     Returns:
     Returns:
         Flask Response object with proper JSON headers
         Flask Response object with proper JSON headers
     """
     """
-    return json_response(failed(message, status_code, data), status_code) 
+    return json_response(
+        failed(message, status_code, data, error), status_code
+    )

+ 966 - 0
docs/business_domain_api.md

@@ -0,0 +1,966 @@
+# 业务领域 API 接口文档
+
+> 本文档为前端开发人员提供业务领域(Business Domain)模块的 API 接口说明。
+
+## 目录
+
+- [通用说明](#通用说明)
+- [接口列表](#接口列表)
+  - [1. 获取业务领域列表](#1-获取业务领域列表)
+  - [2. 获取业务领域详情](#2-获取业务领域详情)
+  - [3. 删除业务领域](#3-删除业务领域)
+  - [4. 保存业务领域](#4-保存业务领域)
+  - [5. 更新业务领域](#5-更新业务领域)
+  - [6. 上传文件](#6-上传文件)
+  - [7. 下载文件](#7-下载文件)
+  - [8. 获取关系图谱](#8-获取关系图谱)
+  - [9. 解析DDL语句](#9-解析ddl语句)
+  - [10. 搜索关联元数据](#10-搜索关联元数据)
+  - [11. 组合创建业务领域](#11-组合创建业务领域)
+  - [12. 获取标签列表](#12-获取标签列表)
+- [错误码说明](#错误码说明)
+
+---
+
+## 通用说明
+
+### 基础URL
+
+```
+/api/bd
+```
+
+### 统一响应格式
+
+**成功响应:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": { ... }
+}
+```
+
+**失败响应:**
+
+```json
+{
+  "code": 500,
+  "message": "错误描述",
+  "data": null,
+  "error": "详细错误信息"
+}
+```
+
+### 请求头
+
+大部分接口需要设置 `Content-Type`:
+
+```
+Content-Type: application/json
+```
+
+文件上传接口需要设置:
+
+```
+Content-Type: multipart/form-data
+```
+
+---
+
+## 接口列表
+
+### 1. 获取业务领域列表
+
+获取业务领域的分页列表,支持多种过滤条件。
+
+**请求URL:** `POST /api/bd/list`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| current | integer | 否 | 1 | 当前页码 |
+| size | integer | 否 | 10 | 每页大小 |
+| name_en | string | 否 | - | 英文名称过滤条件 |
+| name_zh | string | 否 | - | 中文名称过滤条件 |
+| type | string | 否 | "all" | 类型过滤条件,"all"表示不过滤 |
+| category | string | 否 | - | 分类过滤条件 |
+| tag | string | 否 | - | 标签过滤条件 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | integer | 状态码 |
+| message | string | 消息 |
+| data.records | array | 业务领域列表 |
+| data.total | integer | 总数量 |
+| data.size | integer | 每页大小 |
+| data.current | integer | 当前页码 |
+
+**请求示例:**
+
+```javascript
+// JavaScript (fetch)
+const response = await fetch('/api/bd/list', {
+  method: 'POST',
+  headers: {
+    'Content-Type': 'application/json'
+  },
+  body: JSON.stringify({
+    current: 1,
+    size: 10,
+    name_zh: "订单"
+  })
+});
+const data = await response.json();
+```
+
+```javascript
+// JavaScript (axios)
+const { data } = await axios.post('/api/bd/list', {
+  current: 1,
+  size: 10,
+  name_zh: "订单"
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "records": [
+      {
+        "id": 12345,
+        "name_zh": "订单管理",
+        "name_en": "order_management",
+        "type": "table",
+        "category": "业务",
+        "describe": "订单管理业务领域",
+        "create_time": "2024-01-15 10:30:00"
+      }
+    ],
+    "total": 100,
+    "size": 10,
+    "current": 1
+  }
+}
+```
+
+**错误响应:**
+
+```json
+{
+  "code": 500,
+  "message": "获取业务领域列表失败",
+  "data": null,
+  "error": "Database connection failed"
+}
+```
+
+---
+
+### 2. 获取业务领域详情
+
+根据ID获取单个业务领域的详细信息。
+
+**请求URL:** `POST /api/bd/detail`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| id | integer | 是 | 业务领域节点ID |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | integer | 状态码 |
+| message | string | 消息 |
+| data | object | 业务领域详情对象 |
+
+**请求示例:**
+
+```javascript
+// JavaScript (fetch)
+const response = await fetch('/api/bd/detail', {
+  method: 'POST',
+  headers: {
+    'Content-Type': 'application/json'
+  },
+  body: JSON.stringify({
+    id: 12345
+  })
+});
+const data = await response.json();
+```
+
+```javascript
+// JavaScript (axios)
+const { data } = await axios.post('/api/bd/detail', {
+  id: 12345
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "id": 12345,
+    "name_zh": "订单管理",
+    "name_en": "order_management",
+    "type": "table",
+    "category": "业务",
+    "describe": "订单管理业务领域",
+    "tag": 100,
+    "data_source": 200,
+    "create_time": "2024-01-15 10:30:00",
+    "update_time": "2024-01-16 14:20:00"
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | 业务领域ID不能为空 | id参数缺失 |
+| 500 | 业务领域ID必须为整数 | id格式错误 |
+| 500 | 业务领域不存在 | 找不到对应记录 |
+
+---
+
+### 3. 删除业务领域
+
+根据ID删除业务领域。
+
+**请求URL:** `POST /api/bd/delete`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| id | integer | 是 | 业务领域节点ID |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | integer | 状态码 |
+| message | string | 消息 |
+| data.message | string | 删除结果描述 |
+
+**请求示例:**
+
+```javascript
+// JavaScript (axios)
+const { data } = await axios.post('/api/bd/delete', {
+  id: 12345
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "message": "业务领域删除成功"
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | 业务领域ID不能为空 | id参数缺失 |
+| 500 | 业务领域删除失败 | 删除操作失败 |
+
+---
+
+### 4. 保存业务领域
+
+保存业务领域,支持新建和更新两种模式。
+
+**请求URL:** `POST /api/bd/save`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| id | integer | 否 | 业务领域ID(有则更新,无则新建) |
+| name_zh | string | 新建时必填 | 中文名称 |
+| name_en | string | 新建时必填 | 英文名称 |
+| describe | string | 否 | 描述 |
+| type | string | 否 | 类型 |
+| category | string | 否 | 分类 |
+| tag | integer | 否 | 标签ID |
+| data_source | integer | 否 | 数据源ID |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| code | integer | 状态码 |
+| message | string | 消息 |
+| data | object | 保存后的业务领域数据 |
+
+**请求示例:**
+
+```javascript
+// 新建
+const { data } = await axios.post('/api/bd/save', {
+  name_zh: "客户管理",
+  name_en: "customer_management",
+  type: "table",
+  category: "业务",
+  describe: "客户信息管理业务领域"
+});
+
+// 更新
+const { data } = await axios.post('/api/bd/save', {
+  id: 12345,
+  describe: "更新后的描述"
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "id": 12346,
+    "name_zh": "客户管理",
+    "name_en": "customer_management",
+    "type": "table",
+    "category": "业务",
+    "describe": "客户信息管理业务领域",
+    "create_time": "2024-01-17 09:00:00"
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | 新建时 name_zh 和 name_en 为必填项 | 新建时缺少必填字段 |
+
+---
+
+### 5. 更新业务领域
+
+更新已存在的业务领域。
+
+**请求URL:** `POST /api/bd/update`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| id | integer | 是 | 业务领域节点ID |
+| name_zh | string | 否 | 中文名称 |
+| name_en | string | 否 | 英文名称 |
+| describe | string | 否 | 描述 |
+| tag | integer | 否 | 标签ID |
+| data_source | integer | 否 | 数据源ID |
+
+**请求示例:**
+
+```javascript
+const { data } = await axios.post('/api/bd/update', {
+  id: 12345,
+  name_zh: "订单管理系统",
+  describe: "更新后的描述信息"
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "id": 12345,
+    "name_zh": "订单管理系统",
+    "name_en": "order_management",
+    "describe": "更新后的描述信息",
+    "update_time": "2024-01-17 10:30:00"
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 参数不完整 | 未提供id参数 |
+
+---
+
+### 6. 上传文件
+
+上传业务领域相关文件到对象存储。
+
+**请求URL:** `POST /api/bd/upload`
+
+**Content-Type:** `multipart/form-data`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| file | File | 是 | 上传的文件 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| data.filename | string | 原始文件名 |
+| data.size | integer | 文件大小(字节) |
+| data.type | string | 文件类型(扩展名) |
+| data.url | string | 文件存储路径 |
+
+**请求示例:**
+
+```javascript
+// JavaScript (FormData)
+const formData = new FormData();
+formData.append('file', fileInput.files[0]);
+
+const response = await fetch('/api/bd/upload', {
+  method: 'POST',
+  body: formData
+});
+const data = await response.json();
+```
+
+```javascript
+// JavaScript (axios)
+const formData = new FormData();
+formData.append('file', file);
+
+const { data } = await axios.post('/api/bd/upload', formData, {
+  headers: {
+    'Content-Type': 'multipart/form-data'
+  }
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "filename": "schema.sql",
+    "size": 2048,
+    "type": "sql",
+    "url": "business_domain/schema_20240117103000.sql"
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 没有找到上传的文件 | 请求中无file字段 |
+| 500 | 未选择文件 | 文件名为空 |
+| 500 | 不支持的文件类型 | 文件扩展名不在允许列表中 |
+
+---
+
+### 7. 下载文件
+
+下载业务领域相关文件。
+
+**请求URL:** `GET /api/bd/download`
+
+**请求参数(Query String):**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| url | string | 是 | 文件存储路径(从上传接口返回) |
+
+**返回:**
+
+成功时返回文件流(作为附件下载),失败时返回 JSON 错误信息。
+
+**请求示例:**
+
+```javascript
+// 方式1:直接打开链接下载
+window.open('/api/bd/download?url=business_domain/schema_20240117103000.sql');
+
+// 方式2:使用fetch下载
+const response = await fetch('/api/bd/download?url=' + encodeURIComponent(fileUrl));
+if (response.ok) {
+  const blob = await response.blob();
+  const url = window.URL.createObjectURL(blob);
+  const a = document.createElement('a');
+  a.href = url;
+  a.download = 'filename.sql';
+  a.click();
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 文件路径不能为空 | 未提供url参数 |
+| 500 | 文件获取失败 | MinIO获取文件失败 |
+
+---
+
+### 8. 获取关系图谱
+
+获取业务领域的完整关系图谱数据,用于可视化展示。
+
+**请求URL:** `POST /api/bd/graphall`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| id | integer | 是 | - | 业务领域节点ID |
+| meta | boolean | 否 | true | 是否包含元数据节点 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| data.nodes | array | 节点列表 |
+| data.lines | array | 关系列表 |
+
+**请求示例:**
+
+```javascript
+const { data } = await axios.post('/api/bd/graphall', {
+  id: 12345,
+  meta: true
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "nodes": [
+      {
+        "id": 12345,
+        "name": "订单管理",
+        "type": "BusinessDomain",
+        "properties": { ... }
+      },
+      {
+        "id": 12346,
+        "name": "order_id",
+        "type": "MetaData",
+        "properties": { ... }
+      }
+    ],
+    "lines": [
+      {
+        "from": 12345,
+        "to": 12346,
+        "type": "HAS_METADATA",
+        "properties": { ... }
+      }
+    ]
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | 业务领域ID不能为空 | id参数缺失 |
+| 500 | 业务领域ID必须为整数 | id格式错误 |
+
+---
+
+### 9. 解析DDL语句
+
+解析 SQL DDL 语句,提取表结构信息,用于快速创建业务领域。
+
+**请求URL:** `POST /api/bd/ddlparse`
+
+**请求方式:**
+
+支持两种方式提交 DDL:
+
+1. **文件上传方式** (`multipart/form-data`)
+2. **JSON方式** (`application/json`)
+
+**请求参数(文件上传方式):**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| file | File | 是 | SQL文件(.sql扩展名) |
+
+**请求参数(JSON方式):**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| sql | string | 是 | SQL DDL 内容 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| data | array | 解析后的DDL列表,包含表信息和字段信息 |
+| data[].table_info | object | 表信息 |
+| data[].columns | array | 字段列表 |
+| data[].exist | boolean | 表是否已存在于系统中 |
+
+**请求示例:**
+
+```javascript
+// 方式1:文件上传
+const formData = new FormData();
+formData.append('file', sqlFile);
+
+const { data } = await axios.post('/api/bd/ddlparse', formData, {
+  headers: {
+    'Content-Type': 'multipart/form-data'
+  }
+});
+
+// 方式2:JSON提交
+const { data } = await axios.post('/api/bd/ddlparse', {
+  sql: `
+    CREATE TABLE orders (
+      id INT PRIMARY KEY,
+      customer_id INT,
+      amount DECIMAL(10,2),
+      create_time DATETIME
+    );
+  `
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": [
+    {
+      "table_info": {
+        "name_en": "orders",
+        "name_zh": "订单表",
+        "comment": "订单信息表"
+      },
+      "columns": [
+        {
+          "name_en": "id",
+          "name_zh": "主键",
+          "data_type": "INT",
+          "is_primary": true,
+          "comment": "主键ID"
+        },
+        {
+          "name_en": "customer_id",
+          "name_zh": "客户ID",
+          "data_type": "INT",
+          "is_primary": false,
+          "comment": "关联客户ID"
+        }
+      ],
+      "exist": false
+    }
+  ]
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 只接受SQL文件 | 上传的文件不是.sql格式 |
+| 500 | SQL内容不能为空 | 未提供SQL内容 |
+| 500 | 未找到有效的CREATE TABLE语句 | DDL解析失败 |
+
+---
+
+### 10. 搜索关联元数据
+
+搜索与业务领域关联的元数据列表。
+
+**请求URL:** `POST /api/bd/search`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| id | integer | 是 | - | 业务领域节点ID |
+| current | integer | 否 | 1 | 当前页码 |
+| size | integer | 否 | 10 | 每页大小 |
+| name_en | string | 否 | - | 英文名称过滤条件 |
+| name_zh | string | 否 | - | 中文名称过滤条件 |
+| category | string | 否 | - | 分类过滤条件 |
+| tag | string | 否 | - | 标签过滤条件 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| data.records | array | 元数据列表 |
+| data.total | integer | 总数量 |
+| data.size | integer | 每页大小 |
+| data.current | integer | 当前页码 |
+
+**请求示例:**
+
+```javascript
+const { data } = await axios.post('/api/bd/search', {
+  id: 12345,
+  current: 1,
+  size: 20,
+  name_zh: "订单"
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "records": [
+      {
+        "id": 23456,
+        "name_zh": "订单ID",
+        "name_en": "order_id",
+        "data_type": "INT",
+        "category": "主键",
+        "create_time": "2024-01-15 10:30:00"
+      }
+    ],
+    "total": 50,
+    "size": 20,
+    "current": 1
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | 业务领域ID不能为空 | id参数缺失 |
+| 500 | 业务领域ID必须为整数 | id格式错误 |
+
+---
+
+### 11. 组合创建业务领域
+
+从已有的业务领域和元数据中组合创建新的业务领域。
+
+**请求URL:** `POST /api/bd/compose`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| name_zh | string | 是 | 中文名称 |
+| name_en | string | 否 | 英文名称(不提供则自动翻译) |
+| id_list | array | 是 | 关联的业务领域和元数据列表 |
+| describe | string | 否 | 描述 |
+| type | string | 否 | 类型 |
+| category | string | 否 | 分类 |
+| tag | integer | 否 | 标签ID |
+| data_source | integer | 否 | 数据源ID |
+
+**id_list 格式:**
+
+```json
+[
+  {
+    "domain_id": 123,
+    "metaData": [
+      { "id": 456 },
+      { "id": 789 }
+    ]
+  }
+]
+```
+
+**请求示例:**
+
+```javascript
+const { data } = await axios.post('/api/bd/compose', {
+  name_zh: "销售分析域",
+  name_en: "sales_analysis",
+  describe: "整合订单和客户数据的销售分析业务领域",
+  id_list: [
+    {
+      domain_id: 12345,
+      metaData: [
+        { id: 23456 },
+        { id: 23457 }
+      ]
+    },
+    {
+      domain_id: 12346,
+      metaData: [
+        { id: 23458 }
+      ]
+    }
+  ]
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "business_domain": {
+      "id": 12350,
+      "name_zh": "销售分析域",
+      "name_en": "sales_analysis",
+      "describe": "整合订单和客户数据的销售分析业务领域",
+      "create_time": "2024-01-17 11:00:00"
+    }
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+| 500 | name_zh 为必填项 | 缺少中文名称 |
+| 500 | id_list 为必填项 | 缺少关联列表 |
+
+---
+
+### 12. 获取标签列表
+
+获取可用于业务领域关联的数据标签列表。
+
+**请求URL:** `POST /api/bd/labellist`
+
+**请求参数:**
+
+| 参数名 | 类型 | 必填 | 默认值 | 说明 |
+|--------|------|------|--------|------|
+| current | integer | 否 | 1 | 当前页码 |
+| size | integer | 否 | 10 | 每页大小 |
+| name_en | string | 否 | - | 英文名称过滤条件 |
+| name_zh | string | 否 | - | 中文名称过滤条件 |
+| category | string | 否 | - | 分类过滤条件 |
+| group | string | 否 | - | 分组过滤条件 |
+
+**返回参数:**
+
+| 参数名 | 类型 | 说明 |
+|--------|------|------|
+| data.records | array | 标签列表 |
+| data.total | integer | 总数量 |
+| data.size | integer | 每页大小 |
+| data.current | integer | 当前页码 |
+
+**请求示例:**
+
+```javascript
+const { data } = await axios.post('/api/bd/labellist', {
+  current: 1,
+  size: 20,
+  category: "业务标签"
+});
+```
+
+**返回示例:**
+
+```json
+{
+  "code": 200,
+  "message": "操作成功",
+  "data": {
+    "records": [
+      {
+        "id": 100,
+        "name_zh": "核心业务",
+        "name_en": "core_business",
+        "category": "业务标签",
+        "group": "优先级",
+        "create_time": "2024-01-10 09:00:00"
+      }
+    ],
+    "total": 30,
+    "size": 20,
+    "current": 1
+  }
+}
+```
+
+**错误码:**
+
+| code | message | 说明 |
+|------|---------|------|
+| 500 | 请求数据不能为空 | 未提供请求体 |
+
+---
+
+## 错误码说明
+
+### 通用错误码
+
+| code | 说明 |
+|------|------|
+| 200 | 请求成功 |
+| 500 | 服务器内部错误 |
+
+### 常见错误消息
+
+| message | 说明 | 解决方案 |
+|---------|------|----------|
+| 请求数据不能为空 | 未提供请求体或请求体为空 | 确保发送有效的JSON请求体 |
+| 业务领域ID不能为空 | 必填参数id缺失 | 添加id参数 |
+| 业务领域ID必须为整数 | id参数类型错误 | 确保id为整数类型 |
+| 业务领域不存在 | 指定ID的记录不存在 | 检查ID是否正确 |
+| 参数不完整 | 缺少必填参数 | 检查请求参数完整性 |
+| 新建时 name_zh 和 name_en 为必填项 | 新建时缺少必填字段 | 添加name_zh和name_en参数 |
+| 不支持的文件类型 | 上传文件类型不在允许列表中 | 检查文件扩展名 |
+
+---
+
+## 更新日志
+
+| 版本 | 日期 | 更新内容 |
+|------|------|----------|
+| 1.0.0 | 2025-12-06 | 初始版本 |
+

+ 121 - 156
scripts/AUTO_TASKS_使用说明.md

@@ -1,237 +1,202 @@
-# 自动任务执行脚本 - Windows使用说明
+# 自动任务执行脚本 - 使用说明
 
 
 ## 🚀 快速开始
 ## 🚀 快速开始
 
 
-### 方式1:前台运行(可以看到实时输出)⭐推荐
+### 推荐方式:使用启动器
 
 
-双击运行:
-```
-scripts\start_auto_tasks.bat
-```
-
-或者命令行:
-```cmd
-cd scripts
-start_auto_tasks.bat
-```
-
-**特点**:
-- ✅ 可以看到实时日志输出
-- ✅ 按 Ctrl+C 可以停止
-- ⚠️ 关闭窗口会停止服务
-
----
-
-### 方式2:后台运行(无窗口)⭐推荐生产环境
+双击运行启动器脚本,根据菜单选择运行模式:
 
 
-双击运行:
 ```
 ```
-scripts\start_auto_tasks_background.bat
+scripts\start_task_scheduler.bat
 ```
 ```
 
 
-**特点**:
-- ✅ 在后台运行,不占用窗口
-- ✅ 日志输出到 `logs\auto_execute.log`
-- ✅ 关闭命令行窗口后仍继续运行
-- ⚠️ 需要手动停止进程
+启动器支持以下模式:
+1. **前台运行** - 可以看到实时日志,按 Ctrl+C 停止
+2. **后台运行** - 无窗口运行,日志输出到文件
+3. **执行一次** - 只检查一次 pending 任务
+4. **前台运行 + 自动 Chat** - 自动向 Cursor 发送任务提醒
+5. **后台运行 + 自动 Chat** - 后台运行并自动发送 Chat
+6. **查看服务状态** - 检查进程状态和日志
+7. **停止服务** - 停止后台运行的服务
 
 
 ---
 ---
 
 
-### 方式3:命令行直接运行
+### 命令行方式
 
 
 ```cmd
 ```cmd
-cd G:\code-lab\DataOps-platform
-python scripts\auto_execute_tasks.py
+cd G:\code-lab\DataOps-platform-new
+python scripts\auto_execute_tasks.py [选项]
 ```
 ```
 
 
-参数说明:
-- 默认:每5分钟检查一次(300秒)
-- `--once`:只执行一次检查,不持续运行
-- `--interval N`:设置检查间隔(秒)
+**可用选项:**
+
+| 选项 | 说明 |
+|------|------|
+| `--once` | 只执行一次检查,不循环 |
+| `--interval N` | 设置检查间隔(秒),默认 300 |
+| `--enable-chat` | 启用自动 Cursor Chat |
+| `--chat-input-pos "x,y"` | 指定 Chat 输入框位置 |
+| `--chat-message "消息"` | 自定义 Chat 消息内容 |
+
+**示例:**
 
 
-示例:
 ```cmd
 ```cmd
 # 执行一次
 # 执行一次
 python scripts\auto_execute_tasks.py --once
 python scripts\auto_execute_tasks.py --once
 
 
-# 每10分钟检查一次
+# 每 10 分钟检查一次
 python scripts\auto_execute_tasks.py --interval 600
 python scripts\auto_execute_tasks.py --interval 600
-```
-
----
-
-## 📋 管理命令
 
 
-### 检查服务状态
+# 启用自动 Chat
+python scripts\auto_execute_tasks.py --enable-chat --chat-input-pos "1180,965"
 
 
-双击运行:
-```
-scripts\check_auto_tasks.bat
+# 完整示例
+python scripts\auto_execute_tasks.py --interval 300 --enable-chat --chat-input-pos "1180,965"
 ```
 ```
 
 
-或者命令行:
-```cmd
-cd scripts
-check_auto_tasks.bat
-```
-
-**功能**:
-- ✅ 检查进程是否在运行
-- ✅ 显示进程ID和启动时间
-- ✅ 显示最近20行日志
-- ✅ 执行一次手动检查
-
----
-
-### 停止服务
-
-双击运行:
-```
-scripts\stop_auto_tasks.bat
-```
-
-或者命令行:
-```cmd
-cd scripts
-stop_auto_tasks.bat
-```
-
-**说明**:
-- 自动查找并停止所有 `auto_execute_tasks.py` 进程
-- 如果无法停止,可以在任务管理器中手动结束 Python 进程
-
 ---
 ---
 
 
 ## 📊 工作流程
 ## 📊 工作流程
 
 
 ```
 ```
-1. 脚本启动
-   ↓
-2. 连接数据库(从 mcp-servers/task-manager/config.json 读取配置)
-   ↓
-3. 查询所有 status = 'pending' 的任务
-   ↓
-4. 如果有pending任务:
-   - 打印任务详情(供Cursor识别)
-   - 创建 .cursor/pending_tasks.json 通知文件
-   ↓
-5. 等待指定间隔时间(默认5分钟)
-   ↓
-6. 重复步骤3-5
+┌─────────────────────────────────────────────────────────────┐
+│                    auto_execute_tasks.py                    │
+└─────────────────────────────┬───────────────────────────────┘
+                              │
+      ┌───────────────────────┼───────────────────────┐
+      ▼                       ▼                       ▼
+┌─────────────┐      ┌─────────────────┐      ┌──────────────┐
+│ 1. 同步完成 │      │ 2. 获取pending  │      │ 3. 生成文件  │
+│    状态     │      │    任务         │      │              │
+└─────────────┘      └─────────────────┘      └──────────────┘
+      │                       │                       │
+      │                       ▼                       │
+      │              ┌─────────────────┐              │
+      │              │ 4. 创建任务文件 │              │
+      │              │    更新状态为   │              │
+      │              │    processing   │              │
+      │              └─────────────────┘              │
+      │                       │                       │
+      │                       ▼                       │
+      │        ┌──────────────────────────────┐       │
+      │        │ 5. 写入 pending_tasks.json   │       │
+      │        │ 6. 生成 instructions.md      │       │
+      │        └──────────────────────────────┘       │
+      │                       │                       │
+      │                       ▼                       │
+      │        ┌──────────────────────────────┐       │
+      │        │ 7. (可选) 发送 Cursor Chat   │       │
+      │        └──────────────────────────────┘       │
+      │                       │                       │
+      └───────────────────────┼───────────────────────┘
+                              ▼
+                    ┌─────────────────┐
+                    │  等待下一次检查  │
+                    └─────────────────┘
 ```
 ```
 
 
 ---
 ---
 
 
-## 📝 日志文件
-
-### 前台运行
-- 日志直接输出到控制台
-
-### 后台运行
-- 日志文件:`logs\auto_execute.log`
+## 📁 生成的文件
 
 
-查看日志:
-```cmd
-# 查看全部日志
-type logs\auto_execute.log
-
-# 查看最后50行
-powershell "Get-Content logs\auto_execute.log -Tail 50"
-```
+| 文件 | 说明 |
+|------|------|
+| `.cursor/pending_tasks.json` | 待处理任务列表(JSON 格式) |
+| `.cursor/task_execute_instructions.md` | Cursor 执行指令文件 |
+| `.cursor/task_trigger.txt` | 触发器标记文件 |
+| `logs/auto_execute.log` | 日志文件(后台模式) |
+| `app/core/data_flow/*.py` | 任务占位文件(可配置路径) |
 
 
 ---
 ---
 
 
 ## ⚙️ 配置说明
 ## ⚙️ 配置说明
 
 
 ### 数据库配置
 ### 数据库配置
+
 编辑文件:`mcp-servers/task-manager/config.json`
 编辑文件:`mcp-servers/task-manager/config.json`
 
 
 ```json
 ```json
 {
 {
   "database": {
   "database": {
-    "uri": "postgresql://postgres:dataOps@192.168.3.143:5432/dataops"
+    "uri": "postgresql://user:password@host:5432/database"
   }
   }
 }
 }
 ```
 ```
 
 
-### 检查间隔
-默认:300秒(5分钟)
+### 自动 Chat 配置
+
+启用自动 Chat 需要安装以下依赖:
+
+```cmd
+pip install pywin32 pyautogui pyperclip
+```
 
 
-修改方式:
-1. 编辑批处理文件中的 `--interval 300` 参数
-2. 或直接命令行运行:`python scripts\auto_execute_tasks.py --interval 600`
+**获取 Chat 输入框位置:**
+1. 打开 Cursor 并显示 Chat 面板
+2. 将鼠标移动到 Chat 输入框
+3. 记录鼠标坐标(可使用屏幕坐标工具)
+4. 使用 `--chat-input-pos "x,y"` 参数指定
 
 
 ---
 ---
 
 
 ## 🔍 故障排查
 ## 🔍 故障排查
 
 
-### 问题1:脚本无法启动
+### 问题 1:脚本无法启动
 
 
-**检查**
-1. Python是否安装:`python --version`
-2. psycopg2是否安装:`pip show psycopg2-binary`
+**检查**
+1. Python 是否安装:`python --version`
+2. psycopg2 是否安装:`pip show psycopg2-binary`
 3. 如果未安装:`pip install psycopg2-binary`
 3. 如果未安装:`pip install psycopg2-binary`
 
 
-### 问题2:无法连接数据库
+### 问题 2:无法连接数据库
 
 
-**检查**
-1. PostgreSQL服务是否运行
+**检查**
+1. PostgreSQL 服务是否运行
 2. `mcp-servers/task-manager/config.json` 配置是否正确
 2. `mcp-servers/task-manager/config.json` 配置是否正确
 3. 网络连接是否正常
 3. 网络连接是否正常
 
 
-### 问题3:找不到pending任务
+### 问题 3:自动 Chat 不工作
 
 
-**检查**:
-1. 数据库中是否有 status = 'pending' 的任务
-2. 执行一次手动检查:`python scripts\auto_execute_tasks.py --once`
+**检查:**
+1. 是否安装 GUI 依赖:`pip install pywin32 pyautogui pyperclip`
+2. Cursor 窗口是否打开
+3. Chat 输入框位置是否正确
 
 
-### 问题4:进程无法停止
+### 问题 4:进程无法停止
 
 
-**解决方法**:
-1. 打开任务管理器(Ctrl+Shift+Esc)
-2. 找到 python.exe 进程
-3. 右键 → 结束任务
-
-或使用PowerShell:
-```powershell
-Get-Process python | Where-Object {$_.Path -like "*DataOps-platform*"} | Stop-Process -Force
-```
+**解决方法:**
+1. 运行启动器选择 7 停止服务
+2. 或打开任务管理器手动结束 Python 进程
 
 
 ---
 ---
 
 
-## 💡 使用建议
+## 📝 日志说明
 
 
-### 开发环境
-- 使用 **方式1(前台运行)**
-- 可以看到实时日志,方便调试
-
-### 生产环境
-- 使用 **方式2(后台运行)**
-- 设置为Windows服务或开机自启动
-
-### 测试
-- 使用 `--once` 参数执行一次检查
-- 或使用 `check_auto_tasks.bat` 查看状态
+### 前台运行
+- 日志直接输出到控制台
 
 
----
+### 后台运行
+- 日志文件:`logs\auto_execute.log`
 
 
-## 📞 相关文档
+查看日志:
+```cmd
+# 查看全部日志
+type logs\auto_execute.log
 
 
-- **完整文档**:`docs/CURSOR_AUTO_TASK_EXECUTION.md`
-- **快速开始**:`docs/TASK_EXECUTION_QUICK_START.md`
-- **实施总结**:`CURSOR_TASK_AUTOMATION_SUMMARY.md`
+# 查看最后 50 行
+powershell "Get-Content logs\auto_execute.log -Tail 50"
+```
 
 
 ---
 ---
 
 
-## ✅ 当前状态
+## 📞 相关文件
 
 
-**脚本已启动**:✅ 后台运行中
-**检查间隔**:5分钟(300秒)
-**日志位置**:`logs\auto_execute.log`(后台模式)或控制台(前台模式)
+| 文件 | 说明 |
+|------|------|
+| `scripts/auto_execute_tasks.py` | 核心调度脚本 |
+| `scripts/start_task_scheduler.bat` | 启动器脚本 |
+| `mcp-servers/task-manager/config.json` | 数据库配置 |
 
 
 ---
 ---
 
 
 **祝您使用愉快!🚀**
 **祝您使用愉快!🚀**
-
-
-

+ 0 - 164
scripts/CURSOR_AUTO_CHAT_README.md

@@ -1,164 +0,0 @@
-# Cursor自动聊天工具使用说明
-
-## 功能概述
-
-这个工具可以自动查找Windows系统中运行的Cursor程序,定位到chat窗口,并自动发送消息。
-
-### 主要功能
-
-1. ✅ 查找Windows操作系统中运行的Cursor程序
-2. ✅ 找到当前运行Cursor实例,并定位到当前的chat窗口
-3. ✅ 模拟鼠标点击到chat窗口
-4. ✅ 模拟键盘输入"请检查并执行所有待处理任务。"到chat窗口
-5. ✅ 模拟鼠标点击chat窗口的"提交"按钮
-6. ✅ 以服务方式持续运行,间隔300秒进行一次上述操作
-
-## 安装依赖
-
-首先需要安装必要的Python依赖包:
-
-```bash
-pip install pywin32 pyautogui pywinauto psutil
-```
-
-或者使用项目的requirements.txt:
-
-```bash
-pip install -r requirements.txt
-```
-
-## 使用方法
-
-### 方法1: 使用批处理脚本(推荐)
-
-#### 前台运行(可以看到日志输出)
-双击运行:`scripts/start_cursor_auto_chat.bat`
-
-#### 后台运行(静默运行)
-双击运行:`scripts/start_cursor_auto_chat_background.bat`
-
-#### 停止工具
-双击运行:`scripts/stop_cursor_auto_chat.bat`
-
-### 方法2: 命令行运行
-
-#### 单次执行(只执行一次,不循环)
-```bash
-python scripts/cursor_auto_chat.py --once
-```
-
-#### 守护进程模式(默认,持续运行)
-```bash
-python scripts/cursor_auto_chat.py --daemon
-```
-
-#### 自定义执行间隔(秒)
-```bash
-python scripts/cursor_auto_chat.py --interval 300
-```
-
-#### 自定义消息内容
-```bash
-python scripts/cursor_auto_chat.py --message "你的自定义消息"
-```
-
-#### 组合使用
-```bash
-python scripts/cursor_auto_chat.py --daemon --interval 600 --message "请执行待处理任务"
-```
-
-## 参数说明
-
-| 参数 | 说明 | 默认值 |
-|------|------|--------|
-| `--once` | 只执行一次,不持续运行 | - |
-| `--daemon` | 作为守护进程运行(持续运行) | 默认模式 |
-| `--interval` | 执行间隔(秒) | 300(5分钟) |
-| `--message` | 要发送的消息内容 | "请检查并执行所有待处理任务。" |
-
-## 日志文件
-
-工具运行时会生成日志文件:
-- 位置:`logs/cursor_auto_chat.log`
-- 格式:包含时间戳、日志级别和详细信息
-
-## 工作原理
-
-1. **查找Cursor进程**:使用`psutil`或`win32api`查找所有运行的Cursor进程
-2. **定位窗口**:通过窗口标题和类名查找Cursor主窗口
-3. **激活窗口**:将Cursor窗口置于前台
-4. **定位Chat输入框**:
-   - 使用快捷键(Ctrl+L)打开chat窗口
-   - 尝试点击可能的输入区域
-   - 使用Tab键导航到输入框
-5. **输入消息**:模拟键盘输入,模拟真实打字速度
-6. **提交消息**:按Enter键提交消息
-
-## 注意事项
-
-### 安全设置
-
-- **紧急停止**:将鼠标快速移动到屏幕左上角可以触发紧急停止(pyautogui的FAILSAFE功能)
-- **操作间隔**:每个操作之间有0.5秒的暂停,避免操作过快
-
-### 使用限制
-
-1. **Cursor必须正在运行**:工具需要Cursor程序处于运行状态
-2. **窗口可见**:Cursor窗口不能被完全遮挡
-3. **管理员权限**:某些情况下可能需要管理员权限来访问其他进程的窗口
-4. **屏幕分辨率**:如果使用固定坐标定位,可能在不同分辨率下需要调整
-
-### 故障排除
-
-#### 问题1: 找不到Cursor进程
-- 确保Cursor正在运行
-- 检查Cursor进程名称是否正确(可能因版本而异)
-
-#### 问题2: 无法定位chat窗口
-- 尝试手动打开Cursor的chat窗口(Ctrl+L)
-- 检查Cursor窗口是否被其他窗口遮挡
-- 可能需要调整输入框定位的坐标
-
-#### 问题3: 消息未发送成功
-- 检查输入框是否已激活(应该有光标闪烁)
-- 尝试手动点击chat输入框后再运行工具
-- 检查Enter键是否被其他程序拦截
-
-#### 问题4: 权限错误
-- 尝试以管理员身份运行
-- 检查Windows用户账户控制(UAC)设置
-
-## 开发说明
-
-### 代码结构
-
-- `CursorAutoChat`类:主要的工具类
-  - `find_cursor_processes()`: 查找Cursor进程
-  - `find_cursor_window()`: 查找Cursor窗口
-  - `activate_cursor_window()`: 激活窗口
-  - `find_chat_input_area()`: 定位chat输入区域
-  - `send_message()`: 发送消息
-  - `click_submit_button()`: 点击提交按钮
-  - `execute_once()`: 执行一次完整流程
-  - `run_daemon()`: 守护进程模式运行
-
-### 扩展功能
-
-如果需要扩展功能,可以修改以下部分:
-
-1. **消息内容**:修改`--message`参数或默认消息
-2. **执行间隔**:修改`--interval`参数
-3. **定位策略**:在`find_chat_input_area()`方法中添加更多定位策略
-4. **快捷键**:根据Cursor版本调整快捷键(Ctrl+L, Ctrl+K等)
-
-## 版本历史
-
-- **v1.0.0** (2025-11-29)
-  - 初始版本
-  - 实现基本的自动聊天功能
-  - 支持单次执行和守护进程模式
-
-## 许可证
-
-本项目遵循项目主许可证。
-

+ 293 - 524
scripts/auto_execute_tasks.py

@@ -1,26 +1,19 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
 """
 """
-自动任务执行 + Cursor Chat 工具(合并版)
+自动任务执行核心调度脚本(简化版)
 
 
-本脚本整合了原来的:
-- auto_execute_tasks.py:检查数据库中的 pending 任务,生成本地任务文件和 pending_tasks.json
-- cursor_auto_chat.py:在 Windows 下自动操作 Cursor Chat,发送指定消息
-
-合并后,一个脚本即可完成:
-1. 从 task_list 中读取 pending 任务
-2. 为任务生成本地 Python 占位文件
-3. 维护 .cursor/pending_tasks.json
-4. (可选)在 Cursor Chat 中自动发起「请检查并执行所有待处理任务。」
-5. 将 .cursor/pending_tasks.json 中 status=completed 的任务状态同步到 task_list
+工作流程:
+1. 从 PostgreSQL 数据库 task_list 表中读取 pending 任务
+2. 生成 .cursor/task_execute_instructions.md 执行指令文件
+3. 更新任务状态为 processing,并维护 .cursor/pending_tasks.json
+4. (可选)向 Cursor Chat 发送执行提醒
+5. Cursor 完成任务后,将 pending_tasks.json 中的状态改为 completed
+6. 调度脚本将 completed 状态的任务同步回数据库
 
 
 使用方式:
 使用方式:
-1. 仅任务调度(不发 Chat):
-   python scripts/auto_execute_tasks.py --once
-   python scripts/auto_execute_tasks.py --interval 300
-
-2. 任务调度 + 自动 Chat:
-   python scripts/auto_execute_tasks.py --once --enable-chat --chat-input-pos "1180,965"
-   python scripts/auto_execute_tasks.py --interval 300 --enable-chat --chat-input-pos "1180,965"
+  python scripts/auto_execute_tasks.py --once
+  python scripts/auto_execute_tasks.py --interval 300
+  python scripts/auto_execute_tasks.py --once --enable-chat
 """
 """
 
 
 from __future__ import annotations
 from __future__ import annotations
@@ -29,94 +22,93 @@ import json
 import time
 import time
 import argparse
 import argparse
 import logging
 import logging
-import sys
 from pathlib import Path
 from pathlib import Path
 from datetime import datetime
 from datetime import datetime
 from typing import Any, Dict, List, Optional, Tuple
 from typing import Any, Dict, List, Optional, Tuple
 
 
-# 配置日志
+# ============================================================================
+# 日志配置
+# ============================================================================
 logging.basicConfig(
 logging.basicConfig(
     level=logging.INFO,
     level=logging.INFO,
     format="%(asctime)s - %(levelname)s - %(message)s",
     format="%(asctime)s - %(levelname)s - %(message)s",
 )
 )
 logger = logging.getLogger("AutoExecuteTasks")
 logger = logging.getLogger("AutoExecuteTasks")
 
 
-# ==== Cursor Chat 相关依赖(Windows GUI 自动化) ====
+# ============================================================================
+# Windows GUI 自动化依赖(可选)
+# ============================================================================
+HAS_CURSOR_GUI = False
+HAS_PYPERCLIP = False
+
 try:
 try:
     import win32gui
     import win32gui
     import win32con
     import win32con
-    import win32process
     import pyautogui
     import pyautogui
 
 
+    pyautogui.FAILSAFE = True
+    pyautogui.PAUSE = 0.5
+    HAS_CURSOR_GUI = True
+
     try:
     try:
         import pyperclip
         import pyperclip
-
         HAS_PYPERCLIP = True
         HAS_PYPERCLIP = True
     except ImportError:
     except ImportError:
-        HAS_PYPERCLIP = False
-
-    HAS_CURSOR_GUI = True
-    # pyautogui 安全 & 节奏
-    pyautogui.FAILSAFE = True
-    pyautogui.PAUSE = 0.5
+        pass
 except ImportError:
 except ImportError:
-    HAS_CURSOR_GUI = False
-    HAS_PYPERCLIP = False
-    logger.warning(
-        "未安装 Windows GUI 自动化依赖(pywin32/pyautogui/pyperclip),"
+    logger.info(
+        "未安装 Windows GUI 自动化依赖(pywin32/pyautogui),"
         "将禁用自动 Cursor Chat 功能。"
         "将禁用自动 Cursor Chat 功能。"
     )
     )
 
 
-# 全局配置(由命令行参数控制)
-ENABLE_CHAT: bool = False
-CHAT_MESSAGE: str = "请检查并执行所有待处理任务。"
-CHAT_INPUT_POS: Optional[Tuple[int, int]] = None
-
-
+# ============================================================================
+# 全局配置
+# ============================================================================
 WORKSPACE_ROOT = Path(__file__).parent.parent
 WORKSPACE_ROOT = Path(__file__).parent.parent
 CURSOR_DIR = WORKSPACE_ROOT / ".cursor"
 CURSOR_DIR = WORKSPACE_ROOT / ".cursor"
 PENDING_TASKS_FILE = CURSOR_DIR / "pending_tasks.json"
 PENDING_TASKS_FILE = CURSOR_DIR / "pending_tasks.json"
+INSTRUCTIONS_FILE = CURSOR_DIR / "task_execute_instructions.md"
 
 
+# 命令行参数控制的全局变量
+ENABLE_CHAT: bool = False
+CHAT_MESSAGE: str = "请阅读 .cursor/task_execute_instructions.md 并执行任务。"
+CHAT_INPUT_POS: Optional[Tuple[int, int]] = None
 
 
+
+# ============================================================================
+# 数据库操作
+# ============================================================================
 def get_db_connection():
 def get_db_connection():
-    """
-    获取数据库连接
-    
-    Returns:
-        数据库连接对象,如果失败返回None
-    """
+    """获取数据库连接"""
     try:
     try:
         import psycopg2
         import psycopg2
-        from psycopg2.extras import RealDictCursor
-        
-        # 读取数据库配置
-        config_file = Path(__file__).parent.parent / 'mcp-servers' / 'task-manager' / 'config.json'
-        with open(config_file, 'r', encoding='utf-8') as f:
-            config = json.load(f)
-        
-        db_uri = config['database']['uri']
-        conn = psycopg2.connect(db_uri)
-        return conn
-        
+        import sys
+
+        sys.path.insert(0, str(WORKSPACE_ROOT))
+        from app.config.config import config, current_env
+
+        app_config = config.get(current_env, config['default'])
+        db_uri = app_config.SQLALCHEMY_DATABASE_URI
+        return psycopg2.connect(db_uri)
+
+    except ImportError as e:
+        logger.error(f"导入依赖失败: {e}")
+        return None
     except Exception as e:
     except Exception as e:
         logger.error(f"连接数据库失败: {e}")
         logger.error(f"连接数据库失败: {e}")
         return None
         return None
 
 
 
 
-def get_pending_tasks():
-    """
-    从数据库获取所有pending任务
-    """
+def get_pending_tasks() -> List[Dict[str, Any]]:
+    """从数据库获取所有 pending 任务"""
     try:
     try:
         from psycopg2.extras import RealDictCursor
         from psycopg2.extras import RealDictCursor
-        
+
         conn = get_db_connection()
         conn = get_db_connection()
         if not conn:
         if not conn:
             return []
             return []
-        
+
         cursor = conn.cursor(cursor_factory=RealDictCursor)
         cursor = conn.cursor(cursor_factory=RealDictCursor)
-        
-        # 查询pending任务
         cursor.execute("""
         cursor.execute("""
             SELECT task_id, task_name, task_description, status,
             SELECT task_id, task_name, task_description, status,
                    code_name, code_path, create_time, create_by
                    code_name, code_path, create_time, create_by
@@ -124,43 +116,36 @@ def get_pending_tasks():
             WHERE status = 'pending'
             WHERE status = 'pending'
             ORDER BY create_time ASC
             ORDER BY create_time ASC
         """)
         """)
-        
+
         tasks = cursor.fetchall()
         tasks = cursor.fetchall()
         cursor.close()
         cursor.close()
         conn.close()
         conn.close()
-        
+
         return [dict(task) for task in tasks]
         return [dict(task) for task in tasks]
-        
+
     except Exception as e:
     except Exception as e:
-        logger.error(f"获取pending任务失败: {e}")
+        logger.error(f"获取 pending 任务失败: {e}")
         return []
         return []
 
 
 
 
-def update_task_status(task_id, status, code_name=None, code_path=None):
-    """
-    更新任务状态
-    
-    Args:
-        task_id: 任务ID
-        status: 新状态('pending', 'processing', 'completed', 'failed')
-        code_name: 代码文件名(可选)
-        code_path: 代码文件路径(可选)
-    
-    Returns:
-        是否更新成功
-    """
+def update_task_status(
+    task_id: int,
+    status: str,
+    code_name: Optional[str] = None,
+    code_path: Optional[str] = None,
+) -> bool:
+    """更新任务状态"""
     try:
     try:
         conn = get_db_connection()
         conn = get_db_connection()
         if not conn:
         if not conn:
             return False
             return False
-        
+
         cursor = conn.cursor()
         cursor = conn.cursor()
-        
-        # 构建更新SQL
+
         if code_name and code_path:
         if code_name and code_path:
             cursor.execute("""
             cursor.execute("""
                 UPDATE task_list
                 UPDATE task_list
-                SET status = %s, code_name = %s, code_path = %s, 
+                SET status = %s, code_name = %s, code_path = %s,
                     update_time = CURRENT_TIMESTAMP
                     update_time = CURRENT_TIMESTAMP
                 WHERE task_id = %s
                 WHERE task_id = %s
             """, (status, code_name, code_path, task_id))
             """, (status, code_name, code_path, task_id))
@@ -170,31 +155,150 @@ def update_task_status(task_id, status, code_name=None, code_path=None):
                 SET status = %s, update_time = CURRENT_TIMESTAMP
                 SET status = %s, update_time = CURRENT_TIMESTAMP
                 WHERE task_id = %s
                 WHERE task_id = %s
             """, (status, task_id))
             """, (status, task_id))
-        
+
         conn.commit()
         conn.commit()
         updated = cursor.rowcount > 0
         updated = cursor.rowcount > 0
         cursor.close()
         cursor.close()
         conn.close()
         conn.close()
-        
+
         if updated:
         if updated:
             logger.info(f"✅ 任务 {task_id} 状态已更新为: {status}")
             logger.info(f"✅ 任务 {task_id} 状态已更新为: {status}")
-        else:
-            logger.warning(f"⚠️ 任务 {task_id} 状态更新失败(任务不存在)")
-        
         return updated
         return updated
-        
+
     except Exception as e:
     except Exception as e:
         logger.error(f"更新任务状态失败: {e}")
         logger.error(f"更新任务状态失败: {e}")
         return False
         return False
 
 
 
 
-# ==== Cursor Chat 辅助函数(简化版,依赖固定输入框坐标) ====
+# ============================================================================
+# 任务文件生成
+# ============================================================================
+def write_pending_tasks_json(tasks: List[Dict[str, Any]]) -> None:
+    """将任务列表写入 .cursor/pending_tasks.json"""
+    CURSOR_DIR.mkdir(parents=True, exist_ok=True)
+
+    # 读取现有任务
+    existing_tasks = []
+    if PENDING_TASKS_FILE.exists():
+        try:
+            with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
+                existing_tasks = json.load(f)
+        except Exception:
+            existing_tasks = []
+
+    existing_ids = {t["task_id"] for t in existing_tasks if "task_id" in t}
+
+    # 添加新任务
+    for task in tasks:
+        if task["task_id"] not in existing_ids:
+            create_time = task.get("create_time", "")
+            if hasattr(create_time, "isoformat"):
+                create_time = create_time.isoformat()
+
+            task_info = {
+                "task_id": task["task_id"],
+                "task_name": task["task_name"],
+                "task_description": task["task_description"],
+                "code_path": task.get("code_path", ""),
+                "code_name": task.get("code_name", ""),
+                "status": "processing",
+                "notified_at": datetime.now().isoformat(),
+            }
+            existing_tasks.append(task_info)
+
+    with PENDING_TASKS_FILE.open("w", encoding="utf-8") as f:
+        json.dump(existing_tasks, f, indent=2, ensure_ascii=False)
+
+    logger.info(f"✅ pending_tasks.json 已更新,任务数: {len(existing_tasks)}")
+
+
+def create_execute_instructions(tasks: List[Dict[str, Any]]) -> None:
+    """生成任务执行指令文件 .cursor/task_execute_instructions.md"""
+    CURSOR_DIR.mkdir(parents=True, exist_ok=True)
+
+    with INSTRUCTIONS_FILE.open("w", encoding="utf-8") as f:
+        f.write("# 🤖 Cursor 自动任务执行指令\n\n")
+        f.write("**⚠️ 重要:请立即执行以下任务!**\n\n")
+        gen_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+        f.write(f"**生成时间**: {gen_time}\n\n")
+        f.write(f"**待执行任务数量**: {len(tasks)}\n\n")
+
+        f.write("## 📋 任务完成后的操作\n\n")
+        f.write("完成每个任务后,请更新 `.cursor/pending_tasks.json` 中")
+        f.write("对应任务的 `status` 为 `completed`,\n")
+        f.write("并填写 `code_name`(代码文件名)和 `code_path`(代码路径)。\n\n")
+        f.write("调度脚本会自动将完成的任务同步到数据库。\n\n")
+        f.write("---\n\n")
+
+        for idx, task in enumerate(tasks, 1):
+            task_id = task['task_id']
+            task_name = task['task_name']
+            task_desc = task['task_description']
+
+            create_time = task.get("create_time", "")
+            if hasattr(create_time, "strftime"):
+                create_time = create_time.strftime("%Y-%m-%d %H:%M:%S")
+
+            f.write(f"## 🔴 任务 {idx}: {task_name}\n\n")
+            f.write(f"- **任务ID**: `{task_id}`\n")
+            f.write(f"- **创建时间**: {create_time}\n")
+            f.write(f"- **创建者**: {task.get('create_by', 'unknown')}\n\n")
+            f.write(f"### 📝 任务描述\n\n{task_desc}\n\n")
+            f.write("---\n\n")
+
+    logger.info(f"✅ 执行指令文件已创建: {INSTRUCTIONS_FILE}")
+
+
+# ============================================================================
+# 状态同步
+# ============================================================================
+def sync_completed_tasks_to_db() -> int:
+    """将 pending_tasks.json 中 completed 的任务同步到数据库"""
+    if not PENDING_TASKS_FILE.exists():
+        return 0
+
+    try:
+        with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
+            tasks = json.load(f)
+    except Exception as e:
+        logger.error(f"读取 pending_tasks.json 失败: {e}")
+        return 0
+
+    if not isinstance(tasks, list):
+        return 0
+
+    updated = 0
+    remaining_tasks = []
+
+    for t in tasks:
+        if t.get("status") == "completed":
+            task_id = t.get("task_id")
+            if not task_id:
+                continue
+
+            code_name = t.get("code_name")
+            code_path = t.get("code_path")
+            if update_task_status(task_id, "completed", code_name, code_path):
+                updated += 1
+                logger.info(f"已同步任务 {task_id} 为 completed")
+            else:
+                remaining_tasks.append(t)
+        else:
+            remaining_tasks.append(t)
+
+    if updated > 0:
+        with PENDING_TASKS_FILE.open("w", encoding="utf-8") as f:
+            json.dump(remaining_tasks, f, indent=2, ensure_ascii=False)
+        logger.info(f"本次共同步 {updated} 个 completed 任务到数据库")
+
+    return updated
 
 
-def _find_cursor_window() -> Optional[int]:
-    """
-    查找 Cursor 主窗口句柄(简化版)
-    通过窗口标题 / 类名中包含 'Cursor' / 'Chrome_WidgetWin_1' 来判断。
-    """
+
+# ============================================================================
+# Cursor Chat 自动化
+# ============================================================================
+def find_cursor_window() -> Optional[int]:
+    """查找 Cursor 主窗口句柄"""
     if not HAS_CURSOR_GUI:
     if not HAS_CURSOR_GUI:
         return None
         return None
 
 
@@ -205,537 +309,202 @@ def _find_cursor_window() -> Optional[int]:
             title = win32gui.GetWindowText(hwnd) or ""
             title = win32gui.GetWindowText(hwnd) or ""
             class_name = win32gui.GetClassName(hwnd) or ""
             class_name = win32gui.GetClassName(hwnd) or ""
 
 
-            is_cursor = False
-            if "cursor" in title.lower():
-                is_cursor = True
+            is_cursor = "cursor" in title.lower()
             if class_name and "chrome_widgetwin" in class_name.lower():
             if class_name and "chrome_widgetwin" in class_name.lower():
                 is_cursor = True
                 is_cursor = True
 
 
             if is_cursor:
             if is_cursor:
                 left, top, right, bottom = win32gui.GetWindowRect(hwnd)
                 left, top, right, bottom = win32gui.GetWindowRect(hwnd)
-                width = right - left
-                height = bottom - top
-                area = width * height
-                cursor_windows.append(
-                    {
-                        "hwnd": hwnd,
-                        "title": title,
-                        "class": class_name,
-                        "width": width,
-                        "height": height,
-                        "area": area,
-                    }
-                )
+                area = (right - left) * (bottom - top)
+                cursor_windows.append({"hwnd": hwnd, "area": area})
         return True
         return True
 
 
     win32gui.EnumWindows(enum_windows_callback, None)
     win32gui.EnumWindows(enum_windows_callback, None)
+
     if not cursor_windows:
     if not cursor_windows:
         logger.warning("未找到 Cursor 窗口")
         logger.warning("未找到 Cursor 窗口")
         return None
         return None
 
 
-    # 选取面积最大的窗口作为主窗口
     cursor_windows.sort(key=lambda x: x["area"], reverse=True)
     cursor_windows.sort(key=lambda x: x["area"], reverse=True)
-    main = cursor_windows[0]
-    logger.info(
-        "找到 Cursor 主窗口: %s (%s), size=%dx%d, hwnd=%s",
-        main["title"],
-        main["class"],
-        main["width"],
-        main["height"],
-        main["hwnd"],
-    )
-    return main["hwnd"]
+    return cursor_windows[0]["hwnd"]
 
 
 
 
-def _activate_cursor_window(hwnd: int) -> bool:
-    """激活 Cursor 主窗口"""
+def send_chat_message(
+    message: str, input_pos: Optional[Tuple[int, int]]
+) -> bool:
+    """在 Cursor Chat 中发送消息"""
     if not HAS_CURSOR_GUI:
     if not HAS_CURSOR_GUI:
+        logger.warning("当前环境不支持 Cursor GUI 自动化")
         return False
         return False
+
+    hwnd = find_cursor_window()
+    if not hwnd:
+        return False
+
     try:
     try:
         win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
         win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
         time.sleep(0.3)
         time.sleep(0.3)
         win32gui.SetForegroundWindow(hwnd)
         win32gui.SetForegroundWindow(hwnd)
         time.sleep(0.5)
         time.sleep(0.5)
-        logger.info("Cursor 窗口已激活")
-        return True
-    except Exception as exc:  # noqa: BLE001
-        logger.error("激活 Cursor 窗口失败: %s", exc)
-        return False
-
-
-def _send_chat_message_once(message: str, input_pos: Optional[Tuple[int, int]]) -> bool:
-    """
-    在 Cursor Chat 中发送一条消息(单次):
-    1. 激活窗口
-    2. 如果提供了输入框坐标,则移动并点击
-    3. 使用剪贴板粘贴消息
-    4. 按 Enter 提交
-    """
-    if not HAS_CURSOR_GUI:
-        logger.warning("当前环境不支持 Cursor GUI 自动化,跳过自动发 Chat")
-        return False
-
-    hwnd = _find_cursor_window()
-    if not hwnd:
-        return False
-    if not _activate_cursor_window(hwnd):
+    except Exception as e:
+        logger.error(f"激活 Cursor 窗口失败: {e}")
         return False
         return False
 
 
-    # 点击/激活输入框
+    # 点击输入框或使用快捷键
     if input_pos:
     if input_pos:
         x, y = input_pos
         x, y = input_pos
-        logger.info("移动鼠标到输入框位置: (%d, %d)", x, y)
-        pyautogui.moveTo(x, y, duration=0.3)
-        time.sleep(0.2)
         pyautogui.click(x, y)
         pyautogui.click(x, y)
         time.sleep(0.4)
         time.sleep(0.4)
     else:
     else:
-        # 未指定位置时,尝试快捷键打开 Chat(Ctrl+K),然后点击窗口底部中间
-        logger.info("未指定输入框位置,尝试使用 Ctrl+K 打开 Chat")
-        pyautogui.hotkey("ctrl", "k")
-        time.sleep(1.5)
-        screen_width, screen_height = pyautogui.size()
-        x, y = int(screen_width * 0.6), int(screen_height * 0.9)
-        pyautogui.moveTo(x, y, duration=0.3)
-        pyautogui.click(x, y)
-        time.sleep(0.4)
+        pyautogui.hotkey("ctrl", "l")
+        time.sleep(1.0)
 
 
-    # 清空旧内容
     pyautogui.hotkey("ctrl", "a")
     pyautogui.hotkey("ctrl", "a")
-    time.sleep(0.3)
-
-    # 再次点击保证焦点
-    pyautogui.click()
     time.sleep(0.2)
     time.sleep(0.2)
 
 
-    logger.info("正在向 Cursor Chat 输入消息: %s", message)
-    # 优先使用剪贴板(兼容中文)
+    # 输入消息
     if HAS_PYPERCLIP:
     if HAS_PYPERCLIP:
-        try:
-            old_clipboard = pyperclip.paste()
-        except Exception:  # noqa: BLE001
-            old_clipboard = None
-
         try:
         try:
             pyperclip.copy(message)
             pyperclip.copy(message)
-            time.sleep(0.3)
             pyautogui.hotkey("ctrl", "v")
             pyautogui.hotkey("ctrl", "v")
-            time.sleep(1.0)
-            logger.info("使用剪贴板粘贴消息成功")
-        except Exception as exc:  # noqa: BLE001
-            logger.error("剪贴板粘贴失败: %s,退回到直接输入", exc)
-            pyautogui.write(message, interval=0.05)
-            time.sleep(0.8)
-        finally:
-            if old_clipboard is not None:
-                try:
-                    pyperclip.copy(old_clipboard)
-                except Exception:
-                    pass
+            time.sleep(0.5)
+        except Exception:
+            pyautogui.write(message, interval=0.03)
     else:
     else:
-        pyautogui.write(message, interval=0.05)
-        time.sleep(0.8)
+        pyautogui.write(message, interval=0.03)
 
 
-    # 提交(Enter)
-    logger.info("按 Enter 提交消息")
+    time.sleep(0.3)
     pyautogui.press("enter")
     pyautogui.press("enter")
-    time.sleep(1.0)
-    logger.info("消息已提交")
+    logger.info("✅ 消息已发送到 Cursor Chat")
     return True
     return True
 
 
 
 
-def send_chat_for_pending_tasks() -> None:
-    """
-    如果启用了 Chat 功能且存在 pending 任务,则向 Cursor Chat 发送一次统一消息。
-    """
+def send_chat_for_tasks() -> None:
+    """向 Cursor Chat 发送任务提醒"""
     if not ENABLE_CHAT:
     if not ENABLE_CHAT:
         return
         return
 
 
-    # 仅检查 .cursor/pending_tasks.json 中是否存在 status 为 processing 的任务
     if not PENDING_TASKS_FILE.exists():
     if not PENDING_TASKS_FILE.exists():
-        logger.info("未找到 .cursor/pending_tasks.json,跳过自动 Chat")
         return
         return
 
 
     try:
     try:
         with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
         with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
             data = json.load(f)
             data = json.load(f)
-        if not isinstance(data, list) or not any(
-            t.get("status") == "processing" for t in data
-        ):
-            logger.info("pending_tasks.json 中没有 processing 任务,跳过自动 Chat")
+        if not any(t.get("status") == "processing" for t in data):
             return
             return
-    except Exception as exc:  # noqa: BLE001
-        logger.error("读取 pending_tasks.json 失败: %s", exc)
+    except Exception:
         return
         return
 
 
-    logger.info("检测到 processing 任务,准备自动向 Cursor Chat 发送提醒消息")
-    _send_chat_message_once(CHAT_MESSAGE, CHAT_INPUT_POS)
-
+    logger.info("发送任务提醒到 Cursor Chat...")
+    send_chat_message(CHAT_MESSAGE, CHAT_INPUT_POS)
 
 
-def sync_completed_tasks_from_pending_file() -> int:
-    """
-    将 .cursor/pending_tasks.json 中 status == 'completed' 的任务,同步到数据库。
 
 
-    Returns:
-        成功更新的任务数量
-    """
-    if not PENDING_TASKS_FILE.exists():
-        return 0
+# ============================================================================
+# 主执行流程
+# ============================================================================
+def auto_execute_tasks_once() -> int:
+    """执行一次任务检查和处理"""
+    # 1. 先同步已完成任务到数据库
+    sync_completed_tasks_to_db()
 
 
-    try:
-        with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
-            tasks = json.load(f)
-    except Exception as exc:  # noqa: BLE001
-        logger.error("读取 pending_tasks.json 失败: %s", exc)
-        return 0
+    # 2. 获取 pending 任务
+    logger.info("🔍 检查 pending 任务...")
+    tasks = get_pending_tasks()
 
 
-    if not isinstance(tasks, list):
+    if not tasks:
+        logger.info("✅ 没有 pending 任务")
         return 0
         return 0
 
 
-    updated = 0
-    for t in tasks:
-        if t.get("status") != "completed":
-            continue
-
-        task_id = t.get("task_id")
-        if not task_id:
-            continue
-
-        code_name = t.get("code_name")
-        code_path = t.get("code_path")
-        if update_task_status(task_id, "completed", code_name, code_path):
-            updated += 1
-            logger.info(
-                "已根据 pending_tasks.json 将任务 %s 同步为 completed (code_name=%s, code_path=%s)",
-                task_id,
-                code_name,
-                code_path,
-            )
-
-    if updated:
-        logger.info("本次共同步 %d 个 completed 任务到数据库", updated)
-    return updated
-
-
-def print_task_for_cursor_execution(task, task_file_path=None):
-    """
-    打印任务信息,供Cursor识别并执行
-    
-    这个函数会以特定格式输出任务,Cursor可以识别并自动执行
-    """
-    print("\n" + "=" * 80)
-    print(f"🤖 [AUTO-EXECUTE-TASK] Task ID: {task['task_id']}")
-    print("=" * 80)
-    print(f"\n**任务名称**: {task['task_name']}")
-    print(f"**任务ID**: {task['task_id']}")
-    print(f"**状态**: processing(已更新)")
-    print(f"**创建时间**: {task['create_time']}")
-    print(f"**创建者**: {task['create_by']}")
-    
-    if task_file_path:
-        print(f"**任务文件**: {task_file_path}")
-    
-    print(f"\n## 任务描述\n")
-    print(task['task_description'])
-    print(f"\n## 执行指令")
-    print(f"\n请Cursor AI根据上述任务描述,自动完成以下步骤:")
-    print(f"1. 打开并查看任务文件: {task_file_path or '未创建'}")
-    print(f"2. 根据任务描述实现具体功能")
-    print(f"3. 确保代码符合项目规范")
-    print(f"4. 完成后调用MCP工具更新任务状态:")
-    print(f"   工具: update_task_status")
-    print(f"   参数: {{")
-    print(f"     \"task_id\": {task['task_id']},")
-    if task_file_path:
-        import os
-        file_name = os.path.basename(task_file_path)
-        file_dir = os.path.dirname(task_file_path).replace(str(Path(__file__).parent.parent), '').strip('\\').strip('/')
-        print(f"     \"code_name\": \"{file_name}\",")
-        print(f"     \"code_path\": \"{file_dir}\",")
-    print(f"     \"status\": \"completed\"")
-    print(f"   }}")
-    print(f"\n任务文件保存路径:{task.get('code_path', 'app/core/data_flow')}")
-    print("\n" + "=" * 80)
-    print(f"🔚 [END-AUTO-EXECUTE-TASK]")
-    print("=" * 80 + "\n")
-
-
-def create_task_file(task):
-    """
-    在指定目录创建任务文件
-    
-    Args:
-        task: 任务字典
-    
-    Returns:
-        生成的文件路径,如果失败返回None
-    """
-    try:
-        workspace = Path(__file__).parent.parent
-        code_path = task.get('code_path', 'app/core/data_flow')
-        target_dir = workspace / code_path
-        
-        # 确保目录存在
-        target_dir.mkdir(parents=True, exist_ok=True)
-        
-        # 生成文件名(从任务名称或代码名称)
-        code_name = task.get('code_name')
-        if not code_name:
-            # 从任务名称生成文件名
-            import re
-            task_name = task['task_name']
-            # 清理文件名:去除特殊字符,替换为下划线
-            safe_name = re.sub(r'[^\w\u4e00-\u9fff]+', '_', task_name)
-            safe_name = re.sub(r'_+', '_', safe_name).strip('_')
-            code_name = f"{safe_name}.py"
-        
-        # 确保是.py文件
-        if not code_name.endswith('.py'):
-            code_name = f"{code_name}.py"
-        
-        file_path = target_dir / code_name
-        
-        # 如果文件已存在,添加时间戳
-        if file_path.exists():
-            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
-            base_name = file_path.stem
-            file_path = target_dir / f"{base_name}_{timestamp}.py"
-            code_name = file_path.name
-        
-        # 生成任务文件内容
-        file_content = f'''#!/usr/bin/env python3
-"""
-{task['task_name']}
+    logger.info(f"📋 找到 {len(tasks)} 个 pending 任务")
 
 
-任务ID: {task['task_id']}
-创建时间: {task['create_time']}
-创建者: {task['create_by']}
+    # 3. 更新任务状态为 processing
+    for task in tasks:
+        update_task_status(task["task_id"], "processing")
 
 
-任务描述:
-{task['task_description']}
+    # 4. 写入 pending_tasks.json
+    write_pending_tasks_json(tasks)
 
 
-注意:此文件为任务占位符,需要根据任务描述实现具体功能。
-"""
+    # 5. 生成执行指令文件
+    create_execute_instructions(tasks)
 
 
-# TODO: 根据任务描述实现功能
-# {task['task_description'][:100]}...
-
-if __name__ == '__main__':
-    print("任务文件已创建,请根据任务描述实现具体功能")
-    pass
-'''
-        
-        # 写入文件
-        with open(file_path, 'w', encoding='utf-8') as f:
-            f.write(file_content)
-        
-        logger.info(f"✅ 任务文件已创建: {file_path}")
-        
-        # 更新数据库中的code_name和code_path
-        update_task_status(
-            task['task_id'], 
-            'processing',  # 状态改为processing
-            code_name=code_name,
-            code_path=code_path
-        )
-        
-        return str(file_path)
-        
-    except Exception as e:
-        logger.error(f"创建任务文件失败: {e}")
-        # 即使文件创建失败,也要更新状态为processing
-        update_task_status(task['task_id'], 'processing')
-        return None
+    return len(tasks)
 
 
 
 
-def notify_cursor_to_execute_task(task, task_file_path=None):
-    """
-    通知Cursor执行任务
-    
-    通过创建一个标记文件,让Cursor知道有新任务需要执行
-    """
-    workspace = Path(__file__).parent.parent
-    task_trigger_file = workspace / '.cursor' / 'pending_tasks.json'
-    task_trigger_file.parent.mkdir(parents=True, exist_ok=True)
-    
-    # 读取现有的pending tasks
-    pending_tasks = []
-    if task_trigger_file.exists():
-        try:
-            with open(task_trigger_file, 'r', encoding='utf-8') as f:
-                pending_tasks = json.load(f)
-        except:
-            pending_tasks = []
-    
-    # 检查任务是否已存在
-    task_exists = any(t['task_id'] == task['task_id'] for t in pending_tasks)
-    if not task_exists:
-        task_info = {
-            'task_id': task['task_id'],
-            'task_name': task['task_name'],
-            'task_description': task['task_description'],
-            'code_path': task.get('code_path', 'app/core/data_flow'),
-            'code_name': task.get('code_name'),
-            'status': 'processing',  # 标记为processing
-            'notified_at': datetime.now().isoformat()
-        }
-        
-        if task_file_path:
-            task_info['task_file'] = task_file_path
-        
-        pending_tasks.append(task_info)
-        
-        # 写入文件
-        with open(task_trigger_file, 'w', encoding='utf-8') as f:
-            json.dump(pending_tasks, f, indent=2, ensure_ascii=False)
-        
-        logger.info(f"✅ Task {task['task_id']} added to pending_tasks.json")
-
-
-def auto_execute_tasks_once():
-    """
-    执行一次任务检查和通知
-    """
-    # 先尝试把本地 completed 状态同步到数据库
-    sync_completed_tasks_from_pending_file()
-
-    logger.info("🔍 检查pending任务...")
-    
-    tasks = get_pending_tasks()
-    
-    if not tasks:
-        logger.info("✅ 没有pending任务")
-        return 0
-    
-    logger.info(f"📋 找到 {len(tasks)} 个pending任务")
-    
-    processed_count = 0
-    for task in tasks:
-        logger.info(f"\n{'='*80}")
-        logger.info(f"处理任务: [{task['task_id']}] {task['task_name']}")
-        logger.info(f"{'='*80}")
-        
-        try:
-            # 1. 创建任务文件(同时更新状态为processing)
-            task_file_path = create_task_file(task)
-            
-            if not task_file_path:
-                logger.warning(f"⚠️ 任务 {task['task_id']} 文件创建失败,但状态已更新为processing")
-                # 即使文件创建失败,也继续通知Cursor
-            
-            # 2. 打印任务详情,供Cursor识别
-            print_task_for_cursor_execution(task, task_file_path)
-            
-            # 3. 创建通知文件
-            notify_cursor_to_execute_task(task, task_file_path)
-            
-            processed_count += 1
-            logger.info(f"✅ 任务 {task['task_id']} 处理完成")
-            
-        except Exception as e:
-            logger.error(f"❌ 处理任务 {task['task_id']} 时出错: {e}")
-            # 标记任务为failed
-            update_task_status(task['task_id'], 'failed')
-    
-    return processed_count
-
-
-def auto_execute_tasks_loop(interval=300):
-    """
-    循环执行任务检查
-    
-    Args:
-        interval: 检查间隔(秒),默认300秒(5分钟)
-    """
-    logger.info("=" * 80)
+def auto_execute_tasks_loop(interval: int = 300) -> None:
+    """循环执行任务检查"""
+    logger.info("=" * 60)
     logger.info("🚀 自动任务执行服务已启动")
     logger.info("🚀 自动任务执行服务已启动")
-    logger.info(f"⏰ 检查间隔: {interval}秒 ({interval//60}分钟)")
+    logger.info(f"⏰ 检查间隔: {interval} 秒")
+    logger.info(f"💬 自动 Chat: {'已启用' if ENABLE_CHAT else '未启用'}")
     logger.info("按 Ctrl+C 停止服务")
     logger.info("按 Ctrl+C 停止服务")
-    logger.info("=" * 80 + "\n")
-    
+    logger.info("=" * 60)
+
     try:
     try:
         while True:
         while True:
             try:
             try:
                 count = auto_execute_tasks_once()
                 count = auto_execute_tasks_once()
-
-                # 如果有新任务且启用了自动 Chat,则向 Cursor 发送提醒
                 if count > 0:
                 if count > 0:
-                    send_chat_for_pending_tasks()
+                    send_chat_for_tasks()
+                    logger.info(f"✅ 已处理 {count} 个任务")
 
 
-                if count > 0:
-                    logger.info(f"\n✅ 已通知 {count} 个任务")
-                
-                logger.info(f"\n⏳ 下次检查时间: {interval}秒后...")
+                logger.info(f"⏳ {interval} 秒后再次检查...")
                 time.sleep(interval)
                 time.sleep(interval)
-                
+
             except KeyboardInterrupt:
             except KeyboardInterrupt:
                 raise
                 raise
             except Exception as e:
             except Exception as e:
                 logger.error(f"❌ 执行出错: {e}")
                 logger.error(f"❌ 执行出错: {e}")
-                logger.info(f"⏳ {interval}秒后重试...")
                 time.sleep(interval)
                 time.sleep(interval)
-                
+
     except KeyboardInterrupt:
     except KeyboardInterrupt:
-        logger.info("\n" + "=" * 80)
-        logger.info("⛔ 用户停止了自动任务执行服务")
-        logger.info("=" * 80)
+        logger.info("\n⛔ 服务已停止")
 
 
 
 
-def main():
-    """
-    主函数
-    """
+def main() -> None:
+    """主函数"""
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
-        description='自动任务执行脚本(含可选Cursor Chat)- 定期检查并执行pending任务'
+        description="自动任务执行调度脚本",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+    parser.add_argument(
+        "--once", action="store_true", help="只执行一次"
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--once',
-        action='store_true',
-        help='只执行一次,不循环'
+        "--interval", type=int, default=300, help="检查间隔(秒)"
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--interval',
-        type=int,
-        default=300,
-        help='检查间隔(秒),默认300秒(5分钟)'
+        "--enable-chat", action="store_true", help="启用自动 Cursor Chat"
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--enable-chat',
-        action='store_true',
-        help='启用自动 Cursor Chat,在有pending任务时自动向 Cursor 发送提醒消息'
+        "--chat-input-pos", type=str, help='Chat 输入框位置 "x,y"'
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--chat-input-pos',
-        type=str,
-        default=None,
-        help='Cursor Chat 输入框位置,格式 "x,y"(例如 "1180,965"),不指定则自动尝试定位'
+        "--chat-message", type=str,
+        default="请阅读 .cursor/task_execute_instructions.md 并执行任务。",
+        help="发送到 Chat 的消息"
     )
     )
-    
+
     args = parser.parse_args()
     args = parser.parse_args()
 
 
-    # 设置全局 Chat 配置
-    global ENABLE_CHAT, CHAT_INPUT_POS  # noqa: PLW0603
+    global ENABLE_CHAT, CHAT_INPUT_POS, CHAT_MESSAGE
     ENABLE_CHAT = bool(args.enable_chat)
     ENABLE_CHAT = bool(args.enable_chat)
-    CHAT_INPUT_POS = None
+    CHAT_MESSAGE = args.chat_message
+
     if args.chat_input_pos:
     if args.chat_input_pos:
         try:
         try:
-            x_str, y_str = args.chat_input_pos.split(',')
-            CHAT_INPUT_POS = (int(x_str.strip()), int(y_str.strip()))
-            logger.info("使用指定的 Chat 输入框位置: %s", CHAT_INPUT_POS)
-        except Exception as exc:  # noqa: BLE001
-            logger.warning("解析 --chat-input-pos 失败 %r: %s", args.chat_input_pos, exc)
-    
+            x, y = args.chat_input_pos.split(",")
+            CHAT_INPUT_POS = (int(x.strip()), int(y.strip()))
+        except Exception:
+            pass
+
     if args.once:
     if args.once:
-        # 执行一次
         count = auto_execute_tasks_once()
         count = auto_execute_tasks_once()
         if count > 0:
         if count > 0:
-            send_chat_for_pending_tasks()
-        logger.info(f"\n✅ 完成!处理了 {count} 个任务")
+            send_chat_for_tasks()
+        logger.info(f"✅ 完成!处理了 {count} 个任务")
     else:
     else:
-        # 循环执行
         auto_execute_tasks_loop(interval=args.interval)
         auto_execute_tasks_loop(interval=args.interval)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()
-

+ 0 - 342
scripts/auto_tasks_chat_runner.py

@@ -1,342 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-自动任务+自动Chat一体化脚本
-
-功能整合:
-1. 从 task_list 表中读取所有 pending 任务
-2. 在本地生成 .cursor/pending_tasks.json 和 task_execute_instructions.md
-3. 自动在 Cursor Chat 中发送「请检查并执行所有待处理任务。」消息
-4. 轮询 .cursor/pending_tasks.json 中状态为 completed 的任务,并自动回写 task_list 状态
-
-依赖:
-- scripts/auto_execute_tasks.py 负责数据库连接和任务状态更新
-- scripts/trigger_cursor_execution.py 负责生成 task_execute_instructions.md 和触发文件
-- scripts/cursor_auto_chat.py 负责在 Cursor 中自动发送 Chat 消息
-
-使用方式:
-1) 单次执行(检查一次并触发一次 Chat):
-   python scripts/auto_tasks_chat_runner.py --once
-
-2) 守护进程模式(推荐):
-   python scripts/auto_tasks_chat_runner.py --daemon --interval 300
-"""
-
-from __future__ import annotations
-
-import json
-import time
-import argparse
-import logging
-from pathlib import Path
-from typing import Any, Dict, List, Optional, Tuple
-
-# 复用现有脚本中的能力
-from scripts.auto_execute_tasks import get_pending_tasks, update_task_status  # type: ignore[import]
-from scripts.trigger_cursor_execution import create_execute_instructions_file  # type: ignore[import]
-from scripts.cursor_auto_chat import CursorAutoChat  # type: ignore[import]
-
-
-logger = logging.getLogger("AutoTasksChatRunner")
-logging.basicConfig(
-    level=logging.INFO,
-    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
-)
-
-
-WORKSPACE_ROOT = Path(__file__).parent.parent
-CURSOR_DIR = WORKSPACE_ROOT / ".cursor"
-PENDING_TASKS_FILE = CURSOR_DIR / "pending_tasks.json"
-
-
-def write_pending_tasks_file(tasks: List[Dict[str, Any]]) -> None:
-    """
-    将数据库中的 pending 任务写入 .cursor/pending_tasks.json
-
-    约定:
-    - 初始写入时,status 字段统一设为 'processing'
-    - Cursor / 用户 / 自动化脚本 可以把 status 更新为 'completed'
-    """
-    CURSOR_DIR.mkdir(parents=True, exist_ok=True)
-
-    payload: List[Dict[str, Any]] = []
-    for t in tasks:
-        payload.append(
-            {
-                "task_id": t["task_id"],
-                "task_name": t["task_name"],
-                "task_description": t["task_description"],
-                "code_path": t.get("code_path") or "app/core/data_flow",
-                "code_name": t.get("code_name") or "",
-                "status": "processing",
-                "notified_at": t.get("create_time") and t["create_time"].isoformat()
-                if hasattr(t.get("create_time"), "isoformat")
-                else None,
-                "task_file": t.get("task_file") or "",
-            }
-        )
-
-    with PENDING_TASKS_FILE.open("w", encoding="utf-8") as f:
-        json.dump(payload, f, ensure_ascii=False, indent=2)
-
-    logger.info("已写入 .cursor/pending_tasks.json,任务数量: %d", len(payload))
-
-
-def load_pending_tasks_file() -> List[Dict[str, Any]]:
-    """读取 .cursor/pending_tasks.json(如果不存在则返回空列表)"""
-    if not PENDING_TASKS_FILE.exists():
-        return []
-    try:
-        with PENDING_TASKS_FILE.open("r", encoding="utf-8") as f:
-            data = json.load(f)
-        if isinstance(data, list):
-            return data
-        return []
-    except Exception as exc:  # noqa: BLE001
-        logger.error("读取 pending_tasks.json 失败: %s", exc)
-        return []
-
-
-def sync_completed_tasks_to_db() -> int:
-    """
-    将 .cursor/pending_tasks.json 中 status == 'completed' 的任务,同步回数据库。
-
-    返回:
-        成功更新到数据库的任务数量
-    """
-    tasks = load_pending_tasks_file()
-    if not tasks:
-        logger.info("pending_tasks.json 中没有任务记录")
-        return 0
-
-    updated = 0
-    for t in tasks:
-        if t.get("status") != "completed":
-            continue
-
-        task_id = t.get("task_id")
-        code_name = t.get("code_name") or ""
-        code_path = t.get("code_path") or ""
-
-        if not task_id:
-            continue
-
-        ok = update_task_status(
-            task_id=task_id,
-            status="completed",
-            code_name=code_name or None,
-            code_path=code_path or None,
-        )
-        if ok:
-            updated += 1
-            logger.info(
-                "已将任务 %s 同步为 completed (code_name=%s, code_path=%s)",
-                task_id,
-                code_name,
-                code_path,
-            )
-
-    if updated == 0:
-        logger.info("没有需要同步到数据库的 completed 任务")
-    else:
-        logger.info("本次共同步 %d 个任务状态到数据库", updated)
-
-    return updated
-
-
-def prepare_tasks_for_execution() -> int:
-    """
-    检查数据库中的 pending 任务,生成本地任务文件和 Cursor 指令文件。
-
-    返回:
-        准备好的 pending 任务数量
-    """
-    logger.info("开始从数据库读取 pending 任务...")
-    tasks = get_pending_tasks()
-    if not tasks:
-        logger.info("数据库中没有 pending 任务")
-        return 0
-
-    logger.info("从数据库读取到 %d 个 pending 任务", len(tasks))
-
-    # 写入 .cursor/pending_tasks.json
-    write_pending_tasks_file(tasks)
-
-    # 生成 task_execute_instructions.md + task_trigger.txt
-    # trigger_cursor_execution.create_execute_instructions_file 期望的是 processing_tasks 列表
-    create_execute_instructions_file(WORKSPACE_ROOT, tasks)
-
-    return len(tasks)
-
-
-def send_chat_to_cursor(message: str, input_box_pos: Optional[Tuple[int, int]]) -> bool:
-    """
-    使用 CursorAutoChat 在 Cursor 中发送一条 Chat 消息。
-    """
-    logger.info("准备向 Cursor Chat 发送消息: %s", message)
-    tool = CursorAutoChat(
-        message=message,
-        interval=300,
-        input_box_pos=input_box_pos,
-    )
-    return tool.execute_once()
-
-
-def run_once(message: str, input_box_pos: Optional[Tuple[int, int]]) -> None:
-    """
-    单次执行流程:
-    1. 同步已完成任务到数据库
-    2. 从数据库读取 pending 任务并生成本地文件
-    3. 如果有任务,向 Cursor 发送 Chat 消息
-    """
-    logger.info("=" * 80)
-    logger.info("开始单次自动任务 + Chat 执行流程")
-
-    # 第一步:先把本地已完成任务同步到数据库
-    sync_completed_tasks_to_db()
-
-    # 第二步:准备新的 pending 任务
-    count = prepare_tasks_for_execution()
-    if count == 0:
-        logger.info("当前没有新的 pending 任务,无需触发 Cursor Chat")
-        logger.info("=" * 80)
-        return
-
-    # 第三步:通过 Cursor Chat 提醒 AI 执行所有待处理任务
-    ok = send_chat_to_cursor(message=message, input_box_pos=input_box_pos)
-    if ok:
-        logger.info("已通过 Cursor Chat 发送任务执行指令")
-    else:
-        logger.warning("向 Cursor Chat 发送消息失败,请检查窗口状态")
-
-    logger.info("=" * 80)
-
-
-def run_daemon(
-    message: str,
-    input_box_pos: Optional[Tuple[int, int]],
-    interval: int,
-    sync_interval: int,
-) -> None:
-    """
-    守护进程模式:
-    - 每次循环:
-      1) 同步本地 completed 任务到数据库
-      2) 检查数据库 pending 任务并生成本地文件
-      3) 如有任务则触发 Cursor Chat
-    - 之后休眠 interval 秒
-    """
-    logger.info("=" * 80)
-    logger.info("自动任务 + 自动Chat 一体化守护进程已启动")
-    logger.info("检查间隔: %d 秒", interval)
-    logger.info("同步 completed 状态间隔(同检查逻辑): %d 秒", sync_interval)
-    logger.info("按 Ctrl+C 结束")
-    logger.info("=" * 80)
-
-    try:
-        while True:
-            try:
-                # 先同步 completed 状态
-                sync_completed_tasks_to_db()
-
-                # 再检查新的 pending 任务并触发 Chat
-                count = prepare_tasks_for_execution()
-                if count > 0:
-                    send_chat_to_cursor(message=message, input_box_pos=input_box_pos)
-
-                logger.info("下次检查将在 %d 秒后进行...", interval)
-                time.sleep(interval)
-
-            except KeyboardInterrupt:
-                raise
-            except Exception as exc:  # noqa: BLE001
-                logger.error("守护进程循环中出错: %s", exc)
-                logger.info("将在 %d 秒后重试...", interval)
-                time.sleep(interval)
-
-    except KeyboardInterrupt:
-        logger.info("\n自动任务 + 自动Chat 守护进程已停止")
-
-
-def parse_args() -> argparse.Namespace:
-    parser = argparse.ArgumentParser(
-        description="自动任务 + 自动Cursor Chat 一体化工具",
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-示例:
-  # 单次执行:检查pending、生成文件并触发一次Chat
-  python scripts/auto_tasks_chat_runner.py --once
-
-  # 守护进程模式(每5分钟检查一次)
-  python scripts/auto_tasks_chat_runner.py --daemon --interval 300
-
-  # 自定义Chat消息
-  python scripts/auto_tasks_chat_runner.py --daemon --message "请执行所有待处理任务"
-
-  # 指定Chat输入框位置
-  python scripts/auto_tasks_chat_runner.py --daemon --input-box-pos "1180,965"
-        """,
-    )
-    parser.add_argument(
-        "--once",
-        action="store_true",
-        help="只执行一次,不持续运行",
-    )
-    parser.add_argument(
-        "--daemon",
-        action="store_true",
-        help="以守护进程模式运行,定期检查任务并触发Chat",
-    )
-    parser.add_argument(
-        "--interval",
-        type=int,
-        default=300,
-        help="守护进程模式下的检查间隔(秒),默认 300 秒",
-    )
-    parser.add_argument(
-        "--message",
-        type=str,
-        default="请检查并执行所有待处理任务。",
-        help='发送到 Cursor Chat 的消息内容,默认: "请检查并执行所有待处理任务。"',
-    )
-    parser.add_argument(
-        "--input-box-pos",
-        type=str,
-        default=None,
-        help='Cursor Chat 输入框位置,格式为 "x,y"(例如 "1180,965"),不指定则自动尝试定位',
-    )
-    return parser.parse_args()
-
-
-def main() -> None:
-    args = parse_args()
-
-    # 解析输入框位置
-    input_box_pos: Optional[Tuple[int, int]] = None
-    if args.input_box_pos:
-        try:
-            x_str, y_str = args.input_box_pos.split(",")
-            input_box_pos = (int(x_str.strip()), int(y_str.strip()))
-            logger.info("使用指定的输入框位置: %s", input_box_pos)
-        except Exception as exc:  # noqa: BLE001
-            logger.warning("解析输入框位置失败 %r: %s", args.input_box_pos, exc)
-            input_box_pos = None
-
-    if args.once:
-        run_once(message=args.message, input_box_pos=input_box_pos)
-    else:
-        run_daemon(
-            message=args.message,
-            input_box_pos=input_box_pos,
-            interval=args.interval,
-            sync_interval=args.interval,
-        )
-
-
-if __name__ == "__main__":
-    main()
-
-
-
-
-

+ 0 - 56
scripts/check_auto_tasks.bat

@@ -1,56 +0,0 @@
-@echo off
-chcp 65001 >nul
-REM 检查自动任务执行脚本运行状态
-
-echo ================================================
-echo 检查自动任务执行服务状态
-echo ================================================
-echo.
-
-REM 切换到项目根目录
-cd /d %~dp0..
-
-REM 使用PowerShell检查进程
-echo [服务状态]
-powershell -Command "$processes = Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like '*auto_execute_tasks.py*' }; if ($processes) { Write-Host '[运行中] 找到以下进程:' -ForegroundColor Green; $processes | ForEach-Object { Write-Host ('  进程ID: ' + $_.ProcessId + '  启动时间: ' + $_.CreationDate) } } else { Write-Host '[未运行] 未找到auto_execute_tasks.py进程' -ForegroundColor Yellow }"
-
-echo.
-echo ================================================
-echo 查看最近日志(最后20行)
-echo ================================================
-echo.
-
-if exist "logs\auto_execute.log" (
-    powershell -Command "Get-Content logs\auto_execute.log -Tail 20 -ErrorAction SilentlyContinue"
-) else (
-    echo [提示] 日志文件不存在,脚本可能未运行或使用标准输出
-)
-
-echo.
-echo ================================================
-echo 检查pending_tasks.json状态
-echo ================================================
-echo.
-
-if exist ".cursor\pending_tasks.json" (
-    echo [文件存在] .cursor\pending_tasks.json
-    powershell -Command "$tasks = Get-Content '.cursor\pending_tasks.json' -Raw -ErrorAction SilentlyContinue | ConvertFrom-Json; if ($tasks) { Write-Host ('  任务数量: ' + $tasks.Count); $tasks | ForEach-Object { Write-Host ('  - [' + $_.task_id + '] ' + $_.task_name + ' (' + $_.status + ')') } } else { Write-Host '  [空] 没有待处理任务' }"
-) else (
-    echo [提示] pending_tasks.json 不存在
-)
-
-echo.
-echo ================================================
-echo 是否执行一次手动检查?(Y/N)
-echo ================================================
-echo.
-
-set /p choice="请输入选择: "
-if /i "%choice%"=="Y" (
-    echo.
-    echo [执行] 手动运行一次任务检查...
-    python scripts\auto_execute_tasks.py --once
-)
-
-echo.
-pause

+ 0 - 689
scripts/cursor_auto_chat.py

@@ -1,689 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-Cursor自动聊天工具
-
-这个工具可以自动查找Cursor窗口,定位到chat窗口,并自动发送消息。
-
-功能:
-1. 查找Windows操作系统中运行的Cursor程序
-2. 找到当前运行Cursor实例,并定位到当前的chat窗口
-3. 模拟鼠标点击到chat窗口
-4. 模拟键盘输入"请检查并执行所有待处理任务。"到chat窗口
-5. 模拟鼠标点击chat窗口的"提交"按钮
-6. 以服务方式持续运行,间隔300秒进行一次上述操作
-
-使用方法:
-1. 单次执行:python scripts/cursor_auto_chat.py --once
-2. 服务模式:python scripts/cursor_auto_chat.py --daemon
-3. 自定义间隔:python scripts/cursor_auto_chat.py --interval 300
-4. 指定输入框位置:python scripts/cursor_auto_chat.py --input-box-pos "1180,965"
-"""
-
-import sys
-import time
-import argparse
-import logging
-from pathlib import Path
-from datetime import datetime
-
-try:
-    import win32gui
-    import win32con
-    import win32process
-    import win32api
-    import pyautogui
-    import pywinauto
-    from pywinauto import Application
-    try:
-        import pyperclip
-        HAS_PYPERCLIP = True
-    except ImportError:
-        HAS_PYPERCLIP = False
-except ImportError as e:
-    print(f"❌ 缺少必要的依赖库: {e}")
-    print("请运行: pip install pywin32 pyautogui pywinauto pyperclip")
-    sys.exit(1)
-
-# 配置日志
-logs_dir = Path(__file__).parent.parent / 'logs'
-logs_dir.mkdir(exist_ok=True)
-
-logging.basicConfig(
-    level=logging.INFO,
-    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
-    handlers=[
-        logging.FileHandler(logs_dir / 'cursor_auto_chat.log', encoding='utf-8'),
-        logging.StreamHandler(sys.stdout)
-    ]
-)
-logger = logging.getLogger('CursorAutoChat')
-
-# 配置pyautogui安全设置
-pyautogui.FAILSAFE = True  # 鼠标移到屏幕左上角会触发异常,用于紧急停止
-pyautogui.PAUSE = 0.5  # 每个操作之间的暂停时间(秒)
-
-# 检查pyperclip并记录
-if not HAS_PYPERCLIP:
-    logger.warning("未安装pyperclip,中文输入可能有问题,建议安装: pip install pyperclip")
-
-
-class CursorAutoChat:
-    """Cursor自动聊天工具类"""
-    
-    def __init__(self, message="请检查并执行所有待处理任务。", interval=300, input_box_pos=None):
-        """
-        初始化工具
-        
-        Args:
-            message: 要发送的消息内容
-            interval: 执行间隔(秒)
-            input_box_pos: 输入框位置 (x, y),如果提供则直接使用,不进行自动定位
-        """
-        self.message = message
-        self.interval = interval
-        self.cursor_window = None
-        self.input_box_pos = input_box_pos  # 用户指定的输入框位置
-        logger.info(f"Cursor自动聊天工具已初始化")
-        logger.info(f"消息内容: {self.message}")
-        logger.info(f"执行间隔: {self.interval}秒")
-        if self.input_box_pos:
-            logger.info(f"使用指定的输入框位置: {self.input_box_pos}")
-    
-    def find_cursor_processes(self):
-        """
-        查找所有运行的Cursor进程
-        
-        Returns:
-            list: Cursor进程ID列表
-        """
-        cursor_pids = []
-        try:
-            import psutil
-            for proc in psutil.process_iter(['pid', 'name', 'exe']):
-                try:
-                    proc_name = proc.info['name'].lower() if proc.info['name'] else ''
-                    proc_exe = proc.info['exe'].lower() if proc.info['exe'] else ''
-                    
-                    # 查找Cursor相关进程
-                    if 'cursor' in proc_name or 'cursor' in proc_exe:
-                        cursor_pids.append(proc.info['pid'])
-                        logger.debug(f"找到Cursor进程: PID={proc.info['pid']}, Name={proc.info['name']}")
-                except (psutil.NoSuchProcess, psutil.AccessDenied):
-                    continue
-        except ImportError:
-            # 如果没有psutil,使用win32api枚举窗口
-            logger.warning("未安装psutil,使用窗口枚举方式查找Cursor")
-            cursor_pids = self._find_cursor_by_windows()
-        
-        logger.info(f"找到 {len(cursor_pids)} 个Cursor进程")
-        return cursor_pids
-    
-    def _find_cursor_by_windows(self):
-        """通过枚举窗口查找Cursor进程"""
-        cursor_pids = []
-        
-        def enum_windows_callback(hwnd, windows):
-            if win32gui.IsWindowVisible(hwnd):
-                window_title = win32gui.GetWindowText(hwnd)
-                if 'cursor' in window_title.lower():
-                    _, pid = win32process.GetWindowThreadProcessId(hwnd)
-                    if pid not in cursor_pids:
-                        cursor_pids.append(pid)
-            return True
-        
-        win32gui.EnumWindows(enum_windows_callback, None)
-        return cursor_pids
-    
-    def find_cursor_window(self):
-        """
-        查找Cursor主窗口
-        
-        Returns:
-            int: 窗口句柄,如果未找到返回None
-        """
-        cursor_windows = []  # 存储所有可能的Cursor窗口
-        
-        def enum_windows_callback(hwnd, windows):
-            if win32gui.IsWindowVisible(hwnd):
-                window_title = win32gui.GetWindowText(hwnd)
-                class_name = win32gui.GetClassName(hwnd)
-                
-                # Cursor基于Electron,窗口类名可能是Chrome_WidgetWin_1或类似
-                # 查找可能的Cursor窗口
-                is_cursor = False
-                
-                # 检查窗口标题
-                if window_title and 'cursor' in window_title.lower():
-                    is_cursor = True
-                
-                # 检查窗口类名(Electron应用通常有特定类名)
-                if class_name and ('chrome_widgetwin' in class_name.lower() or 'electron' in class_name.lower()):
-                    # 进一步检查:Electron窗口通常比较大
-                    rect = win32gui.GetWindowRect(hwnd)
-                    width = rect[2] - rect[0]
-                    height = rect[3] - rect[1]
-                    if width > 800 and height > 600:
-                        is_cursor = True
-                
-                if is_cursor:
-                    rect = win32gui.GetWindowRect(hwnd)
-                    width = rect[2] - rect[0]
-                    height = rect[3] - rect[1]
-                    area = width * height
-                    cursor_windows.append({
-                        'hwnd': hwnd,
-                        'title': window_title,
-                        'class': class_name,
-                        'width': width,
-                        'height': height,
-                        'area': area
-                    })
-                    logger.debug(f"找到可能的Cursor窗口: {window_title} ({class_name}), Size: {width}x{height}")
-            
-            return True
-        
-        win32gui.EnumWindows(enum_windows_callback, None)
-        
-        if not cursor_windows:
-            logger.warning("未找到Cursor窗口")
-            return None
-        
-        # 选择最大的窗口作为主窗口(通常是主应用窗口)
-        cursor_windows.sort(key=lambda x: x['area'], reverse=True)
-        main_window = cursor_windows[0]
-        
-        logger.info(f"找到Cursor主窗口: {main_window['title']} ({main_window['class']})")
-        logger.info(f"窗口大小: {main_window['width']}x{main_window['height']} (HWND: {main_window['hwnd']})")
-        
-        return main_window['hwnd']
-    
-    def activate_cursor_window(self, hwnd):
-        """
-        激活Cursor窗口
-        
-        Args:
-            hwnd: 窗口句柄
-        """
-        try:
-            # 恢复窗口(如果最小化)
-            win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
-            time.sleep(0.3)
-            
-            # 激活窗口
-            win32gui.SetForegroundWindow(hwnd)
-            time.sleep(0.5)
-            
-            logger.info("Cursor窗口已激活")
-            return True
-        except Exception as e:
-            logger.error(f"激活窗口失败: {e}")
-            return False
-    
-    def find_chat_input_area(self, hwnd):
-        """
-        查找chat输入区域
-        
-        使用多种策略定位Cursor的chat输入框:
-        1. 尝试多个快捷键打开chat(Ctrl+K, Ctrl+L, Ctrl+Shift+L等)
-        2. 使用相对窗口坐标定位输入框(chat通常在窗口底部中央或右侧)
-        3. 验证输入框是否被激活
-        
-        Args:
-            hwnd: Cursor窗口句柄
-        
-        Returns:
-            tuple: (是否成功, 输入框坐标(x, y)) 或 (False, None)
-        """
-        try:
-            # 获取窗口位置和大小
-            rect = win32gui.GetWindowRect(hwnd)
-            window_left = rect[0]
-            window_top = rect[1]
-            window_width = rect[2] - rect[0]
-            window_height = rect[3] - rect[1]
-            
-            logger.info(f"窗口位置: ({window_left}, {window_top}), 大小: {window_width}x{window_height}")
-            
-            # 策略1: 尝试多个快捷键打开chat
-            logger.info("尝试使用快捷键打开chat窗口...")
-            shortcuts = [
-                ('ctrl', 'k'),  # Cursor最常用的快捷键
-                ('ctrl', 'l'),  # 备用快捷键
-                ('ctrl', 'shift', 'l'),  # 另一个可能的快捷键
-            ]
-            
-            for shortcut in shortcuts:
-                try:
-                    logger.debug(f"尝试快捷键: {'+'.join(shortcut)}")
-                    pyautogui.hotkey(*shortcut)
-                    time.sleep(1.5)  # 给chat窗口时间打开
-                    
-                    # 尝试定位输入框
-                    result = self._try_activate_input_box(hwnd, window_left, window_top, window_width, window_height)
-                    if result:
-                        input_pos = result
-                        logger.info(f"成功定位并激活chat输入框,位置: {input_pos}")
-                        return (True, input_pos)
-                except Exception as e:
-                    logger.debug(f"快捷键 {shortcut} 失败: {e}")
-                    continue
-            
-            # 策略2: 直接尝试点击可能的输入框位置(即使chat已打开)
-            logger.info("尝试直接点击可能的输入框位置...")
-            result = self._try_activate_input_box(hwnd, window_left, window_top, window_width, window_height)
-            if result:
-                input_pos = result
-                logger.info(f"成功定位并激活chat输入框,位置: {input_pos}")
-                return (True, input_pos)
-            
-            logger.warning("未能定位chat输入框,将尝试通用方法")
-            return (False, None)
-            
-        except Exception as e:
-            logger.error(f"查找chat输入区域失败: {e}")
-            return (False, None)
-    
-    def _try_activate_input_box(self, hwnd, window_left, window_top, window_width, window_height):
-        """
-        尝试激活输入框
-        
-        Args:
-            hwnd: 窗口句柄
-            window_left: 窗口左边界
-            window_top: 窗口上边界
-            window_width: 窗口宽度
-            window_height: 窗口高度
-        
-        Returns:
-            tuple: 成功时返回 (x, y) 坐标,失败时返回 None
-        """
-        # Cursor的chat输入框通常在窗口底部中央或右侧底部
-        # 尝试多个可能的位置(相对于窗口)
-        # 根据实际测试,输入框位置约为(0.61, 0.92)
-        possible_relative_positions = [
-            (0.61, 0.92),  # 实际测试位置(优先尝试)
-            (0.6, 0.92),   # 稍微偏左
-            (0.62, 0.92),  # 稍微偏右
-            (0.61, 0.91),  # 稍微偏上
-            (0.61, 0.93),  # 稍微偏下
-            (0.75, 0.92),  # 窗口右侧底部(备用)
-            (0.5, 0.92),   # 窗口底部中央(备用)
-            (0.5, 0.88),   # 窗口底部稍上(备用)
-            (0.8, 0.9),    # 窗口右侧(备用)
-        ]
-        
-        for rel_x, rel_y in possible_relative_positions:
-            try:
-                # 计算绝对坐标
-                abs_x = window_left + int(window_width * rel_x)
-                abs_y = window_top + int(window_height * rel_y)
-                
-                logger.debug(f"尝试点击位置(相对窗口): ({rel_x:.2f}, {rel_y:.2f}) -> 绝对坐标: ({abs_x}, {abs_y})")
-                
-                # 点击输入框位置
-                pyautogui.click(abs_x, abs_y)
-                time.sleep(0.8)  # 等待输入框激活
-                
-                # 验证输入框是否激活:尝试输入一个测试字符然后删除
-                # 如果输入框已激活,这个操作应该成功
-                pyautogui.write('test', interval=0.1)
-                time.sleep(0.3)
-                
-                # 删除测试文本
-                for _ in range(4):
-                    pyautogui.press('backspace')
-                time.sleep(0.3)
-                
-                # 如果到这里没有异常,说明输入框可能已激活
-                logger.info(f"成功激活输入框,位置: ({abs_x}, {abs_y})")
-                return (abs_x, abs_y)
-                
-            except Exception as e:
-                logger.debug(f"位置 ({rel_x:.2f}, {rel_y:.2f}) 失败: {e}")
-                continue
-        
-        return None
-    
-    def send_message(self, message, input_pos=None):
-        """
-        发送消息到chat窗口
-        
-        Args:
-            message: 要发送的消息
-            input_pos: 输入框坐标 (x, y),如果提供则点击该位置确保激活
-        """
-        try:
-            # 如果提供了输入框位置,先移动鼠标到该位置,然后点击
-            if input_pos:
-                x, y = input_pos
-                logger.info(f"移动鼠标到输入框位置: ({x}, {y})")
-                pyautogui.moveTo(x, y, duration=0.3)  # 平滑移动到输入框位置
-                time.sleep(0.2)
-                
-                logger.info(f"点击输入框位置确保激活: ({x}, {y})")
-                pyautogui.click(x, y)
-                time.sleep(0.5)  # 等待输入框激活
-            else:
-                # 如果没有提供位置,点击当前鼠标位置
-                logger.info("点击当前位置确保输入框激活")
-                pyautogui.click()
-                time.sleep(0.3)
-            
-            # 清空可能的现有文本
-            logger.info("清空输入框中的现有文本...")
-            pyautogui.hotkey('ctrl', 'a')
-            time.sleep(0.3)
-            
-            # 再次确保鼠标在输入框位置并点击(如果提供了位置)
-            if input_pos:
-                x, y = input_pos
-                logger.info(f"再次移动鼠标到输入框并点击: ({x}, {y})")
-                pyautogui.moveTo(x, y, duration=0.2)
-                time.sleep(0.2)
-                pyautogui.click(x, y)
-                time.sleep(0.4)  # 给足够时间让输入框完全激活
-            else:
-                pyautogui.click()
-                time.sleep(0.2)
-            
-            # 输入消息
-            logger.info(f"正在输入消息: {message}")
-            
-            # 对于中文文本,使用剪贴板方法更可靠
-            if HAS_PYPERCLIP:
-                try:
-                    # 保存当前剪贴板内容
-                    old_clipboard = pyperclip.paste() if hasattr(pyperclip, 'paste') else None
-                    
-                    # 复制消息到剪贴板
-                    pyperclip.copy(message)
-                    time.sleep(0.3)
-                    
-                    # 确保鼠标在输入框位置(如果提供了位置)
-                    if input_pos:
-                        x, y = input_pos
-                        logger.info(f"粘贴前确保鼠标在输入框位置: ({x}, {y})")
-                        pyautogui.moveTo(x, y, duration=0.2)
-                        time.sleep(0.2)
-                        # 再次点击确保焦点
-                        pyautogui.click(x, y)
-                        time.sleep(0.3)
-                    
-                    # 粘贴消息
-                    logger.info("执行Ctrl+V粘贴消息...")
-                    pyautogui.hotkey('ctrl', 'v')
-                    time.sleep(1.5)  # 等待粘贴完成,给足够时间
-                    
-                    # 验证粘贴是否成功(可选:再次点击确保文本已输入)
-                    if input_pos:
-                        # 轻微移动鼠标确认输入框仍有焦点
-                        x, y = input_pos
-                        pyautogui.moveTo(x + 1, y + 1, duration=0.1)
-                        time.sleep(0.2)
-                    
-                    # 恢复剪贴板(可选)
-                    if old_clipboard:
-                        try:
-                            pyperclip.copy(old_clipboard)
-                        except:
-                            pass
-                    
-                    logger.info("使用剪贴板方法输入消息成功")
-                    return True
-                except Exception as e:
-                    logger.warning(f"剪贴板方法失败: {e},尝试其他方法...")
-            
-            # 备用方法:直接输入(对英文有效)
-            try:
-                # 检查是否包含中文字符
-                has_chinese = any('\u4e00' <= char <= '\u9fff' for char in message)
-                if has_chinese:
-                    logger.warning("消息包含中文,但pyperclip不可用,输入可能失败")
-                
-                pyautogui.write(message, interval=0.05)
-                time.sleep(0.8)
-                logger.info("使用write方法输入成功")
-                return True
-            except Exception as e2:
-                logger.error(f"使用write方法也失败: {e2}")
-                return False
-                
-        except Exception as e:
-            logger.error(f"输入消息失败: {e}")
-            return False
-    
-    def click_submit_button(self):
-        """
-        点击提交按钮
-        
-        Cursor的提交方式可能是:
-        1. Enter键(单行输入)
-        2. Ctrl+Enter组合键(多行输入或某些配置)
-        3. 点击提交按钮(如果存在)
-        """
-        try:
-            # 策略1: 先尝试Enter键(最常见)
-            logger.info("尝试按Enter键提交...")
-            pyautogui.press('enter')
-            time.sleep(1.0)  # 等待消息发送
-            
-            # 策略2: 如果Enter不行,尝试Ctrl+Enter(某些配置下需要)
-            # 但先等待一下,看看Enter是否生效
-            logger.info("等待消息发送完成...")
-            time.sleep(1.5)  # 给足够时间让消息发送
-            
-            logger.info("消息已提交(使用Enter键)")
-            logger.info("提示: 如果消息未出现在chat历史中,可能需要使用Ctrl+Enter")
-            return True
-        except Exception as e:
-            logger.error(f"点击提交按钮失败: {e}")
-            # 尝试备用方法
-            try:
-                logger.info("尝试使用Ctrl+Enter提交...")
-                pyautogui.hotkey('ctrl', 'enter')
-                time.sleep(1.5)
-                logger.info("使用Ctrl+Enter提交完成")
-                return True
-            except Exception as e2:
-                logger.error(f"使用Ctrl+Enter也失败: {e2}")
-                return False
-    
-    def execute_once(self):
-        """
-        执行一次完整的操作流程
-        
-        Returns:
-            bool: 是否成功执行
-        """
-        logger.info("=" * 60)
-        logger.info("开始执行自动聊天操作...")
-        logger.info("=" * 60)
-        
-        try:
-            # 步骤1: 查找Cursor进程
-            logger.info("步骤1: 查找Cursor进程...")
-            cursor_pids = self.find_cursor_processes()
-            if not cursor_pids:
-                logger.error("未找到Cursor进程,请确保Cursor正在运行")
-                return False
-            
-            # 步骤2: 查找Cursor窗口
-            logger.info("步骤2: 查找Cursor主窗口...")
-            cursor_hwnd = self.find_cursor_window()
-            if not cursor_hwnd:
-                logger.error("未找到Cursor主窗口")
-                return False
-            
-            # 步骤3: 激活窗口
-            logger.info("步骤3: 激活Cursor窗口...")
-            if not self.activate_cursor_window(cursor_hwnd):
-                logger.error("激活窗口失败")
-                return False
-            
-            # 步骤4: 定位chat输入区域
-            logger.info("步骤4: 定位chat输入区域...")
-            
-            # 如果用户指定了输入框位置,直接使用
-            if self.input_box_pos:
-                input_pos = self.input_box_pos
-                logger.info(f"使用用户指定的输入框位置: {input_pos}")
-                # 激活窗口后,直接使用指定位置
-                time.sleep(0.5)
-            else:
-                # 自动定位输入框
-                result = self.find_chat_input_area(cursor_hwnd)
-                if isinstance(result, tuple) and len(result) == 2:
-                    success, input_pos = result
-                    if success and input_pos:
-                        logger.info(f"已定位到输入框位置: {input_pos}")
-                    else:
-                        logger.warning("未能精确定位输入区域,将尝试直接输入")
-                        input_pos = None
-                else:
-                    # 兼容旧版本返回格式
-                    if result:
-                        logger.info("已定位到输入框")
-                        input_pos = None  # 旧版本不返回位置
-                    else:
-                        logger.warning("未能精确定位输入区域,将尝试直接输入")
-                        input_pos = None
-                    time.sleep(1)
-            
-            # 步骤5: 输入消息
-            logger.info("步骤5: 输入消息...")
-            if not self.send_message(self.message, input_pos):
-                logger.error("输入消息失败")
-                return False
-            
-            # 步骤6: 点击提交按钮
-            logger.info("步骤6: 提交消息...")
-            if not self.click_submit_button():
-                logger.error("提交消息失败")
-                return False
-            
-            logger.info("=" * 60)
-            logger.info("✅ 自动聊天操作执行成功!")
-            logger.info("=" * 60)
-            return True
-            
-        except Exception as e:
-            logger.error(f"执行过程中出错: {e}", exc_info=True)
-            return False
-    
-    def run_daemon(self):
-        """
-        以守护进程模式运行,定期执行操作
-        """
-        logger.info("=" * 60)
-        logger.info("🚀 Cursor自动聊天工具已启动(守护进程模式)")
-        logger.info(f"⏰ 执行间隔: {self.interval}秒 ({self.interval//60}分钟)")
-        logger.info("按 Ctrl+C 停止服务")
-        logger.info("=" * 60)
-        
-        try:
-            while True:
-                try:
-                    success = self.execute_once()
-                    
-                    if success:
-                        logger.info(f"✅ 操作执行成功,{self.interval}秒后再次执行...")
-                    else:
-                        logger.warning(f"⚠️ 操作执行失败,{self.interval}秒后重试...")
-                    
-                    time.sleep(self.interval)
-                    
-                except KeyboardInterrupt:
-                    raise
-                except Exception as e:
-                    logger.error(f"执行过程中出错: {e}")
-                    logger.info(f"⏳ {self.interval}秒后重试...")
-                    time.sleep(self.interval)
-                    
-        except KeyboardInterrupt:
-            logger.info("\n" + "=" * 60)
-            logger.info("⛔ 用户停止了Cursor自动聊天工具")
-            logger.info("=" * 60)
-
-
-def main():
-    """主函数"""
-    parser = argparse.ArgumentParser(
-        description='Cursor自动聊天工具 - 自动向Cursor chat发送消息',
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-示例:
-  # 单次执行
-  python scripts/cursor_auto_chat.py --once
-  
-  # 守护进程模式(默认)
-  python scripts/cursor_auto_chat.py --daemon
-  
-  # 自定义间隔(秒)
-  python scripts/cursor_auto_chat.py --interval 300
-  
-  # 自定义消息
-  python scripts/cursor_auto_chat.py --message "你的消息内容"
-  
-  # 指定输入框位置
-  python scripts/cursor_auto_chat.py --input-box-pos "1180,965"
-        """
-    )
-    
-    parser.add_argument(
-        '--once',
-        action='store_true',
-        help='只执行一次,不持续运行'
-    )
-    parser.add_argument(
-        '--daemon',
-        action='store_true',
-        help='作为守护进程运行(默认模式)'
-    )
-    parser.add_argument(
-        '--interval',
-        type=int,
-        default=300,
-        help='执行间隔(秒),默认300秒(5分钟)'
-    )
-    parser.add_argument(
-        '--message',
-        type=str,
-        default='请检查并执行所有待处理任务。',
-        help='要发送的消息内容,默认: "请检查并执行所有待处理任务。"'
-    )
-    parser.add_argument(
-        '--input-box-pos',
-        type=str,
-        default=None,
-        help='输入框位置,格式: "x,y" (例如: "1180,965"),如果提供则直接使用该位置,不进行自动定位'
-    )
-    
-    args = parser.parse_args()
-    
-    # 解析输入框位置(如果提供)
-    input_box_pos = None
-    if args.input_box_pos:
-        try:
-            parts = args.input_box_pos.split(',')
-            if len(parts) == 2:
-                input_box_pos = (int(parts[0].strip()), int(parts[1].strip()))
-                logger.info(f"解析输入框位置: {input_box_pos}")
-            else:
-                logger.warning(f"输入框位置格式错误,应使用 'x,y' 格式: {args.input_box_pos}")
-        except ValueError as e:
-            logger.warning(f"无法解析输入框位置: {e}")
-    
-    # 创建工具实例
-    tool = CursorAutoChat(message=args.message, interval=args.interval, input_box_pos=input_box_pos)
-    
-    # 根据参数运行
-    if args.once:
-        tool.execute_once()
-    else:
-        tool.run_daemon()
-
-
-if __name__ == '__main__':
-    main()
-

+ 0 - 276
scripts/cursor_task_agent.py

@@ -1,276 +0,0 @@
-#!/usr/bin/env python3
-"""
-Cursor任务执行Agent
-
-这个脚本会定期检查task-manager MCP中的pending任务,
-并自动通过Cursor CLI或API触发任务执行。
-
-使用方法:
-1. 直接运行:python scripts/cursor_task_agent.py
-2. 作为后台服务运行:python scripts/cursor_task_agent.py --daemon
-3. 单次检查:python scripts/cursor_task_agent.py --once
-"""
-
-import json
-import time
-import argparse
-import logging
-import sys
-from pathlib import Path
-from datetime import datetime
-
-# 配置日志
-logging.basicConfig(
-    level=logging.INFO,
-    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
-    handlers=[
-        logging.FileHandler('logs/cursor_task_agent.log'),
-        logging.StreamHandler(sys.stdout)
-    ]
-)
-logger = logging.getLogger('CursorTaskAgent')
-
-
-class CursorTaskAgent:
-    """
-    Cursor任务执行Agent
-    
-    负责:
-    1. 定期从MCP获取pending任务
-    2. 为每个任务创建Cursor执行指令
-    3. 触发Cursor执行任务(通过创建临时提示文件)
-    """
-    
-    def __init__(self, check_interval=300, workspace_path=None):
-        """
-        初始化Agent
-        
-        Args:
-            check_interval: 检查间隔(秒),默认300秒(5分钟)
-            workspace_path: 工作区路径
-        """
-        self.check_interval = check_interval
-        self.workspace_path = workspace_path or Path(__file__).parent.parent
-        self.prompt_dir = self.workspace_path / '.cursor' / 'task_prompts'
-        self.prompt_dir.mkdir(parents=True, exist_ok=True)
-        
-        logger.info(f"Cursor Task Agent initialized")
-        logger.info(f"Workspace: {self.workspace_path}")
-        logger.info(f"Prompt directory: {self.prompt_dir}")
-        logger.info(f"Check interval: {self.check_interval}s")
-    
-    def get_pending_tasks_from_db(self):
-        """
-        从数据库直接获取pending任务
-        
-        注意:这里我们绕过MCP,直接连接数据库
-        因为MCP的轮询机制有限制
-        """
-        try:
-            import psycopg2
-            from psycopg2.extras import RealDictCursor
-            
-            # 从配置文件读取数据库连接信息
-            config_file = self.workspace_path / 'mcp-servers' / 'task-manager' / 'config.json'
-            with open(config_file, 'r', encoding='utf-8') as f:
-                config = json.load(f)
-            
-            db_uri = config['database']['uri']
-            
-            # 连接数据库
-            conn = psycopg2.connect(db_uri)
-            cursor = conn.cursor(cursor_factory=RealDictCursor)
-            
-            # 查询pending任务
-            cursor.execute("""
-                SELECT task_id, task_name, task_description, status, 
-                       code_name, code_path, create_time, create_by
-                FROM task_list
-                WHERE status = 'pending'
-                ORDER BY create_time ASC
-            """)
-            
-            tasks = cursor.fetchall()
-            
-            cursor.close()
-            conn.close()
-            
-            logger.info(f"Found {len(tasks)} pending tasks")
-            return [dict(task) for task in tasks]
-            
-        except Exception as e:
-            logger.error(f"Failed to get pending tasks from database: {e}")
-            return []
-    
-    def create_task_prompt(self, task):
-        """
-        为任务创建Cursor提示文件
-        
-        这个文件会告诉用户有新任务需要执行
-        """
-        prompt_file = self.prompt_dir / f"task_{task['task_id']}.md"
-        
-        prompt_content = f"""# 🔔 新任务通知
-
-**任务ID**: {task['task_id']}  
-**任务名称**: {task['task_name']}  
-**创建时间**: {task['create_time']}  
-**创建者**: {task['create_by']}
-
----
-
-## 📋 任务描述
-
-{task['task_description']}
-
----
-
-## 🚀 执行指令
-
-请在Cursor中执行以下操作来完成此任务:
-
-### 方式1:使用MCP工具(推荐)
-
-在Cursor Chat中输入:
-
-```
-@task-manager 请执行task_id={task['task_id']}的任务
-```
-
-或者直接使用工具:
-```
-调用工具: execute_task
-参数: {{
-  "task_id": {task['task_id']},
-  "auto_complete": true
-}}
-```
-
-### 方式2:手动执行
-
-1. 阅读上述任务描述
-2. 根据描述开发相应的Python代码
-3. 完成后调用update_task_status工具更新状态
-
----
-
-**⚠️ 注意**:任务完成后,此提示文件将自动删除。
-"""
-        
-        # 写入提示文件
-        with open(prompt_file, 'w', encoding='utf-8') as f:
-            f.write(prompt_content)
-        
-        logger.info(f"Created task prompt: {prompt_file}")
-        return prompt_file
-    
-    def check_and_notify_tasks(self):
-        """
-        检查pending任务并创建通知
-        """
-        logger.info("Checking for pending tasks...")
-        
-        tasks = self.get_pending_tasks_from_db()
-        
-        if not tasks:
-            logger.info("No pending tasks found")
-            return 0
-        
-        # 为每个任务创建提示文件
-        for task in tasks:
-            try:
-                prompt_file = self.create_task_prompt(task)
-                logger.info(f"Task {task['task_id']} ({task['task_name']}) - prompt created at {prompt_file}")
-            except Exception as e:
-                logger.error(f"Failed to create prompt for task {task['task_id']}: {e}")
-        
-        return len(tasks)
-    
-    def run_once(self):
-        """
-        执行一次检查
-        """
-        logger.info("=" * 60)
-        logger.info("Running single check...")
-        count = self.check_and_notify_tasks()
-        logger.info(f"Check completed. Found {count} pending tasks.")
-        logger.info("=" * 60)
-        return count
-    
-    def run_daemon(self):
-        """
-        作为守护进程运行,定期检查任务
-        """
-        logger.info("=" * 60)
-        logger.info("Starting Cursor Task Agent in daemon mode...")
-        logger.info(f"Will check for new tasks every {self.check_interval} seconds")
-        logger.info("Press Ctrl+C to stop")
-        logger.info("=" * 60)
-        
-        try:
-            while True:
-                try:
-                    count = self.check_and_notify_tasks()
-                    logger.info(f"Next check in {self.check_interval} seconds...")
-                    time.sleep(self.check_interval)
-                except KeyboardInterrupt:
-                    raise
-                except Exception as e:
-                    logger.error(f"Error during check: {e}")
-                    logger.info(f"Retrying in {self.check_interval} seconds...")
-                    time.sleep(self.check_interval)
-        except KeyboardInterrupt:
-            logger.info("\n" + "=" * 60)
-            logger.info("Cursor Task Agent stopped by user")
-            logger.info("=" * 60)
-
-
-def main():
-    """
-    主函数
-    """
-    parser = argparse.ArgumentParser(
-        description='Cursor任务执行Agent - 自动检查并通知pending任务'
-    )
-    parser.add_argument(
-        '--once',
-        action='store_true',
-        help='只执行一次检查,不持续运行'
-    )
-    parser.add_argument(
-        '--daemon',
-        action='store_true',
-        help='作为守护进程运行(与--once互斥)'
-    )
-    parser.add_argument(
-        '--interval',
-        type=int,
-        default=300,
-        help='检查间隔(秒),默认300秒(5分钟)'
-    )
-    
-    args = parser.parse_args()
-    
-    # 创建logs目录
-    logs_dir = Path(__file__).parent.parent / 'logs'
-    logs_dir.mkdir(exist_ok=True)
-    
-    # 创建Agent实例
-    agent = CursorTaskAgent(check_interval=args.interval)
-    
-    # 根据参数运行
-    if args.once:
-        agent.run_once()
-    else:
-        # 默认或明确指定daemon模式
-        agent.run_daemon()
-
-
-if __name__ == '__main__':
-    main()
-
-
-
-
-
-

+ 0 - 111
scripts/field_standardization.py

@@ -1,111 +0,0 @@
-#!/usr/bin/env python3
-"""
-Neo4j 字段名标准化脚本
-用于批量替换 app/core 目录下的字段名
-"""
-
-import re
-import os
-from pathlib import Path
-
-# 定义替换规则
-REPLACEMENTS = [
-    # 基本字段替换
-    (r'\bn\.name\s+CONTAINS', 'n.name_zh CONTAINS'),
-    (r'\bn\.name\s*=', 'n.name_zh ='),
-    (r'\bn\.name\s*=~', 'n.name_zh =~'),
-    (r'\bn\.name\s+as\s+name\b', 'n.name_zh as name_zh'),
-    (r'\bn\.name\s+as\s+cn_name', 'n.name_zh as cn_name'),
-    (r'text:\s*n\.name\b', 'text: n.name_zh'),
-    (r'text:\s*\(n\.name\)', 'text:(n.name_zh)'),
-    (r'name:\s*n\.name\b', 'name_zh: n.name_zh'),
-    (r'{id:\s*id\([^)]+\),\s*name:\s*[^.]+\.name\b', lambda m: str(m.group(0).replace('name:', 'name_zh:'))),
-    
-    # en_name 替换
-    (r'\bn\.en_name\s+CONTAINS', 'n.name_en CONTAINS'),
-    (r'\bn\.en_name\s*=~', 'n.name_en =~'),
-    (r'\bn\.en_name\s+as\s+en_name', 'n.name_en as en_name'),
-    (r'en_name:\s*n\.en_name', 'name_en: n.name_en'),
-    
-    # time/createTime 替换
-    (r'\bn\.time\s+CONTAINS', 'n.create_time CONTAINS'),
-    (r'\bn\.time\s+as\s+time', 'n.create_time as time'),
-    (r'ORDER\s+BY\s+n\.time', 'ORDER BY n.create_time'),
-    (r'\bn\.createTime\s+CONTAINS', 'n.create_time CONTAINS'),
-    (r'ORDER\s+BY\s+n\.createTime', 'ORDER BY n.create_time'),
-    (r'time:\s*n\.time', 'create_time: n.create_time'),
-]
-
-# 需要处理的文件列表
-FILES_TO_PROCESS = [
-    'app/core/data_model/model.py',
-    'app/core/data_resource/resource.py',
-    'app/core/data_flow/dataflows.py',
-    'app/core/production_line/production_line.py',
-]
-
-def process_file(filepath):
-    """处理单个文件"""
-    print(f"Processing: {filepath}")
-    
-    with open(filepath, 'r', encoding='utf-8') as f:
-        content = f.read()
-    
-    original_content = content
-    changes = 0
-    
-    # 应用所有替换规则
-    for pattern, replacement in REPLACEMENTS:
-        if callable(replacement):
-            # 如果replacement是函数,使用re.sub(函数作为repl参数)
-            # 类型: Callable[[re.Match[str]], str]
-            new_content = re.sub(pattern, replacement, content)  # type: ignore[arg-type]
-        else:
-            # 如果replacement是字符串,直接使用
-            new_content = re.sub(pattern, str(replacement), content)
-        
-        if new_content != content:
-            changes += len(re.findall(pattern, content))
-            content = new_content
-    
-    # 如果有变更,写入文件
-    if content != original_content:
-        with open(filepath, 'w', encoding='utf-8') as f:
-            f.write(content)
-        print(f"  ✓ Applied {changes} changes")
-        return changes
-    else:
-        print(f"  - No changes needed")
-        return 0
-
-def main():
-    """主函数"""
-    print("=" * 60)
-    print("Neo4j 字段名标准化脚本")
-    print("=" * 60)
-    
-    total_changes = 0
-    processed_files = 0
-    
-    for filepath in FILES_TO_PROCESS:
-        if os.path.exists(filepath):
-            changes = process_file(filepath)
-            total_changes += changes
-            if changes > 0:
-                processed_files += 1
-        else:
-            print(f"Warning: {filepath} not found")
-    
-    print("=" * 60)
-    print(f"Summary:")
-    print(f"  Files processed: {processed_files}")
-    print(f"  Total changes: {total_changes}")
-    print("=" * 60)
-
-if __name__ == '__main__':
-    main()
-
-
-
-
-

+ 0 - 29
scripts/run_once.bat

@@ -1,29 +0,0 @@
-@echo off
-chcp 65001 >nul
-REM 执行一次任务检查(不循环)
-
-echo ================================================
-echo 执行一次任务检查
-echo ================================================
-echo.
-
-REM 切换到项目根目录
-cd /d %~dp0..
-
-REM 检查Python
-python --version >nul 2>&1
-if errorlevel 1 (
-    echo [错误] 未找到Python
-    pause
-    exit /b 1
-)
-
-echo [信息] 当前目录: %cd%
-echo [执行] 正在检查pending任务...
-echo.
-
-python scripts\auto_execute_tasks.py --once
-
-echo.
-pause
-

+ 0 - 47
scripts/start_auto_tasks.bat

@@ -1,47 +0,0 @@
-@echo off
-chcp 65001 >nul
-REM 启动自动任务执行脚本
-REM 每5分钟检查一次pending任务
-
-echo ================================================
-echo 启动自动任务执行服务...
-echo ================================================
-echo.
-
-REM 切换到项目根目录(确保相对路径正确)
-cd /d %~dp0..
-
-REM 检查Python是否安装
-python --version >nul 2>&1
-if errorlevel 1 (
-    echo [错误] 未找到Python,请先安装Python
-    pause
-    exit /b 1
-)
-
-REM 检查脚本文件是否存在
-if not exist "scripts\auto_execute_tasks.py" (
-    echo [错误] 未找到脚本文件: scripts\auto_execute_tasks.py
-    pause
-    exit /b 1
-)
-
-REM 检查数据库配置是否存在
-if not exist "mcp-servers\task-manager\config.json" (
-    echo [错误] 未找到数据库配置: mcp-servers\task-manager\config.json
-    pause
-    exit /b 1
-)
-
-REM 创建logs目录
-if not exist "logs" mkdir logs
-
-echo [信息] 当前目录: %cd%
-echo [信息] 正在启动自动任务执行服务...
-echo [信息] 检查间隔: 5分钟 (300秒)
-echo [信息] 按 Ctrl+C 可停止服务
-echo.
-
-python scripts\auto_execute_tasks.py --interval 300
-
-pause

+ 0 - 55
scripts/start_auto_tasks_background.bat

@@ -1,55 +0,0 @@
-@echo off
-chcp 65001 >nul
-REM 在后台启动自动任务执行脚本(无窗口)
-
-echo ================================================
-echo 在后台启动自动任务执行服务...
-echo ================================================
-echo.
-
-REM 切换到项目根目录(确保相对路径正确)
-cd /d %~dp0..
-
-REM 检查Python是否安装
-python --version >nul 2>&1
-if errorlevel 1 (
-    echo [错误] 未找到Python,请先安装Python
-    pause
-    exit /b 1
-)
-
-REM 检查脚本文件是否存在
-if not exist "scripts\auto_execute_tasks.py" (
-    echo [错误] 未找到脚本文件: scripts\auto_execute_tasks.py
-    pause
-    exit /b 1
-)
-
-REM 检查数据库配置是否存在
-if not exist "mcp-servers\task-manager\config.json" (
-    echo [错误] 未找到数据库配置: mcp-servers\task-manager\config.json
-    pause
-    exit /b 1
-)
-
-REM 创建logs目录
-if not exist "logs" mkdir logs
-
-echo [信息] 当前目录: %cd%
-echo [信息] 正在后台启动自动任务执行服务...
-echo [信息] 检查间隔: 5分钟 (300秒)
-echo [信息] 日志输出: logs\auto_execute.log
-echo.
-
-REM 后台运行并输出到日志
-start /B "" python scripts\auto_execute_tasks.py --interval 300 > logs\auto_execute.log 2>&1
-
-echo [成功] 服务已在后台启动!
-echo.
-echo [提示] 相关命令:
-echo   - 查看状态: scripts\check_auto_tasks.bat
-echo   - 停止服务: scripts\stop_auto_tasks.bat
-echo   - 查看日志: type logs\auto_execute.log
-echo.
-
-pause

+ 0 - 17
scripts/start_cursor_auto_chat.bat

@@ -1,17 +0,0 @@
-@echo off
-REM Cursor自动聊天工具启动脚本(前台运行)
-REM 使用方法: 双击此文件或命令行运行
-
-cd /d %~dp0\..
-
-echo ========================================
-echo Cursor自动聊天工具
-echo ========================================
-echo.
-echo 使用指定的输入框位置: 1180,965
-echo.
-
-python scripts/cursor_auto_chat.py --daemon --input-box-pos "1180,965"
-
-pause
-

+ 0 - 26
scripts/start_cursor_auto_chat_background.bat

@@ -1,26 +0,0 @@
-@echo off
-REM Cursor自动聊天工具启动脚本(后台运行)
-REM 使用方法: 双击此文件,工具将在后台运行
-
-cd /d %~dp0\..
-
-echo ========================================
-echo 正在后台启动Cursor自动聊天工具...
-echo ========================================
-echo.
-echo 使用指定的输入框位置: 1180,965
-echo.
-
-start /B python scripts/cursor_auto_chat.py --daemon --input-box-pos "1180,965"
-
-echo 工具已在后台启动
-echo 日志文件位置: logs\cursor_auto_chat.log
-echo.
-echo 要停止工具,请运行: scripts\stop_cursor_auto_chat.bat
-echo.
-
-pause
-
-
-
-

+ 0 - 36
scripts/start_cursor_task_trigger.bat

@@ -1,36 +0,0 @@
-@echo off
-REM 启动Cursor任务执行触发器(定期模式)
-
-echo ================================================
-echo 启动Cursor任务执行触发器...
-echo ================================================
-echo.
-
-REM 检查Python是否安装
-python --version >nul 2>&1
-if errorlevel 1 (
-    echo [错误] 未找到Python,请先安装Python
-    pause
-    exit /b 1
-)
-
-REM 检查脚本文件是否存在
-if not exist "scripts\trigger_cursor_execution.py" (
-    echo [错误] 未找到脚本文件: scripts\trigger_cursor_execution.py
-    pause
-    exit /b 1
-)
-
-echo [信息] 正在启动Cursor任务执行触发器...
-echo [信息] 检查间隔: 5分钟 (300秒)
-echo [信息] 任务指令文件: .cursor\task_execute_instructions.md
-echo [信息] Cursor会自动检测并执行任务
-echo [信息] 按 Ctrl+C 停止服务
-echo.
-
-REM 切换到项目根目录
-cd /d %~dp0..
-python scripts\trigger_cursor_execution.py --interval 300
-
-pause
-

+ 0 - 48
scripts/start_cursor_task_trigger_background.bat

@@ -1,48 +0,0 @@
-@echo off
-REM 在后台启动Cursor任务执行触发器(无窗口)
-
-echo ================================================
-echo 在后台启动Cursor任务执行触发器...
-echo ================================================
-echo.
-
-REM 检查Python是否安装
-python --version >nul 2>&1
-if errorlevel 1 (
-    echo [错误] 未找到Python,请先安装Python
-    pause
-    exit /b 1
-)
-
-REM 检查脚本文件是否存在
-if not exist "scripts\trigger_cursor_execution.py" (
-    echo [错误] 未找到脚本文件: scripts\trigger_cursor_execution.py
-    pause
-    exit /b 1
-)
-
-REM 创建logs目录
-if not exist "logs" mkdir logs
-
-echo [信息] 正在后台启动Cursor任务执行触发器...
-echo [信息] 检查间隔: 5分钟 (300秒)
-echo [信息] 任务指令文件: .cursor\task_execute_instructions.md
-echo [信息] 日志输出: logs\cursor_task_trigger.log
-echo.
-
-REM 切换到项目根目录并后台运行
-cd /d %~dp0..
-start /B python scripts\trigger_cursor_execution.py --interval 300 > logs\cursor_task_trigger.log 2>&1
-
-echo [成功] 服务已在后台启动!
-echo [提示] 使用 scripts\stop_cursor_task_trigger.bat 可停止服务
-echo [提示] 使用 scripts\check_cursor_task_trigger.bat 可查看服务状态
-echo.
-
-pause
-
-
-
-
-
-

+ 188 - 0
scripts/start_task_scheduler.bat

@@ -0,0 +1,188 @@
+@echo off
+chcp 65001 >nul
+REM ============================================================
+REM 自动任务调度脚本启动器
+REM ============================================================
+REM 功能:启动核心任务调度脚本 auto_execute_tasks.py
+REM 支持前台运行、后台运行、单次执行等多种模式
+REM ============================================================
+
+setlocal enabledelayedexpansion
+
+REM 切换到项目根目录
+cd /d %~dp0..
+
+echo.
+echo ========================================================
+echo           自动任务调度脚本启动器
+echo ========================================================
+echo.
+
+REM 检查 Python 是否安装
+python --version >nul 2>&1
+if errorlevel 1 (
+    echo [错误] 未找到 Python,请先安装 Python
+    pause
+    exit /b 1
+)
+
+REM 检查脚本文件是否存在
+if not exist "scripts\auto_execute_tasks.py" (
+    echo [错误] 未找到脚本文件: scripts\auto_execute_tasks.py
+    pause
+    exit /b 1
+)
+
+REM 检查数据库配置是否存在
+if not exist "mcp-servers\task-manager\config.json" (
+    echo [错误] 未找到数据库配置: mcp-servers\task-manager\config.json
+    pause
+    exit /b 1
+)
+
+REM 创建 logs 目录
+if not exist "logs" mkdir logs
+
+echo [信息] 当前目录: %cd%
+echo.
+echo 请选择运行模式:
+echo.
+echo   1. 前台运行(可以看到实时日志,按 Ctrl+C 停止)
+echo   2. 后台运行(无窗口,日志输出到 logs\auto_execute.log)
+echo   3. 执行一次(只检查一次 pending 任务)
+echo   4. 前台运行 + 启用自动 Chat
+echo   5. 后台运行 + 启用自动 Chat
+echo   6. 查看服务状态
+echo   7. 停止服务
+echo   0. 退出
+echo.
+
+set /p choice="请输入选择 [1-7, 0]: "
+
+if "%choice%"=="1" goto :run_foreground
+if "%choice%"=="2" goto :run_background
+if "%choice%"=="3" goto :run_once
+if "%choice%"=="4" goto :run_foreground_chat
+if "%choice%"=="5" goto :run_background_chat
+if "%choice%"=="6" goto :check_status
+if "%choice%"=="7" goto :stop_service
+if "%choice%"=="0" goto :exit
+
+echo [错误] 无效的选择,请重新运行
+pause
+exit /b 1
+
+:run_foreground
+echo.
+echo [启动] 前台运行模式(检查间隔: 5分钟)
+echo [提示] 按 Ctrl+C 可停止服务
+echo.
+python scripts\auto_execute_tasks.py --interval 300
+pause
+goto :exit
+
+:run_background
+echo.
+echo [启动] 后台运行模式(检查间隔: 5分钟)
+echo [信息] 日志输出到: logs\auto_execute.log
+start /B "" python scripts\auto_execute_tasks.py --interval 300 > logs\auto_execute.log 2>&1
+echo.
+echo [成功] 服务已在后台启动!
+echo.
+echo [提示] 相关命令:
+echo   - 查看日志: type logs\auto_execute.log
+echo   - 停止服务: 再次运行此脚本选择 7
+echo.
+pause
+goto :exit
+
+:run_once
+echo.
+echo [执行] 单次检查模式
+echo.
+python scripts\auto_execute_tasks.py --once
+echo.
+pause
+goto :exit
+
+:run_foreground_chat
+echo.
+set /p chat_pos="请输入 Chat 输入框位置 (格式: x,y,直接回车使用默认): "
+echo.
+echo [启动] 前台运行模式 + 自动 Chat
+if "%chat_pos%"=="" (
+    python scripts\auto_execute_tasks.py --interval 300 --enable-chat
+) else (
+    python scripts\auto_execute_tasks.py --interval 300 --enable-chat --chat-input-pos "%chat_pos%"
+)
+pause
+goto :exit
+
+:run_background_chat
+echo.
+set /p chat_pos="请输入 Chat 输入框位置 (格式: x,y,直接回车使用默认): "
+echo.
+echo [启动] 后台运行模式 + 自动 Chat
+echo [信息] 日志输出到: logs\auto_execute.log
+if "%chat_pos%"=="" (
+    start /B "" python scripts\auto_execute_tasks.py --interval 300 --enable-chat > logs\auto_execute.log 2>&1
+) else (
+    start /B "" python scripts\auto_execute_tasks.py --interval 300 --enable-chat --chat-input-pos "%chat_pos%" > logs\auto_execute.log 2>&1
+)
+echo.
+echo [成功] 服务已在后台启动!
+echo.
+pause
+goto :exit
+
+:check_status
+echo.
+echo ========================================================
+echo                   服务状态检查
+echo ========================================================
+echo.
+
+echo [进程状态]
+powershell -Command "$processes = Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like '*auto_execute_tasks.py*' }; if ($processes) { Write-Host '[运行中] 找到以下进程:' -ForegroundColor Green; $processes | ForEach-Object { Write-Host ('  进程ID: ' + $_.ProcessId) } } else { Write-Host '[未运行] 未找到 auto_execute_tasks.py 进程' -ForegroundColor Yellow }"
+
+echo.
+echo ========================================================
+echo                   最近日志(最后 20 行)
+echo ========================================================
+echo.
+
+if exist "logs\auto_execute.log" (
+    powershell -Command "Get-Content logs\auto_execute.log -Tail 20 -ErrorAction SilentlyContinue"
+) else (
+    echo [提示] 日志文件不存在
+)
+
+echo.
+echo ========================================================
+echo                   pending_tasks.json 状态
+echo ========================================================
+echo.
+
+if exist ".cursor\pending_tasks.json" (
+    echo [文件存在] .cursor\pending_tasks.json
+    powershell -Command "$tasks = Get-Content '.cursor\pending_tasks.json' -Raw -ErrorAction SilentlyContinue | ConvertFrom-Json; if ($tasks) { Write-Host ('  任务数量: ' + $tasks.Count); $tasks | ForEach-Object { Write-Host ('  - [' + $_.task_id + '] ' + $_.task_name + ' (' + $_.status + ')') } } else { Write-Host '  [空] 没有待处理任务' }"
+) else (
+    echo [提示] pending_tasks.json 不存在
+)
+
+echo.
+pause
+goto :exit
+
+:stop_service
+echo.
+echo [操作] 正在停止服务...
+powershell -Command "$processes = Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like '*auto_execute_tasks.py*' }; if ($processes) { Write-Host '[找到] 以下进程将被停止:' -ForegroundColor Yellow; $processes | ForEach-Object { Write-Host ('  进程ID: ' + $_.ProcessId); Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Write-Host '[完成] 进程已停止' -ForegroundColor Green } else { Write-Host '[提示] 未找到运行中的进程' -ForegroundColor Cyan }"
+echo.
+pause
+goto :exit
+
+:exit
+endlocal
+exit /b 0
+

+ 0 - 22
scripts/stop_auto_tasks.bat

@@ -1,22 +0,0 @@
-@echo off
-chcp 65001 >nul
-REM 停止自动任务执行脚本
-
-echo ================================================
-echo 停止自动任务执行服务...
-echo ================================================
-echo.
-
-REM 切换到项目根目录
-cd /d %~dp0..
-
-REM 使用PowerShell查找和停止进程
-echo [查找] 正在查找auto_execute_tasks.py进程...
-powershell -Command "$processes = Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like '*auto_execute_tasks.py*' }; if ($processes) { Write-Host '[找到] 以下进程将被停止:' -ForegroundColor Yellow; $processes | ForEach-Object { Write-Host ('  进程ID: ' + $_.ProcessId); Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Write-Host '[完成] 进程已停止' -ForegroundColor Green } else { Write-Host '[提示] 未找到运行中的进程' -ForegroundColor Cyan }"
-
-echo.
-echo [验证] 再次检查进程状态...
-powershell -Command "$processes = Get-WmiObject Win32_Process | Where-Object { $_.CommandLine -like '*auto_execute_tasks.py*' }; if ($processes) { Write-Host '[警告] 仍有进程在运行,请手动在任务管理器中结束' -ForegroundColor Red } else { Write-Host '[确认] 所有进程已停止' -ForegroundColor Green }"
-
-echo.
-pause

+ 0 - 26
scripts/stop_cursor_auto_chat.bat

@@ -1,26 +0,0 @@
-@echo off
-REM Cursor自动聊天工具停止脚本
-REM 使用方法: 双击此文件停止运行中的工具
-
-echo ========================================
-echo 正在停止Cursor自动聊天工具...
-echo ========================================
-echo.
-
-REM 查找并终止Python进程(运行cursor_auto_chat.py的进程)
-taskkill /F /FI "WINDOWTITLE eq *cursor_auto_chat*" 2>nul
-taskkill /F /IM python.exe /FI "COMMANDLINE eq *cursor_auto_chat.py*" 2>nul
-
-REM 如果上面的方法不行,尝试更通用的方法
-for /f "tokens=2" %%a in ('tasklist /FI "IMAGENAME eq python.exe" /FO LIST ^| findstr /I "cursor_auto_chat"') do (
-    taskkill /F /PID %%a 2>nul
-)
-
-echo 工具已停止
-echo.
-
-pause
-
-
-
-

+ 0 - 280
scripts/trigger_cursor_execution.py

@@ -1,280 +0,0 @@
-#!/usr/bin/env python3
-"""
-Cursor任务执行触发器
-
-这个脚本会读取pending_tasks.json,并以明确格式输出任务信息,
-供Cursor识别并自动执行。
-
-支持两种模式:
-1. 单次执行:python scripts/trigger_cursor_execution.py --once
-2. 定期执行:python scripts/trigger_cursor_execution.py --interval 300
-
-定期执行模式下,脚本会:
-- 定期检查processing任务
-- 自动生成执行指令文件(.cursor/task_execute_instructions.md)
-- 输出任务信息供Cursor识别
-"""
-
-import json
-import sys
-import time
-import argparse
-import logging
-from pathlib import Path
-from datetime import datetime
-
-# 配置日志
-logging.basicConfig(
-    level=logging.INFO,
-    format='%(asctime)s - %(levelname)s - %(message)s'
-)
-logger = logging.getLogger('CursorTaskTrigger')
-
-
-def get_task_file_path(task, workspace):
-    """获取任务文件路径"""
-    import os
-    import re
-    
-    task_file = task.get('task_file')
-    if not task_file and task.get('code_path'):
-        # 从任务名称生成文件名
-        task_name = task['task_name']
-        safe_name = re.sub(r'[^\w\u4e00-\u9fff]+', '_', task_name)
-        safe_name = re.sub(r'_+', '_', safe_name).strip('_')
-        code_path = task.get('code_path', 'app/core/data_flow')
-        task_file = os.path.join(workspace, code_path, f"{safe_name}.py")
-        if not os.path.exists(task_file):
-            task_file = None
-    return task_file
-
-
-def create_execute_instructions_file(workspace, processing_tasks):
-    """
-    创建任务执行指令文件,供Cursor自动读取
-    
-    Args:
-        workspace: 工作区路径
-        processing_tasks: 待处理任务列表
-    """
-    instructions_file = workspace / '.cursor' / 'task_execute_instructions.md'
-    instructions_file.parent.mkdir(parents=True, exist_ok=True)
-    
-    with open(instructions_file, 'w', encoding='utf-8') as f:
-        f.write(f"# 🤖 Cursor自动任务执行指令\n\n")
-        f.write(f"**⚠️ 重要:Cursor AI请立即执行此文件中的任务!**\n\n")
-        f.write(f"**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
-        f.write(f"**待执行任务数量**: {len(processing_tasks)}\n\n")
-        f.write(f"**执行状态**: ⏳ 等待执行\n\n")
-        f.write("---\n\n")
-        f.write(f"## 📋 执行指令\n\n")
-        f.write(f"**Cursor AI:请按照以下步骤执行任务:**\n\n")
-        f.write(f"1. 依次处理每个任务\n")
-        f.write(f"2. 打开任务文件并实现功能\n")
-        f.write(f"3. 完成后调用MCP工具更新状态\n\n")
-        f.write("---\n\n")
-        
-        for idx, task in enumerate(processing_tasks, 1):
-            task_file = get_task_file_path(task, workspace)
-            
-            f.write(f"## 🔴 任务 {idx}/{len(processing_tasks)}: [{task['task_id']}] {task['task_name']}\n\n")
-            f.write(f"**执行状态**: ⏳ 待执行\n\n")
-            f.write(f"**任务文件**: `{task_file or '未创建'}`\n\n")
-            f.write(f"### 📝 任务描述\n\n{task['task_description']}\n\n")
-            f.write(f"### ✅ 执行步骤(Cursor AI请按此执行)\n\n")
-            f.write(f"#### 步骤1:打开任务文件\n")
-            f.write(f"- 文件路径: `{task_file or 'N/A'}`\n")
-            f.write(f"- 如果文件不存在,需要先创建\n\n")
-            f.write(f"#### 步骤2:实现功能\n")
-            f.write(f"- 仔细阅读上述任务描述\n")
-            f.write(f"- 根据描述实现完整的功能\n")
-            f.write(f"- 确保代码符合项目规范(PEP 8)\n")
-            f.write(f"- 添加必要的错误处理和日志记录\n\n")
-            f.write(f"#### 步骤3:验证代码\n")
-            f.write(f"- 确保代码可以正常运行\n")
-            f.write(f"- 检查代码逻辑是否正确\n\n")
-            f.write(f"#### 步骤4:更新任务状态\n")
-            f.write(f"- 调用MCP工具 `update_task_status`\n")
-            f.write(f"- 使用以下参数:\n\n")
-            f.write(f"```json\n")
-            f.write(f"{{\n")
-            f.write(f"  \"tool\": \"update_task_status\",\n")
-            f.write(f"  \"task_id\": {task['task_id']},\n")
-            if task_file:
-                import os
-                file_name = os.path.basename(task_file)
-                file_dir = os.path.dirname(task_file).replace(str(workspace), '').strip('\\').strip('/').replace('\\', '/')
-                f.write(f"  \"code_name\": \"{file_name}\",\n")
-                f.write(f"  \"code_path\": \"{file_dir}\",\n")
-            f.write(f"  \"status\": \"completed\"\n")
-            f.write(f"}}\n")
-            f.write(f"```\n\n")
-            f.write(f"**⚠️ 重要:必须完成上述所有步骤!**\n\n")
-            f.write("---\n\n")
-    
-    logger.info(f"✅ 任务执行指令文件已创建: {instructions_file}")
-    
-    # 创建触发器文件,更新时间戳
-    trigger_file = workspace / '.cursor' / 'task_trigger.txt'
-    with open(trigger_file, 'w', encoding='utf-8') as f:
-        f.write(f"CURSOR_AUTO_EXECUTE_TASK_TRIGGER\n")
-        f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
-        f.write(f"状态: 有 {len(processing_tasks)} 个任务待执行\n\n")
-        f.write(f"此文件用于触发Cursor自动执行任务。\n")
-        f.write(f"当此文件更新时间在5分钟内时,Cursor应该检查 .cursor/task_execute_instructions.md 并执行其中的任务。\n")
-    
-    logger.info(f"✅ 触发器文件已更新: {trigger_file}")
-
-
-def execute_tasks_once():
-    """
-    执行一次任务检查和触发
-    
-    Returns:
-        找到的任务数量
-    """
-    workspace = Path(__file__).parent.parent
-    pending_tasks_file = workspace / '.cursor' / 'pending_tasks.json'
-    
-    if not pending_tasks_file.exists():
-        logger.info("⚠️ 没有找到pending任务文件")
-        return 0
-    
-    try:
-        with open(pending_tasks_file, 'r', encoding='utf-8') as f:
-            pending_tasks = json.load(f)
-        
-        if not pending_tasks:
-            logger.info("✅ 没有pending任务")
-            return 0
-        
-        # 处理所有任务(如果没有status字段,默认为processing)
-        # 只处理状态为processing或没有status的任务
-        processing_tasks = [
-            t for t in pending_tasks 
-            if t.get('status', 'processing') == 'processing' or 'status' not in t
-        ]
-        
-        if not processing_tasks:
-            logger.info("✅ 没有processing状态的任务")
-            return 0
-        
-        logger.info(f"📋 找到 {len(processing_tasks)} 个processing任务,需要执行")
-        
-        # 创建执行指令文件
-        create_execute_instructions_file(workspace, processing_tasks)
-        
-        # 输出任务信息供Cursor识别
-        print("\n" + "=" * 80)
-        print(f"🤖 [CURSOR-AUTO-EXECUTE] 发现 {len(processing_tasks)} 个待执行任务")
-        print("=" * 80 + "\n")
-        
-        for task in processing_tasks:
-            task_file = get_task_file_path(task, workspace)
-            
-            print(f"\n🤖 [CURSOR-EXECUTE-TASK]")
-            print(f"任务ID: {task['task_id']}")
-            print(f"任务名称: {task['task_name']}")
-            print(f"任务文件: {task_file or '未创建'}")
-            print(f"\n任务描述:\n{task['task_description']}")
-            print(f"\n执行指令:")
-            print(f"1. 打开任务文件: {task_file or task.get('code_path', 'app/core/data_flow') + '/' + task['task_name'] + '.py'}")
-            print(f"2. 根据任务描述实现功能")
-            print(f"3. 调用MCP工具更新状态:")
-            print(f"   工具: update_task_status")
-            print(f"   参数: {{")
-            print(f"     \"task_id\": {task['task_id']},")
-            if task_file:
-                import os
-                file_path = task_file
-                file_name = os.path.basename(file_path)
-                file_dir = os.path.dirname(file_path).replace(str(workspace), '').strip('\\').strip('/').replace('\\', '/')
-                print(f"     \"code_name\": \"{file_name}\",")
-                print(f"     \"code_path\": \"{file_dir}\",")
-            print(f"     \"status\": \"completed\"")
-            print(f"   }}")
-            print(f"\n🔚 [END-CURSOR-EXECUTE-TASK]\n")
-            print("=" * 80)
-        
-        print(f"\n💡 提示:任务执行指令已保存到 .cursor/task_execute_instructions.md")
-        print(f"💡 Cursor可以自动读取此文件并执行任务\n")
-        
-        return len(processing_tasks)
-        
-    except Exception as e:
-        logger.error(f"❌ 读取任务文件失败: {e}")
-        return 0
-
-
-def execute_tasks_loop(interval=300):
-    """
-    循环执行任务检查和触发
-    
-    Args:
-        interval: 检查间隔(秒),默认300秒(5分钟)
-    """
-    logger.info("=" * 80)
-    logger.info("🚀 Cursor任务执行触发器已启动(定期模式)")
-    logger.info(f"⏰ 检查间隔: {interval}秒 ({interval//60}分钟)")
-    logger.info("按 Ctrl+C 停止服务")
-    logger.info("=" * 80 + "\n")
-    
-    try:
-        while True:
-            try:
-                count = execute_tasks_once()
-                
-                if count > 0:
-                    logger.info(f"\n✅ 已触发 {count} 个任务执行")
-                    logger.info(f"💡 任务指令已保存到 .cursor/task_execute_instructions.md")
-                    logger.info(f"💡 Cursor将自动读取并执行任务\n")
-                
-                logger.info(f"\n⏳ 下次检查时间: {interval}秒后...")
-                time.sleep(interval)
-                
-            except KeyboardInterrupt:
-                raise
-            except Exception as e:
-                logger.error(f"❌ 执行出错: {e}")
-                logger.info(f"⏳ {interval}秒后重试...")
-                time.sleep(interval)
-                
-    except KeyboardInterrupt:
-        logger.info("\n" + "=" * 80)
-        logger.info("⛔ 用户停止了Cursor任务执行触发器")
-        logger.info("=" * 80)
-
-
-def main():
-    """
-    主函数
-    """
-    parser = argparse.ArgumentParser(
-        description='Cursor任务执行触发器 - 定期检查并触发任务执行'
-    )
-    parser.add_argument(
-        '--once',
-        action='store_true',
-        help='只执行一次检查,不持续运行'
-    )
-    parser.add_argument(
-        '--interval',
-        type=int,
-        default=300,
-        help='检查间隔(秒),默认300秒(5分钟)'
-    )
-    
-    args = parser.parse_args()
-    
-    if args.once:
-        # 执行一次
-        count = execute_tasks_once()
-        logger.info(f"\n✅ 完成!找到 {count} 个待执行任务")
-    else:
-        # 循环执行
-        execute_tasks_loop(interval=args.interval)
-
-
-if __name__ == '__main__':
-    main()
-