Browse Source

可以从数据库获取json类型数据

yes-yes-yes-k 2 days ago
parent
commit
763ce9e57b

+ 16 - 6
app/api/vector.py

@@ -6,7 +6,7 @@ from ..database import get_db
 from ..services import vector_service
 import os
 import shutil
-from typing import List
+from typing import List, Optional
 
 router = APIRouter()
 
@@ -43,18 +43,25 @@ async def export_vector_data(vector_id: int, db: Session = Depends(get_db)):
         background=BackgroundTasks().add_task(shutil.rmtree, result["temp_dir"]) if "temp_dir" in result else None
     )
 
-@router.get("/export/all", summary="导出矢量数据", description="将指定的矢量数据表导出为GeoJSON文件")
-async def export_all_vector_data_api(table_name: str = "vector_data", db: Session = Depends(get_db)):
-    """导出指定矢量数据表为GeoJSON文件
+@router.get("/export/all", summary="导出矢量数据", description="将指定的矢量数据表导出为GeoJSON或JSON文件")
+async def export_all_vector_data_api(table_name: str = "vector_data", db: Session = Depends(get_db),format:Optional[str] = "geojson"):
+    """导出指定矢量数据表为GeoJSON文件或普通JSON文件
     
     Args:
         table_name (str): 要导出的矢量数据表名,默认为'vector_data'
         db (Session): 数据库会话
+        format (str,optional):导出格式支持'geojson'和'json',默认为'geojson'
         
     Returns:
         FileResponse: GeoJSON文件下载响应
     """
-    result = vector_service.export_all_vector_data(db, table_name)
+    #验证格式参数
+    if format not in ["geojson" , "json"]:
+        raise HTTPException(
+            status_code=400,
+            detail="只支持'geojson'和'json'格式"
+        )
+    result = vector_service.export_all_vector_data(db, table_name,export_format=format)#传递导出格式
     
     # 检查文件是否存在
     if not os.path.exists(result["file_path"]):
@@ -62,11 +69,14 @@ async def export_all_vector_data_api(table_name: str = "vector_data", db: Sessio
             shutil.rmtree(result["temp_dir"])
         raise HTTPException(status_code=404, detail="导出文件不存在")
     
+    #根据格式设置正确的媒体类型
+    media_type = "application/geo+json" if format == "geojson" else "application/json"
+
     # 返回文件下载
     return FileResponse(
         path=result["file_path"],
         filename=os.path.basename(result["file_path"]),
-        media_type="application/json",
+        media_type=media_type,
         background=BackgroundTasks().add_task(shutil.rmtree, result["temp_dir"]) if "temp_dir" in result else None
     )
 

+ 30 - 3
app/database.py

@@ -1,4 +1,5 @@
-from sqlalchemy import create_engine
+from sqlalchemy import create_engine , text
+from app.models.orm_models import Base
 from sqlalchemy.orm import sessionmaker
 from sqlalchemy.ext.declarative import declarative_base
 import os
@@ -14,7 +15,7 @@ logging.basicConfig(level=logging.INFO)
 logger = logging.getLogger(__name__)
 
 # 创建Base类
-Base = declarative_base()
+#Base = declarative_base()
 Base.metadata.clear()
 
 # 加载环境变量
@@ -99,11 +100,37 @@ def execute_sql(sql_statement):
 # 新增:自动创建数据库表(关键!)
 def create_tables():
     try:
+         # ✨ 新增:同时启用 PostGIS 核心和 Raster 扩展
+        with engine.begin() as conn:
+            # 启用 PostGIS 核心(矢量)
+            conn.execute(text("CREATE EXTENSION IF NOT EXISTS postgis;")) # pyright: ignore[reportUndefinedVariable]
+            # 启用 PostGIS Raster(栅格)
+            conn.execute(text("CREATE EXTENSION IF NOT EXISTS postgis_raster;")) # pyright: ignore[reportUndefinedVariable]
+            logger.info("PostGIS 核心及 Raster 扩展已启用(或已存在)")
         # 必须导入所有模型,否则 Base 不知道要创建哪些表
         # 替换成你项目中实际的模型文件路径(根据你的目录结构调整)
         from app.models.orm_models import Base  # 确保模型继承自这个 Base
         from app.models.vector import VectorData  # 导入需要创建的表
-        
+        #from app.models.raster import RasterData  # 补充其他模型
+        from app.models.county import County
+        from app.models.farmland import FarmlandData
+
+        from app.models.agricultural import AgriculturalData  # 假设类名是 Agricultural
+        from app.models.assessment import Assessment
+        from app.models.atmo_company import AtmoCompany
+        from app.models.atmo_sample import AtmoSampleData
+        from app.models.CropCd_input import CropCdInputData
+        from app.models.CropCd_output import CropCdOutputData
+        from app.models.cross_section import CrossSection
+        from app.models.EffCd_input import EffCdInputData
+        from app.models.EffCd_output import EffCdOutputData
+        from app.models.FluxCd_input import FluxCdInputData
+        from app.models.FluxCd_output import FluxCdOutputData
+        from app.models.MSM_input import MSMInputData
+        from app.models.MSM_output import MSMOutputData
+        from app.models.parameters import Parameters
+        from app.models.soil import SoilData  
+        from app.models.water_sample import WaterSampleData
         # 创建所有表
         Base.metadata.create_all(bind=engine)
         logger.info("数据库表自动创建成功!")

+ 1 - 1
app/models/CropCd_input.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float
 from sqlalchemy import ForeignKeyConstraint
-from app.database import Base  # 统一的基础模型
+from app.models.orm_models import Base  # 统一的基础模型
 
 
 class CropCdInputData(Base):

+ 1 - 1
app/models/CropCd_output.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float
 from sqlalchemy import ForeignKeyConstraint
-from app.database import Base  # 统一的基础模型
+from app.models.orm_models import Base  # 统一的基础模型
 
 
 class CropCdOutputData(Base):

+ 1 - 1
app/models/EffCd_input.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float
 from sqlalchemy import ForeignKeyConstraint
-from app.database import Base  # 统一的Base导入
+from app.models.orm_models import Base  # 统一的Base导入
 
 
 class EffCdInputData(Base):

+ 1 - 1
app/models/EffCd_output.py

@@ -1,7 +1,7 @@
 # app/models/effcd_output.py
 from sqlalchemy import Column, Integer, Float
 from sqlalchemy import ForeignKeyConstraint
-from app.database import Base  # 统一的基础模型
+from app.models.orm_models import Base  # 统一的基础模型
 
 
 class EffCdOutputData(Base):

+ 1 - 1
app/models/FluxCd_input.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float
 from sqlalchemy import ForeignKeyConstraint
-from app.database import Base  # 统一的基础模型
+from app.models.orm_models import Base  # 统一的基础模型
 
 
 class FluxCdInputData(Base):

+ 1 - 1
app/models/FluxCd_output.py

@@ -1,5 +1,5 @@
 from sqlalchemy import Column, Integer, Float, ForeignKeyConstraint
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class FluxCdOutputData(Base):

+ 1 - 1
app/models/MSM_input.py

@@ -1,7 +1,7 @@
 from sqlalchemy import Column, Integer, Float, String
 from sqlalchemy import ForeignKey, ForeignKeyConstraint
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class MSMInputData(Base):

+ 1 - 1
app/models/MSM_output.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, Text
 from sqlalchemy import ForeignKey, ForeignKeyConstraint
-from app.database import Base  # 统一的基础模型
+from app.models.orm_models import Base  # 统一的基础模型
 
 
 class MSMOutputData(Base):

+ 1 - 1
app/models/agricultural.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, String
 from sqlalchemy.dialects.postgresql import TEXT
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class AgriculturalData(Base):

+ 1 - 1
app/models/assessment.py

@@ -1,7 +1,7 @@
 from sqlalchemy import Column, Integer, Float, ForeignKey
 from sqlalchemy.orm import relationship, backref
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 from sqlalchemy import ForeignKeyConstraint
 
 

+ 1 - 1
app/models/atmo_company.py

@@ -1,5 +1,5 @@
 from sqlalchemy import Column, Integer, Float, String
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class AtmoCompany(Base):

+ 1 - 1
app/models/atmo_sample.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, String, Float
 from sqlalchemy.dialects.postgresql import TEXT
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class AtmoSampleData(Base):

+ 1 - 1
app/models/county.py

@@ -1,7 +1,7 @@
 from sqlalchemy import Column, Integer, String, Text, Index, JSON
 from sqlalchemy.dialects.postgresql import JSONB
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 import json
 
 class County(Base):

+ 1 - 1
app/models/cross_section.py

@@ -1,5 +1,5 @@
 from sqlalchemy import Column, Integer, Float, String, Text
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class CrossSection(Base):

+ 1 - 1
app/models/farmland.py

@@ -6,7 +6,7 @@
 
 from sqlalchemy import Column, Integer, Float, ForeignKey
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class FarmlandData(Base):

+ 1 - 1
app/models/orm_models.py

@@ -140,7 +140,7 @@ class FiftyThousandSurveyDatum(Base):
     geom = Column(Geometry('POINT', from_text='ST_GeomFromEWKT', name='geometry'), index=True, comment='几何位置信息')
 
 class RasterTable(Base):
-    __tablename__ = 'raster_table'
+    __tablename__ = 'raster_table_v2'
 
     id = Column(Integer, primary_key=True, autoincrement=True)
     rast = Column(Raster(from_text='raster', name='raster'), index=False)

+ 1 - 1
app/models/parameters.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, String, create_engine
 from sqlalchemy.orm import declarative_base
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class Parameters(Base):

+ 1 - 1
app/models/raster.py

@@ -2,7 +2,7 @@
 
 from sqlalchemy import Column, Integer
 from geoalchemy2 import Raster
-from ..database import Base
+from app.models.orm_models import Base
 
 class RasterData(Base):
     __tablename__ = "raster_table"

+ 1 - 1
app/models/soil.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, ForeignKey, ForeignKeyConstraint
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class SoilData(Base):

+ 2 - 2
app/models/vector.py

@@ -1,12 +1,12 @@
 # 从数据库配置里导入 Base(用来管理表)
-from app.database import Base
+from app.models.orm_models import Base
 from sqlalchemy import Table, Column, Integer, String, Float, MetaData
 
 # 关联到 Base,让它能自动创建表
 metadata = Base.metadata
 # 新增 ORM 类定义(服务层需要的 VectorData)
 class VectorData(Base):
-    __tablename__ = "surveydata"  # 使用相同的表名
+    __tablename__ = "vector_surveydata"  # 使用相同的表名
     
     id = Column(Integer, primary_key=True)
     name = Column(String(100))

+ 1 - 1
app/models/water_sample.py

@@ -1,6 +1,6 @@
 from sqlalchemy import Column, Integer, Float, String, Text, DateTime
 from geoalchemy2 import Geometry
-from app.database import Base
+from app.models.orm_models import Base
 
 
 class WaterSampleData(Base):

+ 26 - 4
app/services/vector_service.py

@@ -165,20 +165,42 @@ def export_vector_data_batch(db: Session, vector_ids: List[int]):
     return _export_vector_data_to_file(vector_data_list, f"export_batch_{'_'.join(map(str, vector_ids))}", "surveydata")
 
 
-def export_all_vector_data(db: Session, table_name: str = "surveydata"):
-    """导出指定的矢量数据表为GeoJSON格式并保存到文件"""
+def export_all_vector_data(
+    db: Session, 
+    table_name: str = "surveydata", 
+    export_format: str = "geojson"  # 新增:格式参数,默认geojson
+):
+    """导出指定表数据为GeoJSON或普通JSON格式"""
     try:
+        # 1. 查询表数据(保持原有逻辑)
         query = text(f'SELECT * FROM "{table_name}"')
         result = db.execute(query)
         columns = [col.name for col in result.cursor.description]
-        vector_data_list = [dict(zip(columns, row)) for row in result.fetchall()]  # 类型修正
-        return _export_vector_data_to_file(vector_data_list, f"export_{table_name}", table_name)
+        # 转换为字典列表(原生查询结果处理)
+        data_list = [dict(zip(columns, row)) for row in result.fetchall()]
+        
+        # 2. 根据格式生成不同文件
+        if export_format == "geojson":
+            # 原有逻辑:生成GeoJSON(依赖你的build_geojson工具)
+            return _export_vector_data_to_file(data_list, f"export_{table_name}", table_name)
+        else:
+            # 新增逻辑:生成普通JSON
+            return _export_json_data_to_file(data_list, f"export_{table_name}", table_name)
+            
     except Exception as e:
         raise HTTPException(
             status_code=500,
             detail=f"查询表{table_name}失败:{str(e)}"
         )
 
+# 新增函数:生成普通JSON文件
+def _export_json_data_to_file(data_list, base_filename, table_name):
+    temp_dir = tempfile.mkdtemp()
+    file_path = os.path.join(temp_dir, f"{base_filename}.json")  # 后缀改为json
+    # 直接写入原始数据(无需GeoJSON转换)
+    with open(file_path, "w", encoding="utf-8") as f:
+        json.dump(data_list, f, ensure_ascii=False, indent=2)
+    return {"file_path": file_path, "temp_dir": temp_dir}
 
 def parse_geom_field(geom_value) -> dict:
     """解析 geom 字段为 GeoJSON 格式的 geometry"""

+ 20 - 14
main.py

@@ -1,21 +1,27 @@
-from app.main import app
+# 1. 先创建 FastAPI 应用实例(只创建一次)
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware  # 导入 CORS 模块
-if __name__ == "__main__":
-    import uvicorn
-    # uvicorn.run(app, host="0.0.0.0", port=8000, ssl_keyfile="ssl/cert.key", ssl_certfile="ssl/cert.crt")
-    uvicorn.run(app, host="0.0.0.0", port=8000)
 
-# 创建 FastAPI 应用实例
-app = FastAPI()
+app = FastAPI()  # 核心实例,只定义一次
 
-# ========= 新增 CORS 配置 =========
+
+# 2. 配置 CORS(在注册路由前配置)
 app.add_middleware(
     CORSMiddleware,
-    allow_origins=["http://localhost:5173"],  # 允许前端地址
-    allow_credentials=True,
-    allow_methods=["*"],  # 允许所有方法
-    allow_headers=["*"],  # 允许所有头
+    allow_origins=["http://localhost:5173"],  # 允许前端 Vite 服务地址
+    allow_credentials=True,  # 允许携带 cookies
+    allow_methods=["*"],  # 允许所有 HTTP 方法(GET/POST等)
+    allow_headers=["*"],  # 允许所有请求
 )
-# 注册路由
-from app.api import vector  # 导入 API 路由
+
+
+# 3. 注册路由(必须显式挂载,否则路由不生效)
+from app.api import vector  # 导入路由模块
+app.include_router(vector.router)  # 将 vector 中的路由挂载到 app
+
+
+# 4. 启动服务(最后执行)
+if __name__ == "__main__":
+    import uvicorn
+    # 运行上面定义的 app 实例(已包含 CORS 和路由配置)
+    uvicorn.run(app, host="0.0.0.0", port=8000)

+ 0 - 0
scripts/python


+ 0 - 0
soilgd.sql