From d64018d5a2d279ff80bad86fef0bbc694c2953e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Thu, 12 Nov 2026 10:22:37 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BA=BA=E5=91=98=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=B2=BB=E7=90=86=E8=BF=81=E7=A7=BB=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PersonApiConfigController.java | 6 + .../service/PersonApiConfigService.java | 2 + .../impl/PersonApiConfigServiceImpl.java | 113 +++++++++++++++++- .../personGovernance/apiConfig/form.html | 56 +++++++++ .../personGovernance/apiConfig/index.html | 36 +++++- 5 files changed, 208 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/controller/PersonApiConfigController.java b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/controller/PersonApiConfigController.java index 82ed3ed..527ce72 100644 --- a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/controller/PersonApiConfigController.java +++ b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/controller/PersonApiConfigController.java @@ -144,6 +144,12 @@ public class PersonApiConfigController { return Result.success(personApiConfigService.queryThirdParty(data, "DATA")); } + @At(value = "/queryFieldValues", methods = "POST") + @SaCheckPermission("data.person.governance.api.test") + public Result queryFieldValues(@Param("data") String data, @Param("fields") String fields) { + return Result.success(personApiConfigService.queryFieldValues(data, fields)); + } + /** 保存当前接口请求及返回结果为可重复执行的测试用例。 */ @At @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/PersonApiConfigService.java b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/PersonApiConfigService.java index fbce7fc..1c9802e 100644 --- a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/PersonApiConfigService.java +++ b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/PersonApiConfigService.java @@ -47,6 +47,8 @@ public interface PersonApiConfigService extends BaseService { /** 使用页面当前config、params查询,返回第三方JSON值;非JSON返回原文,不写数据库。 */ Object queryThirdParty(String data, String requestScope); + NutMap queryFieldValues(String data, String fields); + /** page为当前页码或起始数,pageSize为数量或区间长度;结束数=page+pageSize,返回第三方JSON。 */ Object querySyncPage(PersonApiConfig config, List params, long page, int pageSize); diff --git a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/impl/PersonApiConfigServiceImpl.java b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/impl/PersonApiConfigServiceImpl.java index f52800f..1f4e3b2 100644 --- a/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/impl/PersonApiConfigServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/personGovernance/apiConfig/service/impl/PersonApiConfigServiceImpl.java @@ -44,6 +44,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -506,8 +507,12 @@ public class PersonApiConfigServiceImpl extends BaseServiceImpl List params = payload.get("params") == null ? new ArrayList<>() : Json.fromJsonAsList(PersonApiRequestParam.class, Json.toJson(payload.get("params"))); - // 状态信息仅供内部判断,查询接口不额外包装第三方业务字段;同步入口仍沿用原结构。 - NutMap response = executeHttp(config, params, requestScope, null); + return queryResponse(config, params, requestScope, false); + } + + private Object queryResponse(PersonApiConfig config, List params, + String requestScope, boolean fieldQuery) { + NutMap response = executeHttp(config, params, requestScope, null, fieldQuery); Assert.isTrue(response.getBoolean("success"), response.getString("error") == null ? "第三方返回HTTP错误:" + response.get("status") : response.getString("error")); String body = response.getString("data"); @@ -522,6 +527,101 @@ public class PersonApiConfigServiceImpl extends BaseServiceImpl } } + @Override + public NutMap queryFieldValues(String data, String fields) { + Assert.hasText(fields, "请输入查询字段"); + List names = java.util.Arrays.stream(fields.split("[,,]", -1)) + .map(String::trim).distinct().toList(); + Assert.isTrue(names.stream().noneMatch(String::isEmpty), "请输入字段名,字段之间用逗号分隔"); + Assert.hasText(data, "请求配置不能为空"); + NutMap payload = Json.fromJson(NutMap.class, data); + Assert.notNull(payload, "请求配置格式不正确"); + PersonApiConfig config = Json.fromJson(PersonApiConfig.class, Json.toJson(payload.get("config"))); + Assert.notNull(config, "请求配置不能为空"); + List params = payload.get("params") == null ? List.of() + : Json.fromJsonAsList(PersonApiRequestParam.class, Json.toJson(payload.get("params"))); + + setFieldQueryPage(config, params, 100); + Object firstPage = queryResponse(config, params, "DATA", true); + String totalPath = Strings.isBlank(config.getResponseTotalPath()) ? "data->total" : config.getResponseTotalPath(); + int total = fieldQueryTotal(firstPage, totalPath); + if (total == 0) { + return NutMap.NEW().addv("fields", names).addv("rows", List.of()).addv("total", 0); + } + + setFieldQueryPage(config, params, total); + Object response = queryResponse(config, params, "DATA", true); + int returnedTotal = fieldQueryTotal(response, totalPath); + String listPath = config.getResponseDataPath(); + if (Strings.isBlank(listPath) || "$.data".equals(listPath.trim()) || "data".equals(listPath.trim())) { + Object configured = readResponsePath(response, listPath); + if (!(configured instanceof List)) listPath = "data->list"; + } + Object dataset = readResponsePath(response, listPath); + Assert.isTrue(dataset instanceof List, "数据列表路径未找到数组:" + listPath); + List records = (List) dataset; + Assert.isTrue(records.size() >= Math.max(total, returnedTotal), + "接口未返回全部数据,预计 " + Math.max(total, returnedTotal) + " 条,实际返回 " + records.size() + " 条"); + + LinkedHashSet> groups = new LinkedHashSet<>(); + LinkedHashSet found = new LinkedHashSet<>(); + for (Object record : records) { + Assert.isTrue(record instanceof Map, "数据列表中的记录必须为对象"); + Map row = (Map) record; + List values = new ArrayList<>(names.size()); + for (String name : names) { + if (row.containsKey(name)) found.add(name); + values.add(row.get(name)); + } + groups.add(values); + } + List missing = names.stream().filter(name -> !found.contains(name)).toList(); + Assert.isTrue(missing.isEmpty(), "返回数据中找不到字段:" + String.join("、", missing)); + return NutMap.NEW().addv("fields", names).addv("rows", new ArrayList<>(groups)).addv("total", records.size()); + } + + private int fieldQueryTotal(Object response, String path) { + if (response instanceof Map map) { + Assert.isTrue(!Boolean.FALSE.equals(map.get("status")), "数据接口返回失败:" + Strings.sNull(map.get("message"))); + } + Object value = readResponsePath(response, path); + try { + int total = new java.math.BigDecimal(String.valueOf(value)).intValueExact(); + if (total >= 0) return total; + } catch (NumberFormatException | ArithmeticException ignored) { + } + throw new IllegalArgumentException("返回总数必须为非负整数,请检查路径:" + path); + } + + private void setFieldQueryPage(PersonApiConfig config, List params, int pageSize) { + boolean sendBody = !List.of("GET", "HEAD").contains(Strings.sNull(config.getRequestMethod()).toUpperCase(Locale.ROOT)); + Map paging = Map.of("pageNumber", 1, "pageSize", pageSize); + LinkedHashSet replaced = new LinkedHashSet<>(); + if (sendBody && "JSON".equals(config.getBodyType()) && Strings.isNotBlank(config.getBodyContent())) { + Object body = parseResponseObject(config.getBodyContent()); + if (body instanceof com.alibaba.fastjson.JSONObject json) { + paging.forEach((name, value) -> { + if (json.containsKey(name)) { + json.put(name, value); + replaced.add(name); + } + }); + config.setBodyContent(com.alibaba.fastjson.JSON.toJSONString(json, + com.alibaba.fastjson.serializer.SerializerFeature.WriteMapNullValue)); + } + } + for (PersonApiRequestParam param : params) { + boolean sent = "QUERY".equals(param.getParamLocation()) + || (sendBody && "FORM_DATA".equals(config.getBodyType()) && "BODY".equals(param.getParamLocation())); + if (sent && "DATA".equals(normalizeScope(param.getRequestScope())) + && param.getParamName() != null && paging.containsKey(param.getParamName())) { + param.setParamValue(String.valueOf(paging.get(param.getParamName()))); + replaced.add(param.getParamName()); + } + } + Assert.isTrue(replaced.size() == paging.size(), "请在数据请求参数中配置 pageNumber 和 pageSize"); + } + /** * 同步取数:params是保存的两套参数,page为页码或起点,pageSize为数量或区间长度;仅替换请求副本。 * Access_Token标记传入缓存Token,参数值可使用Bearer ${Access_Token}保留前缀。 @@ -788,9 +888,14 @@ public class PersonApiConfigServiceImpl extends BaseServiceImpl /** 已保存测试和页面查询共享HTTP组装;校验仅在实际请求时执行,响应不自动落库。 */ private NutMap executeHttp(PersonApiConfig config, List params, String scope, String testCondition) { + return executeHttp(config, params, scope, testCondition, false); + } + + private NutMap executeHttp(PersonApiConfig config, List params, + String scope, String testCondition, boolean fieldQuery) { Assert.isTrue(List.of("TOKEN", "DATA").contains(scope), "请求配置范围不正确"); long start = System.currentTimeMillis(); - boolean omitToken = "DATA".equals(scope) && Strings.isBlank(config.getTokenRequestUrl()); + boolean omitToken = !fieldQuery && "DATA".equals(scope) && Strings.isBlank(config.getTokenRequestUrl()); params = params.stream() .filter(param -> scope.equals(normalizeScope(param.getRequestScope()))) // 未配置Token接口时,Header、Query及Form Data中的Token参数均不进入实际请求。 @@ -855,7 +960,7 @@ public class PersonApiConfigServiceImpl extends BaseServiceImpl try (HttpResponse response = request.execute()) { String responseBody = response.body(); // 按排查要求打印第三方响应原文;返回值复用同一正文,不附加请求参数或统计信息。 - Logs.get().info(responseBody); + if (!fieldQuery) Logs.get().info(responseBody); return NutMap.NEW() .addv("success", response.isOk()) .addv("status", response.getStatus()) diff --git a/src/main/resources/views/platform/personGovernance/apiConfig/form.html b/src/main/resources/views/platform/personGovernance/apiConfig/form.html index f51c15a..6fab95c 100644 --- a/src/main/resources/views/platform/personGovernance/apiConfig/form.html +++ b/src/main/resources/views/platform/personGovernance/apiConfig/form.html @@ -55,6 +55,7 @@ :loading="queryLoading[activeEndpoint]" @click="testApi" >查询 + 查询字段值 @@ -439,6 +440,24 @@ @click="save" >保存配置 + + +
+

读取 {{ fieldValueQueryResult.total }} 条记录,共 {{ fieldValueQueryResult.rows.length }} 组不同值

+ + + + + +
+
+ 关闭 + 查询 +
+
@@ -543,6 +562,10 @@ testResults: { TOKEN: '', DATA: '' }, responseWarnings: { TOKEN: '', DATA: '' }, queryLoading: { TOKEN: false, DATA: false }, + fieldValueQueryVisible: false, + fieldValueQueryInput: '', + fieldValueQueryLoading: false, + fieldValueQueryResult: null, cronValidating: false, cronDialogVisible: false, cronDialogLoading: false, @@ -1180,6 +1203,39 @@ }) }) }, + openFieldValueQuery() { + this.$set(this, 'fieldValueQueryVisible', true) + }, + queryFieldValues() { + if (this.fieldValueQueryLoading) return + const fields = this.fieldValueQueryInput.trim() + if (!fields) { + this.notifyWarning('请输入查询字段') + return + } + this.$set(this, 'fieldValueQueryLoading', true) + this.$set(this, 'fieldValueQueryResult', null) + $.post(apiBase + '/queryFieldValues', { + fields, + data: JSON.stringify({ + config: this.form, + params: this.params.filter((param) => param.requestScope === 'DATA'), + }), + }).then((res) => { + if (res.code === 0) { + this.$set(this, 'fieldValueQueryResult', res.data) + } else { + this.notifyWarning(res.msg || '查询字段值失败') + } + }).fail(() => { + this.notifyWarning('查询字段值失败,请检查网络和接口配置') + }).always(() => { + this.$set(this, 'fieldValueQueryLoading', false) + }) + }, + formatFieldValue(value) { + return typeof value === 'string' && value ? value : JSON.stringify(value) + }, testApi() { // 捕获发起时的页签:请求期间切换页签,响应仍写回原来的范围。 const scope = this.activeEndpoint diff --git a/src/main/resources/views/platform/personGovernance/apiConfig/index.html b/src/main/resources/views/platform/personGovernance/apiConfig/index.html index 0a28570..ecddc64 100644 --- a/src/main/resources/views/platform/personGovernance/apiConfig/index.html +++ b/src/main/resources/views/platform/personGovernance/apiConfig/index.html @@ -51,9 +51,10 @@ {{row.enabled ? '是' : '否'}} - + @@ -102,6 +103,7 @@ pageLeaving: false, typeVisible: false, pendingCreateType: '', + syncingRows: {}, pageForm: { configName: '', sourceType: '', enabled: '', pageNumber: 1, pageSize: 10, totalCount: 0 }, } }, @@ -183,6 +185,38 @@ edit(row) { this.leavePage('/platform/personGovernance/apiConfig/form?id=' + row.id) }, + async syncOnce(row) { + if (this.syncingRows[row.id]) return + this.$set(this.syncingRows, row.id, true) + try { + await this.$confirm('确认执行一次「' + row.configName + '」同步?本次执行会写入目标表。', '执行一次', { + confirmButtonText: '执行', + cancelButtonText: '取消', + type: 'warning', + }) + } catch (cancelled) { + this.$delete(this.syncingRows, row.id) + return + } + try { + const res = await $.post(loc() + '/sync', { id: row.id }) + if (res.code !== 0) { + this.notifyWarning(res.msg || '同步执行失败') + return + } + const batch = res.data + if (!batch || batch.status !== 'SUCCESS') { + this.notifyWarning((batch && batch.errorMessage) || '同步执行失败') + return + } + this.notifySuccess('同步完成,新增 ' + (batch.newCount || 0) + ' 条,更新 ' + (batch.changeCount || 0) + ' 条') + this.pageData() + } catch (error) { + this.notifyWarning('未收到同步结果,执行状态未知') + } finally { + this.$delete(this.syncingRows, row.id) + } + }, remove(row) { this.$confirm('删除后不可继续同步,确认删除吗?', '提示').then(() => { $.post(loc() + '/delete', { id: row.id })