前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据库设计采用行扩展,实现系统设置功能

数据库设计采用行扩展,实现系统设置功能

作者头像
用户7741497
发布2022-03-24 09:59:29
3910
发布2022-03-24 09:59:29
举报
文章被收录于专栏:hml_知识记录
一、平时我们实现系统设置功能的方式

在我们开发的系统功能中,大部分都有设置功能,比如APP端的用户推送开关设置,平时我们是这样设计的:

代码语言:javascript
复制
public class AppSettingPush {

    @ApiModelProperty(value = "会员ID")
    private Long memberId;

    @ApiModelProperty(value = "获赞推送")
    private Boolean belike;
    
    @ApiModelProperty(value = "评论推送")
    private Boolean com;
    
    @ApiModelProperty(value = "收藏推送")
    private Boolean coll;
    
    @ApiModelProperty(value = "转发推送")
    private Boolean tran;
    
    @ApiModelProperty(value = "关注推送")
    private Boolean focus;
    
    @ApiModelProperty(value = "@我的推送")
    private Boolean at;
}

采用这种方式有一个弊端,那就是将来如果要加一个推送设置开关,比如聊天推送,就需要增加字段,修改表结构。另外,如果随着业务功能的增加,还需要增加其他模块设置 ,比如用户空间数据设置(是否显示关注列表、是否显示粉丝列表、是否显示被点赞列表等等),又需要增加一个设置表,且重新需要实现一套CURD代码。

代码语言:javascript
复制
public class AppSettingSpaceData{

    @ApiModelProperty(value = "会员ID")
    private Long memberId;

    @ApiModelProperty(value = "是否显示关注列表")
    private Boolean lfouce;

    @ApiModelProperty(value = "是否显示粉丝列表")
    private Boolean lfans;

    @ApiModelProperty(value = "是否显示被点赞列表")
    private Boolean lbelike;

    @ApiModelProperty(value = "是否显示访客列表")
    private Boolean lvisit;

    @ApiModelProperty(value = "是否显示点赞列表")
    private Boolean llike;

    @ApiModelProperty(value = "是否显示收藏列表")
    private Boolean lcoll;

    @ApiModelProperty(value = "是否显示我评论的动态列表")
    private Boolean lcd;

    @ApiModelProperty(value = "是否显示@我的动态列表")
    private Boolean lad;
}
二、使用行扩展实现通用设置功能
2.1 表结构
代码语言:javascript
复制
CREATE TABLE `t_mem_member_settings` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `member_id` bigint NOT NULL COMMENT '会员id',
  `setting_module` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '设置模块',
  `setting_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '设置类型',
  `setting_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '设置值',
  `order_num` int DEFAULT '0' COMMENT '显示排序字段',
  `deleted` bit(1) NOT NULL DEFAULT b'0',
  `modify_time` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `IDX_deleted` (`deleted`) USING BTREE,
  KEY `idx_member_module` (`member_id`,`deleted`,`setting_module`) USING BTREE,
  KEY `idx_member_type` (`member_id`,`deleted`,`setting_type`,`setting_module`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=210391324491786 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci AVG_ROW_LENGTH=2048 ROW_FORMAT=DYNAMIC COMMENT='会员APP设置'
2.2 实体对象
代码语言:javascript
复制
public class MemberSettings{

    @ApiModelProperty(value = "会员ID")
    private Long memberId;

    @ApiModelProperty(value = "设置模块")
    private MemberSettingModuleEnum settingModule;

    @ApiModelProperty(value = "设置类型")
    private MemberSettingTypeEnum settingType;

    @ApiModelProperty(value = "设置值")
    private String settingValue;

    @ApiModelProperty(value = "显示排序字段")
    private int orderNum;
}
2.3 模块枚举
代码语言:javascript
复制
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "会员APP设置模块")
public enum MemberSettingModuleEnum {
    PUSH("PUSH", "推送"),
    SPACE_DATA("SPACE_DATA", "空间数据")
    ;

    @EnumValue
    @JsonValue
    private String code;
    private String name;
}
2.4 设置项枚举
代码语言:javascript
复制
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "会员APP设置类型")
public enum MemberSettingTypeEnum {
    PUSH_BE_LIKE("PUSH_BE_LIKE", "获赞推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,1),
    PUSH_COM("PUSH_COM", "评论推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,2),
    PUSH_COLL("PUSH_COLL", "收藏推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,3),
    PUSH_TRAN("PUSH_TRAN", "转发推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,4),
    PUSH_FOCUS("PUSH_FOCUS", "关注推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,5),
    PUSH_AT("PUSH_AT", "@我的推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,6),
    PUSH_IM_CHAT("PUSH_IM_CHAT", "@IM聊天推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,7),
    PUSH_IM_APPLY("PUSH_IM_APPLY", "IM申请推送", MemberSettingModuleEnum.PUSH,"1",Boolean.class,8),

    SPACE_DATA_LFOUCE("SPACE_DATA_LFOUCE", "是否显示关注列表", MemberSettingModuleEnum.SPACE_DATA,"1",Boolean.class,1),
    SPACE_DATA_LFANS("SPACE_DATA_LFANS", "是否显示粉丝列表", MemberSettingModuleEnum.SPACE_DATA,"1",Boolean.class,2),
    SPACE_DATA_LBELIKE("SPACE_DATA_LBELIKE", "是否显示被点赞列表", MemberSettingModuleEnum.SPACE_DATA,"1",Boolean.class,3),
    SPACE_DATA_LVISIT("SPACE_DATA_LVISIT", "是否显示访客列表", MemberSettingModuleEnum.SPACE_DATA,"1",Boolean.class,4),
    SPACE_DATA_LLIKE("SPACE_DATA_LLIKE", "是否显示点赞列表", MemberSettingModuleEnum.SPACE_DATA,"1",Boolean.class,5),
    SPACE_DATA_LCOLL("SPACE_DATA_LCOLL", "是否显示收藏列表", MemberSettingModuleEnum.SPACE_DATA,"0",Boolean.class,6),
    SPACE_DATA_LCD("SPACE_DATA_LCD", "是否显示我评论的动态列表", MemberSettingModuleEnum.SPACE_DATA,"0",Boolean.class,7),
    SPACE_DATA_LAD("SPACE_DATA_LAD", "是否显示@我的动态列表", MemberSettingModuleEnum.SPACE_DATA,"0",Boolean.class,8);

    @EnumValue
    @JsonValue
    private String code;
    private String name;
    private MemberSettingModuleEnum settingModule;
    private String defaultValue;
    private Class valueType;
    private int orderNum;

    public static List<MemberSettingTypeEnum> listMemberSetting(MemberSettingModuleEnum module){
        MemberSettingTypeEnum[] arry = MemberSettingTypeEnum.values();
        List<MemberSettingTypeEnum> list = new ArrayList<>();

        for(MemberSettingTypeEnum item : arry){
            if(item.getSettingModule() == module){
                list.add(item);
            }
        }
        return list;
    }
}
2.5 contoller类
代码语言:javascript
复制
@Slf4j
@Api(tags="会员APP通用设置")
@RestController
@RequestMapping("membersettings")
public class MemberSettingsController{

    @Resource
    private MemberSettingsService memberSettingsService;

    @PutMapping("list")
    @ApiOperation(value="按模块查询会员APP设置数据")
    public Result<MemberSettingsVO> listMemberSettings(@RequestBody QueryMemberSettingsVO queryVO){
        return Result.success(memberSettingsService.listMemberSettings(queryVO));
    }

    @PutMapping("update")
    @ApiOperation(value="更新设置")
    public Result<Boolean> update(@RequestBody @Valid MemberSettingsUpdateVO updateVO){
        return Result.success(memberSettingsService.update(updateVO));
    }

    @PutMapping("updatebatch")
    @ApiOperation(value="批量更新设置")
    public Result<Boolean> updateBatch(@RequestBody @Valid MemberSettingsUpdateBatchVO updateBatchVO){
        return Result.success(memberSettingsService.updateBatch(updateBatchVO));
    }

    @PostMapping("boolsetting")
    @ApiOperation(value="获取用户Bool类型设置值")
    public Result<Boolean> getMemberBoolSetting(@RequestBody SearchMemberSettingsVO search){
        return Result.success(memberSettingsService.getMemberBoolSetting(search));
    }

    @PostMapping("settingvalue")
    @ApiOperation(value="获取用户设置的Value值")
    public Result<String> getMemberSettingValue(@RequestBody SearchMemberSettingsVO search){
        return Result.success(memberSettingsService.getMemberSettingValue(search));
    }
}
2.6 service实现类
代码语言:javascript
复制
@Slf4j
@Service
public class MemberSettingsServiceImpl implements MemberSettingsService {

    @Override
    public MemberSettingsVO listMemberSettings(QueryMemberSettingsVO queryVO) {
        log.info("listMemberSettings queryVO:{}",queryVO);
        MemberSettingsVO memberSettingsVO = memberSettingsRdsHelper.get(queryVO.getMemberId());

        // Redis没数据,则从数据库取数据
        if (memberSettingsVO == null) {
            memberSettingsVO = new MemberSettingsVO();
            memberSettingsVO.setMemberId(queryVO.getMemberId());

            List<MemberSettingsItemVO> dbList = mapper.listMemberSettings(queryVO);
            if (dbList == null || dbList.size() == 0) {
                //数据库无数据,则生成默认记录
                saveDefaultValue(queryVO);
                dbList = mapper.listMemberSettings(queryVO);
            }else{
                // 检查是否有配置新的枚举项
                List<MemberSettingTypeEnum> typeList = MemberSettingTypeEnum.listMemberSetting(queryVO.getSettingModule());
                if(dbList.size()!=typeList.size()){
                    dbList = dealNewTypes(dbList,typeList,queryVO);
                }
            }

            memberSettingsVO.setSettingItems(dbList);
            memberSettingsRdsHelper.add(memberSettingsVO);
        }else{
            // 检查是否有配置新的枚举项
            List<MemberSettingsItemVO> dbList = memberSettingsVO.getSettingItems();
            List<MemberSettingTypeEnum> typeList = MemberSettingTypeEnum.listMemberSetting(queryVO.getSettingModule());
            if(dbList.size()!=typeList.size()){
                memberSettingsRdsHelper.del(queryVO.getMemberId());

                // 为了防止Redis数据出错,从数据库取数据
                dbList = mapper.listMemberSettings(queryVO);
                dbList = dealNewTypes(dbList,typeList,queryVO);

                memberSettingsVO.setSettingItems(dbList);
                memberSettingsRdsHelper.add(memberSettingsVO);
            }
        }
        return memberSettingsVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(MemberSettingsUpdateVO updateVO) {
        memberSettingsRdsHelper.del(updateVO.getMemberId());
        memberSettingsItemRdsHelper.del(updateVO.getMemberId(),updateVO.getSettingType().getCode());
        mapper.update(updateVO);
        return true;
    }

    @Override
    public boolean updateBatch(MemberSettingsUpdateBatchVO updateBatchVO) {
        return updateBatch(updateBatchVO.getUpdateList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateBatch(List<MemberSettingsUpdateVO> updateVOList) {
        if (updateVOList != null && updateVOList.size() > 0) {
            for (MemberSettingsUpdateVO item : updateVOList) {
                update(item);
            }
        }
        return true;
    }

    @Override
    public boolean insertBatch(List<MemberSettingsUpdateVO> updateVOList) {
        if (updateVOList != null && updateVOList.size() > 0) {
            List<MemberSettings> entityList = new ArrayList<>();
            for (MemberSettingsUpdateVO item : updateVOList) {
                MemberSettings entity = new MemberSettings();
                BeanUtil.copyProperties(item, entity);

                entity.setOrderNum(entity.getSettingType().getOrderNum());
                entity.setSettingModule(entity.getSettingType().getSettingModule());

                entityList.add(entity);
            }
            saveBatch(entityList);
        }
        return true;
    }

    @Override
    public String getMemberSettingValue(SearchMemberSettingsVO search) {
        MemberSettings memberSettings = memberSettingsItemRdsHelper.get(search.getMemberId(),search.getSettingType());

        // Redis没数据,则从数据库取数据
        if(memberSettings == null){
            memberSettings = mapper.getMemberSettings(search);

            // 数据库没数据,则表示是新配置的枚举项
            if(memberSettings == null){
                MemberSettingTypeEnum item = MemberSettingTypeEnum.valueOf(search.getSettingType());
                if(item!=null){
                    memberSettings =  packageEntity(search.getMemberId(),item);
                    save(memberSettings);
                }
            }

            if(memberSettings!=null){
                memberSettingsItemRdsHelper.add(memberSettings);
            }
        }

        if(memberSettings!=null){
            return memberSettings.getSettingValue();
        }else{
            return null;
        }
    }

    @Override
    public Boolean getMemberBoolSetting(SearchMemberSettingsVO search) {
        Member member = memberService.getById(search.getMemberId());
        if(member == null || member.getForbiddenStatus() == MemberForbiddenStatusEnum.FORBIDDEN){
            return false;
        }
        String value = getMemberSettingValue(search);
        boolean result = "1".equals(value) ? true : false;
        return result;
    }

    /**
     * 生成默认记录
     *
     * @param queryVO
     */
    private void saveDefaultValue(QueryMemberSettingsVO queryVO) {
        List<MemberSettingTypeEnum> list = MemberSettingTypeEnum.listMemberSetting(queryVO.getSettingModule());
        Long memberId = queryVO.getMemberId();

        List<MemberSettings> entityList = packageSettings(memberId,list);
        saveBatch(entityList);
    }

    private List<MemberSettings> packageSettings(Long memberId,List<MemberSettingTypeEnum> list){
        List<MemberSettings> entityList = new ArrayList<>();
        for (MemberSettingTypeEnum item : list) {
            MemberSettings entity = packageEntity(memberId,item);
            entityList.add(entity);
        }
        return entityList;
    }

    private MemberSettings packageEntity(Long memberId,MemberSettingTypeEnum item){
        MemberSettings entity = new MemberSettings();
        entity.setMemberId(memberId);
        entity.setSettingType(item);
        entity.setSettingModule(item.getSettingModule());
        entity.setSettingValue(item.getDefaultValue());
        entity.setOrderNum(item.getOrderNum());
        return entity;
    }

    private List<MemberSettingsItemVO> dealNewTypes(List<MemberSettingsItemVO> dbList,List<MemberSettingTypeEnum> typeList,QueryMemberSettingsVO queryVO){
        List<MemberSettingTypeEnum> dbTypeList = new ArrayList<>();
        for(MemberSettingsItemVO dbItem : dbList){
            dbTypeList.add(dbItem.getSettingType());
        }

        // 差集 (typeList - dbTypeList) = 新配置的枚举类型
        List<MemberSettingTypeEnum> newTypeList = typeList.stream().filter(item -> !dbTypeList.contains(item)).collect(Collectors.toList());
        List<MemberSettings> entityList = packageSettings(queryVO.getMemberId(),newTypeList);

        //将新的枚举项存入数据库
        saveBatch(entityList);
        //重新查询数据
        dbList = mapper.listMemberSettings(queryVO);

        return dbList;
    }
}
2.7 扩展说明

如果有新的设置项或设置模块,新增枚举类型即可,不需要修改其他代码。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、平时我们实现系统设置功能的方式
  • 二、使用行扩展实现通用设置功能
    • 2.1 表结构
      • 2.2 实体对象
        • 2.3 模块枚举
          • 2.4 设置项枚举
            • 2.5 contoller类
              • 2.6 service实现类
                • 2.7 扩展说明
                相关产品与服务
                云数据库 Redis
                腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档