Improve external API logging readability

This commit is contained in:
wkc
2026-05-06 10:18:28 +08:00
parent 709a314107
commit abc8b127e1
7 changed files with 68 additions and 9 deletions

View File

@@ -16,6 +16,7 @@
- 开发完成后必须执行与本次改动直接对应的验证步骤,完成验证后才能结束本次任务
- 如果是接口开发完成,先重启后端进程,确保最新代码已经生效,再调用接口进行测试
- 接口测试时必须覆盖多种情况,至少包含正常场景、必填/参数错误场景、分支场景;如接口逻辑包含状态、类型、金额、期限等关键条件,需要分别验证对应分支
- 每次调用外部接口进行测试或联调时,必须在后端日志中完整输出请求 URL、请求参数和返回参数
- 如果是前端页面开发完成,必须启动前端页面并调用浏览器检查功能是否正常,确认页面展示、交互流程、接口联动和关键提示信息符合预期
- 测试结束后,自动结束测试时开启的前后端进程

View File

@@ -0,0 +1,17 @@
# 实施记录 - 外部接口调用日志
## 日期
2026-04-30
## 修改内容
- 在根目录 `AGENTS.md` 的测试规范中新增外部接口调用日志要求:每次调用外部接口进行测试或联调时,必须在后端日志中完整输出请求 URL、请求参数和返回参数。
- 补齐客户映射接口、历史贷款合同接口、通用 `HttpUtils` 外部接口调用日志,输出请求 URL、请求参数和返回参数。
- 将外部接口日志调整为多行可读格式:请求 URL、请求参数、返回参数分段输出参数对象和返回对象使用 pretty JSON 展开。
- 调整单元测试,覆盖客户映射外呼日志、历史贷款合同外呼日志。
## 验证
- 已检查规则保存路径为项目根目录 `AGENTS.md`
- 已执行 `mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingCustomerMapServiceTest,LoanRateHistoryServiceTest -Dsurefire.failIfNoSpecifiedTests=false test`,测试通过。

BIN
ruoyi-admin/.DS_Store vendored

Binary file not shown.

View File

@@ -21,6 +21,8 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.*;
@@ -75,7 +77,7 @@ public class HttpUtils
try
{
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
log.info("sendGet - {}", urlNameString);
log.info("后端外部接口调用开始\n请求URL{}\n请求参数\n{}", urlNameString, formatLogValue(param));
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
@@ -88,7 +90,8 @@ public class HttpUtils
{
result.append(line);
}
log.info("recv - {}", result);
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
urlNameString, formatLogValue(param), formatLogValue(result));
}
catch (ConnectException e)
{
@@ -150,7 +153,7 @@ public class HttpUtils
StringBuilder result = new StringBuilder();
try
{
log.info("sendPost - {}", url);
log.info("后端外部接口调用开始\n请求URL{}\n请求参数\n{}", url, formatLogValue(param));
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
@@ -169,7 +172,8 @@ public class HttpUtils
{
result.append(line);
}
log.info("recv - {}", result);
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
url, formatLogValue(param), formatLogValue(result));
}
catch (ConnectException e)
{
@@ -219,7 +223,7 @@ public class HttpUtils
String urlNameString = url + "?" + param;
try
{
log.info("sendSSLPost - {}", urlNameString);
log.info("后端外部接口调用开始\n请求URL{}\n请求参数\n{}", urlNameString, formatLogValue(param));
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
URL console = new URL(urlNameString);
@@ -245,7 +249,8 @@ public class HttpUtils
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
}
}
log.info("recv - {}", result);
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
urlNameString, formatLogValue(param), formatLogValue(result));
conn.disconnect();
br.close();
}
@@ -327,7 +332,8 @@ public class HttpUtils
requestEntity,
responseType
);
log.info("---------------------->POST(form-urlencoded) 请求成功URL{},响应结果:{}", url, response.getBody());
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
url, formatLogValue(params), formatLogValue(response.getBody()));
return response.getBody();
} catch (Exception e) {
throw new RuntimeException("POST(form-urlencoded) 请求失败URL" + url + ",异常信息:" + e.getMessage(), e);
@@ -364,10 +370,31 @@ public class HttpUtils
requestEntity,
responseType
);
log.info("---------------------->POST(JSON) 请求成功URL{},响应结果:{}", url, response.getBody());
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
url, formatLogValue(requestBody), formatLogValue(response.getBody()));
return response.getBody();
} catch (Exception e) {
throw new RuntimeException("POST(JSON) 请求失败URL" + url + ",异常信息:" + e.getMessage(), e);
}
}
}
public static String formatLogValue(Object value)
{
if (value == null)
{
return "null";
}
if (value instanceof CharSequence)
{
return value.toString();
}
try
{
return JSON.toJSONString(value, JSONWriter.Feature.PrettyFormat);
}
catch (Exception e)
{
return String.valueOf(value);
}
}
}

View File

@@ -2,11 +2,13 @@ package com.ruoyi.loanpricing.service;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.loanpricing.domain.vo.CustomerMapRecordVO;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -17,6 +19,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* 客户号与客户内码映射查询服务。
*/
@Service
@Slf4j
public class LoanPricingCustomerMapService
{
private final RestTemplate restTemplate;
@@ -67,6 +70,10 @@ public class LoanPricingCustomerMapService
.encode()
.toUri();
CustomerMapResponse response = restTemplate.getForObject(uri, CustomerMapResponse.class);
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
uri,
HttpUtils.formatLogValue(Collections.singletonMap("cust_id", normalizedCustId)),
HttpUtils.formatLogValue(response));
if (response == null)
{
throw new ServiceException("客户号映射接口无返回");

View File

@@ -2,11 +2,13 @@ package com.ruoyi.loanpricing.service;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.loanpricing.domain.vo.HistoryLoanContractVO;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -14,6 +16,7 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
@Service
@Slf4j
public class LoanRateHistoryService
{
private final RestTemplate restTemplate;
@@ -50,6 +53,10 @@ public class LoanRateHistoryService
.encode()
.toUri();
HistoryLoanContractResponse response = restTemplate.getForObject(uri, HistoryLoanContractResponse.class);
log.info("后端外部接口调用完成\n请求URL{}\n请求参数\n{}\n返回参数\n{}",
uri,
HttpUtils.formatLogValue(Collections.singletonMap("cust_isn", normalizedCustIsn)),
HttpUtils.formatLogValue(response));
if (response == null)
{
throw new ServiceException("历史贷款合同接口无返回");