feat(api): standardize REST contracts and auth

This commit is contained in:
2026-07-30 20:38:51 +08:00
parent ae1a657554
commit ba947b616b
86 changed files with 54193 additions and 990 deletions
+26 -72
View File
@@ -27,8 +27,6 @@ from .core import (
parse_time_with_timezone,
parse_valve_setting_file,
request_json,
require_network,
require_username,
resolve_scheme,
)
from .option_types import DataSource, ValveMode
@@ -41,11 +39,9 @@ def simulation_run(
duration: Annotated[int, typer.Option("--duration", help="持续分钟数")],
) -> None:
runtime = runtime_context(ctx)
network = require_network(runtime)
parsed = parse_time_with_timezone(start_time, option_name="--start-time")
end_time = (parsed + timedelta(minutes=duration)).isoformat()
body = {
"name": network,
"start_time": parsed.replace(microsecond=0).isoformat(),
"duration": duration,
}
@@ -53,10 +49,9 @@ def simulation_run(
ctx,
summary="触发模拟成功",
method="POST",
path="/simulations/run-by-date",
path="/simulation-runs",
json_body=body,
require_auth=True,
require_network_ctx=True,
next_commands=[
f"tjwater-cli data timeseries realtime links --start-time {parsed.isoformat()} --end-time {end_time}",
f"tjwater-cli data timeseries realtime nodes --start-time {parsed.isoformat()} --end-time {end_time}",
@@ -76,9 +71,8 @@ def analysis_burst(
ids, sizes = parse_burst_file(burst_file)
scheme_name = resolve_scheme(runtime, scheme, required=True)
params = {
"network": require_network(runtime),
"modify_pattern_start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"burst_ID": ids,
"burst_id": ids,
"burst_size": sizes,
"modify_total_duration": duration,
"scheme_name": scheme_name,
@@ -86,11 +80,10 @@ def analysis_burst(
emit_api(
ctx,
summary="爆管分析执行成功",
method="GET",
path="/burst-analysis",
method="POST",
path="/burst-analyses",
params=params,
require_auth=True,
require_network_ctx=True,
next_commands=[
f"tjwater-cli data scheme get --name {scheme_name}",
"tjwater-cli data scheme list",
@@ -110,7 +103,6 @@ def analysis_valve(
scheme: Annotated[str | None, typer.Option("--scheme", help="close 模式的方案名称")] = None,
) -> None:
runtime = runtime_context(ctx)
network = require_network(runtime)
if mode == ValveMode.CLOSE:
if not start_time or not valve:
raise CLIError(
@@ -120,7 +112,6 @@ def analysis_valve(
exit_code=2,
)
params = {
"network": network,
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"valves": valve,
"duration": duration or 900,
@@ -129,11 +120,10 @@ def analysis_valve(
emit_api(
ctx,
summary="阀门关闭分析执行成功",
method="GET",
path="/valve_close_analysis/",
method="POST",
path="/valve-isolation-analyses",
params=params,
require_auth=True,
require_network_ctx=True,
)
return
if mode == ValveMode.ISOLATION:
@@ -144,17 +134,16 @@ def analysis_valve(
message="isolation mode requires at least one --element",
exit_code=2,
)
params = {"network": network, "accident_element": element}
params = {"accident_element": element}
if disabled_valve:
params["disabled_valves"] = disabled_valve
emit_api(
ctx,
summary="阀门隔离分析执行成功",
method="GET",
path="/valve-isolation-analysis",
method="POST",
path="/valve-isolation-analyses",
params=params,
require_auth=True,
require_network_ctx=True,
)
return
raise AssertionError(f"unreachable valve mode: {mode}")
@@ -173,11 +162,10 @@ def analysis_flushing(
runtime = runtime_context(ctx)
valves, openings = parse_valve_setting_file(valve_setting_file)
params = {
"network": require_network(runtime),
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"valves": valves,
"valves_k": openings,
"drainage_node_ID": drainage_node,
"drainage_node_id": drainage_node,
"flush_flow": flow,
"duration": duration or 900,
"scheme_name": resolve_scheme(runtime, scheme, required=True),
@@ -185,11 +173,10 @@ def analysis_flushing(
emit_api(
ctx,
summary="冲洗分析执行成功",
method="GET",
path="/flushing-analysis",
method="POST",
path="/flushing-analyses",
params=params,
require_auth=True,
require_network_ctx=True,
)
@@ -203,15 +190,13 @@ def analysis_age(
emit_api(
ctx,
summary="水龄分析执行成功",
method="GET",
path="/age_analysis/",
method="POST",
path="/water-age-analyses",
params={
"network": require_network(runtime),
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"duration": duration,
},
require_auth=True,
require_network_ctx=True,
)
@@ -227,7 +212,6 @@ def analysis_contaminant(
) -> None:
runtime = runtime_context(ctx)
params = {
"network": require_network(runtime),
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"source": source_node,
"concentration": concentration,
@@ -239,11 +223,10 @@ def analysis_contaminant(
emit_api(
ctx,
summary="污染物模拟执行成功",
method="GET",
path="/contaminant-simulation",
method="POST",
path="/contaminant-simulations",
params=params,
require_auth=True,
require_network_ctx=True,
)
@@ -256,21 +239,17 @@ def analysis_sensor_placement_kmeans(
) -> None:
runtime = runtime_context(ctx)
body = {
"name": require_network(runtime),
"scheme_name": resolve_scheme(runtime, scheme, required=True),
"sensor_number": count,
"min_diameter": min_diameter,
"username": require_username(runtime),
}
emit_api(
ctx,
summary="传感器选址执行成功",
method="POST",
path="/pressure_sensor_placement_kmeans/",
path="/pressure-sensor-placement-kmeans",
json_body=body,
require_auth=True,
require_network_ctx=True,
require_username_ctx=True,
)
@@ -283,7 +262,6 @@ def analysis_leakage_identify(
) -> None:
runtime = runtime_context(ctx)
body = {
"network": require_network(runtime),
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
"scheme_name": resolve_scheme(runtime, scheme, required=True),
@@ -292,10 +270,9 @@ def analysis_leakage_identify(
ctx,
summary="漏损识别执行成功",
method="POST",
path="/leakage/identify/",
path="/leakage-identifications",
json_body=body,
require_auth=True,
require_network_ctx=True,
)
@@ -308,11 +285,9 @@ def analysis_leakage_schemes_list(ctx: typer.Context) -> None:
method="GET",
path="/schemes",
params={
"network": require_network(runtime),
"scheme_type": "dma_leak_identification",
},
require_auth=True,
require_network_ctx=True,
)
@@ -328,11 +303,9 @@ def analysis_leakage_schemes_get(
method="GET",
path=f"/schemes/{scheme_name}",
params={
"network": require_network(runtime),
"scheme_type": "dma_leak_identification",
},
require_auth=True,
require_network_ctx=True,
)
@@ -345,7 +318,6 @@ def analysis_burst_detection_detect(
) -> None:
runtime = runtime_context(ctx)
body = {
"network": require_network(runtime),
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
"scheme_name": resolve_scheme(runtime, scheme, required=True),
@@ -354,10 +326,9 @@ def analysis_burst_detection_detect(
ctx,
summary="爆管检测执行成功",
method="POST",
path="/burst-detection/detect/",
path="/burst-detections",
json_body=body,
require_auth=True,
require_network_ctx=True,
)
@@ -370,11 +341,9 @@ def analysis_burst_detection_schemes_list(ctx: typer.Context) -> None:
method="GET",
path="/schemes",
params={
"network": require_network(runtime),
"scheme_type": "burst_detection",
},
require_auth=True,
require_network_ctx=True,
)
@@ -390,11 +359,9 @@ def analysis_burst_detection_schemes_get(
method="GET",
path=f"/schemes/{scheme_name}",
params={
"network": require_network(runtime),
"scheme_type": "burst_detection",
},
require_auth=True,
require_network_ctx=True,
)
@@ -416,7 +383,6 @@ def analysis_burst_location_locate(
pressure_payload = parse_optional_dataset_file(pressure_file, label="pressure") or {}
flow_payload = parse_optional_dataset_file(flow_file, label="flow") or {}
body = {
"network": require_network(runtime),
"scheme_name": resolve_scheme(runtime, scheme, required=True),
"data_source": data_source.value,
"scada_burst_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
@@ -436,10 +402,9 @@ def analysis_burst_location_locate(
ctx,
summary="爆管定位执行成功",
method="POST",
path="/burst-location/locate/",
path="/burst-locations",
json_body=body,
require_auth=True,
require_network_ctx=True,
)
@@ -452,11 +417,9 @@ def analysis_burst_location_schemes_list(ctx: typer.Context) -> None:
method="GET",
path="/schemes",
params={
"network": require_network(runtime),
"scheme_type": "burst_location",
},
require_auth=True,
require_network_ctx=True,
)
@@ -472,11 +435,9 @@ def analysis_burst_location_schemes_get(
method="GET",
path=f"/schemes/{scheme_name}",
params={
"network": require_network(runtime),
"scheme_type": "burst_location",
},
require_auth=True,
require_network_ctx=True,
)
@@ -490,10 +451,9 @@ def analysis_risk_pipe_now(
ctx,
summary="读取当前管道风险成功",
method="GET",
path="/getpiperiskprobabilitynow/",
params={"network": require_network(runtime), "pipe_id": pipe},
path="/pipes/risk-probability-now",
params={"pipe_id": pipe},
require_auth=True,
require_network_ctx=True,
)
@@ -507,32 +467,26 @@ def analysis_risk_pipe_history(
ctx,
summary="读取历史管道风险成功",
method="GET",
path="/getpiperiskprobability/",
params={"network": require_network(runtime), "pipe_id": pipe},
path="/pipes/risk-probability",
params={"pipe_id": pipe},
require_auth=True,
require_network_ctx=True,
)
@analysis_risk_app.command("network")
def analysis_risk_network(ctx: typer.Context) -> None:
runtime = runtime_context(ctx)
network = require_network(runtime)
probabilities, duration_prob = request_json(
runtime,
method="GET",
path="/getnetworkpiperiskprobabilitynow/",
params={"network": network},
path="/network-pipe-risk-probability-nows",
require_auth=True,
require_network_ctx=True,
)
geometries, duration_geo = request_json(
runtime,
method="GET",
path="/getpiperiskprobabilitygeometries/",
params={"network": network},
path="/pipes/risk-probability-geometries",
require_auth=True,
require_network_ctx=True,
)
emit_success(
summary="读取全网风险成功",