跳转至

Config 模块(loadup-modules-config)

系统参数管理与数据字典模块,提供运行时可配置的键值存储、变更历史追踪和多级字典体系。


模块结构

loadup-modules-config/
├── loadup-modules-config-client/          # 对外 DTO + Command + Query
├── loadup-modules-config-domain/          # 领域模型(POJO)+ Gateway 接口 + 枚举
├── loadup-modules-config-infrastructure/  # DO + GatewayImpl + 本地缓存
├── loadup-modules-config-app/             # @Service 业务编排 + AutoConfiguration
└── loadup-modules-config-test/            # 集成测试(Testcontainers MySQL)

功能概览

功能 说明
系统参数管理 全局 Key-Value 配置,支持多种值类型,运行时可修改
类型化取值 getTypedValue(key, Integer.class, default) 自动转换类型
加密标记 encrypted=true 的配置项在 DTO 层自动脱敏为 ******
系统内置保护 systemDefined=true 的配置项禁止删除
变更历史 每次 CREATE / UPDATE / DELETE 自动写入 config_history
Spring 事件 写操作发布 ConfigChangedEvent,联动缓存自动 evict
数据字典类型 字典分类(DictType),如 genderuser_status
数据字典项 字典条目(DictItem),支持级联(parentValue)和 CSS 样式类
本地缓存 基于 Caffeine,配置项 TTL 5 min / 最大 2000 条,字典 TTL 5 min / 最大 500 组

领域模型

ConfigItem(系统参数)

字段 类型 说明
id String 主键(UUID 无连字符)
configKey String 配置键(全局唯一)
configValue String 配置值(加密项 DTO 层展示 ******
valueType String 值类型:STRING / INTEGER / LONG / DOUBLE / BOOLEAN / JSON
category String 分类(如 systemuploadsecurity
editable Boolean 是否允许修改
encrypted Boolean 是否加密存储
systemDefined Boolean 系统内置,禁止删除
sortOrder Integer 排序权重
enabled Boolean 是否启用

ConfigHistory(变更历史)

字段 类型 说明
configKey String 关联配置键
oldValue String 变更前值
newValue String 变更后值
changeType ChangeType CREATE / UPDATE / DELETE
operator String 操作人

DictType(字典类型)

字段 类型 说明
dictCode String 字典编码(全局唯一,如 gender
dictName String 字典名称(如 性别
systemDefined Boolean 系统内置,禁止删除
sortOrder Integer 排序权重
enabled Boolean 是否启用

DictItem(字典项)

字段 类型 说明
dictCode String 所属字典类型编码
itemLabel String 显示标签(如
itemValue String 字典值(如 1
parentValue String 父级值,用于级联字典
cssClass String CSS 样式类(如 text-success
sortOrder Integer 排序权重

枚举

ValueType

STRING, INTEGER, LONG, DOUBLE, BOOLEAN, JSON

ChangeType

CREATE, UPDATE, DELETE

Service API

ConfigItemService

方法 说明
listAll() 查询所有配置项
listByCategory(category) 按分类查询
getByKey(configKey) 按键查询单条
getValue(configKey) 获取原始字符串值
getTypedValue(key, Class<T>, defaultValue) 获取类型化值(自动转换)
create(cmd) 创建配置项(写历史 + 发事件)
update(cmd) 更新配置值(校验 editable + 写历史 + 发事件)
delete(configKey) 删除配置项(校验 systemDefined + 写历史 + 发事件)
refreshCache() 手动清空本地缓存

DictService

方法 说明
listAllTypes() 查询所有字典类型
getDictData(dictCode) 按字典编码查询字典项列表
getCascadeData(dictCode, parentValue) 查询级联子项
getDictLabel(dictCode, itemValue) 通过值反查标签
createType(cmd) 创建字典类型
deleteType(dictCode) 删除字典类型(级联删除字典项)
createItem(cmd) 创建字典项(写后 evict 缓存)
deleteItem(id) 删除字典项

缓存策略

基于 Caffeine 本地缓存,无需 Redis。

缓存 最大容量 TTL evict 时机
配置项(by configKey) 2000 条 5 min 写操作后由 ConfigChangedEvent 自动触发
字典项(by dictCode) 500 组 5 min createItem / deleteItem 后手动 evict

ConfigChangedEventConfigItemService 在每次写操作后发布,ConfigLocalCache 通过 @EventListener 自动响应,无需显式调用。


Gateway 路由配置

loadup-application/src/main/resources/application.yml 中添加以下路由:

loadup:
  gateway:
    routes:
      # ── 系统参数 ──
      - path: /api/v1/config/list
        method: POST
        target: "bean://configItemService:listAll"
        securityCode: "default"
      - path: /api/v1/config/list-by-category
        method: POST
        target: "bean://configItemService:listByCategory"
        securityCode: "default"
      - path: /api/v1/config/get
        method: POST
        target: "bean://configItemService:getByKey"
        securityCode: "default"
      - path: /api/v1/config/value
        method: POST
        target: "bean://configItemService:getValue"
        securityCode: "OFF"
      - path: /api/v1/config/create
        method: POST
        target: "bean://configItemService:create"
        securityCode: "default"
      - path: /api/v1/config/update
        method: POST
        target: "bean://configItemService:update"
        securityCode: "default"
      - path: /api/v1/config/delete
        method: POST
        target: "bean://configItemService:delete"
        securityCode: "default"
      - path: /api/v1/config/refresh-cache
        method: POST
        target: "bean://configItemService:refreshCache"
        securityCode: "default"
      # ── 数据字典 ──
      - path: /api/v1/dict/types
        method: POST
        target: "bean://dictService:listAllTypes"
        securityCode: "default"
      - path: /api/v1/dict/data
        method: POST
        target: "bean://dictService:getDictData"
        securityCode: "OFF"
      - path: /api/v1/dict/cascade
        method: POST
        target: "bean://dictService:getCascadeData"
        securityCode: "OFF"
      - path: /api/v1/dict/label
        method: POST
        target: "bean://dictService:getDictLabel"
        securityCode: "OFF"
      - path: /api/v1/dict/type/create
        method: POST
        target: "bean://dictService:createType"
        securityCode: "default"
      - path: /api/v1/dict/type/delete
        method: POST
        target: "bean://dictService:deleteType"
        securityCode: "default"
      - path: /api/v1/dict/item/create
        method: POST
        target: "bean://dictService:createItem"
        securityCode: "default"
      - path: /api/v1/dict/item/delete
        method: POST
        target: "bean://dictService:deleteItem"
        securityCode: "default"

数据库表结构

config_item

CREATE TABLE IF NOT EXISTS config_item (
    id             VARCHAR(64)  NOT NULL                                          COMMENT 'ID',
    tenant_id      VARCHAR(64)                                                    COMMENT '租户ID',
    config_key     VARCHAR(200) NOT NULL                                          COMMENT '配置键(全局唯一)',
    config_value   TEXT                                                           COMMENT '配置值',
    value_type     VARCHAR(20)  NOT NULL DEFAULT 'STRING'                        COMMENT '值类型: STRING/INTEGER/LONG/DOUBLE/BOOLEAN/JSON',
    category       VARCHAR(50)  NOT NULL DEFAULT 'default'                       COMMENT '分类',
    description    VARCHAR(500)                                                   COMMENT '描述',
    editable       TINYINT      NOT NULL DEFAULT 1                                COMMENT '是否可编辑',
    encrypted      TINYINT      NOT NULL DEFAULT 0                                COMMENT '是否加密存储',
    system_defined TINYINT      NOT NULL DEFAULT 0                                COMMENT '是否系统内置',
    sort_order     INT          NOT NULL DEFAULT 0                                COMMENT '排序',
    enabled        TINYINT      NOT NULL DEFAULT 1                                COMMENT '是否启用',
    created_by     VARCHAR(64)                                                    COMMENT '创建人',
    created_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP                COMMENT '创建时间',
    updated_by     VARCHAR(64)                                                    COMMENT '更新人',
    updated_at     DATETIME              NULL ON UPDATE CURRENT_TIMESTAMP         COMMENT '更新时间',
    deleted        TINYINT      NOT NULL DEFAULT 0                                COMMENT '删除标记',
    PRIMARY KEY (id),
    UNIQUE KEY uk_config_key (config_key),
    KEY idx_tenant_id (tenant_id),
    KEY idx_category  (category),
    KEY idx_enabled   (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统参数配置表';

dict_type

CREATE TABLE IF NOT EXISTS dict_type (
    id             VARCHAR(64)  NOT NULL                                          COMMENT 'ID',
    tenant_id      VARCHAR(64)                                                    COMMENT '租户ID',
    dict_code      VARCHAR(100) NOT NULL                                          COMMENT '字典编码(全局唯一)',
    dict_name      VARCHAR(200) NOT NULL                                          COMMENT '字典名称',
    description    VARCHAR(500)                                                   COMMENT '描述',
    system_defined TINYINT      NOT NULL DEFAULT 0                                COMMENT '是否系统内置',
    sort_order     INT          NOT NULL DEFAULT 0                                COMMENT '排序',
    enabled        TINYINT      NOT NULL DEFAULT 1                                COMMENT '是否启用',
    created_by     VARCHAR(64)                                                    COMMENT '创建人',
    created_at     DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP                COMMENT '创建时间',
    updated_by     VARCHAR(64)                                                    COMMENT '更新人',
    updated_at     DATETIME              NULL ON UPDATE CURRENT_TIMESTAMP         COMMENT '更新时间',
    deleted        TINYINT      NOT NULL DEFAULT 0                                COMMENT '删除标记',
    PRIMARY KEY (id),
    UNIQUE KEY uk_dict_code (dict_code),
    KEY idx_tenant_id (tenant_id),
    KEY idx_enabled   (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典类型表';

dict_item

CREATE TABLE IF NOT EXISTS dict_item (
    id           VARCHAR(64)  NOT NULL                                            COMMENT 'ID',
    tenant_id    VARCHAR(64)                                                      COMMENT '租户ID',
    dict_code    VARCHAR(100) NOT NULL                                            COMMENT '字典编码',
    item_label   VARCHAR(200) NOT NULL                                            COMMENT '显示标签',
    item_value   VARCHAR(200) NOT NULL                                            COMMENT '字典值',
    parent_value VARCHAR(200)                                                     COMMENT '父级值(级联字典)',
    css_class    VARCHAR(100)                                                     COMMENT 'CSS 样式类',
    sort_order   INT          NOT NULL DEFAULT 0                                  COMMENT '排序',
    enabled      TINYINT      NOT NULL DEFAULT 1                                  COMMENT '是否启用',
    created_by   VARCHAR(64)                                                      COMMENT '创建人',
    created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP                  COMMENT '创建时间',
    updated_by   VARCHAR(64)                                                      COMMENT '更新人',
    updated_at   DATETIME              NULL ON UPDATE CURRENT_TIMESTAMP           COMMENT '更新时间',
    deleted      TINYINT      NOT NULL DEFAULT 0                                  COMMENT '删除标记',
    PRIMARY KEY (id),
    UNIQUE KEY uk_dict_code_value (dict_code, item_value),
    KEY idx_tenant_id    (tenant_id),
    KEY idx_dict_code    (dict_code),
    KEY idx_parent_value (parent_value),
    KEY idx_enabled      (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典项表';

config_history

CREATE TABLE IF NOT EXISTS config_history (
    id          VARCHAR(64)  NOT NULL                                             COMMENT 'ID',
    tenant_id   VARCHAR(64)                                                       COMMENT '租户ID',
    config_key  VARCHAR(200) NOT NULL                                             COMMENT '配置键',
    old_value   TEXT                                                              COMMENT '变更前值',
    new_value   TEXT                                                              COMMENT '变更后值',
    change_type VARCHAR(20)  NOT NULL                                             COMMENT '变更类型: CREATE/UPDATE/DELETE',
    operator    VARCHAR(64)  NOT NULL DEFAULT 'system'                            COMMENT '操作人',
    remark      VARCHAR(500)                                                      COMMENT '备注',
    created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP                   COMMENT '创建时间',
    updated_at  DATETIME              NULL ON UPDATE CURRENT_TIMESTAMP            COMMENT '更新时间',
    deleted     TINYINT      NOT NULL DEFAULT 0                                   COMMENT '删除标记',
    PRIMARY KEY (id),
    KEY idx_tenant_id  (tenant_id),
    KEY idx_config_key (config_key),
    KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置变更历史表';

预置种子数据

INSERT IGNORE INTO dict_type (id, dict_code, dict_name, system_defined, sort_order, enabled, created_at)
VALUES
    ('01', 'user_status', '用户状态', 1, 1, 1, NOW()),
    ('02', 'gender',      '性别',     1, 2, 1, NOW()),
    ('03', 'yes_no',      '是否',     1, 3, 1, NOW());

INSERT IGNORE INTO dict_item (id, dict_code, item_label, item_value, sort_order, enabled, created_at)
VALUES
    ('001', 'user_status', '正常', '1', 1, 1, NOW()),
    ('002', 'user_status', '停用', '0', 2, 1, NOW()),
    ('003', 'user_status', '锁定', '2', 3, 1, NOW()),
    ('004', 'gender',      '男',   '1', 1, 1, NOW()),
    ('005', 'gender',      '女',   '2', 2, 1, NOW()),
    ('006', 'gender',      '未知', '0', 3, 1, NOW()),
    ('007', 'yes_no',      '是',   '1', 1, 1, NOW()),
    ('008', 'yes_no',      '否',   '0', 2, 1, NOW());

INSERT IGNORE INTO config_item
    (id, config_key, config_value, value_type, category, description, editable, system_defined, enabled, created_at)
VALUES
    ('c01', 'system.name',                   'LoadUp Framework', 'STRING',  'system',   '系统名称',             0, 1, 1, NOW()),
    ('c02', 'upload.max-file-size',          '10485760',         'LONG',    'upload',   '文件上传大小限制(字节)', 1, 0, 1, NOW()),
    ('c03', 'security.password-expire-days', '90',               'INTEGER', 'security', '密码过期天数',           1, 0, 1, NOW());

测试

测试模块:loadup-modules-config-test,使用 Testcontainers 启动真实 MySQL,禁止 @MockBean 替代数据库。

# 运行所有集成测试(需要 Docker)
mvn test -pl modules/loadup-modules-config/loadup-modules-config-test

测试类说明

测试类 场景
ConfigItemServiceIT 配置项 CRUD、类型化取值、重复键校验、系统内置保护、缓存 evict 验证
DictServiceIT 字典类型 / 字典项 CRUD、级联查询、标签反查、系统内置保护

测试资源文件

src/test/resources/ 目录下包含以下文件:

文件 说明
application.yml 激活 test profile(固定内容:spring.profiles.active: test
application-test.yml 本地开发配置(Testcontainers reuse=true,print-sql=true)
application-ci.yml CI 流水线配置(Testcontainers reuse=false,print-sql=false)
schema.sql 测试数据库 Schema(与生产 schema 完全一致)