feat(msgcenter): 消息读取改走 SDK msg 读方法并带当前激活身份学校口径(resolveReadSchId→/oapi/identity/current,0/异常降级 null);todo 角标沿用主学校口径不变;直批标读联动带口径(消息按学校隔离 批次3)
This commit is contained in:
@@ -3,7 +3,15 @@ package com.yunhai.userhome.service;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.yunhai.platform.openapi.core.TargetSpec;
|
||||
import com.yunhai.platform.openapi.core.YschOpenCall;
|
||||
import com.yunhai.platform.openapi.msg.OpenApiMsgClient;
|
||||
import com.yunhai.platform.openapi.msg.impl.DefaultOpenApiMsgClient;
|
||||
import com.yunhai.platform.openapi.msg.model.AppMsgCount;
|
||||
import com.yunhai.platform.openapi.msg.model.MsgListItem;
|
||||
import com.yunhai.platform.openapi.msg.model.MsgListPage;
|
||||
import com.yunhai.platform.openapi.msg.model.MsgListQuery;
|
||||
import com.yunhai.platform.openapi.msg.model.UserMsgSummary;
|
||||
import com.yunhai.userhome.config.MsgCenterProperties;
|
||||
import com.yunhai.userhome.entity.UserCurrentRole;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -21,8 +29,11 @@ import java.util.Set;
|
||||
* (Tier 0,per-target base-path 剥离——客户端签名 path ≡ UserMsg requestURI 减 context-path),
|
||||
* UserHome 前端不直连 UserMsg。
|
||||
* <ul>
|
||||
* <li>列表/角标:/oapi/msg/list、/oapi/msg/unreadCount、/oapi/msg/todo/list;</li>
|
||||
* <li>已读:/oapi/msg/read、/oapi/msg/readAll(按当前登录人代理,userId 服务端取);</li>
|
||||
* <li>消息读取(列表/摘要/未读数/按应用计数/全部已读)走 SDK msg 域类型化读方法
|
||||
* ({@link OpenApiMsgClient}),并带「当前激活身份」所属学校口径 schId
|
||||
* ({@link #resolveReadSchId})——按学校隔离读取侧;</li>
|
||||
* <li>待办角标 todo/list(仍按主学校 schId,链路口径不变)、单条已读 read、WS 票据
|
||||
* 继续走 {@link YschOpenCall} 泛化直调;</li>
|
||||
* <li>jumpUrl 改写:绝对 URL 直通;相对路径按应用注册表取 pc_url——jumpApp(值域=app_code)
|
||||
* 主查,jumpApp 为空才按发送方 appKey 回落(严格二选一,pc_url 空就是空不猜测,
|
||||
* 手工基址表 jump-bases 已退役),归一化后拼 "#" + jumpPath;</li>
|
||||
@@ -33,7 +44,8 @@ import java.util.Set;
|
||||
* base-path(/userbase),目标 URL 带 /usermsg ctx 时签名路径多一层前缀 → UserMsg 验签 401,
|
||||
* 列表/角标/票据全部静默降级为空。改 YschOpenCall 后 {@code usermsg-base-url} 解析为
|
||||
* {@link TargetSpec}(host + /usermsg ctx),签名按目标 ctx 剥离,与 AppAuthFilter 对齐。
|
||||
* 目标地址来源:usermsg-base-url 显式配置优先,其次服务目录 usermsg(yh.open.services / 枚举兜底)。
|
||||
* 目标地址来源:usermsg-base-url 显式配置优先(ad-hoc TargetSpec,泛化直调与类型化读方法同一寻址),
|
||||
* 其次服务目录 usermsg(yh.open.services / 枚举兜底)。
|
||||
* UserMsg 不可达时降级空结果(铃铛不 500),恢复后自愈。旧 user_message 本地表链路不动。
|
||||
*/
|
||||
@Slf4j
|
||||
@@ -55,6 +67,7 @@ public class MsgCenterProxyService {
|
||||
|
||||
private final YschOpenCall openCall;
|
||||
private final MsgCenterProperties properties;
|
||||
private final IUserCurrentRoleService userCurrentRoleService;
|
||||
|
||||
/** 0=正常;>0=UserBase 查址失败后的自愈时间戳(System.currentTimeMillis 基准) */
|
||||
private volatile long userbaseDownUntil;
|
||||
@@ -141,15 +154,46 @@ public class MsgCenterProxyService {
|
||||
}
|
||||
}
|
||||
|
||||
public MsgCenterProxyService(YschOpenCall openCall, MsgCenterProperties properties) {
|
||||
public MsgCenterProxyService(YschOpenCall openCall, MsgCenterProperties properties,
|
||||
IUserCurrentRoleService userCurrentRoleService) {
|
||||
this.openCall = openCall;
|
||||
this.properties = properties;
|
||||
this.userCurrentRoleService = userCurrentRoleService;
|
||||
}
|
||||
|
||||
private boolean enabled() {
|
||||
return StringUtils.hasText(properties.getUsermsgBaseUrl()) || openCall.hasTarget(TARGET_USERMSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息读取口径 schId(按学校隔离读取侧,R2):取<b>当前激活身份</b>所属学校
|
||||
* ({@code userCurrentRoleService.findById} → UserBase /oapi/identity/current),
|
||||
* <b>严禁</b>用会话主学校(跨校切换会错)。解析为 0(平台/超管)或身份链不可达时返回 null
|
||||
* =不下发过滤(看全部,D5,且与改造前行为一致)。
|
||||
*/
|
||||
public Integer resolveReadSchId(Integer userId) {
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return userCurrentRoleService.findById(userId)
|
||||
.map(UserCurrentRole::getSchId)
|
||||
.filter(schId -> schId != null && schId > 0)
|
||||
.orElse(null);
|
||||
} catch (Exception e) {
|
||||
log.warn("[msgcenter] 当前身份学校解析不可达(降级不过滤=全量口径)userId={}:{}", userId, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 显式 base-url → ad-hoc TargetSpec(path 段即目标 ctx);否则类型化读方法按服务目录 usermsg 寻址 */
|
||||
private OpenApiMsgClient msgClient() {
|
||||
String base = properties.getUsermsgBaseUrl();
|
||||
return StringUtils.hasText(base)
|
||||
? new DefaultOpenApiMsgClient(openCall, TargetSpec.fromUrl(base.trim()))
|
||||
: new DefaultOpenApiMsgClient(openCall);
|
||||
}
|
||||
|
||||
/** 显式 base-url → ad-hoc TargetSpec(path 段即目标 ctx,per-target 签名剥离);否则按名走服务目录 */
|
||||
private JsonNode get(String path, Map<String, String> query) {
|
||||
String base = properties.getUsermsgBaseUrl();
|
||||
@@ -171,9 +215,10 @@ public class MsgCenterProxyService {
|
||||
* 消息分页列表(已过滤删除/过期):{total, list[]},元素含改写后的绝对 jumpUrl 与 showJump 判权出参。
|
||||
* 可选筛选(null/空白不透传,旧 UserMsg 收到未知参数会忽略——不传时行为与历史版本一致):
|
||||
* msgType 0/1/2、appId(注册表应用 id,代理翻译为发送方 appKey 后下行)、isRead 0/1、keyword 标题/内容模糊。
|
||||
* schId=当前身份学校口径({@link #resolveReadSchId}),null=不过滤(平台/超管视角,看全部)。
|
||||
*/
|
||||
public Map<String, Object> list(Integer userId, Integer pageIndex, Integer pageSize, Boolean onlyUnread,
|
||||
Integer msgType, String appId, Integer isRead, String keyword) {
|
||||
Integer msgType, String appId, Integer isRead, String keyword, Integer schId) {
|
||||
Map<String, Object> empty = emptyPage();
|
||||
if (!enabled()) {
|
||||
return empty;
|
||||
@@ -181,34 +226,31 @@ public class MsgCenterProxyService {
|
||||
PerRequestAuthz authz = new PerRequestAuthz(userId);
|
||||
AppDict dict = appDict(userId);
|
||||
try {
|
||||
Map<String, String> query = new LinkedHashMap<>();
|
||||
query.put("userId", String.valueOf(userId));
|
||||
query.put("pageIndex", String.valueOf(pageIndex == null || pageIndex < 1 ? 1 : pageIndex));
|
||||
query.put("pageSize", String.valueOf(pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 100)));
|
||||
query.put("onlyUnread", String.valueOf(Boolean.TRUE.equals(onlyUnread)));
|
||||
if (msgType != null) {
|
||||
query.put("msgType", String.valueOf(msgType));
|
||||
}
|
||||
MsgListQuery query = new MsgListQuery();
|
||||
query.setUserId(userId);
|
||||
query.setPageIndex(pageIndex == null || pageIndex < 1 ? 1 : pageIndex);
|
||||
query.setPageSize(pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 100));
|
||||
query.setOnlyUnread(Boolean.TRUE.equals(onlyUnread));
|
||||
query.setMsgType(msgType);
|
||||
if (StringUtils.hasText(appId)) {
|
||||
AppDictEntry entry = dict.byId.get(Integer.valueOf(appId.trim()));
|
||||
if (entry == null) {
|
||||
return empty; // 字典查无此应用:视作该应用无消息
|
||||
}
|
||||
query.put("appKey", entry.appKey);
|
||||
query.setAppKey(entry.appKey);
|
||||
}
|
||||
if (isRead != null) {
|
||||
query.put("isRead", String.valueOf(isRead));
|
||||
}
|
||||
putIfText(query, "keyword", keyword);
|
||||
JsonNode data = get("/oapi/msg/list", query);
|
||||
query.setIsRead(isRead);
|
||||
query.setKeyword(StringUtils.hasText(keyword) ? keyword.trim() : null);
|
||||
query.setSchId(schId);
|
||||
MsgListPage page = msgClient().list(query);
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
if (data != null && data.path("content").isArray()) {
|
||||
for (JsonNode row : data.get("content")) {
|
||||
rows.add(rewrite(row, authz, dict));
|
||||
if (page != null && page.getContent() != null) {
|
||||
for (MsgListItem item : page.getContent()) {
|
||||
rows.add(rewrite(item, authz, dict));
|
||||
}
|
||||
}
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
out.put("total", data == null || !data.has("total") ? 0 : data.get("total").asInt());
|
||||
out.put("total", page == null ? 0 : page.getTotal());
|
||||
out.put("list", rows);
|
||||
return out;
|
||||
} catch (Exception e) {
|
||||
@@ -220,9 +262,12 @@ public class MsgCenterProxyService {
|
||||
/**
|
||||
* 铃铛摘要:未读数 + 各应用待办角标 + 分类计数(停机各自降级 0/空)。
|
||||
* 分类计数(sysUnread/bizUnread/approvalUnread/total/readCount)来自 UserMsg /oapi/msg/summary
|
||||
* (消息中心左栏一次喂饱);旧版 UserMsg 无该端点 → 字段缺省不出现,前端按降级处理。
|
||||
* (消息中心左栏一次喂饱),按 msgSchId 学校口径取数;UserMsg 不可达/字段缺省时降级 unreadCount 单查。
|
||||
*
|
||||
* @param msgSchId 消息读取口径(当前激活身份学校,null=全部)
|
||||
* @param todoSchId 待办角标口径(沿用会话主学校,链路口径不变,见类注释)
|
||||
*/
|
||||
public Map<String, Object> summary(Integer userId, Integer schId) {
|
||||
public Map<String, Object> summary(Integer userId, Integer msgSchId, Integer todoSchId) {
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
out.put("unreadCount", 0L);
|
||||
out.put("todos", new ArrayList<>());
|
||||
@@ -231,20 +276,18 @@ public class MsgCenterProxyService {
|
||||
}
|
||||
boolean summaryFetched = false;
|
||||
try {
|
||||
Map<String, String> q = new LinkedHashMap<>();
|
||||
q.put("userId", String.valueOf(userId));
|
||||
JsonNode vo = get("/oapi/msg/summary", q);
|
||||
if (vo != null && vo.has("unreadCount")) {
|
||||
out.put("unreadCount", vo.get("unreadCount").asLong());
|
||||
out.put("sysUnread", vo.path("sysUnread").asLong(0));
|
||||
out.put("bizUnread", vo.path("bizUnread").asLong(0));
|
||||
out.put("approvalUnread", vo.path("approvalUnread").asLong(0));
|
||||
out.put("total", vo.path("total").asLong(0));
|
||||
out.put("readCount", vo.path("readCount").asLong(0));
|
||||
// 分类总数(含已读)——左栏计数与列表内容对齐的浏览口径;旧版 UserMsg 无该字段时缺省,前端按降级处理
|
||||
out.put("sysTotal", vo.path("sysTotal").asLong(0));
|
||||
out.put("bizTotal", vo.path("bizTotal").asLong(0));
|
||||
out.put("approvalTotal", vo.path("approvalTotal").asLong(0));
|
||||
UserMsgSummary vo = msgClient().summary(userId, msgSchId);
|
||||
if (vo != null) {
|
||||
out.put("unreadCount", vo.getUnreadCount());
|
||||
out.put("sysUnread", vo.getSysUnread());
|
||||
out.put("bizUnread", vo.getBizUnread());
|
||||
out.put("approvalUnread", vo.getApprovalUnread());
|
||||
out.put("total", vo.getTotal());
|
||||
out.put("readCount", vo.getReadCount());
|
||||
// 分类总数(含已读)——左栏计数与列表内容对齐的浏览口径
|
||||
out.put("sysTotal", vo.getSysTotal());
|
||||
out.put("bizTotal", vo.getBizTotal());
|
||||
out.put("approvalTotal", vo.getApprovalTotal());
|
||||
summaryFetched = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -253,10 +296,7 @@ public class MsgCenterProxyService {
|
||||
}
|
||||
if (!summaryFetched) {
|
||||
try {
|
||||
Map<String, String> q = new LinkedHashMap<>();
|
||||
q.put("userId", String.valueOf(userId));
|
||||
JsonNode unread = get("/oapi/msg/unreadCount", q);
|
||||
out.put("unreadCount", unread == null || !unread.isNumber() ? 0L : unread.asLong());
|
||||
out.put("unreadCount", msgClient().unreadCount(userId, msgSchId));
|
||||
} catch (Exception e) {
|
||||
log.warn("[msgcenter] UserMsg 未读数不可达(降级 0)userId={}:{}", userId, e.toString());
|
||||
}
|
||||
@@ -264,8 +304,8 @@ public class MsgCenterProxyService {
|
||||
try {
|
||||
Map<String, String> q = new LinkedHashMap<>();
|
||||
q.put("userId", String.valueOf(userId));
|
||||
if (schId != null) {
|
||||
q.put("schId", String.valueOf(schId));
|
||||
if (todoSchId != null) {
|
||||
q.put("schId", String.valueOf(todoSchId));
|
||||
}
|
||||
JsonNode todos = get("/oapi/msg/todo/list", q);
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
@@ -288,24 +328,22 @@ public class MsgCenterProxyService {
|
||||
* 按应用消息计数(UserMsg /oapi/msg/countByApp),条目富化注册表 appId/appName:
|
||||
* [{appId, appName, total, unread}]——消息中心「按应用」清单角标(总数口径,前端隐藏 0 行)。
|
||||
* appId 取应用字典(按用户授权列表),字典查无(如授权已回收的来源应用)时 appId=null,前端不展示。
|
||||
* 停机降级空列表。
|
||||
* schId=当前身份学校口径(null=全部)。停机降级空列表。
|
||||
*/
|
||||
public List<Map<String, Object>> countByApp(Integer userId) {
|
||||
public List<Map<String, Object>> countByApp(Integer userId, Integer schId) {
|
||||
List<Map<String, Object>> empty = new ArrayList<>();
|
||||
if (!enabled()) {
|
||||
return empty;
|
||||
}
|
||||
try {
|
||||
Map<String, String> q = new LinkedHashMap<>();
|
||||
q.put("userId", String.valueOf(userId));
|
||||
JsonNode arr = get("/oapi/msg/countByApp", q);
|
||||
if (arr == null || !arr.isArray()) {
|
||||
List<AppMsgCount> counts = msgClient().countByApp(userId, schId);
|
||||
if (counts == null || counts.isEmpty()) {
|
||||
return empty;
|
||||
}
|
||||
AppDict dict = appDict(userId);
|
||||
List<Map<String, Object>> out = new ArrayList<>();
|
||||
for (JsonNode a : arr) {
|
||||
String appKey = text(a, "appKey");
|
||||
for (AppMsgCount a : counts) {
|
||||
String appKey = a == null ? null : a.getAppKey();
|
||||
if (appKey == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -313,8 +351,8 @@ public class MsgCenterProxyService {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("appId", entry == null ? null : entry.appId);
|
||||
row.put("appName", entry == null ? properties.getAppNames().get(appKey) : entry.name);
|
||||
row.put("total", a.path("total").asLong(0));
|
||||
row.put("unread", a.path("unread").asLong(0));
|
||||
row.put("total", a.getTotal());
|
||||
row.put("unread", a.getUnread());
|
||||
out.add(row);
|
||||
}
|
||||
return out;
|
||||
@@ -324,25 +362,29 @@ public class MsgCenterProxyService {
|
||||
}
|
||||
}
|
||||
|
||||
/** 标记已读(代理按登录人发送,UserMsg 侧 UPDATE 带归属人防伪) */
|
||||
public void read(Integer userId, List<Long> msgIds) {
|
||||
/**
|
||||
* 标记已读(代理按登录人发送,UserMsg 侧 UPDATE 带归属人防伪)。
|
||||
* schId 仅用于 UserMsg 侧缓存键(含学校口径,R1)与 readState 帧口径对齐,不参与 UPDATE 归属判定。
|
||||
*/
|
||||
public void read(Integer userId, List<Long> msgIds, Integer schId) {
|
||||
if (!enabled() || msgIds == null || msgIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("msgIds", msgIds);
|
||||
body.put("userId", userId);
|
||||
if (schId != null) {
|
||||
body.put("schId", schId);
|
||||
}
|
||||
post("/oapi/msg/read", new HashMap<>(), body);
|
||||
}
|
||||
|
||||
/** 全部已读 */
|
||||
public void readAll(Integer userId) {
|
||||
/** 全部已读(按 schId 学校口径限定,D6:只标本校+平台未读,他校未读不受影响;schId null=全部) */
|
||||
public void readAll(Integer userId, Integer schId) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("userId", userId);
|
||||
post("/oapi/msg/readAll", new HashMap<>(), body);
|
||||
msgClient().readAll(userId, schId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,28 +454,28 @@ public class MsgCenterProxyService {
|
||||
* 应用标识对前端只出 appId/appName(应用字典富化):appKey 属后端签名标识不下发;
|
||||
* 字典查无(授权已回收等)appId/appName 为 null,前端按类型标签降级。
|
||||
*/
|
||||
private Map<String, Object> rewrite(JsonNode row, PerRequestAuthz authz, AppDict dict) {
|
||||
private Map<String, Object> rewrite(MsgListItem row, PerRequestAuthz authz, AppDict dict) {
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
out.put("msgId", row.has("msgId") ? row.get("msgId").asLong() : null);
|
||||
out.put("title", text(row, "title"));
|
||||
out.put("content", text(row, "content"));
|
||||
out.put("msgType", row.has("msgType") && row.get("msgType").isNumber() ? row.get("msgType").asInt() : null);
|
||||
out.put("jumpType", row.has("jumpType") && row.get("jumpType").isNumber() ? row.get("jumpType").asInt() : null);
|
||||
out.put("jumpPath", text(row, "jumpPath"));
|
||||
out.put("jumpApp", text(row, "jumpApp"));
|
||||
String appKey = text(row, "appKey");
|
||||
out.put("msgId", row.getMsgId());
|
||||
out.put("title", row.getTitle());
|
||||
out.put("content", row.getContent());
|
||||
out.put("msgType", row.getMsgType());
|
||||
out.put("jumpType", row.getJumpType());
|
||||
out.put("jumpPath", row.getJumpPath());
|
||||
out.put("jumpApp", row.getJumpApp());
|
||||
String appKey = row.getAppKey();
|
||||
AppDictEntry entry = appKey == null ? null : dict.byKey.get(appKey);
|
||||
out.put("appId", entry == null ? null : entry.appId);
|
||||
out.put("appName", entry == null ? properties.getAppNames().get(appKey) : entry.name);
|
||||
out.put("bizType", text(row, "bizType"));
|
||||
out.put("bizId", text(row, "bizId"));
|
||||
out.put("sendTime", text(row, "sendTime"));
|
||||
out.put("isRead", row.has("isRead") && row.get("isRead").isNumber() ? row.get("isRead").asInt() : 0);
|
||||
out.put("bizType", row.getBizType());
|
||||
out.put("bizId", row.getBizId());
|
||||
out.put("sendTime", row.getSendTime());
|
||||
out.put("isRead", row.getIsRead() == null ? 0 : row.getIsRead());
|
||||
String jumpUrl = jumpUrl(row);
|
||||
// showJump(C1):jumpApp 有值才判权;无权 → false 且 jumpUrl 一并置 null(双保险防前端漏改);
|
||||
// jumpApp 为空 / UserBase 不可达(判权结果 null)→ 不判权放行
|
||||
Boolean allowed = null;
|
||||
String jumpApp = text(row, "jumpApp");
|
||||
String jumpApp = row.getJumpApp();
|
||||
if (jumpUrl != null && StringUtils.hasText(jumpApp)) {
|
||||
allowed = authz.authorized(jumpApp.trim());
|
||||
}
|
||||
@@ -457,10 +499,10 @@ public class MsgCenterProxyService {
|
||||
* </ol>
|
||||
* 注册表是多端地址(pc/app/小程序/钉钉)唯一来源;jump-bases 手工基址表已退役(C2)。
|
||||
*/
|
||||
private String jumpUrl(JsonNode row) {
|
||||
Integer jumpType = row.has("jumpType") && row.get("jumpType").isNumber() ? row.get("jumpType").asInt() : null;
|
||||
String jumpPath = text(row, "jumpPath");
|
||||
String jumpApp = text(row, "jumpApp");
|
||||
private String jumpUrl(MsgListItem row) {
|
||||
Integer jumpType = row.getJumpType();
|
||||
String jumpPath = row.getJumpPath();
|
||||
String jumpApp = row.getJumpApp();
|
||||
if (jumpType == null || jumpType != 1 || !StringUtils.hasText(jumpPath)) {
|
||||
return null;
|
||||
}
|
||||
@@ -472,7 +514,7 @@ public class MsgCenterProxyService {
|
||||
return null;
|
||||
}
|
||||
String p = path.startsWith("/") ? path : "/" + path;
|
||||
String appKey = text(row, "appKey");
|
||||
String appKey = row.getAppKey();
|
||||
String pcUrl = StringUtils.hasText(jumpApp) ? pcUrlByCode(jumpApp.trim())
|
||||
: (StringUtils.hasText(appKey) ? pcUrlByKey(appKey.trim()) : null);
|
||||
if (!StringUtils.hasText(pcUrl)) {
|
||||
@@ -578,13 +620,6 @@ public class MsgCenterProxyService {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 有值(非空白)才进 query——空串不透传,保持与不传一致 */
|
||||
private static void putIfText(Map<String, String> query, String key, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
query.put(key, value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
private static String text(JsonNode node, String field) {
|
||||
return node != null && node.has(field) && !node.get(field).isNull() ? node.get(field).asText() : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user