feat(config-service): split config-service from iam for plugin/layout config

- new NestJS service on port 3011/gRPC 50059 (ADR-026)
- owns 6 config_ tables (plugin/role-mapping/role-layout/layout-tpl/user-override/outbox)
- GraphQL Federation 2 subgraph with DataLoader + RouterAuthGuard
- gRPC ConfigService + admin REST CRUD + user REST API
- three-layer merge: registry.defaultProps + roleMapping.widget_props + userOverride.props
- Redis cache with 5min TTL
- registered in apollo-router supergraph + docker-compose + port-allocation

Implements M3 of v2.1 migration plan.
This commit is contained in:
SpecialX
2026-07-15 02:13:03 +08:00
parent 163bff6666
commit 1a5fa78fa6
44 changed files with 3538 additions and 33 deletions

View File

@@ -0,0 +1,119 @@
syntax = "proto3";
package next_edu_cloud.config.v1;
import "google/protobuf/empty.proto";
// ConfigService 定义插件配置 + 布局 + 用户偏好契约ADR-026
// 从 iam 服务拆分而来,避免 iam 成为"上帝服务"。
//
// 双入口策略president §2.16
// - REST 供 gateway 透传 + portal 直连
// - gRPC 供 BFF / portal-shell 聚合调用
//
// gRPC 端口 50059v2.1 M3
service ConfigService {
// 获取用户合并后的插件配置(三层合并)
rpc GetPluginConfig(GetPluginConfigRequest) returns (PluginConfigResponse);
// 列出插件注册表
rpc ListPlugins(ListPluginsRequest) returns (ListPluginsResponse);
// 获取所有 Layout 模板5 种内置)
rpc GetLayoutTemplates(google.protobuf.Empty) returns (LayoutTemplatesResponse);
// 获取用户布局覆盖
rpc GetUserLayoutOverride(GetUserLayoutOverrideRequest) returns (UserLayoutOverride);
// 更新或创建用户布局覆盖
rpc UpsertUserLayoutOverride(UpsertUserLayoutOverrideRequest) returns (UserLayoutOverride);
// 重置用户布局覆盖(恢复角色默认)
rpc ResetUserLayoutOverride(ResetUserLayoutOverrideRequest) returns (ResetResponse);
}
// ========== 请求 / 响应 ==========
message GetPluginConfigRequest {
string user_id = 1;
string user_role = 2;
}
message ListPluginsRequest {
string category = 1;
bool is_active = 2;
}
message ListPluginsResponse {
repeated PluginRegistryItem plugins = 1;
}
message GetUserLayoutOverrideRequest {
string user_id = 1;
}
message UpsertUserLayoutOverrideRequest {
string user_id = 1;
string active_layout = 2;
string slot_overrides_json = 3;
string plugin_placements_json = 4;
string hidden_plugins_json = 5;
}
message ResetUserLayoutOverrideRequest {
string user_id = 1;
}
message ResetResponse {
bool success = 1;
}
message LayoutTemplatesResponse {
repeated LayoutTemplate templates = 1;
}
// ========== 业务实体 ==========
message PluginConfigResponse {
LayoutTemplate active_layout = 1;
repeated SlotConfig slots = 2;
repeated PluginPlacement plugins = 3;
repeated PluginRegistryItem registry = 4;
}
message LayoutTemplate {
string layout_id = 1;
string display_name = 2;
string description = 3;
repeated string available_slots = 4;
string layout_schema_json = 5;
}
message SlotConfig {
string slot_name = 1;
repeated string nav_items = 2;
}
message PluginPlacement {
string plugin_id = 1;
string slot = 2;
int32 sort_order = 3;
string size_json = 4;
string props_json = 5;
bool is_visible = 6;
}
message PluginRegistryItem {
string plugin_id = 1;
string category = 2;
string version = 3;
string display_name = 4;
string description = 5;
repeated string required_roles = 6;
bool is_builtin = 7;
bool is_active = 8;
}
message UserLayoutOverride {
string user_id = 1;
string active_layout = 2;
string slot_overrides_json = 3;
string plugin_placements_json = 4;
repeated string hidden_plugins = 5;
string updated_at = 6;
}