fix(sync): align customer backend behavior
This commit is contained in:
+3
-1
@@ -1,6 +1,7 @@
|
|||||||
# TJWater Server 环境变量配置模板
|
# TJWater Server 环境变量配置模板
|
||||||
# 复制此文件为 .env 并填写实际值
|
# 复制此文件为 .env 并填写实际值
|
||||||
ENVIRONMENT="local"
|
# CI/CD: 将生产 .env 的完整内容保存为 Gitea 仓库密钥 TJWATER_SERVER_ENV。
|
||||||
|
ENVIRONMENT="production"
|
||||||
NETWORK_NAME="tjwater"
|
NETWORK_NAME="tjwater"
|
||||||
# ============================================
|
# ============================================
|
||||||
# 安全配置 (必填)
|
# 安全配置 (必填)
|
||||||
@@ -49,6 +50,7 @@ KEYCLOAK_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
|
|||||||
KEYCLOAK_ALGORITHM=RS256
|
KEYCLOAK_ALGORITHM=RS256
|
||||||
KEYCLOAK_AUDIENCE="account"
|
KEYCLOAK_AUDIENCE="account"
|
||||||
|
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
# Bocha Web Search API
|
# Bocha Web Search API
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
import random
|
|
||||||
from fastapi import APIRouter, Query
|
from fastapi import APIRouter, Query
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi import status
|
from fastapi import status
|
||||||
@@ -63,24 +62,3 @@ async def fastapi_test_dict(data: Item) -> dict[str, str]:
|
|||||||
"""
|
"""
|
||||||
item = data.dict()
|
item = data.dict()
|
||||||
return item
|
return item
|
||||||
|
|
||||||
@router.get("/getrealtimedata/", summary="获取实时数据", description="获取实时监测数据")
|
|
||||||
async def fastapi_get_realtimedata():
|
|
||||||
"""
|
|
||||||
获取实时数据
|
|
||||||
|
|
||||||
返回随机生成的实时监测数据示例
|
|
||||||
"""
|
|
||||||
data = [random.randint(0, 100) for _ in range(100)]
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/getsimulationresult/", summary="获取模拟结果", description="获取仿真计算结果")
|
|
||||||
async def fastapi_get_simulationresult():
|
|
||||||
"""
|
|
||||||
获取仿真结果
|
|
||||||
|
|
||||||
返回随机生成的仿真计算结果示例
|
|
||||||
"""
|
|
||||||
data = [random.randint(0, 100) for _ in range(100)]
|
|
||||||
return data
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from app.api.v1.endpoints import (
|
|||||||
scada,
|
scada,
|
||||||
extension,
|
extension,
|
||||||
snapshots,
|
snapshots,
|
||||||
|
# data_query,
|
||||||
users,
|
users,
|
||||||
schemes,
|
schemes,
|
||||||
misc,
|
misc,
|
||||||
@@ -54,7 +55,9 @@ api_router = APIRouter()
|
|||||||
|
|
||||||
# Core Services
|
# Core Services
|
||||||
api_router.include_router(auth.router, prefix="/auth", tags=["Auth"])
|
api_router.include_router(auth.router, prefix="/auth", tags=["Auth"])
|
||||||
api_router.include_router(user_management.router, prefix="/users", tags=["User Management"]) # 新增
|
api_router.include_router(
|
||||||
|
user_management.router, prefix="/users", tags=["User Management"]
|
||||||
|
) # 新增
|
||||||
api_router.include_router(audit.router, prefix="/audit", tags=["Audit Logs"]) # 新增
|
api_router.include_router(audit.router, prefix="/audit", tags=["Audit Logs"]) # 新增
|
||||||
api_router.include_router(meta.router, tags=["Metadata"])
|
api_router.include_router(meta.router, tags=["Metadata"])
|
||||||
api_router.include_router(project.router, tags=["Project"])
|
api_router.include_router(project.router, tags=["Project"])
|
||||||
@@ -84,7 +87,8 @@ api_router.include_router(visuals.router, tags=["Visuals"])
|
|||||||
|
|
||||||
# Simulation & Data
|
# Simulation & Data
|
||||||
api_router.include_router(simulation.router, tags=["Simulation Control"])
|
api_router.include_router(simulation.router, tags=["Simulation Control"])
|
||||||
api_router.include_router(scada.router, tags=["SCADA"])
|
# api_router.include_router(data_query.router, tags=["Data Query & InfluxDB"])
|
||||||
|
api_router.include_router(scada.router)
|
||||||
api_router.include_router(snapshots.router, tags=["Snapshots"])
|
api_router.include_router(snapshots.router, tags=["Snapshots"])
|
||||||
api_router.include_router(users.router, tags=["Users"])
|
api_router.include_router(users.router, tags=["Users"])
|
||||||
api_router.include_router(schemes.router, tags=["Schemes"])
|
api_router.include_router(schemes.router, tags=["Schemes"])
|
||||||
|
|||||||
+3
-1
@@ -6,9 +6,11 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
|||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
PROJECT_NAME: str = "TJWater Server"
|
PROJECT_NAME: str = "TJWater Server"
|
||||||
ENVIRONMENT: str = "local"
|
ENVIRONMENT: str = "production"
|
||||||
API_V1_STR: str = "/api/v1"
|
API_V1_STR: str = "/api/v1"
|
||||||
|
|
||||||
|
NETWORK_NAME: str = "default_network"
|
||||||
|
|
||||||
# JWT 配置
|
# JWT 配置
|
||||||
SECRET_KEY: str = (
|
SECRET_KEY: str = (
|
||||||
"your-secret-key-here-change-in-production-use-openssl-rand-hex-32"
|
"your-secret-key-here-change-in-production-use-openssl-rand-hex-32"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from app.services.scheme_management import (
|
|||||||
store_scheme_info,
|
store_scheme_info,
|
||||||
)
|
)
|
||||||
from app.services.tjnetwork import get_all_scada_info
|
from app.services.tjnetwork import get_all_scada_info
|
||||||
|
from app.services.time_api import extract_date, parse_utc_time, utc_now
|
||||||
|
|
||||||
|
|
||||||
def run_burst_detection(
|
def run_burst_detection(
|
||||||
@@ -241,7 +242,7 @@ def list_burst_detection_schemes(
|
|||||||
network: str,
|
network: str,
|
||||||
query_date: datetime | str | None = None,
|
query_date: datetime | str | None = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
parsed_date = _to_datetime(query_date).date() if query_date is not None else None
|
parsed_date = extract_date(query_date, field_name="query_date") if query_date is not None else None
|
||||||
return query_burst_detection_schemes(
|
return query_burst_detection_schemes(
|
||||||
name=network,
|
name=network,
|
||||||
network=network,
|
network=network,
|
||||||
@@ -269,7 +270,7 @@ def _store_burst_detection_scheme(
|
|||||||
if scheme_name_exists(network, scheme_name):
|
if scheme_name_exists(network, scheme_name):
|
||||||
raise ValueError(f"方案名称已存在: {scheme_name}")
|
raise ValueError(f"方案名称已存在: {scheme_name}")
|
||||||
|
|
||||||
now_iso = datetime.now().isoformat()
|
now_iso = utc_now().isoformat()
|
||||||
scheme_detail = {
|
scheme_detail = {
|
||||||
"network": network,
|
"network": network,
|
||||||
"sensor_nodes": payload.get("sensor_nodes", []),
|
"sensor_nodes": payload.get("sensor_nodes", []),
|
||||||
@@ -426,6 +427,4 @@ def _build_observed_pressure_from_scada(
|
|||||||
|
|
||||||
|
|
||||||
def _to_datetime(value: datetime | str) -> datetime:
|
def _to_datetime(value: datetime | str) -> datetime:
|
||||||
if isinstance(value, datetime):
|
return parse_utc_time(value)
|
||||||
return value
|
|
||||||
return datetime.fromisoformat(value)
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from app.services.scheme_management import (
|
|||||||
store_scheme_info,
|
store_scheme_info,
|
||||||
)
|
)
|
||||||
from app.services.tjnetwork import dump_inp, get_all_scada_info
|
from app.services.tjnetwork import dump_inp, get_all_scada_info
|
||||||
|
from app.services.time_api import extract_date, parse_utc_time, utc_now
|
||||||
|
|
||||||
SeriesInput = pd.Series | dict[str, Any] | list[dict[str, Any]]
|
SeriesInput = pd.Series | dict[str, Any] | list[dict[str, Any]]
|
||||||
FLOW_SCADA_TYPES = {"pipe_flow", "flow", "demand"}
|
FLOW_SCADA_TYPES = {"pipe_flow", "flow", "demand"}
|
||||||
@@ -301,7 +302,7 @@ def run_burst_location_by_network(
|
|||||||
def list_burst_location_schemes(
|
def list_burst_location_schemes(
|
||||||
network: str, query_date: datetime | str | None = None
|
network: str, query_date: datetime | str | None = None
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
parsed_date = _to_datetime(query_date).date() if query_date is not None else None
|
parsed_date = extract_date(query_date, field_name="query_date") if query_date is not None else None
|
||||||
return query_burst_location_schemes(
|
return query_burst_location_schemes(
|
||||||
name=network, network=network, query_date=parsed_date
|
name=network, network=network, query_date=parsed_date
|
||||||
)
|
)
|
||||||
@@ -327,7 +328,7 @@ def _store_burst_scheme(
|
|||||||
if scheme_name_exists(network, scheme_name):
|
if scheme_name_exists(network, scheme_name):
|
||||||
raise ValueError(f"方案名称已存在: {scheme_name}")
|
raise ValueError(f"方案名称已存在: {scheme_name}")
|
||||||
|
|
||||||
now_iso = datetime.now().isoformat()
|
now_iso = utc_now().isoformat()
|
||||||
scheme_detail = {
|
scheme_detail = {
|
||||||
"network": network,
|
"network": network,
|
||||||
"pressure_scada_ids": payload.get("pressure_scada_ids", []),
|
"pressure_scada_ids": payload.get("pressure_scada_ids", []),
|
||||||
@@ -641,9 +642,7 @@ def _dedupe_ids(ids: list[str] | None) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _to_datetime(value: datetime | str) -> datetime:
|
def _to_datetime(value: datetime | str) -> datetime:
|
||||||
if isinstance(value, datetime):
|
return parse_utc_time(value)
|
||||||
return value
|
|
||||||
return datetime.fromisoformat(value)
|
|
||||||
|
|
||||||
|
|
||||||
def _prepare_burst_inp(network: str) -> str:
|
def _prepare_burst_inp(network: str) -> str:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from app.services.tjnetwork import (
|
|||||||
get_network_link_nodes,
|
get_network_link_nodes,
|
||||||
get_network_node_coords,
|
get_network_node_coords,
|
||||||
)
|
)
|
||||||
|
from app.services.time_api import extract_date, parse_utc_time, utc_now
|
||||||
|
|
||||||
DEFAULT_N_WORKERS = max(1, min((os.cpu_count() or 1) - 1, 4))
|
DEFAULT_N_WORKERS = max(1, min((os.cpu_count() or 1) - 1, 4))
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ def run_leakage_identification(
|
|||||||
scheme_start_time = (
|
scheme_start_time = (
|
||||||
_to_datetime(scada_start).isoformat()
|
_to_datetime(scada_start).isoformat()
|
||||||
if scada_start is not None
|
if scada_start is not None
|
||||||
else datetime.now().isoformat()
|
else utc_now().isoformat()
|
||||||
)
|
)
|
||||||
scheme_detail = {
|
scheme_detail = {
|
||||||
"network": network,
|
"network": network,
|
||||||
@@ -177,7 +178,7 @@ def run_leakage_identification(
|
|||||||
def list_leakage_identify_schemes(
|
def list_leakage_identify_schemes(
|
||||||
network: str, query_date: datetime | str | None = None
|
network: str, query_date: datetime | str | None = None
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
parsed_date = _to_datetime(query_date).date() if query_date is not None else None
|
parsed_date = extract_date(query_date, field_name="query_date") if query_date is not None else None
|
||||||
return query_leakage_identify_schemes(
|
return query_leakage_identify_schemes(
|
||||||
name=network, network=network, query_date=parsed_date
|
name=network, network=network, query_date=parsed_date
|
||||||
)
|
)
|
||||||
@@ -509,9 +510,7 @@ def _build_observed_pressure_from_scada(
|
|||||||
|
|
||||||
|
|
||||||
def _to_datetime(value: datetime | str) -> datetime:
|
def _to_datetime(value: datetime | str) -> datetime:
|
||||||
if isinstance(value, datetime):
|
return parse_utc_time(value)
|
||||||
return value
|
|
||||||
return datetime.fromisoformat(value)
|
|
||||||
|
|
||||||
|
|
||||||
def _prepare_leakage_inp(network: str) -> str:
|
def _prepare_leakage_inp(network: str) -> str:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import os
|
from app.core.config import settings
|
||||||
|
|
||||||
# 从环境变量 NETWORK_NAME 读取
|
name = settings.NETWORK_NAME
|
||||||
name = os.getenv("NETWORK_NAME")
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import ast
|
import ast
|
||||||
import json
|
import json
|
||||||
from datetime import date
|
from datetime import date, datetime
|
||||||
|
|
||||||
import geopandas as gpd
|
import geopandas as gpd
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -8,6 +8,7 @@ import psycopg
|
|||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
from app.core.config import get_pgconn_string
|
from app.core.config import get_pgconn_string
|
||||||
|
from app.services.time_api import parse_utc_time
|
||||||
|
|
||||||
|
|
||||||
# 2025/03/23
|
# 2025/03/23
|
||||||
@@ -89,7 +90,7 @@ def store_scheme_info(
|
|||||||
scheme_name: str,
|
scheme_name: str,
|
||||||
scheme_type: str,
|
scheme_type: str,
|
||||||
username: str,
|
username: str,
|
||||||
scheme_start_time: str,
|
scheme_start_time: datetime | str,
|
||||||
scheme_detail: dict,
|
scheme_detail: dict,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -112,13 +113,16 @@ def store_scheme_info(
|
|||||||
"""
|
"""
|
||||||
# 将字典转换为 JSON 字符串
|
# 将字典转换为 JSON 字符串
|
||||||
scheme_detail_json = json.dumps(scheme_detail)
|
scheme_detail_json = json.dumps(scheme_detail)
|
||||||
|
normalized_scheme_start_time = parse_utc_time(
|
||||||
|
scheme_start_time, field_name="scheme_start_time"
|
||||||
|
)
|
||||||
cur.execute(
|
cur.execute(
|
||||||
sql,
|
sql,
|
||||||
(
|
(
|
||||||
scheme_name,
|
scheme_name,
|
||||||
scheme_type,
|
scheme_type,
|
||||||
username,
|
username,
|
||||||
scheme_start_time,
|
normalized_scheme_start_time,
|
||||||
scheme_detail_json,
|
scheme_detail_json,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ services:
|
|||||||
# Core API Service
|
# Core API Service
|
||||||
# ==========================================
|
# ==========================================
|
||||||
api:
|
api:
|
||||||
|
image: ${TJWATER_SERVER_IMAGE:-tjwater-server:local}
|
||||||
build:
|
build:
|
||||||
context: ../..
|
context: ../..
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
|||||||
Reference in New Issue
Block a user