- AppIconKeyService/AppIconEntryVo(新):icon_key/icon_color 资产库键值读取 - UserController:getUserApp 返回体加图标键注入(withIconKeys) - UserSettingsController:用户设置应用列表注入 iconKey/iconColor(未配置 null 走原 img 回退) - UsedAppController/UsedAppServiceImpl:常用应用列表同口径注入 依据:根目录《AppIcon资产库落地-执行报告-20261004.md》P2 数据侧/管理端
This commit is contained in:
@@ -5,7 +5,9 @@ import com.yunhai.platform.utils.model.BoaAppType;
|
|||||||
import com.yunhai.platform.utils.model.BoaUser;
|
import com.yunhai.platform.utils.model.BoaUser;
|
||||||
import com.yunhai.userhome.config.Config;
|
import com.yunhai.userhome.config.Config;
|
||||||
import com.yunhai.userhome.entity.UsedApp;
|
import com.yunhai.userhome.entity.UsedApp;
|
||||||
|
import com.yunhai.userhome.model.AppIconEntryVo;
|
||||||
import com.yunhai.userhome.model.SetType;
|
import com.yunhai.userhome.model.SetType;
|
||||||
|
import com.yunhai.userhome.service.AppIconKeyService;
|
||||||
import com.yunhai.userhome.service.AppRemoteService;
|
import com.yunhai.userhome.service.AppRemoteService;
|
||||||
import com.yunhai.userhome.service.UsedAppService;
|
import com.yunhai.userhome.service.UsedAppService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -36,6 +38,9 @@ public class UsedAppController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AppRemoteService appRemoteService;
|
private AppRemoteService appRemoteService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AppIconKeyService appIconKeyService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取当前角色生效的常用应用(BoaApp 详情,按 appIds 顺序)。
|
* 取当前角色生效的常用应用(BoaApp 详情,按 appIds 顺序)。
|
||||||
*
|
*
|
||||||
@@ -112,7 +117,7 @@ public class UsedAppController {
|
|||||||
usedAppService.saveUserId(usedApp);
|
usedAppService.saveUserId(usedApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按 appIds(逗号分隔)取应用精简映射(id/name/icon/url),在全集中查找。 */
|
/** 按 appIds(逗号分隔)取应用精简映射(id/name/icon/url/iconKey/iconColor),在全集中查找。 */
|
||||||
@GetMapping("getAppByIds")
|
@GetMapping("getAppByIds")
|
||||||
public List<Map<String, Object>> getAppByIds(BoaUser authUserWithDept, String appIds) {
|
public List<Map<String, Object>> getAppByIds(BoaUser authUserWithDept, String appIds) {
|
||||||
List<Map<String, Object>> list = new ArrayList<>();
|
List<Map<String, Object>> list = new ArrayList<>();
|
||||||
@@ -132,6 +137,10 @@ public class UsedAppController {
|
|||||||
m.put("name", app.getName());
|
m.put("name", app.getName());
|
||||||
m.put("icon", app.getIcon());
|
m.put("icon", app.getIcon());
|
||||||
m.put("url", app.getWebAddr());
|
m.put("url", app.getWebAddr());
|
||||||
|
// 矢量图标键(AppIcon 通道;未配置为 null,前端按空走原 img 回退)
|
||||||
|
AppIconEntryVo iconEntry = appIconKeyService.byId(app.getId());
|
||||||
|
m.put("iconKey", iconEntry == null ? null : iconEntry.getIconKey());
|
||||||
|
m.put("iconColor", iconEntry == null ? null : iconEntry.getIconColor());
|
||||||
list.add(m);
|
list.add(m);
|
||||||
});
|
});
|
||||||
} catch (NumberFormatException ignore) {
|
} catch (NumberFormatException ignore) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.yunhai.userhome.controller.api;
|
package com.yunhai.userhome.controller.api;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.yunhai.platform.apistd.BizException;
|
import com.yunhai.platform.apistd.BizException;
|
||||||
import com.yunhai.platform.openapi.base.OpenApiUserClient;
|
import com.yunhai.platform.openapi.base.OpenApiUserClient;
|
||||||
import com.yunhai.platform.openapi.base.YschBaseOpenApi;
|
import com.yunhai.platform.openapi.base.YschBaseOpenApi;
|
||||||
@@ -11,6 +13,8 @@ import com.yunhai.platform.utils.model.BoaAppType;
|
|||||||
import com.yunhai.platform.utils.model.BoaRoleInfo;
|
import com.yunhai.platform.utils.model.BoaRoleInfo;
|
||||||
import com.yunhai.platform.utils.model.BoaUser;
|
import com.yunhai.platform.utils.model.BoaUser;
|
||||||
import com.yunhai.userhome.config.Config;
|
import com.yunhai.userhome.config.Config;
|
||||||
|
import com.yunhai.userhome.model.AppIconEntryVo;
|
||||||
|
import com.yunhai.userhome.service.AppIconKeyService;
|
||||||
import com.yunhai.userhome.service.AppRemoteService;
|
import com.yunhai.userhome.service.AppRemoteService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -48,6 +52,12 @@ public class UserController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private YschOpenCall openCall;
|
private YschOpenCall openCall;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AppIconKeyService appIconKeyService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
|
||||||
/** 当前登录用户信息(CAS 注入)。 */
|
/** 当前登录用户信息(CAS 注入)。 */
|
||||||
@GetMapping("GetUserInfo")
|
@GetMapping("GetUserInfo")
|
||||||
@@ -72,9 +82,41 @@ public class UserController {
|
|||||||
* @param userId 指定用户(缺省用登录用户)
|
* @param userId 指定用户(缺省用登录用户)
|
||||||
*/
|
*/
|
||||||
@GetMapping("GetUserApp")
|
@GetMapping("GetUserApp")
|
||||||
public List<BoaAppType> getUserApp(BoaUser authUserWithDept, Integer schId,
|
public JsonNode getUserApp(BoaUser authUserWithDept, Integer schId,
|
||||||
@RequestParam(name = "userId", required = false) Integer userId) {
|
@RequestParam(name = "userId", required = false) Integer userId) {
|
||||||
return appRemoteService.getUserApps(authUserWithDept);
|
return withIconKeys(appRemoteService.getUserApps(authUserWithDept));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用分组结果上合并注入矢量图标字段(iconKey/iconColor,AppIcon 资产库通道)。<br>
|
||||||
|
* BoaApp 属 BasePlatform SDK 本期不动:SDK 反序列化会丢弃 icon-map 的扩展字段,
|
||||||
|
* 故序列化前经 JsonNode 平铺注入;icon-map 不可达时按空映射跳过,出参结构与原值完全一致(零回归)。
|
||||||
|
*/
|
||||||
|
private JsonNode withIconKeys(List<BoaAppType> types) {
|
||||||
|
JsonNode root = objectMapper.valueToTree(types);
|
||||||
|
Map<Integer, AppIconEntryVo> iconMap = appIconKeyService.iconMap();
|
||||||
|
if (iconMap.isEmpty() || root == null || !root.isArray()) {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
for (JsonNode type : root) {
|
||||||
|
JsonNode apps = type.get("apps");
|
||||||
|
if (apps == null || !apps.isArray()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (JsonNode app : apps) {
|
||||||
|
JsonNode idNode = app.get("id");
|
||||||
|
if (idNode == null || !idNode.isNumber()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AppIconEntryVo entry = iconMap.get(idNode.asInt());
|
||||||
|
if (entry != null) {
|
||||||
|
((com.fasterxml.jackson.databind.node.ObjectNode) app)
|
||||||
|
.put("iconKey", entry.getIconKey())
|
||||||
|
.put("iconColor", entry.getIconColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 按应用ID取单个应用(在已拉取全集中查找,免额外跨仓请求)。 */
|
/** 按应用ID取单个应用(在已拉取全集中查找,免额外跨仓请求)。 */
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import com.yunhai.userhome.entity.*;
|
|||||||
import com.yunhai.userhome.enums.DesktopAppType;
|
import com.yunhai.userhome.enums.DesktopAppType;
|
||||||
import com.yunhai.userhome.enums.LightAppGroup;
|
import com.yunhai.userhome.enums.LightAppGroup;
|
||||||
import com.yunhai.userhome.enums.LinkOpenType;
|
import com.yunhai.userhome.enums.LinkOpenType;
|
||||||
|
import com.yunhai.userhome.model.AppIconEntryVo;
|
||||||
|
import com.yunhai.userhome.service.AppIconKeyService;
|
||||||
import com.yunhai.userhome.service.IUserCurrentRoleService;
|
import com.yunhai.userhome.service.IUserCurrentRoleService;
|
||||||
import com.yunhai.userhome.service.IUserSettingsService;
|
import com.yunhai.userhome.service.IUserSettingsService;
|
||||||
import com.yunhai.userhome.service.UsedAppService;
|
import com.yunhai.userhome.service.UsedAppService;
|
||||||
@@ -59,6 +61,9 @@ public class UserSettingsController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OpenApiUserClient openApiUserClient;
|
private OpenApiUserClient openApiUserClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AppIconKeyService appIconKeyService;
|
||||||
|
|
||||||
// ==================== 壁纸 ====================
|
// ==================== 壁纸 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -376,6 +381,10 @@ public class UserSettingsController {
|
|||||||
map.put("type", DesktopAppType.Bsch.getKey());
|
map.put("type", DesktopAppType.Bsch.getKey());
|
||||||
map.put("linkType", LinkOpenType.Open.getKey());
|
map.put("linkType", LinkOpenType.Open.getKey());
|
||||||
map.put("layout", null);
|
map.put("layout", null);
|
||||||
|
// 矢量图标键(AppIcon 通道;未配置为 null,前端按空走原 img 回退)
|
||||||
|
AppIconEntryVo iconEntry = appIconKeyService.byId(optionalBoaApp.get().getId());
|
||||||
|
map.put("iconKey", iconEntry == null ? null : iconEntry.getIconKey());
|
||||||
|
map.put("iconColor", iconEntry == null ? null : iconEntry.getIconColor());
|
||||||
} else if (desktopApp.getAppType() == DesktopAppType.Light) {
|
} else if (desktopApp.getAppType() == DesktopAppType.Light) {
|
||||||
Optional<LightApp> optionalLightApp = lightAllList.stream()
|
Optional<LightApp> optionalLightApp = lightAllList.stream()
|
||||||
.filter(app -> desktopApp.getAppId().equals(app.getId())).findFirst();
|
.filter(app -> desktopApp.getAppId().equals(app.getId())).findFirst();
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.yunhai.userhome.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserBase 应用矢量图标映射条目({@code /oapi/app/icon-map} 出参镜像)。<br>
|
||||||
|
* BoaApp 属 BasePlatform SDK 本期不动,PC 通道的 iconKey/iconColor 经本 VO 独立补拉后
|
||||||
|
* 在 GetUserApp / 常用区出参上合并注入(加法式改造,不动既有 oapi 契约)。
|
||||||
|
*/
|
||||||
|
public class AppIconEntryVo {
|
||||||
|
|
||||||
|
/** 应用主键 ID(与 BoaApp.id 同源,按此合并) */
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
/** 矢量图标键(AppIcon 资产库图标名) */
|
||||||
|
private String iconKey;
|
||||||
|
|
||||||
|
/** 矢量图标着色(CSS 色值);null = 端侧默认 */
|
||||||
|
private String iconColor;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIconKey() {
|
||||||
|
return iconKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIconKey(String iconKey) {
|
||||||
|
this.iconKey = iconKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIconColor() {
|
||||||
|
return iconColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIconColor(String iconColor) {
|
||||||
|
this.iconColor = iconColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.yunhai.userhome.service;
|
||||||
|
|
||||||
|
import com.yunhai.platform.openapi.core.YschOpenCall;
|
||||||
|
import com.yunhai.userhome.model.AppIconEntryVo;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用矢量图标映射补拉(AppIcon 资产库 P2/P3 数据通道)。<br>
|
||||||
|
* UserBase 的 {@code GET /oapi/app/icon-map} 返回全量已配置行 [{id, iconKey, iconColor}],
|
||||||
|
* 本服务拉取后建 id 索引供各出参组装点合并注入(BoaApp 属 SDK 本期不动,矢量字段走本通道)。<br>
|
||||||
|
* <ul>
|
||||||
|
* <li>60 秒本地缓存:多个出参点(GetUserApp/常用区/搜索)一次渲染只打一次跨仓调用;</li>
|
||||||
|
* <li>失败降级:返回上次成功值(无则空映射),绝不阻塞主应用列表——空映射下端侧
|
||||||
|
* 走原 关键词→URL→首字 回退链,表现与改造前一致(零回归要求);</li>
|
||||||
|
* <li>失败后 10 秒内不重试(简单防雪崩)。</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AppIconKeyService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AppIconKeyService.class);
|
||||||
|
|
||||||
|
/** 服务目录目标名:userbase(与 UserController.TARGET_USERBASE 同口径) */
|
||||||
|
private static final String TARGET_USERBASE = "userbase";
|
||||||
|
|
||||||
|
private static final String PATH_ICON_MAP = "/oapi/app/icon-map";
|
||||||
|
|
||||||
|
/** 成功结果缓存时长 */
|
||||||
|
private static final long TTL_MS = 60_000L;
|
||||||
|
|
||||||
|
/** 拉取失败后的静默期 */
|
||||||
|
private static final long FAIL_BACKOFF_MS = 10_000L;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private YschOpenCall openCall;
|
||||||
|
|
||||||
|
private volatile Map<Integer, AppIconEntryVo> cached = Collections.emptyMap();
|
||||||
|
private volatile long fetchedAt = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id → 矢量图标条目 映射(含缓存与降级)。空映射 = 无任何应用配置矢量键或通道不可达。
|
||||||
|
*/
|
||||||
|
public Map<Integer, AppIconEntryVo> iconMap() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - fetchedAt < TTL_MS) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
List<AppIconEntryVo> list = openCall.getList(TARGET_USERBASE, PATH_ICON_MAP, null,
|
||||||
|
AppIconEntryVo.class);
|
||||||
|
Map<Integer, AppIconEntryVo> map = (list == null) ? Collections.emptyMap()
|
||||||
|
: list.stream()
|
||||||
|
.filter(v -> v != null && v.getId() != null && v.getIconKey() != null)
|
||||||
|
.collect(Collectors.toMap(AppIconEntryVo::getId, Function.identity(), (a, b) -> a));
|
||||||
|
cached = Collections.unmodifiableMap(map);
|
||||||
|
fetchedAt = now;
|
||||||
|
return cached;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 降级:沿用上次值;10 秒内不再重试。应用列表主链路绝不因此报错
|
||||||
|
fetchedAt = now - TTL_MS + FAIL_BACKOFF_MS;
|
||||||
|
log.warn("[appicon] icon-map 拉取失败,按空映射降级:{}", e.toString());
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 按 id 取单条;无配置返回 null。 */
|
||||||
|
public AppIconEntryVo byId(Integer appId) {
|
||||||
|
return (appId == null) ? null : iconMap().get(appId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,10 @@ import com.yunhai.platform.utils.model.BoaApp;
|
|||||||
import com.yunhai.platform.utils.model.BoaAppType;
|
import com.yunhai.platform.utils.model.BoaAppType;
|
||||||
import com.yunhai.platform.utils.model.BoaUser;
|
import com.yunhai.platform.utils.model.BoaUser;
|
||||||
import com.yunhai.userhome.entity.UsedApp;
|
import com.yunhai.userhome.entity.UsedApp;
|
||||||
|
import com.yunhai.userhome.model.AppIconEntryVo;
|
||||||
import com.yunhai.userhome.model.SetType;
|
import com.yunhai.userhome.model.SetType;
|
||||||
import com.yunhai.userhome.repository.UsedAppRepository;
|
import com.yunhai.userhome.repository.UsedAppRepository;
|
||||||
|
import com.yunhai.userhome.service.AppIconKeyService;
|
||||||
import com.yunhai.userhome.service.AppRemoteService;
|
import com.yunhai.userhome.service.AppRemoteService;
|
||||||
import com.yunhai.userhome.service.UsedAppService;
|
import com.yunhai.userhome.service.UsedAppService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -34,6 +36,9 @@ public class UsedAppServiceImpl implements UsedAppService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AppRemoteService appRemoteService;
|
private AppRemoteService appRemoteService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AppIconKeyService appIconKeyService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(UsedApp usedApp) {
|
public void save(UsedApp usedApp) {
|
||||||
Optional<UsedApp> model = usedAppRepository
|
Optional<UsedApp> model = usedAppRepository
|
||||||
@@ -138,6 +143,7 @@ public class UsedAppServiceImpl implements UsedAppService {
|
|||||||
m.put("name", app.getName());
|
m.put("name", app.getName());
|
||||||
m.put("icon", app.getIcon());
|
m.put("icon", app.getIcon());
|
||||||
m.put("url", app.getWebAddr());
|
m.put("url", app.getWebAddr());
|
||||||
|
putIconKeys(m, app.getId());
|
||||||
list.add(m);
|
list.add(m);
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignore) {
|
} catch (NumberFormatException ignore) {
|
||||||
@@ -147,6 +153,13 @@ public class UsedAppServiceImpl implements UsedAppService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 精简映射上补矢量图标键(iconKey/iconColor,AppIcon 通道;未配置时值为 null,前端按空走原回退链)。 */
|
||||||
|
private void putIconKeys(Map<String, Object> m, Integer appId) {
|
||||||
|
AppIconEntryVo entry = appIconKeyService.byId(appId);
|
||||||
|
m.put("iconKey", entry == null ? null : entry.getIconKey());
|
||||||
|
m.put("iconColor", entry == null ? null : entry.getIconColor());
|
||||||
|
}
|
||||||
|
|
||||||
/** BoaAppType 分组平铺为 BoaApp 列表。 */
|
/** BoaAppType 分组平铺为 BoaApp 列表。 */
|
||||||
private List<BoaApp> flattenApps(List<BoaAppType> types) {
|
private List<BoaApp> flattenApps(List<BoaAppType> types) {
|
||||||
List<BoaApp> apps = new ArrayList<>();
|
List<BoaApp> apps = new ArrayList<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user