diff --git a/AGENTS.md b/AGENTS.md index b0feec4..5a59a28 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,6 +16,7 @@ - 开发完成后必须执行与本次改动直接对应的验证步骤,完成验证后才能结束本次任务 - 如果是接口开发完成,先重启后端进程,确保最新代码已经生效,再调用接口进行测试 - 接口测试时必须覆盖多种情况,至少包含正常场景、必填/参数错误场景、分支场景;如接口逻辑包含状态、类型、金额、期限等关键条件,需要分别验证对应分支 +- 每次调用外部接口进行测试或联调时,必须在后端日志中完整输出请求 URL、请求参数和返回参数 - 如果是前端页面开发完成,必须启动前端页面并调用浏览器检查功能是否正常,确认页面展示、交互流程、接口联动和关键提示信息符合预期 - 测试结束后,自动结束测试时开启的前后端进程 diff --git a/doc/implementation-report-2026-04-30-interface-call-log-rule.md b/doc/implementation-report-2026-04-30-interface-call-log-rule.md new file mode 100644 index 0000000..e22524e --- /dev/null +++ b/doc/implementation-report-2026-04-30-interface-call-log-rule.md @@ -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`,测试通过。 diff --git a/doc/~$上虞_客户内码客户_历史利率_映射表.xlsx b/doc/~$上虞_客户内码客户_历史利率_映射表.xlsx deleted file mode 100644 index 3c3a794..0000000 Binary files a/doc/~$上虞_客户内码客户_历史利率_映射表.xlsx and /dev/null differ diff --git a/ruoyi-admin/.DS_Store b/ruoyi-admin/.DS_Store index 5f70785..9d39d1b 100644 Binary files a/ruoyi-admin/.DS_Store and b/ruoyi-admin/.DS_Store differ diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java index fe8bb5c..fc1ebab 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -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); } } -} \ No newline at end of file + + 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); + } + } +} diff --git a/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingCustomerMapService.java b/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingCustomerMapService.java index 3f50e51..33a10f2 100644 --- a/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingCustomerMapService.java +++ b/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanPricingCustomerMapService.java @@ -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("客户号映射接口无返回"); diff --git a/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanRateHistoryService.java b/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanRateHistoryService.java index 6309614..3aaabd5 100644 --- a/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanRateHistoryService.java +++ b/ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/LoanRateHistoryService.java @@ -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("历史贷款合同接口无返回");