();
+ }
+}
diff --git a/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java
new file mode 100644
index 00000000..b6f520b2
--- /dev/null
+++ b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java
@@ -0,0 +1,23 @@
+package com.klp.framework.config;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.Duration;
+
+/**
+ * sql-server-api HTTP 客户端配置
+ */
+@Configuration
+public class SqlServerApiClientConfig {
+
+ @Bean
+ public RestTemplate sqlServerApiRestTemplate(RestTemplateBuilder builder) {
+ return builder
+ .setConnectTimeout(Duration.ofSeconds(5))
+ .setReadTimeout(Duration.ofSeconds(30))
+ .build();
+ }
+}
diff --git a/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java
new file mode 100644
index 00000000..6d995192
--- /dev/null
+++ b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java
@@ -0,0 +1,51 @@
+package com.klp.framework.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * sql-server-api 中间件配置
+ */
+@Component
+@ConfigurationProperties(prefix = "sql-server-api")
+public class SqlServerApiProperties {
+
+ /**
+ * 请求地址
+ */
+ private String host;
+
+ /**
+ * 请求端口
+ */
+ private Integer port;
+
+ /**
+ * 基础访问地址
+ */
+ private String baseUrl;
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+}
diff --git a/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java b/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java
new file mode 100644
index 00000000..141ad459
--- /dev/null
+++ b/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java
@@ -0,0 +1,306 @@
+package com.klp.framework.service;
+
+import com.klp.framework.client.SqlServerApiClient;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 面向业务场景的 sql-server-api 查询服务。
+ *
+ * 当前抽象为三个功能:
+ *
+ * - 计划查询
+ * - 钢卷实际 / SEG 查询
+ * - 实时数据(Gauge / Shape)查询
+ *
+ */
+@Service
+public class SqlServerApiBusinessService {
+
+ private final SqlServerApiClient client;
+
+ public SqlServerApiBusinessService(SqlServerApiClient client) {
+ this.client = client;
+ }
+
+ /**
+ * 计划列表:查询所有计划,按时间倒序。
+ *
+ * 这是后续按 coilId 关联 SEG、实时数据的入口。
+ */
+ public PlanListView getPlanList() {
+ return PlanListView.fromExecuteSqlResponse(client.queryPlanList());
+ }
+
+ /**
+ * 计划列表:按工艺流程编码筛选。
+ */
+ public PlanListView getPlanListByProcessCode(String processCode) {
+ return PlanListView.fromExecuteSqlResponse(client.queryPlanListByProcessCode(processCode));
+ }
+
+ /**
+ * 计划列表:按状态筛选。
+ */
+ public PlanListView getPlanListByStatus(String status) {
+ return PlanListView.fromExecuteSqlResponse(client.queryPlanListByStatus(status));
+ }
+
+ /**
+ * 计划详情:按成品卷号查询单条计划。
+ */
+ public PlanDetailView getPlanByCoilId(String coilId) {
+ return PlanDetailView.fromExecuteSqlResponse(coilId, client.queryPlanByCoilId(coilId));
+ }
+
+ /**
+ * 计划详情:按热卷号查询。
+ */
+ public PlanDetailView getPlanByHotCoilId(String hotCoilId) {
+ return PlanDetailView.fromExecuteSqlResponse(hotCoilId, client.queryPlanByHotCoilId(hotCoilId));
+ }
+
+ /**
+ * 钢卷实际 SEG 查询:按入口卷号查询。
+ *
+ * 返回的是按 SEGNO 排序后的原始行数据。
+ */
+ public SegSeriesView getSegByEncoilId(String encoilId) {
+ return SegSeriesView.fromExecuteSqlResponse(encoilId, client.queryProSegByEncoilId(encoilId));
+ }
+
+ /**
+ * 钢卷实际 SEG 查询:按出口卷号查询。
+ */
+ public SegSeriesView getSegByExcoilId(String excoilId) {
+ return SegSeriesView.fromExecuteSqlResponse(excoilId, client.queryProSegByExcoilId(excoilId));
+ }
+
+ /**
+ * 钢卷 SEG 视图数据:把同一钢卷下按 SEGNO 排序的多行数据,整理成“一个钢卷 id + 一个属性对应一个列表”的结构。
+ *
+ * 例如:厚度、张力、速度都会被整理成数组,方便前端直接画表或曲线。
+ */
+ public SegSeriesView getSegSeriesViewByEncoilId(String encoilId) {
+ return getSegByEncoilId(encoilId);
+ }
+
+ /**
+ * 实时数据:厚度/仪表数据。
+ */
+ public SqlServerApiClient.ExecuteSqlResponse getRealtimeGaugeByMatId(String matId) {
+ return client.queryGaugeByMatId(matId);
+ }
+
+ /**
+ * 实时数据:板形/形状数据。
+ */
+ public SqlServerApiClient.ExecuteSqlResponse getRealtimeShapeByMatId(String matId) {
+ return client.queryShapeByMatId(matId);
+ }
+
+ /**
+ * 实时数据总入口:一次性返回厚度与板形两类数据。
+ */
+ public RealtimeDataBundle getRealtimeData(String matId) {
+ return new RealtimeDataBundle(
+ getRealtimeGaugeByMatId(matId),
+ getRealtimeShapeByMatId(matId)
+ );
+ }
+
+ private static PlanListView toPlanListView(SqlServerApiClient.ExecuteSqlResponse response) {
+ return PlanListView.fromExecuteSqlResponse(response);
+ }
+
+ private static List