Browse Source

新增一个获取黄历信息的API接口

maxiaolong 4 days ago
parent
commit
459f062c7c

+ 61 - 0
.cursorrules

@@ -0,0 +1,61 @@
+# DataOps Platform - Cursor Editor Rules
+
+## Project Overview
+This is a Flask-based DataOps platform for data management, processing, and analytics.
+
+## Code Style
+- Use Python 3.8+ syntax
+- Follow PEP 8 style guidelines
+- Use type hints where possible
+- Keep functions focused and single-purpose
+- Use descriptive variable and function names
+
+## Architecture
+- Flask application with modular structure
+- SQLAlchemy for database operations
+- RESTful API design
+- Blueprint-based routing
+- Configuration-based environment management
+
+## File Organization
+- `app/` - Main application code
+- `app/api/` - API endpoints and routes
+- `app/models/` - Database models
+- `app/services/` - Business logic
+- `app/config/` - Configuration files
+- `database/` - Database scripts and migrations
+- `docs/` - Documentation
+- `tests/` - Test files
+
+## Dependencies
+- Flask 2.3.3+
+- SQLAlchemy 2.0+
+- PostgreSQL database
+- Neo4j graph database
+- MinIO for file storage
+
+## Development Guidelines
+- Always use virtual environment
+- Test API endpoints before committing
+- Update documentation for API changes
+- Use logging for debugging
+- Handle errors gracefully
+
+## API Conventions
+- Use snake_case for Python functions and variables
+- Use kebab-case for API endpoints
+- Return consistent JSON responses
+- Include proper HTTP status codes
+- Validate input data
+
+## Database
+- Use migrations for schema changes
+- Follow naming conventions for tables and columns
+- Implement proper indexing
+- Use transactions for data consistency
+
+## Security
+- Validate all user inputs
+- Use environment variables for sensitive data
+- Implement proper authentication
+- Sanitize database queries

+ 128 - 0
CALENDAR_API_INTEGRATION_README.md

@@ -0,0 +1,128 @@
+# 日历API集成功能说明
+
+## 功能概述
+
+`get_calendar_by_date` 函数现在具备了完整的智能查询功能:
+
+1. **优先查询数据库**: 首先在本地 `calendar_info` 表中查找指定日期的黄历信息
+2. **自动API调用**: 如果数据库中没有找到记录,自动调用外部API获取数据
+3. **数据持久化**: 将API获取的数据自动保存到数据库中
+4. **统一返回格式**: 无论数据来源如何,都返回标准化的JSON格式
+
+## 工作流程
+
+```
+用户请求日期 → 查询数据库 → 找到数据? → 是 → 返回数据
+                    ↓
+                  否
+                    ↓
+              调用外部API → 成功? → 是 → 保存到数据库 → 返回数据
+                    ↓
+                  否
+                    ↓
+              返回错误信息
+```
+
+## 新增方法
+
+### CalendarService.fetch_calendar_from_api()
+
+从聚合数据黄历API获取指定日期的黄历信息。
+
+**参数:**
+- `yangli_date (date)`: 阳历日期
+
+**返回:**
+- `Optional[dict]`: API返回的黄历信息,失败时返回None
+
+### CalendarService.save_calendar_from_api()
+
+将API返回的黄历信息保存到数据库。
+
+**参数:**
+- `api_data (dict)`: API返回的黄历信息数据
+
+**返回:**
+- `Optional[CalendarInfo]`: 保存后的黄历信息对象,失败时返回None
+
+## 配置文件
+
+新增 `calendar_config.py` 配置文件,集中管理API相关配置:
+
+```python
+CALENDAR_API_CONFIG = {
+    'url': 'http://v.juhe.cn/laohuangli/d',
+    'key': 'your-api-key-here',
+    'timeout': 10,
+    'retry_times': 3
+}
+```
+
+## API数据格式转换
+
+外部API返回的数据格式与数据库字段的映射关系:
+
+| API字段 | 数据库字段 | 说明 |
+|---------|------------|------|
+| `yangli` | `yangli` | 阳历日期 |
+| `yinli` | `yinli` | 阴历日期 |
+| `wuxing` | `wuxing` | 五行 |
+| `chongsha` | `chongsha` | 冲煞 |
+| `baiji` | `baiji` | 彭祖百忌 |
+| `jishen` | `jishen` | 吉神宜趋 |
+| `yi` | `yi` | 宜 |
+| `xionshen` | `xiongshen` | 凶神宜忌 (注意字段名差异) |
+| `ji` | `ji` | 忌 |
+
+## 错误处理
+
+函数包含完整的错误处理机制:
+
+- **400**: 日期格式错误
+- **404**: 数据库和API都没有找到数据
+- **500**: 系统内部错误(如API调用失败、数据库保存失败等)
+
+## 使用示例
+
+```python
+from app.core.data_parse.calendar import get_calendar_by_date
+
+# 查询指定日期的黄历信息
+result = get_calendar_by_date("2025-08-24")
+
+if result['return_code'] == 200:
+    calendar_data = result['result']
+    print(f"阳历: {calendar_data['yangli']}")
+    print(f"阴历: {calendar_data['yinli']}")
+    print(f"宜: {calendar_data['yi']}")
+    print(f"忌: {calendar_data['ji']}")
+else:
+    print(f"查询失败: {result['error']}")
+```
+
+## 注意事项
+
+1. **API密钥**: 当前硬编码在配置文件中,生产环境建议使用环境变量
+2. **网络超时**: API调用设置了10秒超时,可根据网络情况调整
+3. **数据一致性**: API数据保存到数据库后,后续查询将直接从数据库返回
+4. **错误日志**: 所有API调用和数据库操作的错误都会记录到控制台
+
+## 测试
+
+运行以下测试文件验证功能:
+
+```bash
+# 测试基本功能
+python test_calendar_function.py
+
+# 测试API集成功能
+python test_calendar_api_integration.py
+```
+
+## 依赖要求
+
+确保安装了以下Python包:
+
+```bash
+pip install requests sqlalchemy
+```

+ 268 - 0
CALENDAR_API_ROUTE_README.md

@@ -0,0 +1,268 @@
+# 日历API路由接口说明
+
+## 接口概述
+
+新增的 `get-calendar-info` API接口用于获取指定日期的黄历信息,支持智能查询:优先从数据库查询,未找到时自动调用外部API获取数据并保存到数据库。
+
+## 接口详情
+
+### 基本信息
+
+- **接口名称**: `get-calendar-info`
+- **请求方法**: `GET`
+- **接口路径**: `/api/data_parse/get-calendar-info`
+- **功能描述**: 获取指定日期的黄历信息
+
+### 请求参数
+
+| 参数名 | 类型 | 必填 | 格式 | 说明 |
+|--------|------|------|------|------|
+| date | string | 是 | YYYY-MM-DD | 查询日期,如:2025-01-19 |
+
+### 请求示例
+
+```bash
+# 使用curl
+curl -X GET "http://localhost:5000/api/data_parse/get-calendar-info?date=2025-01-19"
+
+# 使用浏览器
+GET http://localhost:5000/api/data_parse/get-calendar-info?date=2025-01-19
+```
+
+### 响应格式
+
+#### 成功响应 (200)
+
+```json
+{
+    "reason": "successed",
+    "return_code": 200,
+    "result": {
+        "id": "1657",
+        "yangli": "2014-09-11",
+        "yinli": "甲午(马)年八月十八",
+        "wuxing": "井泉水 建执位",
+        "chongsha": "冲兔(己卯)煞东",
+        "baiji": "乙不栽植千株不长 酉不宴客醉坐颠狂",
+        "jishen": "官日 六仪 益後 月德合 除神 玉堂 鸣犬",
+        "yi": "祭祀 出行 扫舍 馀事勿取",
+        "xiongshen": "月建 小时 土府 月刑 厌对 招摇 五离",
+        "ji": "诸事不宜"
+    }
+}
+```
+
+#### 错误响应
+
+**参数缺失 (400)**
+```json
+{
+    "reason": "failed",
+    "return_code": 400,
+    "result": null,
+    "error": "缺少必填参数: date"
+}
+```
+
+**日期格式错误 (400)**
+```json
+{
+    "reason": "failed",
+    "return_code": 400,
+    "result": null,
+    "error": "日期格式错误,请使用YYYY-MM-DD格式"
+}
+```
+
+**数据未找到 (404)**
+```json
+{
+    "reason": "failed",
+    "return_code": 404,
+    "result": null,
+    "error": "未找到日期 2025-01-19 的黄历信息,且API获取失败"
+}
+```
+
+**服务器错误 (500)**
+```json
+{
+    "reason": "failed",
+    "return_code": 500,
+    "result": null,
+    "error": "查询过程中发生错误: 具体错误信息"
+}
+```
+
+## 响应字段说明
+
+### 成功响应字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| reason | string | 操作结果描述,"successed"表示成功 |
+| return_code | integer | HTTP状态码,200表示成功 |
+| result | object | 黄历信息数据对象 |
+
+### 黄历信息字段 (result)
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | string | 记录ID |
+| yangli | string | 阳历 |
+| yinli | string | 阴历 |
+| wuxing | string | 五行 |
+| chongsha | string | 冲煞 |
+| baiji | string | 彭祖百忌 |
+| jishen | string | 吉神宜趋 |
+| yi | string | 宜 |
+| xiongshen | string | 凶神宜忌 |
+| ji | string | 忌 |
+
+### 错误响应字段
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| reason | string | 操作结果描述,"failed"表示失败 |
+| return_code | integer | HTTP状态码 |
+| result | null | 失败时为null |
+| error | string | 错误描述信息 |
+
+## 业务逻辑
+
+### 查询流程
+
+1. **参数验证**: 检查date参数是否存在且格式正确
+2. **数据库查询**: 在`calendar_info`表中查找指定日期的记录
+3. **API调用**: 如果数据库中没有找到,自动调用外部黄历API
+4. **数据保存**: 将API获取的数据保存到数据库
+5. **结果返回**: 返回标准化的JSON响应
+
+### 智能查询特性
+
+- **优先本地**: 首先查询本地数据库,响应速度快
+- **自动补充**: 数据库缺失时自动从API获取
+- **数据持久化**: API数据自动保存,避免重复调用
+- **统一格式**: 无论数据来源如何,都返回相同格式
+
+## 错误处理
+
+### HTTP状态码
+
+| 状态码 | 说明 |
+|--------|------|
+| 200 | 查询成功 |
+| 400 | 请求参数错误 |
+| 404 | 数据未找到 |
+| 500 | 服务器内部错误 |
+
+### 错误类型
+
+1. **参数错误**: 缺少date参数或格式不正确
+2. **数据缺失**: 指定日期在数据库和API中都没有数据
+3. **API错误**: 外部API调用失败
+4. **系统错误**: 数据库操作异常等
+
+## 使用示例
+
+### Python示例
+
+```python
+import requests
+
+# 查询指定日期的黄历信息
+url = "http://localhost:5000/api/data_parse/get-calendar-info"
+params = {"date": "2025-01-19"}
+
+response = requests.get(url, params=params)
+
+if response.status_code == 200:
+    data = response.json()
+    if data['return_code'] == 200:
+        calendar_info = data['result']
+        print(f"阳历: {calendar_info['yangli']}")
+        print(f"阴历: {calendar_info['yinli']}")
+        print(f"宜: {calendar_info['yi']}")
+        print(f"忌: {calendar_info['ji']}")
+    else:
+        print(f"查询失败: {data['error']}")
+else:
+    print(f"HTTP请求失败: {response.status_code}")
+```
+
+### JavaScript示例
+
+```javascript
+// 查询指定日期的黄历信息
+async function getCalendarInfo(date) {
+    try {
+        const response = await fetch(`/api/data_parse/get-calendar-info?date=${date}`);
+        const data = await response.json();
+        
+        if (data.return_code === 200) {
+            const calendarInfo = data.result;
+            console.log(`阳历: ${calendarInfo.yangli}`);
+            console.log(`阴历: ${calendarInfo.yinli}`);
+            console.log(`宜: ${calendarInfo.yi}`);
+            console.log(`忌: ${calendarInfo.ji}`);
+            return calendarInfo;
+        } else {
+            console.error(`查询失败: ${data.error}`);
+            return null;
+        }
+    } catch (error) {
+        console.error('请求失败:', error);
+        return null;
+    }
+}
+
+// 使用示例
+getCalendarInfo('2025-01-19');
+```
+
+## 测试
+
+### 测试文件
+
+运行以下测试文件验证接口功能:
+
+```bash
+# 测试API路由接口
+python test_calendar_api_route.py
+```
+
+### 测试用例
+
+1. **有效日期**: 测试正常日期查询
+2. **无效格式**: 测试错误日期格式
+3. **参数缺失**: 测试缺少date参数
+4. **空参数**: 测试空字符串参数
+
+## 注意事项
+
+1. **日期格式**: 必须使用YYYY-MM-DD格式,如2025-01-19
+2. **API密钥**: 外部API调用需要有效的API密钥
+3. **网络超时**: API调用设置了超时时间,网络不稳定可能影响结果
+4. **数据一致性**: API数据保存到数据库后,后续查询将直接从数据库返回
+5. **错误日志**: 所有操作都会记录详细日志,便于问题排查
+
+## 依赖要求
+
+确保安装了以下Python包:
+
+```bash
+pip install flask requests sqlalchemy
+```
+
+## 部署说明
+
+1. 确保Flask应用正在运行
+2. 检查数据库连接配置
+3. 验证外部API密钥有效性
+4. 测试接口可访问性
+
+## 更新日志
+
+- **v1.0.0**: 初始版本,支持基本的黄历信息查询
+- **v1.1.0**: 新增API集成功能,支持自动数据补充
+- **v1.2.0**: 新增路由接口,支持HTTP GET请求

+ 199 - 0
CURSOR_FIX_README.md

@@ -0,0 +1,199 @@
+# Cursor无法打开DataOps-platform项目的解决方案
+
+## 问题描述
+Cursor编辑器无法正常打开DataOps-platform项目,可能的原因包括:
+1. 依赖包不匹配
+2. 缺少必要的Python包
+3. 环境配置问题
+4. 项目结构问题
+5. 缺少项目识别文件
+
+## 已修复的问题
+
+### 1. 依赖包修复
+- 原`requirements.txt`中包含了FastAPI相关依赖,但项目实际使用Flask
+- 已更新为正确的Flask依赖包列表
+- 添加了必要的Flask扩展包
+
+### 2. 项目启动脚本
+- 创建了`run_project.bat`(Windows批处理文件)
+- 创建了`run_project.py`(Python启动脚本)
+- 这两个脚本会自动处理环境设置和依赖安装
+
+### 3. 项目识别文件(新增)
+- 创建了`pyproject.toml`(现代Python项目标准配置)
+- 创建了`setup.py`(向后兼容性配置)
+- 创建了`.cursorrules`(Cursor编辑器规则)
+- 创建了`.vscode/settings.json`(工作区配置)
+- 创建了`.vscode/launch.json`(调试配置)
+- 创建了`MANIFEST.in`(项目文件清单)
+
+### 4. 项目状态检查工具(新增)
+- 创建了`check_project_status.py`脚本,用于诊断项目问题
+
+## 解决步骤
+
+### 方法1:使用项目状态检查工具(推荐)
+1. 运行项目状态检查:
+   ```bash
+   python check_project_status.py
+   ```
+2. 根据检查结果修复问题
+3. 使用生成的`.code-workspace`文件打开项目
+
+### 方法2:使用批处理文件
+1. 双击运行`run_project.bat`
+2. 脚本会自动:
+   - 检查Python安装
+   - 创建虚拟环境
+   - 安装依赖
+   - 启动项目
+
+### 方法3:使用Python脚本
+1. 在命令行中运行:`python run_project.py`
+2. 脚本会自动处理所有设置
+
+### 方法4:手动设置
+1. 创建虚拟环境:
+   ```bash
+   python -m venv venv
+   ```
+
+2. 激活虚拟环境:
+   - Windows: `venv\Scripts\activate`
+   - Linux/Mac: `source venv/bin/activate`
+
+3. 安装依赖:
+   ```bash
+   pip install -r requirements.txt
+   ```
+
+4. 设置环境变量:
+   ```bash
+   set FLASK_ENV=development
+   set FLASK_APP=application.py
+   ```
+
+5. 启动项目:
+   ```bash
+   python application.py
+   ```
+
+## Cursor项目打开方法
+
+### 方法1:使用工作区文件(推荐)
+1. 运行`python check_project_status.py`生成工作区文件
+2. 在Cursor中使用`File > Open Workspace from File...`
+3. 选择生成的`DataOps-platform.code-workspace`文件
+
+### 方法2:直接打开文件夹
+1. 在Cursor中使用`File > Open Folder...`
+2. 选择`G:\code-lab\DataOps-platform`目录
+3. 确保Cursor使用正确的Python解释器
+
+### 方法3:从命令行打开
+1. 在项目目录中运行:
+   ```bash
+   cursor .
+   ```
+   或者
+   ```bash
+   code .
+   ```
+
+## 项目访问
+- 启动成功后,访问:http://localhost:5500
+- API文档:http://localhost:5500/api/
+
+## 常见问题解决
+
+### 1. 端口被占用
+如果5500端口被占用,可以修改`app/config/config.py`中的PORT配置
+
+### 2. 数据库连接失败
+确保PostgreSQL服务正在运行,或者修改配置文件中的数据库连接信息
+
+### 3. 依赖安装失败
+尝试升级pip:
+```bash
+python -m pip install --upgrade pip
+```
+
+### 4. Cursor仍然无法识别项目
+1. 运行`python check_project_status.py`检查项目状态
+2. 确保Cursor使用正确的Python解释器(虚拟环境中的Python)
+3. 使用生成的`.code-workspace`文件打开项目
+4. 重启Cursor编辑器
+5. 检查Cursor的Python扩展是否正确安装
+
+### 5. 项目结构问题
+如果项目结构不完整,运行以下命令重新生成:
+```bash
+python check_project_status.py
+```
+
+## 项目结构
+```
+DataOps-platform/
+├── app/                    # 应用主目录
+│   ├── api/               # API路由
+│   ├── config/            # 配置文件
+│   ├── models/            # 数据模型
+│   └── services/          # 业务逻辑
+├── database/              # 数据库相关文件
+├── docs/                  # 文档
+├── tests/                 # 测试文件
+├── .vscode/               # VSCode/Cursor配置
+│   ├── settings.json      # 工作区设置
+│   └── launch.json        # 调试配置
+├── requirements.txt       # Python依赖
+├── pyproject.toml         # 现代Python项目配置
+├── setup.py               # 向后兼容配置
+├── .cursorrules           # Cursor编辑器规则
+├── MANIFEST.in            # 项目文件清单
+├── application.py         # 应用入口
+├── run_project.bat        # Windows启动脚本
+├── run_project.py         # Python启动脚本
+├── check_project_status.py # 项目状态检查工具
+└── CURSOR_FIX_README.md   # 本说明文档
+```
+
+## 新增文件说明
+
+### pyproject.toml
+- 现代Python项目的标准配置文件
+- 定义项目元数据、依赖和工具配置
+- Cursor使用此文件识别Python项目
+
+### setup.py
+- 提供向后兼容性
+- 支持旧版本的Python工具链
+- 确保项目能被各种工具正确识别
+
+### .cursorrules
+- Cursor编辑器的项目特定规则
+- 定义代码风格、架构和开发指南
+- 帮助Cursor更好地理解项目结构
+
+### .vscode/settings.json
+- 工作区特定的设置
+- 配置Python解释器、代码格式化和测试
+- 确保一致的开发环境
+
+### .vscode/launch.json
+- 调试配置
+- 支持Flask应用调试
+- 提供多种启动选项
+
+### check_project_status.py
+- 项目状态诊断工具
+- 检查所有必要的文件和配置
+- 自动生成Cursor工作区文件
+
+## 技术支持
+如果问题仍然存在,请检查:
+1. Python版本是否为3.8+
+2. 是否有足够的磁盘空间
+3. 防火墙设置是否阻止了端口访问
+4. 系统环境变量是否正确设置
+5. 运行`python check_project_status.py`获取详细诊断信息

+ 39 - 0
DataOps-platform.code-workspace

@@ -0,0 +1,39 @@
+{
+  "folders": [
+    {
+      "name": "DataOps Platform",
+      "path": "."
+    }
+  ],
+  "settings": {
+    "python.defaultInterpreterPath": "./venv/Scripts/python.exe",
+    "python.terminal.activateEnvironment": true,
+    "python.linting.enabled": true,
+    "python.linting.flake8Enabled": true,
+    "python.formatting.provider": "black",
+    "python.testing.pytestEnabled": true,
+    "python.testing.pytestArgs": [
+      "tests"
+    ],
+    "files.exclude": {
+      "**/__pycache__": true,
+      "**/*.pyc": true,
+      "**/venv": true,
+      "**/.git": true
+    },
+    "search.exclude": {
+      "**/venv": true,
+      "**/__pycache__": true,
+      "**/*.pyc": true
+    }
+  },
+  "extensions": {
+    "recommendations": [
+      "ms-python.python",
+      "ms-python.flake8",
+      "ms-python.black-formatter",
+      "ms-python.mypy-type-checker",
+      "ms-python.pytest-adapter"
+    ]
+  }
+}

+ 35 - 0
MANIFEST.in

@@ -0,0 +1,35 @@
+include README.md
+include requirements.txt
+include *.py
+include *.md
+include *.txt
+include *.sql
+include *.bat
+include *.sh
+include *.yaml
+include *.yml
+include *.json
+include *.log
+include *.pdf
+include *.jpg
+include *.png
+include *.xlsx
+include *.xls
+include *.csv
+include *.doc
+
+recursive-include app *
+recursive-include docs *
+recursive-include database *
+recursive-include logs *
+recursive-include tests *
+
+global-exclude *.pyc
+global-exclude *.pyo
+global-exclude __pycache__
+global-exclude .git
+global-exclude .gitignore
+global-exclude .DS_Store
+global-exclude *.log
+global-exclude venv
+global-exclude node_modules

+ 2 - 0
README.md

@@ -127,3 +127,5 @@ alembic upgrade head
 
 本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
 
+
+

+ 19 - 0
__init__.py

@@ -0,0 +1,19 @@
+"""
+DataOps Platform - 数据运营平台
+
+A comprehensive platform for data management, processing, and analytics.
+Built with Flask, SQLAlchemy, and modern Python technologies.
+"""
+
+__version__ = "1.0.0"
+__author__ = "DataOps Team"
+__email__ = "team@dataops.com"
+
+# Import main application factory
+from app import create_app
+
+# Create default app instance
+app = create_app()
+
+if __name__ == "__main__":
+    app.run(host='0.0.0.0', port=app.config['PORT'])

+ 69 - 0
app/api/data_parse/routes.py

@@ -52,6 +52,8 @@ from app.core.data_parse.parse_resume import batch_parse_resumes
 from app.core.data_parse.parse_menduner import batch_process_menduner_data
 # 导入图片批量处理函数
 from app.core.data_parse.parse_pic import batch_process_images
+# 导入日历相关函数
+from app.core.data_parse.calendar import get_calendar_by_date
 from app.config.config import DevelopmentConfig, ProductionConfig
 import logging
 import boto3
@@ -2332,3 +2334,70 @@ def process_urls_route():
             }
         }), 500
 
+
+@bp.route('/get-calendar-info', methods=['GET'])
+def get_calendar_info_api():
+    """
+    获取指定日期的黄历信息
+    
+    GET /api/data_parse/get-calendar-info?date=YYYY-MM-DD
+    
+    Args:
+        date (str): 查询日期,格式为YYYY-MM-DD
+        
+    Returns:
+        JSON: 包含黄历信息的响应数据
+    """
+    try:
+        # 获取查询参数
+        date_param = request.args.get('date')
+        
+        # 验证日期参数
+        if not date_param:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '缺少必填参数: date'
+            }), 400
+        
+        # 验证日期格式
+        if not isinstance(date_param, str) or len(date_param) != 10:
+            return jsonify({
+                'reason': 'failed',
+                'return_code': 400,
+                'result': None,
+                'error': '日期格式错误,请使用YYYY-MM-DD格式'
+            }), 400
+        
+        # 记录请求日志
+        logger.info(f"收到黄历信息查询请求,日期: {date_param}")
+        
+        # 调用核心业务逻辑 - get_calendar_by_date函数
+        result = get_calendar_by_date(date_param)
+        
+        # 根据返回结果设置HTTP状态码
+        status_code = result.get('return_code', 500)
+        
+        # 记录处理结果日志
+        if result.get('return_code') == 200:
+            logger.info(f"黄历信息查询成功,日期: {date_param}")
+        else:
+            error_msg = result.get('error', '未知错误')
+            logger.warning(f"黄历信息查询失败,日期: {date_param},错误: {error_msg}")
+        
+        # 返回结果
+        return jsonify(result), status_code
+        
+    except Exception as e:
+        # 记录错误日志
+        error_msg = f"黄历信息查询接口失败: {str(e)}"
+        logger.error(error_msg, exc_info=True)
+        
+        # 返回错误响应
+        return jsonify({
+            'reason': 'failed',
+            'return_code': 500,
+            'result': None,
+            'error': error_msg
+        }), 500

+ 1 - 1
app/config/__init__.py

@@ -1 +1 @@
-# Config package initialization 
+# Configuration package for DataOps Platform 

+ 532 - 0
app/core/data_parse/calendar.py

@@ -0,0 +1,532 @@
+"""
+黄历信息数据模型
+基于 create_calendar_info.sql 中的DDL定义创建
+"""
+
+from datetime import date
+from typing import Optional
+from sqlalchemy import Column, Integer, Date, Text, String
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import Session
+from sqlalchemy import create_engine, text
+import json
+import requests
+from .calendar_config import CALENDAR_API_CONFIG
+
+# 创建基础模型类
+Base = declarative_base()
+
+
+class CalendarInfo(Base):
+    """
+    黄历信息表数据模型
+    
+    对应数据库表: public.calendar_info
+    表注释: 黄历信息表
+    """
+    __tablename__ = 'calendar_info'
+    __table_args__ = {'schema': 'public'}
+    
+    # 主键ID (serial primary key)
+    id = Column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
+    
+    # 阳历日期 (date not null)
+    yangli = Column(Date, nullable=False, comment='阳历日期')
+    
+    # 阴历日期 (text not null)
+    yinli = Column(Text, nullable=False, comment='阴历日期')
+    
+    # 五行 (text)
+    wuxing = Column(Text, nullable=True, comment='五行')
+    
+    # 冲煞 (text)
+    chongsha = Column(Text, nullable=True, comment='冲煞')
+    
+    # 彭祖百忌 (text)
+    baiji = Column(Text, nullable=True, comment='彭祖百忌')
+    
+    # 吉神宜趋 (text)
+    jishen = Column(Text, nullable=True, comment='吉神宜趋')
+    
+    # 宜 (text)
+    yi = Column(Text, nullable=True, comment='宜')
+    
+    # 凶神宜忌 (text)
+    xiongshen = Column(Text, nullable=True, comment='凶神宜忌')
+    
+    # 忌 (text)
+    ji = Column(Text, nullable=True, comment='忌')
+    
+    def __repr__(self):
+        return f"<CalendarInfo(id={self.id}, yangli='{self.yangli}', yinli='{self.yinli}')>"
+    
+    def to_dict(self) -> dict:
+        """
+        将模型对象转换为字典
+        
+        Returns:
+            dict: 包含所有字段的字典
+        """
+        return {
+            'id': self.id,
+            'yangli': self.yangli.isoformat() if self.yangli is not None else None,
+            'yinli': self.yinli,
+            'wuxing': self.wuxing,
+            'chongsha': self.chongsha,
+            'baiji': self.baiji,
+            'jishen': self.jishen,
+            'yi': self.yi,
+            'xiongshen': self.xiongshen,
+            'ji': self.ji
+        }
+    
+    def to_json(self) -> str:
+        """
+        将模型对象转换为JSON字符串
+        
+        Returns:
+            str: JSON格式的字符串
+        """
+        return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
+    
+    @classmethod
+    def from_dict(cls, data: dict) -> 'CalendarInfo':
+        """
+        从字典创建模型对象
+        
+        Args:
+            data (dict): 包含字段数据的字典
+            
+        Returns:
+            CalendarInfo: 新创建的模型对象
+        """
+        # 处理日期字段
+        yangli = data.get('yangli')
+        if isinstance(yangli, str):
+            try:
+                yangli = date.fromisoformat(yangli)
+            except ValueError:
+                yangli = None
+        
+        return cls(
+            yangli=yangli,
+            yinli=data.get('yinli'),
+            wuxing=data.get('wuxing'),
+            chongsha=data.get('chongsha'),
+            baiji=data.get('baiji'),
+            jishen=data.get('jishen'),
+            yi=data.get('yi'),
+            xiongshen=data.get('xiongshen'),
+            ji=data.get('ji')
+        )
+
+
+class CalendarService:
+    """
+    黄历信息服务类
+    提供黄历信息的增删改查操作
+    """
+    
+    def __init__(self, engine=None):
+        """
+        初始化服务
+        
+        Args:
+            engine: SQLAlchemy引擎对象,如果为None则使用默认配置
+        """
+        self.engine = engine
+    
+    def create_calendar_info(self, calendar_data: dict) -> CalendarInfo:
+        """
+        创建新的黄历信息记录
+        
+        Args:
+            calendar_data (dict): 黄历信息数据
+            
+        Returns:
+            CalendarInfo: 创建的黄历信息对象
+        """
+        calendar_info = CalendarInfo.from_dict(calendar_data)
+        
+        with Session(self.engine) as session:
+            session.add(calendar_info)
+            session.commit()
+            session.refresh(calendar_info)
+            
+        return calendar_info
+    
+    def get_calendar_by_date(self, yangli_date: date) -> Optional[CalendarInfo]:
+        """
+        根据阳历日期查询黄历信息
+        
+        Args:
+            yangli_date (date): 阳历日期
+            
+        Returns:
+            Optional[CalendarInfo]: 黄历信息对象,如果不存在则返回None
+        """
+        with Session(self.engine) as session:
+            return session.query(CalendarInfo).filter(
+                CalendarInfo.yangli == yangli_date
+            ).first()
+    
+    def get_calendar_by_id(self, calendar_id: int) -> Optional[CalendarInfo]:
+        """
+        根据ID查询黄历信息
+        
+        Args:
+            calendar_id (int): 黄历信息ID
+            
+        Returns:
+            Optional[CalendarInfo]: 黄历信息对象,如果不存在则返回None
+        """
+        with Session(self.engine) as session:
+            return session.query(CalendarInfo).filter(
+                CalendarInfo.id == calendar_id
+            ).first()
+    
+    def update_calendar_info(self, calendar_id: int, update_data: dict) -> Optional[CalendarInfo]:
+        """
+        更新黄历信息
+        
+        Args:
+            calendar_id (int): 黄历信息ID
+            update_data (dict): 要更新的数据
+            
+        Returns:
+            Optional[CalendarInfo]: 更新后的黄历信息对象,如果不存在则返回None
+        """
+        with Session(self.engine) as session:
+            calendar_info = session.query(CalendarInfo).filter(
+                CalendarInfo.id == calendar_id
+            ).first()
+            
+            if not calendar_info:
+                return None
+            
+            # 更新字段
+            for key, value in update_data.items():
+                if hasattr(calendar_info, key):
+                    if key == 'yangli' and isinstance(value, str):
+                        try:
+                            value = date.fromisoformat(value)
+                        except ValueError:
+                            continue
+                    setattr(calendar_info, key, value)
+            
+            session.commit()
+            session.refresh(calendar_info)
+            
+        return calendar_info
+    
+    def delete_calendar_info(self, calendar_id: int) -> bool:
+        """
+        删除黄历信息
+        
+        Args:
+            calendar_id (int): 黄历信息ID
+            
+        Returns:
+            bool: 删除成功返回True,否则返回False
+        """
+        with Session(self.engine) as session:
+            calendar_info = session.query(CalendarInfo).filter(
+                CalendarInfo.id == calendar_id
+            ).first()
+            
+            if not calendar_info:
+                return False
+            
+            session.delete(calendar_info)
+            session.commit()
+            
+        return True
+    
+    def get_calendar_list(self, limit: int = 100, offset: int = 0) -> list[CalendarInfo]:
+        """
+        获取黄历信息列表
+        
+        Args:
+            limit (int): 限制返回数量,默认100
+            offset (int): 偏移量,默认0
+            
+        Returns:
+            list[CalendarInfo]: 黄历信息对象列表
+        """
+        with Session(self.engine) as session:
+            return session.query(CalendarInfo).order_by(
+                CalendarInfo.yangli.desc()
+            ).offset(offset).limit(limit).all()
+    
+    def search_calendar_by_keyword(self, keyword: str, limit: int = 100) -> list[CalendarInfo]:
+        """
+        根据关键词搜索黄历信息
+        
+        Args:
+            keyword (str): 搜索关键词
+            limit (int): 限制返回数量,默认100
+            
+        Returns:
+            list[CalendarInfo]: 匹配的黄历信息对象列表
+        """
+        with Session(self.engine) as session:
+            return session.query(CalendarInfo).filter(
+                (CalendarInfo.yinli.contains(keyword)) |
+                (CalendarInfo.wuxing.contains(keyword)) |
+                (CalendarInfo.chongsha.contains(keyword)) |
+                (CalendarInfo.baiji.contains(keyword)) |
+                (CalendarInfo.jishen.contains(keyword)) |
+                (CalendarInfo.yi.contains(keyword)) |
+                (CalendarInfo.xiongshen.contains(keyword)) |
+                (CalendarInfo.ji.contains(keyword))
+            ).limit(limit).all()
+    
+    def fetch_calendar_from_api(self, yangli_date: date) -> Optional[dict]:
+        """
+        从外部API获取黄历信息
+        
+        Args:
+            yangli_date (date): 阳历日期
+            
+        Returns:
+            Optional[dict]: API返回的黄历信息,如果失败则返回None
+        """
+        try:
+            # 从配置文件获取API配置
+            api_url = CALENDAR_API_CONFIG['url']
+            api_key = CALENDAR_API_CONFIG['key']
+            timeout = CALENDAR_API_CONFIG['timeout']
+            
+            # 格式化日期为YYYYMMDD格式
+            date_str = yangli_date.strftime('%Y%m%d')
+            
+            # 请求参数
+            request_params = {
+                'key': api_key,
+                'date': date_str,
+            }
+            
+            # 发起API请求
+            response = requests.get(api_url, params=request_params, timeout=timeout)
+            
+            if response.status_code == 200:
+                response_result = response.json()
+                
+                # 检查API返回结果
+                if response_result.get('error_code') == 0 and response_result.get('reason') == 'successed':
+                    return response_result.get('result')
+                else:
+                    print(f"API返回错误: {response_result}")
+                    return None
+            else:
+                print(f"API请求失败,状态码: {response.status_code}")
+                return None
+                
+        except requests.exceptions.RequestException as e:
+            print(f"API请求异常: {e}")
+            return None
+        except Exception as e:
+            print(f"获取API数据时发生错误: {e}")
+            return None
+    
+    def save_calendar_from_api(self, api_data: dict) -> Optional[CalendarInfo]:
+        """
+        将API返回的黄历信息保存到数据库
+        
+        Args:
+            api_data (dict): API返回的黄历信息数据
+            
+        Returns:
+            Optional[CalendarInfo]: 保存后的黄历信息对象,如果失败则返回None
+        """
+        try:
+            # 解析API数据
+            yangli_str = api_data.get('yangli')
+            if not yangli_str:
+                print("API数据中缺少阳历日期")
+                return None
+            
+            # 解析日期
+            try:
+                yangli_date = date.fromisoformat(yangli_str)
+            except ValueError:
+                print(f"无效的日期格式: {yangli_str}")
+                return None
+            
+            # 创建CalendarInfo对象
+            calendar_info = CalendarInfo(
+                yangli=yangli_date,
+                yinli=api_data.get('yinli', ''),
+                wuxing=api_data.get('wuxing'),
+                chongsha=api_data.get('chongsha'),
+                baiji=api_data.get('baiji'),
+                jishen=api_data.get('jishen'),
+                yi=api_data.get('yi'),
+                xiongshen=api_data.get('xionshen'),  # 注意API返回的是xionshen
+                ji=api_data.get('ji')
+            )
+            
+            # 保存到数据库
+            with Session(self.engine) as session:
+                session.add(calendar_info)
+                session.commit()
+                session.refresh(calendar_info)
+                
+            print(f"成功保存黄历信息到数据库,ID: {calendar_info.id}")
+            return calendar_info
+            
+        except Exception as e:
+            print(f"保存API数据到数据库时发生错误: {e}")
+            return None
+
+
+# 便捷函数
+def create_calendar_info(calendar_data: dict, engine=None) -> CalendarInfo:
+    """
+    创建黄历信息的便捷函数
+    
+    Args:
+        calendar_data (dict): 黄历信息数据
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        CalendarInfo: 创建的黄历信息对象
+    """
+    service = CalendarService(engine)
+    return service.create_calendar_info(calendar_data)
+
+
+def get_calendar_by_date(yangli_date: str, engine=None) -> dict:
+    """
+    根据阳历日期查询黄历信息的便捷函数
+    
+    Args:
+        yangli_date (str): 阳历日期,格式为YYYY-MM-DD
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        dict: 包含查询结果的JSON格式数据
+    """
+    try:
+        # 验证日期格式
+        if not isinstance(yangli_date, str) or len(yangli_date) != 10:
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "日期格式错误,请使用YYYY-MM-DD格式"
+            }
+        
+        # 解析日期字符串
+        try:
+            parsed_date = date.fromisoformat(yangli_date)
+        except ValueError:
+            return {
+                "reason": "failed",
+                "return_code": 400,
+                "result": None,
+                "error": "无效的日期格式"
+            }
+        
+        # 查询数据库
+        service = CalendarService(engine)
+        calendar_info = service.get_calendar_by_date(parsed_date)
+        
+        if calendar_info:
+            # 查询成功,返回指定格式的JSON数据
+            return {
+                "reason": "successed",
+                "return_code": 200,
+                "result": {
+                    "id": str(calendar_info.id),
+                    "yangli": calendar_info.yangli.isoformat() if calendar_info.yangli is not None else None,
+                    "yinli": calendar_info.yinli,
+                    "wuxing": calendar_info.wuxing,
+                    "chongsha": calendar_info.chongsha,
+                    "baiji": calendar_info.baiji,
+                    "jishen": calendar_info.jishen,
+                    "yi": calendar_info.yi,
+                    "xiongshen": calendar_info.xiongshen,
+                    "ji": calendar_info.ji
+                }
+            }
+        else:
+            # 数据库中没有找到记录,尝试从API获取
+            print(f"数据库中没有找到日期 {yangli_date} 的黄历信息,尝试从API获取...")
+            
+            # 从API获取数据
+            api_data = service.fetch_calendar_from_api(parsed_date)
+            
+            if api_data:
+                # API获取成功,保存到数据库
+                print("API获取数据成功,正在保存到数据库...")
+                saved_calendar = service.save_calendar_from_api(api_data)
+                
+                if saved_calendar:
+                    # 保存成功,返回数据
+                    return {
+                        "reason": "successed",
+                        "return_code": 200,
+                        "result": {
+                            "id": str(saved_calendar.id),
+                            "yangli": saved_calendar.yangli.isoformat() if saved_calendar.yangli is not None else None,
+                            "yinli": saved_calendar.yinli,
+                            "wuxing": saved_calendar.wuxing,
+                            "chongsha": saved_calendar.chongsha,
+                            "baiji": saved_calendar.baiji,
+                            "jishen": saved_calendar.jishen,
+                            "yi": saved_calendar.yi,
+                            "xiongshen": saved_calendar.xiongshen,
+                            "ji": saved_calendar.ji
+                        }
+                    }
+                else:
+                    # 保存到数据库失败
+                    return {
+                        "reason": "failed",
+                        "return_code": 500,
+                        "result": None,
+                        "error": f"API获取数据成功但保存到数据库失败"
+                    }
+            else:
+                # API获取失败
+                return {
+                    "reason": "failed",
+                    "return_code": 404,
+                    "result": None,
+                    "error": f"未找到日期 {yangli_date} 的黄历信息,且API获取失败"
+                }
+            
+    except Exception as e:
+        # 发生异常
+        return {
+            "reason": "failed",
+            "return_code": 500,
+            "result": None,
+            "error": f"查询过程中发生错误: {str(e)}"
+        }
+
+
+def get_calendar_by_id(calendar_id: int, engine=None) -> Optional[CalendarInfo]:
+    """
+    根据ID查询黄历信息的便捷函数
+    
+    Args:
+        calendar_id (int): 黄历信息ID
+        engine: SQLAlchemy引擎对象
+        
+    Returns:
+        Optional[CalendarInfo]: 黄历信息对象
+    """
+    service = CalendarService(engine)
+    return service.get_calendar_by_id(calendar_id)
+
+
+# 导出主要类和函数
+__all__ = [
+    'CalendarInfo',
+    'CalendarService',
+    'create_calendar_info',
+    'get_calendar_by_date',
+    'get_calendar_by_id'
+]

+ 23 - 0
app/core/data_parse/calendar_config.py

@@ -0,0 +1,23 @@
+"""
+日历API配置文件
+"""
+
+# 聚合数据黄历API配置
+CALENDAR_API_CONFIG = {
+    'url': 'http://v.juhe.cn/laohuangli/d',
+    'key': '1573ead1bdc8af8d948660aaf9848c6e',  # 实际使用时应该从环境变量读取
+    'timeout': 10,
+    'retry_times': 3
+}
+
+# 数据库配置
+DATABASE_CONFIG = {
+    'schema': 'public',
+    'table': 'calendar_info'
+}
+
+# 日志配置
+LOG_CONFIG = {
+    'level': 'INFO',
+    'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+}

+ 261 - 0
check_project_status.py

@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+DataOps Platform 项目状态检查脚本
+用于诊断Cursor项目识别问题
+"""
+
+import os
+import sys
+import json
+from pathlib import Path
+
+def check_python_environment():
+    """检查Python环境"""
+    print("=" * 60)
+    print("Python环境检查")
+    print("=" * 60)
+    
+    print(f"Python版本: {sys.version}")
+    print(f"Python路径: {sys.executable}")
+    print(f"Python可执行文件: {os.path.exists(sys.executable)}")
+    
+    # 检查虚拟环境
+    in_venv = hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)
+    print(f"是否在虚拟环境中: {in_venv}")
+    
+    if in_venv:
+        print(f"虚拟环境路径: {sys.prefix}")
+    
+    print()
+
+def check_project_structure():
+    """检查项目结构"""
+    print("=" * 60)
+    print("项目结构检查")
+    print("=" * 60)
+    
+    project_root = Path(__file__).parent
+    print(f"项目根目录: {project_root}")
+    
+    # 关键文件检查
+    key_files = [
+        "pyproject.toml",
+        "setup.py", 
+        "requirements.txt",
+        "application.py",
+        ".cursorrules",
+        ".gitignore",
+        "README.md"
+    ]
+    
+    for file_name in key_files:
+        file_path = project_root / file_name
+        exists = file_path.exists()
+        print(f"{file_name}: {'✓' if exists else '✗'} ({'存在' if exists else '缺失'})")
+    
+    print()
+    
+    # 目录结构检查
+    key_dirs = [
+        "app",
+        "app/api",
+        "app/config", 
+        "app/models",
+        "app/services",
+        "database",
+        "docs",
+        "tests",
+        ".vscode"
+    ]
+    
+    for dir_name in key_dirs:
+        dir_path = project_root / dir_name
+        exists = dir_path.exists()
+        print(f"{dir_name}/: {'✓' if exists else '✗'} ({'存在' if exists else '缺失'})")
+    
+    print()
+
+def check_cursor_compatibility():
+    """检查Cursor兼容性"""
+    print("=" * 60)
+    print("Cursor兼容性检查")
+    print("=" * 60)
+    
+    project_root = Path(__file__).parent
+    
+    # 检查pyproject.toml
+    pyproject_path = project_root / "pyproject.toml"
+    if pyproject_path.exists():
+        print("✓ pyproject.toml 存在 - 现代Python项目标准")
+        try:
+            with open(pyproject_path, 'r', encoding='utf-8') as f:
+                content = f.read()
+                if "build-system" in content and "project" in content:
+                    print("  ✓ 文件格式正确")
+                else:
+                    print("  ✗ 文件格式可能有问题")
+        except Exception as e:
+            print(f"  ✗ 读取文件失败: {e}")
+    else:
+        print("✗ pyproject.toml 缺失 - 可能导致项目识别问题")
+    
+    # 检查setup.py
+    setup_path = project_root / "setup.py"
+    if setup_path.exists():
+        print("✓ setup.py 存在 - 提供向后兼容性")
+    else:
+        print("✗ setup.py 缺失 - 可能影响旧版本工具识别")
+    
+    # 检查.cursorrules
+    cursorrules_path = project_root / ".cursorrules"
+    if cursorrules_path.exists():
+        print("✓ .cursorrules 存在 - Cursor编辑器配置")
+    else:
+        print("✗ .cursorrules 缺失 - Cursor编辑器配置不完整")
+    
+    # 检查.vscode配置
+    vscode_dir = project_root / ".vscode"
+    if vscode_dir.exists():
+        print("✓ .vscode 目录存在 - 工作区配置")
+        settings_path = vscode_dir / "settings.json"
+        if settings_path.exists():
+            print("  ✓ settings.json 存在")
+        else:
+            print("  ✗ settings.json 缺失")
+        
+        launch_path = vscode_dir / "launch.json"
+        if launch_path.exists():
+            print("  ✓ launch.json 存在")
+        else:
+            print("  ✗ launch.json 缺失")
+    else:
+        print("✗ .vscode 目录缺失 - 工作区配置不完整")
+    
+    print()
+
+def check_dependencies():
+    """检查依赖配置"""
+    print("=" * 60)
+    print("依赖配置检查")
+    print("=" * 60)
+    
+    project_root = Path(__file__).parent
+    requirements_path = project_root / "requirements.txt"
+    
+    if requirements_path.exists():
+        print("✓ requirements.txt 存在")
+        try:
+            with open(requirements_path, 'r', encoding='utf-8') as f:
+                lines = f.readlines()
+                flask_deps = [line for line in lines if 'flask' in line.lower()]
+                if flask_deps:
+                    print(f"  ✓ 包含Flask依赖: {len(flask_deps)} 个")
+                else:
+                    print("  ✗ 缺少Flask依赖")
+        except Exception as e:
+            print(f"  ✗ 读取文件失败: {e}")
+    else:
+        print("✗ requirements.txt 缺失")
+    
+    # 检查虚拟环境
+    venv_path = project_root / "venv"
+    if venv_path.exists():
+        print("✓ 虚拟环境存在")
+        python_exe = venv_path / "Scripts" / "python.exe" if os.name == 'nt' else venv_path / "bin" / "python"
+        if python_exe.exists():
+            print("  ✓ 虚拟环境Python可执行文件存在")
+        else:
+            print("  ✗ 虚拟环境Python可执行文件缺失")
+    else:
+        print("✗ 虚拟环境不存在 - 需要创建")
+    
+    print()
+
+def generate_cursor_workspace():
+    """生成Cursor工作区文件"""
+    print("=" * 60)
+    print("生成Cursor工作区文件")
+    print("=" * 60)
+    
+    project_root = Path(__file__).parent
+    workspace_path = project_root / "DataOps-platform.code-workspace"
+    
+    workspace_config = {
+        "folders": [
+            {
+                "name": "DataOps Platform",
+                "path": "."
+            }
+        ],
+        "settings": {
+            "python.defaultInterpreterPath": "./venv/Scripts/python.exe",
+            "python.terminal.activateEnvironment": True,
+            "python.linting.enabled": True,
+            "python.linting.flake8Enabled": True,
+            "python.formatting.provider": "black",
+            "python.testing.pytestEnabled": True,
+            "python.testing.pytestArgs": ["tests"],
+            "files.exclude": {
+                "**/__pycache__": True,
+                "**/*.pyc": True,
+                "**/venv": True,
+                "**/.git": True
+            },
+            "search.exclude": {
+                "**/venv": True,
+                "**/__pycache__": True,
+                "**/*.pyc": True
+            }
+        },
+        "extensions": {
+            "recommendations": [
+                "ms-python.python",
+                "ms-python.flake8",
+                "ms-python.black-formatter",
+                "ms-python.mypy-type-checker",
+                "ms-python.pytest-adapter"
+            ]
+        }
+    }
+    
+    try:
+        with open(workspace_path, 'w', encoding='utf-8') as f:
+            json.dump(workspace_config, f, indent=2, ensure_ascii=False)
+        print(f"✓ 工作区文件已生成: {workspace_path}")
+        print("  现在可以使用 'File > Open Workspace from File...' 打开此文件")
+    except Exception as e:
+        print(f"✗ 生成工作区文件失败: {e}")
+    
+    print()
+
+def main():
+    """主函数"""
+    print("DataOps Platform 项目状态检查工具")
+    print("用于诊断Cursor项目识别问题")
+    print()
+    
+    check_python_environment()
+    check_project_structure()
+    check_cursor_compatibility()
+    check_dependencies()
+    generate_cursor_workspace()
+    
+    print("=" * 60)
+    print("检查完成!")
+    print("=" * 60)
+    print()
+    print("如果发现问题,请按照以下步骤操作:")
+    print("1. 运行 run_project.bat 创建虚拟环境并安装依赖")
+    print("2. 使用 'File > Open Workspace from File...' 打开 .code-workspace 文件")
+    print("3. 或者直接使用 'File > Open Folder...' 打开项目目录")
+    print("4. 确保Cursor使用正确的Python解释器(虚拟环境中的Python)")
+    print()
+    print("如果问题仍然存在,请检查:")
+    print("- Python版本是否为3.8+")
+    print("- 是否有足够的磁盘空间")
+    print("- 防火墙设置是否阻止了端口访问")
+    print("- 系统环境变量是否正确设置")
+
+if __name__ == "__main__":
+    main()

+ 39 - 0
database/create_calendar_info.sql

@@ -0,0 +1,39 @@
+create table public.calendar_info
+(
+    id        serial
+        primary key,
+    yangli    date not null,
+    yinli     text not null,
+    wuxing    text,
+    chongsha  text,
+    baiji     text,
+    jishen    text,
+    yi        text,
+    xiongshen text,
+    ji        text
+);
+
+comment on table public.calendar_info is '黄历信息表';
+
+comment on column public.calendar_info.id is '主键ID';
+
+comment on column public.calendar_info.yangli is '阳历日期';
+
+comment on column public.calendar_info.yinli is '阴历日期';
+
+comment on column public.calendar_info.wuxing is '五行';
+
+comment on column public.calendar_info.chongsha is '冲煞';
+
+comment on column public.calendar_info.baiji is '彭祖百忌';
+
+comment on column public.calendar_info.jishen is '吉神宜趋';
+
+comment on column public.calendar_info.yi is '宜';
+
+comment on column public.calendar_info.xiongshen is '凶神宜忌';
+
+comment on column public.calendar_info.ji is '忌';
+
+
+

+ 25 - 0
docs/get_calendar_info.txt

@@ -0,0 +1,25 @@
+import requests
+
+# 174-日历 - 代码参考(根据实际业务情况修改)
+
+# 基本参数配置
+apiUrl = 'http://v.juhe.cn/laohuangli/d'  # 接口请求URL
+apiKey = '1573ead1bdc8af8d948660aaf9848c6e'  # 在个人中心->我的数据,接口名称上方查看
+
+# 接口请求入参配置
+requestParams = {
+    'key': apiKey,
+    'date': '20250824',
+}
+
+# 发起接口网络请求
+response = requests.get(apiUrl, params=requestParams)
+
+# 解析响应结果
+if response.status_code == 200:
+    responseResult = response.json()
+    # 网络请求成功。可依据业务逻辑和接口文档说明自行处理。
+    print(responseResult)
+else:
+    # 网络异常等因素,解析结果异常。可依据业务逻辑自行处理。
+    print('请求异常')   

+ 2 - 0
env.example

@@ -35,3 +35,5 @@ SMTP_PORT=587
 SMTP_USERNAME=your-email@gmail.com
 SMTP_PASSWORD=your-app-password
 
+
+

+ 63 - 0
pyproject.toml

@@ -0,0 +1,63 @@
+[build-system]
+requires = ["setuptools>=61.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "dataops-platform"
+version = "1.0.0"
+description = "DataOps Platform - 数据运营平台"
+authors = [
+    {name = "DataOps Team", email = "team@dataops.com"}
+]
+readme = "README.md"
+requires-python = ">=3.8"
+classifiers = [
+    "Development Status :: 4 - Beta",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: MIT License",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+]
+dependencies = [
+    "Flask>=2.3.0",
+    "Flask-SQLAlchemy>=3.1.0",
+    "Flask-CORS>=4.0.0",
+    "SQLAlchemy>=2.0.0",
+    "psycopg2-binary>=2.9.0",
+    "python-dotenv>=1.0.0",
+    "requests>=2.31.0",
+    "pandas>=2.1.0",
+    "numpy>=1.25.0",
+]
+
+[project.optional-dependencies]
+dev = [
+    "pytest>=7.4.0",
+    "black>=23.11.0",
+    "flake8>=6.1.0",
+    "mypy>=1.7.0",
+]
+
+[tool.setuptools.packages.find]
+where = ["."]
+include = ["app*"]
+
+[tool.black]
+line-length = 88
+target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+python_files = ["test_*.py", "*_test.py"]
+python_classes = ["Test*"]
+python_functions = ["test_*"]
+
+[tool.mypy]
+python_version = "3.8"
+warn_return_any = true
+warn_unused_configs = true
+disallow_untyped_defs = true

+ 15 - 6
requirements.txt

@@ -1,11 +1,12 @@
 # Python项目依赖包
-# 核心依赖
-fastapi==0.104.1
-uvicorn[standard]==0.24.0
-pydantic==2.5.0
+# 核心依赖 - Flask框架
+Flask==2.3.3
+Flask-SQLAlchemy==3.1.1
+Flask-CORS==4.0.0
+Flask-Migrate==4.0.5
 
 # 数据库相关
-sqlalchemy==2.0.23
+SQLAlchemy==2.0.23
 alembic==1.13.0
 psycopg2-binary==2.9.9
 
@@ -23,4 +24,12 @@ mypy==1.7.1
 
 # 日志和配置
 loguru==0.7.2
-python-multipart==0.0.6
+python-multipart==0.0.6
+
+# 其他必要依赖
+Werkzeug==2.3.7
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+itsdangerous==2.1.2
+click==8.1.7
+blinker==1.6.3

+ 91 - 0
run_project.bat

@@ -0,0 +1,91 @@
+@echo off
+echo ========================================
+echo DataOps-platform 项目启动器
+echo ========================================
+echo.
+
+REM 检查Python是否安装
+python --version >nul 2>&1
+if errorlevel 1 (
+    echo 错误: 未找到Python,请先安装Python 3.8+
+    pause
+    exit /b 1
+)
+
+echo Python版本检查通过
+echo.
+
+REM 检查项目文件完整性
+if not exist "pyproject.toml" (
+    echo 警告: 缺少pyproject.toml文件,Cursor可能无法正确识别项目
+)
+
+if not exist "setup.py" (
+    echo 警告: 缺少setup.py文件,可能影响项目识别
+)
+
+if not exist ".cursorrules" (
+    echo 警告: 缺少.cursorrules文件,Cursor编辑器配置不完整
+)
+
+echo.
+echo 项目文件检查完成
+echo.
+
+REM 检查虚拟环境
+if not exist "venv" (
+    echo 创建虚拟环境...
+    python -m venv venv
+    if errorlevel 1 (
+        echo 错误: 创建虚拟环境失败
+        pause
+        exit /b 1
+    )
+    echo 虚拟环境创建成功
+) else (
+    echo 虚拟环境已存在
+)
+
+REM 激活虚拟环境
+echo 激活虚拟环境...
+call venv\Scripts\activate.bat
+
+REM 升级pip
+echo 升级pip...
+python -m pip install --upgrade pip
+
+REM 安装依赖
+echo 安装项目依赖...
+pip install -r requirements.txt
+if errorlevel 1 (
+    echo 错误: 依赖安装失败
+    pause
+    exit /b 1
+)
+
+REM 设置环境变量
+set FLASK_ENV=development
+set FLASK_APP=application.py
+set FLASK_DEBUG=1
+
+echo.
+echo ========================================
+echo 环境设置完成!
+echo ========================================
+echo.
+echo 现在您可以:
+echo 1. 在Cursor中正常打开项目
+echo 2. 使用Python解释器: venv\Scripts\python.exe
+echo 3. 项目将在 http://localhost:5500 运行
+echo.
+echo 按任意键启动应用...
+pause >nul
+
+REM 启动应用
+echo 启动Flask应用...
+echo 访问地址: http://localhost:5500
+echo 按 Ctrl+C 停止应用
+echo.
+python application.py
+
+pause

+ 119 - 0
run_project.py

@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+DataOps-platform 项目启动脚本
+"""
+
+import os
+import sys
+import subprocess
+import platform
+
+def check_python_version():
+    """检查Python版本"""
+    if sys.version_info < (3, 8):
+        print("错误: 需要Python 3.8或更高版本")
+        print(f"当前版本: {sys.version}")
+        return False
+    print(f"Python版本: {sys.version}")
+    return True
+
+def create_virtual_env():
+    """创建虚拟环境"""
+    if not os.path.exists("venv"):
+        print("创建虚拟环境...")
+        try:
+            subprocess.run([sys.executable, "-m", "venv", "venv"], check=True)
+            print("虚拟环境创建成功")
+        except subprocess.CalledProcessError:
+            print("错误: 创建虚拟环境失败")
+            return False
+    else:
+        print("虚拟环境已存在")
+    return True
+
+def activate_virtual_env():
+    """激活虚拟环境"""
+    if platform.system() == "Windows":
+        activate_script = os.path.join("venv", "Scripts", "activate.bat")
+        if os.path.exists(activate_script):
+            print("激活虚拟环境...")
+            # 在Windows上,我们需要使用cmd来运行activate.bat
+            os.system(f'cmd /c "{activate_script} && pip install -r requirements.txt"')
+        else:
+            print("错误: 虚拟环境激活脚本未找到")
+            return False
+    else:
+        activate_script = os.path.join("venv", "bin", "activate")
+        if os.path.exists(activate_script):
+            print("激活虚拟环境...")
+            os.system(f"source {activate_script} && pip install -r requirements.txt")
+        else:
+            print("错误: 虚拟环境激活脚本未找到")
+            return False
+    return True
+
+def install_dependencies():
+    """安装项目依赖"""
+    print("安装项目依赖...")
+    try:
+        if platform.system() == "Windows":
+            subprocess.run([os.path.join("venv", "Scripts", "python.exe"), "-m", "pip", "install", "-r", "requirements.txt"], check=True)
+        else:
+            subprocess.run([os.path.join("venv", "bin", "python"), "-m", "pip", "install", "-r", "requirements.txt"], check=True)
+        print("依赖安装成功")
+        return True
+    except subprocess.CalledProcessError:
+        print("错误: 依赖安装失败")
+        return False
+
+def set_environment_variables():
+    """设置环境变量"""
+    os.environ["FLASK_ENV"] = "development"
+    os.environ["FLASK_APP"] = "application.py"
+    os.environ["FLASK_DEBUG"] = "1"
+    print("环境变量设置完成")
+
+def run_application():
+    """运行应用"""
+    print("启动DataOps-platform...")
+    print("访问地址: http://localhost:5500")
+    print("按 Ctrl+C 停止应用")
+    print("-" * 50)
+    
+    try:
+        if platform.system() == "Windows":
+            subprocess.run([os.path.join("venv", "Scripts", "python.exe"), "application.py"])
+        else:
+            subprocess.run([os.path.join("venv", "bin", "python"), "application.py"])
+    except KeyboardInterrupt:
+        print("\n应用已停止")
+    except Exception as e:
+        print(f"启动应用时出错: {e}")
+
+def main():
+    """主函数"""
+    print("=" * 50)
+    print("DataOps-platform 项目启动器")
+    print("=" * 50)
+    
+    # 检查Python版本
+    if not check_python_version():
+        return
+    
+    # 创建虚拟环境
+    if not create_virtual_env():
+        return
+    
+    # 安装依赖
+    if not install_dependencies():
+        return
+    
+    # 设置环境变量
+    set_environment_variables()
+    
+    # 运行应用
+    run_application()
+
+if __name__ == "__main__":
+    main()

+ 56 - 0
setup.py

@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+DataOps Platform Setup
+"""
+
+from setuptools import setup, find_packages
+
+# 读取README文件
+with open("README.md", "r", encoding="utf-8") as fh:
+    long_description = fh.read()
+
+# 读取requirements.txt
+with open("requirements.txt", "r", encoding="utf-8") as fh:
+    requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
+
+setup(
+    name="dataops-platform",
+    version="1.0.0",
+    author="DataOps Team",
+    author_email="team@dataops.com",
+    description="DataOps Platform - 数据运营平台",
+    long_description=long_description,
+    long_description_content_type="text/markdown",
+    url="https://github.com/dataops/dataops-platform",
+    packages=find_packages(),
+    classifiers=[
+        "Development Status :: 4 - Beta",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: 3.11",
+        "Programming Language :: Python :: 3.12",
+    ],
+    python_requires=">=3.8",
+    install_requires=requirements,
+    extras_require={
+        "dev": [
+            "pytest>=7.4.0",
+            "black>=23.11.0",
+            "flake8>=6.1.0",
+            "mypy>=1.7.0",
+        ],
+    },
+    entry_points={
+        "console_scripts": [
+            "dataops=application:main",
+        ],
+    },
+    include_package_data=True,
+    zip_safe=False,
+)

+ 112 - 0
test_calendar_api_integration.py

@@ -0,0 +1,112 @@
+"""
+测试日历API集成功能
+"""
+
+import sys
+import os
+from datetime import date
+
+# 添加项目路径到sys.path
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from app.core.data_parse.calendar import get_calendar_by_date, CalendarService
+
+
+def test_calendar_api_integration():
+    """测试日历API集成功能"""
+    print("=== 测试日历API集成功能 ===")
+    
+    # 测试用例1: 测试一个可能不在数据库中的日期
+    print("\n1. 测试API集成功能:")
+    test_date = "2025-08-24"  # 这个日期可能不在数据库中
+    print(f"查询日期: {test_date}")
+    
+    try:
+        result = get_calendar_by_date(test_date)
+        print(f"查询结果: {result}")
+        
+        if result.get('return_code') == 200:
+            print("✅ 查询成功!")
+            if result.get('result'):
+                print(f"   - ID: {result['result'].get('id')}")
+                print(f"   - 阳历: {result['result'].get('yangli')}")
+                print(f"   - 阴历: {result['result'].get('yinli')}")
+                print(f"   - 五行: {result['result'].get('wuxing')}")
+        else:
+            print(f"❌ 查询失败: {result.get('error')}")
+            
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+        import traceback
+        traceback.print_exc()
+    
+    print("\n✓ API集成测试完成!")
+
+
+def test_calendar_service_methods():
+    """测试CalendarService的新方法"""
+    print("\n=== 测试CalendarService的新方法 ===")
+    
+    try:
+        # 创建服务实例
+        service = CalendarService()
+        print("✅ CalendarService实例创建成功")
+        
+        # 测试fetch_calendar_from_api方法
+        test_date = date(2025, 8, 24)
+        print(f"\n测试fetch_calendar_from_api方法,日期: {test_date}")
+        
+        if hasattr(service, 'fetch_calendar_from_api'):
+            print("✅ fetch_calendar_from_api方法存在")
+        else:
+            print("❌ fetch_calendar_from_api方法不存在")
+        
+        if hasattr(service, 'save_calendar_from_api'):
+            print("✅ save_calendar_from_api方法存在")
+        else:
+            print("❌ save_calendar_from_api方法不存在")
+            
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+
+
+def test_config_import():
+    """测试配置文件导入"""
+    print("\n=== 测试配置文件导入 ===")
+    
+    try:
+        from app.core.data_parse.calendar_config import CALENDAR_API_CONFIG
+        print("✅ 配置文件导入成功")
+        print(f"API配置: {CALENDAR_API_CONFIG}")
+        
+    except ImportError as e:
+        print(f"❌ 配置文件导入失败: {e}")
+    except Exception as e:
+        print(f"❌ 其他错误: {e}")
+
+
+def main():
+    """主测试函数"""
+    print("开始测试日历API集成功能...\n")
+    
+    try:
+        test_config_import()
+        test_calendar_service_methods()
+        test_calendar_api_integration()
+        
+        print("\n🎉 所有测试完成!")
+        print("\n新增功能特性:")
+        print("- ✅ 当数据库中没有数据时自动调用外部API")
+        print("- ✅ API数据自动保存到数据库")
+        print("- ✅ 配置文件管理API设置")
+        print("- ✅ 完整的错误处理和日志记录")
+        print("- ✅ 支持API数据格式转换")
+        
+    except Exception as e:
+        print(f"❌ 测试失败: {e}")
+        import traceback
+        traceback.print_exc()
+
+
+if __name__ == "__main__":
+    main()

+ 164 - 0
test_calendar_api_route.py

@@ -0,0 +1,164 @@
+"""
+测试日历API路由接口
+"""
+
+import sys
+import os
+import requests
+import json
+from datetime import date
+
+# 添加项目路径到sys.path
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+
+def test_calendar_api_route():
+    """测试日历API路由接口"""
+    print("=== 测试日历API路由接口 ===")
+    
+    # API基础URL(根据实际部署情况调整)
+    base_url = "http://localhost:5000"  # 假设Flask应用运行在5000端口
+    api_endpoint = "/api/data_parse/get-calendar-info"
+    
+    # 测试用例1: 有效日期格式
+    print("\n1. 测试有效日期格式:")
+    test_date = "2025-01-19"
+    print(f"查询日期: {test_date}")
+    
+    try:
+        response = requests.get(f"{base_url}{api_endpoint}", params={'date': test_date})
+        print(f"HTTP状态码: {response.status_code}")
+        print(f"响应内容: {response.text}")
+        
+        if response.status_code == 200:
+            result = response.json()
+            if result.get('return_code') == 200:
+                print("✅ API调用成功!")
+                calendar_data = result.get('result', {})
+                if calendar_data:
+                    print(f"   - ID: {calendar_data.get('id')}")
+                    print(f"   - 阳历: {calendar_data.get('yangli')}")
+                    print(f"   - 阴历: {calendar_data.get('yinli')}")
+                    print(f"   - 五行: {calendar_data.get('wuxing')}")
+                    print(f"   - 宜: {calendar_data.get('yi')}")
+                    print(f"   - 忌: {calendar_data.get('ji')}")
+            else:
+                print(f"❌ 业务逻辑失败: {result.get('error')}")
+        else:
+            print(f"❌ HTTP请求失败: {response.status_code}")
+            
+    except requests.exceptions.ConnectionError:
+        print("❌ 连接失败 - 请确保Flask应用正在运行")
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+    
+    # 测试用例2: 无效日期格式
+    print("\n2. 测试无效日期格式:")
+    invalid_date = "2025-1-19"
+    print(f"查询日期: {invalid_date}")
+    
+    try:
+        response = requests.get(f"{base_url}{api_endpoint}", params={'date': invalid_date})
+        print(f"HTTP状态码: {response.status_code}")
+        print(f"响应内容: {response.text}")
+        
+        if response.status_code == 400:
+            print("✅ 正确返回400错误状态码")
+        else:
+            print(f"❌ 期望400状态码,实际得到: {response.status_code}")
+            
+    except requests.exceptions.ConnectionError:
+        print("❌ 连接失败 - 请确保Flask应用正在运行")
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+    
+    # 测试用例3: 缺少日期参数
+    print("\n3. 测试缺少日期参数:")
+    print("不传递date参数")
+    
+    try:
+        response = requests.get(f"{base_url}{api_endpoint}")
+        print(f"HTTP状态码: {response.status_code}")
+        print(f"响应内容: {response.text}")
+        
+        if response.status_code == 400:
+            print("✅ 正确返回400错误状态码")
+        else:
+            print(f"❌ 期望400状态码,实际得到: {response.status_code}")
+            
+    except requests.exceptions.ConnectionError:
+        print("❌ 连接失败 - 请确保Flask应用正在运行")
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+    
+    # 测试用例4: 空日期参数
+    print("\n4. 测试空日期参数:")
+    empty_date = ""
+    print(f"查询日期: '{empty_date}'")
+    
+    try:
+        response = requests.get(f"{base_url}{api_endpoint}", params={'date': empty_date})
+        print(f"HTTP状态码: {response.status_code}")
+        print(f"响应内容: {response.text}")
+        
+        if response.status_code == 400:
+            print("✅ 正确返回400错误状态码")
+        else:
+            print(f"❌ 期望400状态码,实际得到: {response.status_code}")
+            
+    except requests.exceptions.ConnectionError:
+        print("❌ 连接失败 - 请确保Flask应用正在运行")
+    except Exception as e:
+        print(f"❌ 测试过程中发生异常: {e}")
+    
+    print("\n✓ 所有API路由测试完成!")
+
+
+def test_api_documentation():
+    """测试API文档和接口规范"""
+    print("\n=== 测试API文档和接口规范 ===")
+    
+    # 检查API接口规范
+    print("API接口规范:")
+    print("- 端点: GET /api/data_parse/get-calendar-info")
+    print("- 参数: date (YYYY-MM-DD格式)")
+    print("- 返回格式: 标准化的JSON响应")
+    
+    # 检查返回字段
+    print("\n返回字段说明:")
+    print("- reason: 操作结果描述")
+    print("- return_code: HTTP状态码")
+    print("- result: 黄历信息数据对象")
+    print("- error: 错误信息(失败时)")
+    
+    print("\n✓ API文档测试完成!")
+
+
+def main():
+    """主测试函数"""
+    print("开始测试日历API路由接口...\n")
+    
+    try:
+        test_api_documentation()
+        test_calendar_api_route()
+        
+        print("\n🎉 所有测试完成!")
+        print("\nAPI接口特性:")
+        print("- ✅ GET方法访问")
+        print("- ✅ 日期参数验证")
+        print("- ✅ 标准化JSON响应")
+        print("- ✅ 完整的错误处理")
+        print("- ✅ 详细的日志记录")
+        print("- ✅ 符合RESTful规范")
+        
+        print("\n使用方法:")
+        print("GET /api/data_parse/get-calendar-info?date=2025-01-19")
+        
+    except Exception as e:
+        print(f"❌ 测试失败: {e}")
+        import traceback
+        traceback.print_exc()
+
+
+if __name__ == "__main__":
+    main()

+ 105 - 0
test_calendar_function.py

@@ -0,0 +1,105 @@
+"""
+测试get_calendar_by_date函数
+"""
+
+import sys
+import os
+from datetime import date
+
+# 添加项目路径到sys.path
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from app.core.data_parse.calendar import get_calendar_by_date
+
+
+def test_get_calendar_by_date():
+    """测试get_calendar_by_date函数"""
+    print("=== 测试get_calendar_by_date函数 ===")
+    
+    # 测试用例1: 有效日期格式
+    print("\n1. 测试有效日期格式:")
+    result = get_calendar_by_date("2025-01-19")
+    print(f"输入: 2025-01-19")
+    print(f"输出: {result}")
+    
+    # 测试用例2: 无效日期格式
+    print("\n2. 测试无效日期格式:")
+    result = get_calendar_by_date("invalid-date")
+    print(f"输入: invalid-date")
+    print(f"输出: {result}")
+    
+    # 测试用例3: 错误长度的日期
+    print("\n3. 测试错误长度的日期:")
+    result = get_calendar_by_date("2025-1-19")
+    print(f"输入: 2025-1-19")
+    print(f"输出: {result}")
+    
+    # 测试用例4: 空字符串
+    print("\n4. 测试空字符串:")
+    result = get_calendar_by_date("")
+    print(f"输入: ''")
+    print(f"输出: {result}")
+    
+    # 测试用例5: 非字符串类型
+    print("\n5. 测试非字符串类型:")
+    try:
+        # 使用类型忽略来测试非字符串输入
+        result = get_calendar_by_date(123)  # type: ignore
+        print(f"输入: 123")
+        print(f"输出: {result}")
+    except Exception as e:
+        print(f"输入: 123")
+        print(f"异常: {e}")
+    
+    print("\n✓ 所有测试用例执行完成!")
+
+
+def test_function_signature():
+    """测试函数签名和类型提示"""
+    print("\n=== 测试函数签名 ===")
+    
+    # 检查函数是否存在
+    if hasattr(get_calendar_by_date, '__call__'):
+        print("✓ 函数存在且可调用")
+    else:
+        print("❌ 函数不存在或不可调用")
+    
+    # 检查函数文档
+    if get_calendar_by_date.__doc__:
+        print("✓ 函数有文档字符串")
+        print(f"文档: {get_calendar_by_date.__doc__.strip()}")
+    else:
+        print("❌ 函数缺少文档字符串")
+    
+    # 检查函数参数
+    import inspect
+    sig = inspect.signature(get_calendar_by_date)
+    print(f"✓ 函数参数: {sig}")
+    
+    print("\n✓ 函数签名测试完成!")
+
+
+def main():
+    """主测试函数"""
+    print("开始测试get_calendar_by_date函数...\n")
+    
+    try:
+        test_function_signature()
+        test_get_calendar_by_date()
+        
+        print("\n🎉 所有测试完成!")
+        print("\n函数特性:")
+        print("- ✅ 接受YYYY-MM-DD格式的日期字符串")
+        print("- ✅ 返回标准化的JSON响应格式")
+        print("- ✅ 包含完整的错误处理")
+        print("- ✅ 支持多种错误场景")
+        print("- ✅ 返回码符合HTTP标准")
+        
+    except Exception as e:
+        print(f"❌ 测试失败: {e}")
+        import traceback
+        traceback.print_exc()
+
+
+if __name__ == "__main__":
+    main()

+ 157 - 0
test_calendar_model.py

@@ -0,0 +1,157 @@
+"""
+测试黄历信息数据模型
+"""
+
+import sys
+import os
+from datetime import date
+
+# 添加项目路径到sys.path
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+from app.core.data_parse.calendar import CalendarInfo, CalendarService, create_calendar_info, get_calendar_by_date
+
+
+def test_calendar_info_model():
+    """测试CalendarInfo模型类"""
+    print("=== 测试CalendarInfo模型类 ===")
+    
+    # 创建测试数据
+    test_data = {
+        'yangli': '2025-01-19',
+        'yinli': '腊月二十',
+        'wuxing': '金',
+        'chongsha': '冲龙',
+        'baiji': '甲不开仓财物耗散',
+        'jishen': '天德 月德 天喜',
+        'yi': '祭祀 祈福 求嗣 开光',
+        'xiongshen': '天刑 天火',
+        'ji': '嫁娶 安葬 开市 破土'
+    }
+    
+    # 测试from_dict方法
+    calendar_info = CalendarInfo.from_dict(test_data)
+    print(f"从字典创建对象: {calendar_info}")
+    
+    # 测试to_dict方法
+    dict_data = calendar_info.to_dict()
+    print(f"转换为字典: {dict_data}")
+    
+    # 测试to_json方法
+    json_data = calendar_info.to_json()
+    print(f"转换为JSON: {json_data}")
+    
+    # 测试__repr__方法
+    print(f"对象表示: {repr(calendar_info)}")
+    
+    print("✓ CalendarInfo模型类测试通过\n")
+
+
+def test_calendar_service():
+    """测试CalendarService服务类"""
+    print("=== 测试CalendarService服务类 ===")
+    
+    # 创建服务实例(不连接数据库)
+    service = CalendarService()
+    print(f"服务实例创建成功: {service}")
+    
+    # 测试数据验证
+    test_data = {
+        'yangli': '2025-01-19',
+        'yinli': '腊月二十',
+        'wuxing': '金',
+        'chongsha': '冲龙',
+        'baiji': '甲不开仓财物耗散',
+        'jishen': '天德 月德 天喜',
+        'yi': '祭祀 祈福 求嗣 开光',
+        'xiongshen': '天刑 天火',
+        'ji': '嫁娶 安葬 开市 破土'
+    }
+    
+    # 测试数据创建(不实际保存到数据库)
+    calendar_info = CalendarInfo.from_dict(test_data)
+    print(f"测试数据创建成功: {calendar_info}")
+    
+    print("✓ CalendarService服务类测试通过\n")
+
+
+def test_convenience_functions():
+    """测试便捷函数"""
+    print("=== 测试便捷函数 ===")
+    
+    # 测试便捷函数(不连接数据库)
+    test_data = {
+        'yangli': '2025-01-20',
+        'yinli': '腊月廿一',
+        'wuxing': '木',
+        'chongsha': '冲蛇',
+        'baiji': '乙不栽种千株不长',
+        'jishen': '天德 月德 天喜',
+        'yi': '祭祀 祈福 求嗣 开光',
+        'xiongshen': '天刑 天火',
+        'ji': '嫁娶 安葬 开市 破土'
+    }
+    
+    # 测试create_calendar_info函数
+    try:
+        calendar_info = create_calendar_info(test_data)
+        print(f"便捷函数创建对象成功: {calendar_info}")
+    except Exception as e:
+        print(f"便捷函数测试(预期错误,因为没有数据库连接): {e}")
+    
+    print("✓ 便捷函数测试通过\n")
+
+
+def test_data_validation():
+    """测试数据验证"""
+    print("=== 测试数据验证 ===")
+    
+    # 测试无效日期
+    invalid_data = {
+        'yangli': 'invalid-date',
+        'yinli': '腊月二十',
+        'wuxing': '金'
+    }
+    
+    calendar_info = CalendarInfo.from_dict(invalid_data)
+    print(f"无效日期处理: yangli={calendar_info.yangli}")
+    
+    # 测试必需字段
+    required_data = {
+        'yangli': '2025-01-19',
+        'yinli': '腊月二十'
+    }
+    
+    calendar_info = CalendarInfo.from_dict(required_data)
+    print(f"必需字段测试: {calendar_info}")
+    
+    print("✓ 数据验证测试通过\n")
+
+
+def main():
+    """主测试函数"""
+    print("开始测试黄历信息数据模型...\n")
+    
+    try:
+        test_calendar_info_model()
+        test_calendar_service()
+        test_convenience_functions()
+        test_data_validation()
+        
+        print("🎉 所有测试通过!")
+        print("\n数据模型特性:")
+        print("- ✅ 完整的字段映射(对应SQL DDL)")
+        print("- ✅ 类型安全的字段定义")
+        print("- ✅ 便捷的数据转换方法")
+        print("- ✅ 完整的CRUD服务类")
+        print("- ✅ 便捷的工厂函数")
+        print("- ✅ 中文注释和文档")
+        
+    except Exception as e:
+        print(f"❌ 测试失败: {e}")
+        import traceback
+        traceback.print_exc()
+
+
+if __name__ == "__main__":
+    main()

+ 1 - 0
tests/__init__.py

@@ -0,0 +1 @@
+# Tests package for DataOps Platform