liujie
2026-05-19 a80d21eb7cf01ae4d70482fc2f586fb499c60d3b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package com.estate.expert.admin.component.fuzzy;
 
import com.estate.expert.admin.utils.json.CustomFloatDeserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
 
/**
 * 模糊层次分析法(FAHP)评估API调用组件
 * 
 * <p>该组件负责与外部模糊评估API进行通信,发送评估请求并处理响应结果。
 * 主要用于建筑工程安全风险评估、多因素决策分析等场景。</p>
 * 
 * <p>主要功能:</p>
 * <ul>
 *   <li>封装模糊评估API的HTTP调用</li>
 *   <li>处理请求参数的序列化和响应数据的反序列化</li>
 *   <li>提供统一的异常处理和日志记录</li>
 *   <li>支持自定义JSON解析器处理特殊数值格式</li>
 * </ul>
 * 
 * <p>技术特点:</p>
 * <ul>
 *   <li>使用Spring RestTemplate进行HTTP通信</li>
 *   <li>集成Jackson进行JSON处理</li>
 *   <li>支持自定义Float反序列化器处理特殊数值</li>
 *   <li>提供完整的错误处理和日志记录</li>
 * </ul>
 * 
 * @author daisy
 * @since 2023/10/29
 */
@Slf4j
@Component
public class FuzzyApiComponent {
 
    /**
     * 模糊评估API的远程服务地址
     * 
     */
//     private static final String FUZZY_EVALUATION_API_URL = "http://47.93.28.249:8081/api/Fuzzy_evaluation";
    @Value("${fuzzy.api.url}")
    private static final String FUZZY_EVALUATION_API_URL = "http://localhost:5000/api/Fuzzy_evaluation";
 
 
    /**
     * HTTP客户端模板
     * 
     * <p>用于发送HTTP请求到模糊评估API服务。
     * Spring RestTemplate提供了便捷的HTTP客户端功能。</p>
     */
    RestTemplate restTemplate = new RestTemplate();
 
    /**
     * 获取配置了自定义反序列化器的ObjectMapper
     * 
     * @return 配置好的ObjectMapper实例
     */
    public static ObjectMapper getObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        // 允许非标准数值格式,如NaN、Infinity等
        mapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
        SimpleModule module = new SimpleModule();
        // 注册自定义Float反序列化器
        module.addDeserializer(Float.class, new CustomFloatDeserializer());
        mapper.registerModule(module);
        return mapper;
    }
 
    /**
     * 调用模糊评估API进行风险评估
     * 
     * <p>该方法向远程模糊评估服务发送POST请求,传递评估参数并接收评估结果。
     * 包含完整的错误处理和日志记录机制。</p>
     * 
     * <p>处理流程:</p>
     * <ol>
     *   <li>设置HTTP请求头(Content-Type: application/json)</li>
     *   <li>封装请求实体</li>
     *   <li>发送POST请求到模糊评估API</li>
     *   <li>检查响应状态码</li>
     *   <li>解析响应JSON为FuzzyApiResponse对象</li>
     *   <li>处理异常情况并返回默认响应</li>
     * </ol>
     * 
     * <p>异常处理:</p>
     * <ul>
     *   <li>网络异常:记录详细错误日志</li>
     *   <li>HTTP错误:记录状态码信息</li>
     *   <li>JSON解析异常:记录解析错误</li>
     *   <li>所有异常都会返回空的FuzzyApiResponse对象</li>
     * </ul>
     * 
     * @param bo 模糊评估请求参数,包含评估所需的所有数据
     * @return FuzzyApiResponse 评估结果,包含风险得分、权重、一致性指标等
     * 
     * @see FuzzyApiRequest 请求参数说明
     * @see FuzzyApiResponse 响应结果说明
     */
    public FuzzyApiResponse call(FuzzyApiRequest bo) {
        // 设置HTTP请求头,指定内容类型为JSON
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
 
        // 封装请求实体,包含请求参数和请求头
        HttpEntity<FuzzyApiRequest> requestEntity = new HttpEntity<>(bo, headers);
        log.info("发送模糊评估请求: {}", bo);
 
        try {
            // 发送POST请求到模糊评估API
            ResponseEntity<String> response = restTemplate.exchange(
                    FUZZY_EVALUATION_API_URL,
                    HttpMethod.POST,
                    requestEntity,
                    String.class
            );
 
            // 检查响应状态码
            if (response.getStatusCode().is2xxSuccessful()) {
                String responseBody = response.getBody();
                log.info("模糊评估API响应成功: {}", responseBody);
                
                // 检查响应体是否为空
                if (StringUtils.isNotBlank(responseBody)) {
                    // 使用自定义ObjectMapper解析JSON响应
                    return getObjectMapper().readValue(responseBody, FuzzyApiResponse.class);
                } else {
                    log.warn("模糊评估API返回空响应体");
                }
            } else {
                log.error("模糊评估API请求失败,HTTP状态码: {}", response.getStatusCode());
            }
        } catch (Exception e) {
            // 记录详细的异常信息,包括堆栈跟踪
            log.error("模糊评估API调用异常: {}", ExceptionUtils.getStackTrace(e));
        }
        
        // 异常情况下返回空的响应对象
        return new FuzzyApiResponse();
    }
 
    /**
     * 测试方法 - 多因素综合评估示例
     * 
     * <p>该方法演示了如何使用模糊评估API进行多因素综合风险评估。
     * 适用于复杂的工程安全评估场景,如整个项目的综合安全风险评估。</p>
     * 
     */
    public static void main(String[] args) {
        // 创建模糊评估请求对象
        FuzzyApiRequest bo = new FuzzyApiRequest();
        FuzzyApiComponent component = new FuzzyApiComponent();
        
        // 设置评估因素数量
        bo.setN(18);
        
        // 设置评估因素名称数组
        bo.setIndex(new String[]{"临时消防", "基坑工程", "悬挑式脚手架", "扣件式模板支架", "承插型钢管支模架",
                        "文明施工", "施工机具", "施工用电", "有限空间作业", "盘扣式模板支架",
                        "盘扣式落地钢管脚手架(双排、满堂)", "落地式扣件钢管脚手架(双排、满堂)", "轮扣式模板支架", "轮扣式落地钢管脚手架(双排、满堂)",
                "附着式升降脚手架", "附着式脚手架", "高处作业", "高处作业吊篮"});
        
        // 设置隶属度矩阵(18×5)
        // 每行表示一个因素在5个风险等级上的隶属度分布
        // 列含义:[可忽略风险, 低风险, 中等风险, 高风险, 不可接受风险]
        bo.setMembership_data(new float[][]{
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 临时消防
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 基坑工程
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 悬挑式脚手架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 扣件式模板支架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 承插型钢管支模架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 文明施工
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 施工机具
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 施工用电
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 有限空间作业
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 盘扣式模板支架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 盘扣式落地钢管脚手架(双排、满堂)
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 落地式扣件钢管脚手架(双排、满堂)
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 轮扣式模板支架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 轮扣式落地钢管脚手架(双排、满堂)
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 附着式升降脚手架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 附着式脚手架
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f},  // 高处作业
                {0.6917f, 0.2917f, 0.0166f, 0.0f, 0.0f}   // 高处作业吊篮
        });
        
        // 设置两两比较矩阵(18×18)
        // 当前设置为所有因素同等重要(值为1.0)
        // 实际使用时需要根据专家判断设置不同的重要性比值
        bo.setCompare_matrix_data(new float[][]{
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f},
                {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}
        });
        
        // 设置专家数量
        bo.setNum_experts(1);
        
        // 记录请求参数
        log.info("模糊评估请求参数: {}", bo);
 
        // 调用模糊评估API
        var resp = component.call(bo);
        
        // 记录评估结果
        log.info("模糊评估结果: {}", resp);
    }
}