feat(settings+msgcenter): S7 主题/头像读侧级联 + 消息测试修复与 appKey 收尾
- SchSettingsController:schTheme/schAvatarStyle 读侧加学校→平台(schId=0)级联(readThemeWithFallback/readAvatarWithFallback), 回传 hitSchId 供设置中心区分「本校已配」与「回落平台」;写侧与接口签名不变 - MsgCenterProxyService:summary.todos 摘除 appKey 死字段(appKey 属后端签名标识不下发) - MsgCenterProxyServiceTest:两例更新为 appId 口径(字典翻译/字典固定一次出站)+ 补 countByApp 富化/降级、 appId 查无空页、todos 无 appKey 断言;34/34 绿
This commit is contained in:
@@ -26,7 +26,7 @@ import java.util.Optional;
|
||||
* <p>getSchSettings 查本地 sch_settings 表(不再依赖跨仓 schConfig);
|
||||
* saveSchTheme/saveSchLogo 写本地 sch_settings 表。</p>
|
||||
* <p>主题设置轮(2026-09-30)新增学校默认主题档三接口(契约见 YschTheme《主题设置与通道改动-执行方案》§3.5):
|
||||
* schTheme 读(已登录可读,可空不兜底——与 getSchSettings 的兜底语义刻意区分);
|
||||
* schTheme 读(已登录可读,当时"可空不兜底"——S7 起改为学校→平台级联,见文末说明);
|
||||
* saveSchThemeState 写(仅学校管理员/超管);clearSchTheme 清空(置空 theme 字段——sch_settings 行还承载
|
||||
* logo 等字段不能删行,置空后读侧返回 null,效果等同"未配置")。存储形态:theme_name 列存紧凑四段编码
|
||||
* {@code theme|sideDeep01|gradOn01|sideFollow01}(如 teal|0|1|1;不拆列、零 DDL,列宽未确认故不存整份 JSON;
|
||||
@@ -36,6 +36,10 @@ import java.util.Optional;
|
||||
* avatar_style 字段)。</p>
|
||||
* <p>外观偏好轮(2026-10-08):编解码下沉 {@link ThemeCodec}、schId 解析下沉
|
||||
* {@link ISchSettingsService#resolveThemeSchId}(行为等价重构,供 AppearanceController 共享)。</p>
|
||||
* <p>三级重构 S7(2026-10-08):{@code schTheme}/{@code schAvatarStyle} 读侧加"学校 → 平台默认(schId=0)"级联
|
||||
* ({@link #readThemeWithFallback}/{@link #readAvatarWithFallback})——本校未配置或脏值时回落平台档,
|
||||
* 平台也无配置才返回 null。写侧与接口签名、响应体结构均未变;{@code getSchSettings}/{@code findBySchId}
|
||||
* 的"合成默认对象"兜底语义也未变。</p>
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = Config.API_PATH)
|
||||
@@ -58,18 +62,69 @@ public class SchSettingsController {
|
||||
}
|
||||
}
|
||||
|
||||
/** 学校默认头像样式(默认头像轮 2026-09-30,已登录可读):{avatarStyle: "text"|"silhouette"|null, schId}。
|
||||
* 未配置/脏值 → avatarStyle=null(读侧可空不兜底,对齐 schTheme 语义;前端"不写键、不报错")。 */
|
||||
@GetMapping("schAvatarStyle")
|
||||
public Map<String, Object> schAvatarStyle(BoaUser authUserWithDept) {
|
||||
Integer schId = schSettingsService.resolveThemeSchId(authUserWithDept, null);
|
||||
String avatarStyle = schSettingsService.findBySchIdRaw(schId)
|
||||
/** 平台默认层在 sch_settings 中的固定 schId(与 UsedAppController/壁纸/LOGO 的 0 层同口径) */
|
||||
private static final int PLATFORM_SCH_ID = 0;
|
||||
|
||||
/**
|
||||
* 主题档读侧级联(设置中心三级重构 S7,2026-10-08):本校行未配置/旧格式/脏值 → 再查平台默认层(schId=0),
|
||||
* 命中即返回平台档;两级都无 → null。schId 自身即平台层时不二次回落(避免自查两次)。
|
||||
* <p>写侧不受影响:超管仍写 schId=0、校管写本校。</p>
|
||||
*/
|
||||
private LayerValue readThemeWithFallback(Integer schId) {
|
||||
String themeState = decodeTheme(schId);
|
||||
if (themeState != null || schId == null || schId == PLATFORM_SCH_ID) return new LayerValue(themeState, schId);
|
||||
return new LayerValue(decodeTheme(PLATFORM_SCH_ID), PLATFORM_SCH_ID);
|
||||
}
|
||||
|
||||
/** 头像样式读侧级联:语义与 {@link #readThemeWithFallback} 同构(本校空/脏 → 平台层 → null)。 */
|
||||
private LayerValue readAvatarWithFallback(Integer schId) {
|
||||
String avatarStyle = readAvatar(schId);
|
||||
if (avatarStyle != null || schId == null || schId == PLATFORM_SCH_ID) return new LayerValue(avatarStyle, schId);
|
||||
return new LayerValue(readAvatar(PLATFORM_SCH_ID), PLATFORM_SCH_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 级联读命中结果:{@code value}=命中的档位值(两级都无则 null),{@code hitSchId}=**实际命中层**的 schId
|
||||
* (回落发生时是 0,不是解析出的本机 schId)。
|
||||
* <p>响应体的 {@code schId} 字段因此采用"实际命中层"语义(方案 §6.S7 允许二选一)——
|
||||
* 设置中心的"本校默认"层要据此区分"本校已配置"与"本校未配置、正在回落平台默认"。
|
||||
* 现有前端只消费 themeState/avatarStyle,不依赖该字段。</p>
|
||||
*/
|
||||
private static final class LayerValue {
|
||||
final String value;
|
||||
final Integer hitSchId;
|
||||
|
||||
LayerValue(String value, Integer hitSchId) {
|
||||
this.value = value;
|
||||
this.hitSchId = hitSchId;
|
||||
}
|
||||
}
|
||||
|
||||
/** 原样读某层主题档并解码(查无行/脏值 → null) */
|
||||
private String decodeTheme(Integer schId) {
|
||||
return schSettingsService.findBySchIdRaw(schId)
|
||||
.map(SchSettings::getThemeName)
|
||||
.map(ThemeCodec::decodeThemeState)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/** 原样读某层头像样式(查无行/脏值 → null) */
|
||||
private String readAvatar(Integer schId) {
|
||||
return schSettingsService.findBySchIdRaw(schId)
|
||||
.map(SchSettings::getAvatarStyle)
|
||||
.filter(ThemeCodec::isAvatarStyle)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/** 学校默认头像样式(默认头像轮 2026-09-30,已登录可读):{avatarStyle: "text"|"silhouette"|null, schId}。
|
||||
* 未配置/脏值 → 回落平台默认层(见 {@link #readAvatarWithFallback});两级都无 → avatarStyle=null
|
||||
* (前端"不写键、不报错")。 */
|
||||
@GetMapping("schAvatarStyle")
|
||||
public Map<String, Object> schAvatarStyle(BoaUser authUserWithDept) {
|
||||
LayerValue hit = readAvatarWithFallback(schSettingsService.resolveThemeSchId(authUserWithDept, null));
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
out.put("avatarStyle", avatarStyle);
|
||||
out.put("schId", schId);
|
||||
out.put("avatarStyle", hit.value);
|
||||
out.put("schId", hit.hitSchId);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -91,7 +146,7 @@ public class SchSettingsController {
|
||||
schSettingsService.save(schSettings);
|
||||
}
|
||||
|
||||
/** 清空学校默认头像样式(仅管理员):置空 avatar_style 字段(行还承载 logo/theme 等配置不能删行,置空后读侧返回 null)。 */
|
||||
/** 清空学校默认头像样式(仅管理员):置空 avatar_style 字段(行还承载 logo/theme 等配置不能删行;置空后读侧回落平台层,平台也无则 null)。 */
|
||||
@PostMapping("clearSchAvatarStyle")
|
||||
public void clearSchAvatarStyle(BoaUser authUserWithDept,
|
||||
@RequestParam(name = "schId", required = false) Integer schId) {
|
||||
@@ -104,20 +159,16 @@ public class SchSettingsController {
|
||||
schSettingsService.save(schSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 学校默认主题档(已登录可读):{themeState: "<ThemeState JSON>" | null, schId}。
|
||||
* 未配置/旧格式值/脏数据 → themeState=null(前端"不写键、不报错")。
|
||||
/** 学校默认主题档(已登录可读):{themeState: "<ThemeState JSON>" | null, schId};schId 为实际命中层(见 {@link LayerValue})。
|
||||
* 本校未配置/脏值 → 回落平台默认层(见 {@link #readThemeWithFallback});两级都无 → themeState=null
|
||||
* (前端"不写键、不报错")。
|
||||
*/
|
||||
@GetMapping("schTheme")
|
||||
public Map<String, Object> schTheme(BoaUser authUserWithDept) {
|
||||
Integer schId = schSettingsService.resolveThemeSchId(authUserWithDept, null);
|
||||
String themeState = schSettingsService.findBySchIdRaw(schId)
|
||||
.map(SchSettings::getThemeName)
|
||||
.map(ThemeCodec::decodeThemeState)
|
||||
.orElse(null);
|
||||
LayerValue hit = readThemeWithFallback(schSettingsService.resolveThemeSchId(authUserWithDept, null));
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
out.put("themeState", themeState);
|
||||
out.put("schId", schId);
|
||||
out.put("themeState", hit.value);
|
||||
out.put("schId", hit.hitSchId);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user