fix(portal): distinguish 1c portfolio companies
This commit is contained in:
@@ -1051,6 +1051,7 @@ struct Snapshot {
|
|||||||
worktime: SourceStatus,
|
worktime: SourceStatus,
|
||||||
worktime_management: SourceStatus,
|
worktime_management: SourceStatus,
|
||||||
one_c: SourceStatus,
|
one_c: SourceStatus,
|
||||||
|
one_c_overview: SourceStatus,
|
||||||
agent_quality: AgentQuality,
|
agent_quality: AgentQuality,
|
||||||
agent_quality_history: Vec<AgentQualityHistoryItem>,
|
agent_quality_history: Vec<AgentQualityHistoryItem>,
|
||||||
agent_quality_history_summary: AgentQualityHistorySummary,
|
agent_quality_history_summary: AgentQualityHistorySummary,
|
||||||
@@ -1882,6 +1883,14 @@ fn build_snapshot(args: &Cli) -> Snapshot {
|
|||||||
&format!("{}/api/health", args.one_c_url.trim_end_matches('/')),
|
&format!("{}/api/health", args.one_c_url.trim_end_matches('/')),
|
||||||
timeout,
|
timeout,
|
||||||
),
|
),
|
||||||
|
one_c_overview: http_json_source(
|
||||||
|
"one_c_overview",
|
||||||
|
&format!(
|
||||||
|
"{}/api/1/analytics-1c/companies/overview",
|
||||||
|
args.one_c_url.trim_end_matches('/')
|
||||||
|
),
|
||||||
|
timeout,
|
||||||
|
),
|
||||||
agent_quality: load_agent_quality(&args.telemetry_store_path),
|
agent_quality: load_agent_quality(&args.telemetry_store_path),
|
||||||
agent_quality_history,
|
agent_quality_history,
|
||||||
agent_quality_history_summary,
|
agent_quality_history_summary,
|
||||||
@@ -9680,19 +9689,39 @@ fn one_c_block(snapshot: &Snapshot) -> SummaryBlock {
|
|||||||
if !snapshot.one_c.ok {
|
if !snapshot.one_c.ok {
|
||||||
return block("FAIL", "1C analytics API не отвечает");
|
return block("FAIL", "1C analytics API не отвечает");
|
||||||
}
|
}
|
||||||
let companies = snapshot
|
let portfolio_companies = snapshot
|
||||||
|
.one_c_overview
|
||||||
|
.payload
|
||||||
|
.as_ref()
|
||||||
|
.and_then(one_c_overview_count)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let analytics_records = snapshot
|
||||||
.one_c
|
.one_c
|
||||||
.payload
|
.payload
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|value| value.get("companies_total"))
|
.and_then(|value| value.get("companies_total"))
|
||||||
.and_then(Value::as_u64)
|
.and_then(Value::as_u64)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
let overview_note = if snapshot.one_c_overview.ok {
|
||||||
|
format!("компаний в портфеле={portfolio_companies}")
|
||||||
|
} else {
|
||||||
|
"портфель компаний не получен".to_string()
|
||||||
|
};
|
||||||
block(
|
block(
|
||||||
"OK",
|
"OK",
|
||||||
&format!("1C analytics отвечает, компаний={companies}"),
|
&format!("1C analytics отвечает, {overview_note}, записей аналитики={analytics_records}"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn one_c_overview_count(value: &Value) -> Option<u64> {
|
||||||
|
value.get("count").and_then(Value::as_u64).or_else(|| {
|
||||||
|
value
|
||||||
|
.get("items")?
|
||||||
|
.as_array()
|
||||||
|
.map(|items| items.len() as u64)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn owner_recommendations(snapshot: &Snapshot, summary: &SummaryResponse) -> Vec<String> {
|
fn owner_recommendations(snapshot: &Snapshot, summary: &SummaryResponse) -> Vec<String> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
if !summary.operator_ok {
|
if !summary.operator_ok {
|
||||||
@@ -9844,7 +9873,7 @@ fn source_summary(name: &str, payload: &Value) -> String {
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
),
|
),
|
||||||
"one_c" => format!(
|
"one_c" => format!(
|
||||||
"status={}, companies={}",
|
"status={}, analytics_records={}",
|
||||||
payload
|
payload
|
||||||
.get("status")
|
.get("status")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
@@ -9854,6 +9883,10 @@ fn source_summary(name: &str, payload: &Value) -> String {
|
|||||||
.and_then(Value::as_u64)
|
.and_then(Value::as_u64)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
),
|
),
|
||||||
|
"one_c_overview" => format!(
|
||||||
|
"portfolio_companies={}",
|
||||||
|
one_c_overview_count(payload).unwrap_or(0)
|
||||||
|
),
|
||||||
_ => "source loaded".to_string(),
|
_ => "source loaded".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11381,9 +11414,16 @@ mod tests {
|
|||||||
one_c: SourceStatus {
|
one_c: SourceStatus {
|
||||||
ok: true,
|
ok: true,
|
||||||
status: "OK".to_string(),
|
status: "OK".to_string(),
|
||||||
summary: "status=ok, companies=47".to_string(),
|
summary: "status=ok, analytics_records=6134".to_string(),
|
||||||
error: None,
|
error: None,
|
||||||
payload: Some(json!({"status": "ok", "companies_total": 47})),
|
payload: Some(json!({"status": "ok", "companies_total": 6134})),
|
||||||
|
},
|
||||||
|
one_c_overview: SourceStatus {
|
||||||
|
ok: true,
|
||||||
|
status: "OK".to_string(),
|
||||||
|
summary: "portfolio_companies=43".to_string(),
|
||||||
|
error: None,
|
||||||
|
payload: Some(json!({"count": 43, "items": []})),
|
||||||
},
|
},
|
||||||
agent_quality: AgentQuality::default(),
|
agent_quality: AgentQuality::default(),
|
||||||
agent_quality_history: Vec::new(),
|
agent_quality_history: Vec::new(),
|
||||||
@@ -11422,6 +11462,11 @@ mod tests {
|
|||||||
agent_coverage_sla: AgentCoverageSla::default(),
|
agent_coverage_sla: AgentCoverageSla::default(),
|
||||||
security_events_summary: SecurityEventsSummary::disabled(),
|
security_events_summary: SecurityEventsSummary::disabled(),
|
||||||
};
|
};
|
||||||
|
let one_c = one_c_block(&snapshot);
|
||||||
|
assert_eq!(one_c.status, "OK");
|
||||||
|
assert!(one_c.text.contains("компаний в портфеле=43"));
|
||||||
|
assert!(one_c.text.contains("записей аналитики=6134"));
|
||||||
|
assert!(!one_c.text.contains("компаний=6134"));
|
||||||
let evidence = DlpEvidenceResponse {
|
let evidence = DlpEvidenceResponse {
|
||||||
ok: true,
|
ok: true,
|
||||||
generated_at_utc: "2026-06-03T10:00:00Z".to_string(),
|
generated_at_utc: "2026-06-03T10:00:00Z".to_string(),
|
||||||
@@ -11979,6 +12024,13 @@ confidence:
|
|||||||
error: None,
|
error: None,
|
||||||
payload: None,
|
payload: None,
|
||||||
},
|
},
|
||||||
|
one_c_overview: SourceStatus {
|
||||||
|
ok: false,
|
||||||
|
status: "FAIL".to_string(),
|
||||||
|
summary: "".to_string(),
|
||||||
|
error: None,
|
||||||
|
payload: None,
|
||||||
|
},
|
||||||
agent_quality: AgentQuality::default(),
|
agent_quality: AgentQuality::default(),
|
||||||
agent_quality_history: Vec::new(),
|
agent_quality_history: Vec::new(),
|
||||||
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
||||||
@@ -12088,6 +12140,13 @@ confidence:
|
|||||||
error: None,
|
error: None,
|
||||||
payload: None,
|
payload: None,
|
||||||
},
|
},
|
||||||
|
one_c_overview: SourceStatus {
|
||||||
|
ok: false,
|
||||||
|
status: "FAIL".to_string(),
|
||||||
|
summary: "".to_string(),
|
||||||
|
error: None,
|
||||||
|
payload: None,
|
||||||
|
},
|
||||||
agent_quality: AgentQuality::default(),
|
agent_quality: AgentQuality::default(),
|
||||||
agent_quality_history: Vec::new(),
|
agent_quality_history: Vec::new(),
|
||||||
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
||||||
@@ -12182,6 +12241,13 @@ confidence:
|
|||||||
error: None,
|
error: None,
|
||||||
payload: None,
|
payload: None,
|
||||||
},
|
},
|
||||||
|
one_c_overview: SourceStatus {
|
||||||
|
ok: false,
|
||||||
|
status: "FAIL".to_string(),
|
||||||
|
summary: "".to_string(),
|
||||||
|
error: None,
|
||||||
|
payload: None,
|
||||||
|
},
|
||||||
agent_quality: AgentQuality::default(),
|
agent_quality: AgentQuality::default(),
|
||||||
agent_quality_history: Vec::new(),
|
agent_quality_history: Vec::new(),
|
||||||
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
||||||
@@ -12287,6 +12353,13 @@ confidence:
|
|||||||
error: None,
|
error: None,
|
||||||
payload: None,
|
payload: None,
|
||||||
},
|
},
|
||||||
|
one_c_overview: SourceStatus {
|
||||||
|
ok: false,
|
||||||
|
status: "FAIL".to_string(),
|
||||||
|
summary: "".to_string(),
|
||||||
|
error: None,
|
||||||
|
payload: None,
|
||||||
|
},
|
||||||
agent_quality: AgentQuality::default(),
|
agent_quality: AgentQuality::default(),
|
||||||
agent_quality_history: Vec::new(),
|
agent_quality_history: Vec::new(),
|
||||||
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
agent_quality_history_summary: AgentQualityHistorySummary::default(),
|
||||||
|
|||||||
@@ -34,6 +34,18 @@
|
|||||||
То есть это слой прогноза **операционной активности по компаниям/базам**, а не
|
То есть это слой прогноза **операционной активности по компаниям/базам**, а не
|
||||||
финансовый forecast по первичке.
|
финансовый forecast по первичке.
|
||||||
|
|
||||||
|
### Счетчики компаний
|
||||||
|
|
||||||
|
Важно не смешивать разные счетчики:
|
||||||
|
|
||||||
|
- `companies_total` в `/api/health` — технический счетчик записей/срезов
|
||||||
|
1C analytics, а не число компаний в управленческом портфеле;
|
||||||
|
- количество компаний для портала и демонстрации берется из
|
||||||
|
`GET /api/1/analytics-1c/companies/overview` и
|
||||||
|
`analytics_1c.v_company_portfolio_overview`;
|
||||||
|
- если registry и portfolio расходятся, это считается вопросом качества
|
||||||
|
привязок/актуальности 1C analytics, а не ошибкой портала.
|
||||||
|
|
||||||
## Что добавлено
|
## Что добавлено
|
||||||
|
|
||||||
### ClickHouse
|
### ClickHouse
|
||||||
|
|||||||
Reference in New Issue
Block a user