Compare commits

...

8 Commits

Author SHA1 Message Date
Leo
55734b2b12 feat(config): 集成字典管理模块配置
- 在插件父pom中添加字典模块引用
- 在Web启动模块中添加字典模块依赖
- 启用@EnableCoderDict字典翻译插件
- 配置应用启动时字典缓存预热
- 完成字典管理系统完整集成
2025-09-26 16:37:50 +08:00
Leo
f74779d0ad feat(plugin): 新增字典翻译插件模块
- 新增coder-common-thin-dict插件模块
- 实现CoderDictAspect AOP切面自动字典翻译
- 支持@CoderDict和@CoderDictClass注解
- 自动翻译List、Page和单对象返回结果
- 集成Redis缓存提升翻译性能
- 提供@EnableCoderDict启用注解
2025-09-26 16:37:26 +08:00
Leo
1dc2300763 feat(controller): 新增字典管理REST API接口
- 新增SysDictTypeController字典类型管理接口
- 新增SysDictDataController字典数据管理接口
- 提供完整的CRUD操作API
- 支持分页查询和条件搜索
- 集成Sa-Token权限验证和操作日志
- 添加Swagger文档注解
2025-09-26 16:36:44 +08:00
Leo
f887d73063 feat(service): 新增字典管理业务逻辑层
- 新增SysDictTypeService字典类型业务接口和实现
- 新增SysDictDataService字典数据业务接口和实现
- 实现完整的CRUD操作和分页查询
- 集成Redis缓存机制和缓存同步
- 支持字典数据的缓存预热和刷新
2025-09-26 16:36:23 +08:00
Leo
c53be83c20 feat(mapper): 新增字典管理数据访问层
- 新增SysDictTypeMapper字典类型数据访问接口
- 新增SysDictDataMapper字典数据数据访问接口
- 新增对应的MyBatis XML映射文件
- 继承BaseMapper提供基础CRUD操作
2025-09-26 16:35:59 +08:00
Leo
c486af6f9f feat(model): 新增字典管理数据模型
- 新增SysDictType字典类型实体类
- 新增SysDictData字典数据实体类
- 新增对应的VO查询类
- 支持MyBatis Plus注解和数据验证
2025-09-26 16:35:40 +08:00
Leo
b90f63559a feat(database): 新增字典管理数据库表结构和初始数据
- 新增sys_dict_type字典类型表
- 新增sys_dict_data字典数据表
- 添加完整的初始字典数据
- 支持用户性别、菜单状态、通知类型等常用字典
2025-09-26 16:35:21 +08:00
Leo
602b79aca0 chore(ide): 更新SQL方言和编码配置 2025-09-26 16:35:01 +08:00
26 changed files with 1931 additions and 653 deletions

View File

@ -13,6 +13,8 @@
<file url="file://$PROJECT_DIR$/coder-common-thin-mybatisplus/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-mybatisplus/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-desensitize/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-desensitize/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-desensitize/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-desensitize/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-dict/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-dict/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-easyexcel/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/coder-common-thin-plugins/coder-common-thin-limit/src/main/java" charset="UTF-8" />

7
.idea/sqldialects.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/sql/20250926-sys_dict.sql" dialect="MySQL" />
<file url="file://$PROJECT_DIR$/sql/coder-common-thin.sql" dialect="MySQL" />
</component>
</project>

View File

@ -0,0 +1,35 @@
package org.leocoder.thin.domain.model.vo.system;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.leocoder.thin.domain.model.vo.base.BaseVo;
/**
* @author Leocoder
* @description [字典数据表-模型][Vo类]
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysDictDataVo extends BaseVo {
/**
* 字典标签
*/
private String dictLabel;
/**
* 字典键值
*/
private String dictValue;
/**
* 字典类型
*/
private String dictType;
/**
* 状态0正常 1停用
*/
private String dictStatus;
}

View File

@ -0,0 +1,30 @@
package org.leocoder.thin.domain.model.vo.system;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.leocoder.thin.domain.model.vo.base.BaseVo;
/**
* @author Leocoder
* @description [字典类型表-模型][Vo类]
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SysDictTypeVo extends BaseVo {
/**
* 字典名称
*/
private String dictName;
/**
* 字典类型
*/
private String dictType;
/**
* 状态[0-正常 1-停用]
*/
private String dictStatus;
}

View File

@ -0,0 +1,110 @@
package org.leocoder.thin.domain.pojo.system;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author Leocoder
* @description [字典数据表-模型]
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sys_dict_data")
public class SysDictData implements Serializable {
/**
* 字典编码
*/
@TableId(value = "dict_id", type = IdType.ASSIGN_ID)
private Long dictId;
/**
* 字典标签
*/
@NotBlank(message = "字典标签不能为空")
@TableField(value = "dict_label")
private String dictLabel;
/**
* 字典键值
*/
@NotBlank(message = "字典键值不能为空")
@TableField(value = "dict_value")
private String dictValue;
/**
* 字典类型
*/
@NotBlank(message = "字典类型不能为空")
@TableField(value = "dict_type")
private String dictType;
/**
* 字典状态[0启用 1停用]
*/
@NotBlank(message = "字典状态不能为空")
@TableField(value = "dict_status")
private String dictStatus;
/**
* ElementPlus官方颜色[默认-primary]
*/
@NotBlank(message = "标签不能为空")
@TableField(value = "dict_tag")
private String dictTag;
/**
* tags自定义背景颜色[有值会进行覆盖ElementPlus官方颜色][例如16进制或者pink等]
*/
@TableField(value = "dict_color")
private String dictColor;
/**
* 显示顺序
*/
@NotNull(message = "显示顺序不能为空")
@TableField(value = "sorted")
private Integer sorted;
/**
* 创建者
*/
@TableField(value = "create_by")
private String createBy;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新者
*/
@TableField(value = "update_by")
private String updateBy;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
/**
* 备注
*/
@TableField(value = "remark")
private String remark;
}

View File

@ -0,0 +1,81 @@
package org.leocoder.thin.domain.pojo.system;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author Leocoder
* @description [字典类型表-模型]
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sys_dict_type")
public class SysDictType implements Serializable {
/**
* 字典主键
*/
@TableId(value = "dict_id", type = IdType.ASSIGN_ID)
private Long dictId;
/**
* 字典类型
*/
@NotBlank(message = "字典类型不能为空")
@TableField("dict_type")
private String dictType;
/**
* 字典名称
*/
@NotBlank(message = "字典名称不能为空")
@TableField("dict_name")
private String dictName;
/**
* 字典状态[0-正常 1-停用]
*/
@NotBlank(message = "字典状态不能为空")
@TableField("dict_status")
private String dictStatus;
/**
* 创建者
*/
@TableField("create_by")
private String createBy;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新者
*/
@TableField("update_by")
private String updateBy;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
/**
* 备注
*/
@TableField("remark")
private String remark;
}

View File

@ -1,6 +1,8 @@
package org.leocoder.thin.system.config.init; package org.leocoder.thin.system.config.init;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.leocoder.thin.system.service.dictdata.SysDictDataService;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -16,9 +18,13 @@ import org.springframework.stereotype.Component;
@Component @Component
public class ServerCommandLineRunner implements CommandLineRunner { public class ServerCommandLineRunner implements CommandLineRunner {
@Resource
private SysDictDataService sysDictDataService;
@Override @Override
public void run(String... args) { public void run(String... args) {
// 逻辑代码[字典数据缓存]
sysDictDataService.listDictCacheRedis();
log.info("CommandLineRunner项目启动后立即执行重新获取缓存 => [推荐使用]"); log.info("CommandLineRunner项目启动后立即执行重新获取缓存 => [推荐使用]");
} }

View File

@ -0,0 +1,239 @@
package org.leocoder.thin.system.controller.dictdata;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.leocoder.thin.common.constants.CoderCacheConstants;
import org.leocoder.thin.common.constants.CoderConstants;
import org.leocoder.thin.common.exception.coder.YUtil;
import org.leocoder.thin.common.satoken.CoderLoginUtil;
import org.leocoder.thin.common.utils.cache.RedisUtil;
import org.leocoder.thin.domain.enums.oper.OperType;
import org.leocoder.thin.domain.model.vo.system.SysDictDataVo;
import org.leocoder.thin.domain.pojo.system.SysDictData;
import org.leocoder.thin.operlog.annotation.OperLog;
import org.leocoder.thin.system.service.dictdata.SysDictDataService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
/**
* @author Leocoder
* @description [字典数据表-控制层]
*/
@Tag(name = "字典数据管理", description = "系统字典数据的增删改查操作")
@Validated
@RequestMapping("/coder")
@RequiredArgsConstructor
@RestController
public class SysDictDataController {
private final SysDictDataService sysDictDataService;
private final RedisUtil redisUtil;
/**
* @description [分页查询]
* @author Leocoder
*/
@Operation(summary = "分页查询字典数据列表", description = "根据查询条件分页获取字典数据信息")
@SaCheckPermission("system:dict:list")
@GetMapping("/sysDictData/listPage")
public IPage<SysDictData> listPage(SysDictDataVo vo) {
// 分页构造器
Page<SysDictData> page = new Page<>(vo.getPageNo(), vo.getPageSize());
// 条件构造器
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.isNotBlank(vo.getDictType()), SysDictData::getDictType, vo.getDictType());
wrapper.like(StringUtils.isNotBlank(vo.getDictLabel()), SysDictData::getDictLabel, vo.getDictLabel());
wrapper.eq(StringUtils.isNotBlank(vo.getDictStatus()), SysDictData::getDictStatus, vo.getDictStatus());
wrapper.orderByDesc(SysDictData::getDictType);
wrapper.orderByAsc(SysDictData::getSorted);
// 进行分页查询
page = sysDictDataService.page(page, wrapper);
return page;
}
/**
* @description [查询所有]
* @author Leocoder
*/
@Operation(summary = "查询所有字典数据", description = "获取所有字典数据信息")
@SaCheckPermission("system:dict:list")
@GetMapping("/sysDictData/list")
public List<SysDictData> list() {
return sysDictDataService.list();
}
/**
* @description [根据主键进行查询]
* @author Leocoder
*/
@Operation(summary = "根据ID查询字典数据", description = "通过ID获取字典数据详细信息")
@GetMapping("/sysDictData/getById/{id}")
public SysDictData getById(@PathVariable Long id) {
return sysDictDataService.getById(id);
}
/**
* @description [新增]
* @author Leocoder
*/
@Operation(summary = "新增字典数据", description = "创建新的字典数据")
@SaCheckPermission("system:dict:add")
@PostMapping("/sysDictData/add")
@OperLog(value = "新增字典数据", operType = OperType.INSERT)
public void add(@Validated @RequestBody SysDictData sysDictData) {
// 查询是否已经存在字典名称
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictLabel, sysDictData.getDictLabel());
wrapper.eq(SysDictData::getDictType, sysDictData.getDictType());
long count = sysDictDataService.count(wrapper);
YUtil.isTrue(count > 0, "该字典名称已存在,请重新输入");
if (StringUtils.isNotBlank(CoderLoginUtil.getUserName())) {
sysDictData.setCreateBy(CoderLoginUtil.getUserName());
}
YUtil.isTrue(!sysDictDataService.save(sysDictData), "新增失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [获取最新排序]
* @author Leocoder
*/
@Operation(summary = "获取最新排序", description = "根据字典类型获取最新的排序号")
@GetMapping("/sysDictData/getSorted/{dictType}")
public int getSorted(@PathVariable("dictType") String dictType) {
YUtil.isTrue(StringUtils.isBlank(dictType), "请传递参数");
LambdaQueryWrapper<SysDictData> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.select(SysDictData::getSorted);
lambdaQueryWrapper.eq(SysDictData::getDictType, dictType);
lambdaQueryWrapper.orderByDesc(SysDictData::getSorted);
lambdaQueryWrapper.last("LIMIT 1");
SysDictData sysDictData = sysDictDataService.getOne(lambdaQueryWrapper);
if (ObjectUtils.isEmpty(sysDictData)) return CoderConstants.ONE_NUMBER;
return sysDictData.getSorted() != null ? sysDictData.getSorted() + CoderConstants.ONE_NUMBER : CoderConstants.ONE_NUMBER;
}
/**
* @description [修改]
* @author Leocoder
*/
@Operation(summary = "修改字典数据", description = "更新字典数据信息")
@SaCheckPermission("system:dict:update")
@PostMapping("/sysDictData/update")
@OperLog(value = "修改字典数据", operType = OperType.UPDATE)
public void update(@Validated @RequestBody SysDictData sysDictData) {
// 查询是否已经存在字典名称
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictLabel, sysDictData.getDictLabel());
wrapper.eq(SysDictData::getDictType, sysDictData.getDictType());
SysDictData dictData = sysDictDataService.getOne(wrapper);
YUtil.isTrue(ObjectUtils.isNotEmpty(dictData) && !dictData.getDictId().equals(sysDictData.getDictId()), "该字典名称已存在,请重新输入");
if (StringUtils.isNotBlank(CoderLoginUtil.getUserName())) {
sysDictData.setUpdateBy(CoderLoginUtil.getUserName());
}
YUtil.isTrue(!sysDictDataService.updateById(sysDictData), "修改失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [删除]
* @author Leocoder
*/
@Operation(summary = "删除字典数据", description = "根据ID删除字典数据")
@SaCheckPermission("system:dict:delete")
@PostMapping("/sysDictData/deleteById/{id}")
@OperLog(value = "删除字典数据", operType = OperType.DELETE)
public void delete(@PathVariable Long id) {
YUtil.isTrue(!sysDictDataService.removeById(id), "删除失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [批量删除]
* @author Leocoder
*/
@Operation(summary = "批量删除字典数据", description = "根据ID列表批量删除字典数据")
@SaCheckPermission("system:dict:delete")
@PostMapping("/sysDictData/batchDelete")
@OperLog(value = "批量删除字典数据", operType = OperType.DELETE)
public void batchDelete(@NotNull(message = "请选择需要删除的数据") @RequestBody List<Long> ids) {
YUtil.isTrue(!sysDictDataService.removeBatchByIds(ids), "批量删除失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [修改状态]
* @author Leocoder
*/
@Operation(summary = "修改字典数据状态", description = "启用或停用字典数据")
@SaCheckPermission("system:dict:update")
@PostMapping("/sysDictData/updateStatus/{dictId}/{dictStatus}")
@OperLog(value = "修改字典数据状态", operType = OperType.UPDATE)
public void updateStatus(@PathVariable("dictId") Long dictId, @PathVariable("dictStatus") String dictStatus) {
UpdateWrapper<SysDictData> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("dict_status", dictStatus).eq("dict_id", dictId);
YUtil.isTrue(!sysDictDataService.update(updateWrapper), "修改失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [根据类型查询字典数据]
* @author Leocoder
*/
@Operation(summary = "根据类型查询字典数据", description = "通过字典类型获取对应的字典数据列表")
@GetMapping("/sysDictData/listDataByType/{dictType}")
public List<SysDictData> listDataByType(@PathVariable("dictType") String dictType) {
Boolean isExist = redisUtil.hasKey(CoderCacheConstants.DICT_REDIS_KEY + dictType);
if (!isExist) {
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
// 查询指定字段
wrapper.select(SysDictData::getDictLabel, SysDictData::getDictValue, SysDictData::getDictType, SysDictData::getDictTag, SysDictData::getDictColor);
wrapper.eq(StringUtils.isNotBlank(dictType), SysDictData::getDictType, dictType);
wrapper.eq(SysDictData::getDictStatus, CoderConstants.ZERO_STRING);
List<SysDictData> dictDataList = sysDictDataService.list(wrapper);
if (CollectionUtils.isNotEmpty(dictDataList)) {
return dictDataList;
} else {
return Collections.emptyList();
}
} else {
List<SysDictData> redisDictData = redisUtil.getKey(CoderCacheConstants.DICT_REDIS_KEY + dictType);
if (CollectionUtils.isNotEmpty(redisDictData)) {
return redisDictData;
} else {
return Collections.emptyList();
}
}
}
/**
* @description [字典数据同步Redis进行缓存]
* @author Leocoder
*/
@Operation(summary = "同步字典缓存", description = "手动同步所有字典数据到Redis缓存")
@SaCheckPermission("system:dict:update")
@GetMapping("/sysDictData/listDictCacheRedis")
@OperLog(value = "字典数据同步缓存", operType = OperType.UPDATE)
public void listDictCacheRedis() {
sysDictDataService.listDictCacheRedis();
}
}

View File

@ -0,0 +1,234 @@
package org.leocoder.thin.system.controller.dicttype;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.leocoder.thin.common.constants.CoderCacheConstants;
import org.leocoder.thin.common.constants.CoderConstants;
import org.leocoder.thin.common.exception.coder.YUtil;
import org.leocoder.thin.common.satoken.CoderLoginUtil;
import org.leocoder.thin.common.utils.cache.RedisUtil;
import org.leocoder.thin.domain.enums.oper.OperType;
import org.leocoder.thin.domain.model.vo.system.SysDictTypeVo;
import org.leocoder.thin.domain.pojo.system.SysDictData;
import org.leocoder.thin.domain.pojo.system.SysDictType;
import org.leocoder.thin.operlog.annotation.OperLog;
import org.leocoder.thin.system.service.dictdata.SysDictDataService;
import org.leocoder.thin.system.service.dicttype.SysDictTypeService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
/**
* @author Leocoder
* @description [字典类型表-控制层]
*/
@Tag(name = "字典类型管理", description = "系统字典类型的增删改查操作")
@Validated
@RequestMapping("/coder")
@RequiredArgsConstructor
@RestController
public class SysDictTypeController {
private final SysDictTypeService sysDictTypeService;
private final SysDictDataService sysDictDataService;
private final RedisUtil redisUtil;
/**
* @description [分页查询]
* @author Leocoder
*/
@Operation(summary = "分页查询字典类型列表", description = "根据查询条件分页获取字典类型信息")
@SaCheckPermission("system:dict:list")
@GetMapping("/sysDictType/listPage")
public IPage<SysDictType> listPage(SysDictTypeVo vo) {
// 分页构造器
Page<SysDictType> page = new Page<>(vo.getPageNo(), vo.getPageSize());
// 条件构造器
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(vo.getDictName()), SysDictType::getDictName, vo.getDictName());
wrapper.like(StringUtils.isNotBlank(vo.getDictType()), SysDictType::getDictType, vo.getDictType());
wrapper.eq(StringUtils.isNotBlank(vo.getDictStatus()), SysDictType::getDictStatus, vo.getDictStatus());
wrapper.orderByDesc(SysDictType::getCreateTime);
// 进行分页查询
page = sysDictTypeService.page(page, wrapper);
return page;
}
/**
* @description [查询所有]
* @author Leocoder
*/
@Operation(summary = "查询所有字典类型", description = "获取所有字典类型信息")
@SaCheckPermission("system:dict:list")
@GetMapping("/sysDictType/list")
public List<SysDictType> list() {
return sysDictTypeService.list();
}
/**
* @description [根据主键查询]
* @author Leocoder
*/
@Operation(summary = "根据ID查询字典类型", description = "通过ID获取字典类型详细信息")
@GetMapping("/sysDictType/getById/{id}")
public SysDictType getById(@PathVariable Long id) {
return sysDictTypeService.getById(id);
}
/**
* @description [新增]
* @author Leocoder
*/
@Operation(summary = "新增字典类型", description = "创建新的字典类型")
@SaCheckPermission("system:dict:add")
@PostMapping("/sysDictType/add")
@OperLog(value = "新增字典类型", operType = OperType.INSERT)
public void add(@Validated @RequestBody SysDictType sysDictType) {
// 查询是否已经存在字典名称
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictType::getDictType, sysDictType.getDictType());
long count = sysDictTypeService.count(wrapper);
YUtil.isTrue(count > 0, "该字典类型已存在,请重新输入");
if (StringUtils.isNotBlank(CoderLoginUtil.getUserName())) {
sysDictType.setCreateBy(CoderLoginUtil.getUserName());
}
YUtil.isTrue(!sysDictTypeService.save(sysDictType), "新增失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [修改]
* @author Leocoder
*/
@Operation(summary = "修改字典类型", description = "更新字典类型信息")
@SaCheckPermission("system:dict:update")
@PostMapping("/sysDictType/update")
@OperLog(value = "修改字典类型", operType = OperType.UPDATE)
public void update(@Validated @RequestBody SysDictType sysDictType) {
// 根据ID进行查询用来同步修改字典数据类型
SysDictType sysDictTypeModel = sysDictTypeService.getById(sysDictType.getDictId());
// 查询是否已经存在字典名称
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictType::getDictType, sysDictType.getDictType());
SysDictType dictType = sysDictTypeService.getOne(wrapper);
YUtil.isTrue(ObjectUtils.isNotEmpty(dictType) && !dictType.getDictId().equals(sysDictType.getDictId()), "该字典类型已存在,请重新输入");
if (StringUtils.isNotBlank(CoderLoginUtil.getUserName())) {
sysDictType.setUpdateBy(CoderLoginUtil.getUserName());
}
YUtil.isTrue(!sysDictTypeService.updateById(sysDictType), "修改失败,请稍后重试");
if(!sysDictTypeModel.getDictType().equals(sysDictType.getDictType())) {
// 将字典数据的类型也同步修改
LambdaUpdateWrapper<SysDictData> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(SysDictData::getDictType, sysDictType.getDictType());
updateWrapper.eq(SysDictData::getDictType, sysDictTypeModel.getDictType());
sysDictDataService.update(updateWrapper);
}
// 先删除该缓存
redisUtil.deleteKey(CoderCacheConstants.DICT_REDIS_KEY + sysDictTypeModel.getDictType());
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [删除]
* @author Leocoder
*/
@Operation(summary = "删除字典类型", description = "根据ID删除字典类型及其关联的字典数据")
@SaCheckPermission("system:dict:delete")
@Transactional(rollbackFor = Exception.class)
@PostMapping("/sysDictType/deleteById/{id}")
@OperLog(value = "删除字典类型", operType = OperType.DELETE)
public void delete(@PathVariable("id") Long id) {
SysDictType sysDictType = sysDictTypeService.getById(id);
YUtil.isTrue(ObjectUtils.isEmpty(sysDictType) || StringUtils.isBlank(sysDictType.getDictType()), "请检查该数据是否存在");
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictType, sysDictType.getDictType());
// 删除字典详情
sysDictDataService.remove(wrapper);
YUtil.isTrue(!sysDictTypeService.removeById(id), "删除失败,请稍后重试");
// 删除缓存
redisUtil.deleteKey(CoderCacheConstants.DICT_REDIS_KEY + sysDictType.getDictType());
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [批量删除]
* @author Leocoder
*/
@Operation(summary = "批量删除字典类型", description = "根据ID列表批量删除字典类型")
@SaCheckPermission("system:dict:delete")
@Transactional(rollbackFor = Exception.class)
@PostMapping("/sysDictType/batchDelete")
@OperLog(value = "批量删除字典类型", operType = OperType.DELETE)
public void batchDelete(@NotNull(message = "请选择需要删除的数据") @RequestBody List<Long> ids) {
if (CollectionUtil.isNotEmpty(ids)) {
for (Long id : ids) {
SysDictType sysDictType = sysDictTypeService.getById(id);
YUtil.isTrue(ObjectUtils.isEmpty(sysDictType) || StringUtils.isBlank(sysDictType.getDictType()), "请检查该数据是否存在");
LambdaQueryWrapper<SysDictData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysDictData::getDictType, sysDictType.getDictType());
YUtil.isTrue(!sysDictDataService.remove(wrapper), "删除失败,请稍后重试");
// 删除缓存
redisUtil.deleteKey(CoderCacheConstants.DICT_REDIS_KEY + sysDictType.getDictType());
}
}
YUtil.isTrue(!sysDictTypeService.removeBatchByIds(ids), "批量删除失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [修改状态]
* @author Leocoder
*/
@Operation(summary = "修改字典类型状态", description = "启用或停用字典类型")
@SaCheckPermission("system:dict:update")
@PostMapping("/sysDictType/updateStatus/{dictId}/{dictStatus}")
@OperLog(value = "修改字典类型状态", operType = OperType.UPDATE)
public void updateStatus(@PathVariable("dictId") Long dictId, @PathVariable("dictStatus") String dictStatus) {
UpdateWrapper<SysDictType> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("dict_status", dictStatus).eq("dict_id", dictId);
YUtil.isTrue(!sysDictTypeService.update(updateWrapper), "修改失败,请稍后重试");
// 同步缓存
sysDictDataService.listDictCacheRedis();
}
/**
* @description [查询字典类型下拉框]
* @author Leocoder
*/
@Operation(summary = "查询字典类型下拉框", description = "获取启用状态的字典类型列表,用于下拉选择")
@GetMapping("/sysDictType/listDictType")
public List<SysDictType> listDictType() {
LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
// 按需加载
wrapper.select(SysDictType::getDictType, SysDictType::getDictName);
wrapper.eq(SysDictType::getDictStatus, CoderConstants.ZERO_STRING);
List<SysDictType> dictTypeList = sysDictTypeService.list(wrapper);
if (CollectionUtils.isNotEmpty(dictTypeList)) {
return dictTypeList;
} else {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,17 @@
package org.leocoder.thin.system.service.dictdata;
import com.baomidou.mybatisplus.extension.service.IService;
import org.leocoder.thin.domain.pojo.system.SysDictData;
/**
* @author Leocoder
* @description [字典数据表-服务实现层接口]
*/
public interface SysDictDataService extends IService<SysDictData> {
/**
* @description [字典数据同步Redis进行缓存]
* @author Leocoder
*/
void listDictCacheRedis();
}

View File

@ -0,0 +1,62 @@
package org.leocoder.thin.system.service.dictdata;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.leocoder.thin.common.constants.CoderCacheConstants;
import org.leocoder.thin.common.constants.CoderConstants;
import org.leocoder.thin.common.utils.cache.RedisUtil;
import org.leocoder.thin.domain.pojo.system.SysDictData;
import org.leocoder.thin.domain.pojo.system.SysDictType;
import org.leocoder.thin.mybatisplus.mapper.system.SysDictDataMapper;
import org.leocoder.thin.mybatisplus.mapper.system.SysDictTypeMapper;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Leocoder
* @description [字典数据表-服务实现层]
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDictData> implements SysDictDataService {
private final SysDictDataMapper sysDictDataMapper;
private final SysDictTypeMapper sysDictTypeMapper;
private final RedisUtil redisUtil;
/**
* @description [字典数据同步Redis进行缓存]
* @author Leocoder
*/
@Override
public void listDictCacheRedis() {
// 1查询所有可用的字典数据
LambdaQueryWrapper<SysDictType> dictTypeWrapper = new LambdaQueryWrapper<>();
dictTypeWrapper.select(SysDictType::getDictType, SysDictType::getDictStatus);
List<SysDictType> dictTypeList = sysDictTypeMapper.selectList(dictTypeWrapper);
if (CollectionUtil.isNotEmpty(dictTypeList)) {
// 2根据字典类型查询字典数据
for (SysDictType sysDictType : dictTypeList) {
if (sysDictType.getDictStatus().equals(CoderConstants.ZERO_STRING)) {
LambdaQueryWrapper<SysDictData> dictDataWrapper = new LambdaQueryWrapper<>();
dictDataWrapper.eq(SysDictData::getDictStatus, CoderConstants.ZERO_STRING);
dictDataWrapper.eq(SysDictData::getDictType, sysDictType.getDictType());
dictDataWrapper.orderByAsc(SysDictData::getSorted);
List<SysDictData> dictDataList = sysDictDataMapper.selectList(dictDataWrapper);
// 3把字典数据循环存到Redis设计Redis的keyCoderDict:dictType -> [{},{},{}]
redisUtil.setCacheObject(CoderCacheConstants.DICT_REDIS_KEY + sysDictType.getDictType(), dictDataList);
} else {
redisUtil.deleteKey(CoderCacheConstants.DICT_REDIS_KEY + sysDictType.getDictType());
}
}
}
}
}

View File

@ -0,0 +1,12 @@
package org.leocoder.thin.system.service.dicttype;
import com.baomidou.mybatisplus.extension.service.IService;
import org.leocoder.thin.domain.pojo.system.SysDictType;
/**
* @author Leocoder
* @description [字典类型表-服务实现层接口]
*/
public interface SysDictTypeService extends IService<SysDictType> {
}

View File

@ -0,0 +1,21 @@
package org.leocoder.thin.system.service.dicttype;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.leocoder.thin.domain.pojo.system.SysDictType;
import org.leocoder.thin.mybatisplus.mapper.system.SysDictTypeMapper;
import org.springframework.stereotype.Service;
/**
* @author Leocoder
* @description [字典类型表-服务实现层]
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements SysDictTypeService {
private final SysDictTypeMapper sysDictTypeMapper;
}

View File

@ -0,0 +1,14 @@
package org.leocoder.thin.mybatisplus.mapper.system;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.leocoder.thin.domain.pojo.system.SysDictData;
/**
* @author Leocoder
* @description [字典数据表-数据库连接层]
*/
@Mapper
public interface SysDictDataMapper extends BaseMapper<SysDictData> {
}

View File

@ -0,0 +1,14 @@
package org.leocoder.thin.mybatisplus.mapper.system;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.leocoder.thin.domain.pojo.system.SysDictType;
/**
* @author Leocoder
* @description [字典类型表-数据库连接层]
*/
@Mapper
public interface SysDictTypeMapper extends BaseMapper<SysDictType> {
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.leocoder.thin.mybatisplus.mapper.system.SysDictDataMapper">
</mapper>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.leocoder.thin.mybatisplus.mapper.system.SysDictTypeMapper">
</mapper>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.leocoder.thin</groupId>
<artifactId>coder-common-thin-plugins</artifactId>
<version>${revision}</version>
</parent>
<name>coder-common-thin-dict</name>
<artifactId>coder-common-thin-dict</artifactId>
<description>字典翻译插件</description>
<dependencies>
<!-- 通用公共模块 -->
<dependency>
<groupId>org.leocoder.thin</groupId>
<artifactId>coder-common-thin-common</artifactId>
<version>${revision}</version>
</dependency>
<!-- 数据模型模块 -->
<dependency>
<groupId>org.leocoder.thin</groupId>
<artifactId>coder-common-thin-model</artifactId>
<version>${revision}</version>
</dependency>
<!-- Aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package org.leocoder.thin.dict.anno;
import org.leocoder.thin.common.utils.cache.RedisUtil;
import org.leocoder.thin.dict.aspect.CoderDictAspect;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({CoderDictAspect.class, RedisUtil.class})
public @interface EnableCoderDict {
}

View File

@ -0,0 +1,212 @@
package org.leocoder.thin.dict.aspect;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.leocoder.thin.common.anno.CoderDict;
import org.leocoder.thin.common.anno.CoderDictClass;
import org.leocoder.thin.common.constants.CoderCacheConstants;
import org.leocoder.thin.common.utils.cache.RedisUtil;
import org.leocoder.thin.domain.pojo.system.SysDictData;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Leocoder
* @description [CoderDictAspect]
*/
@Aspect
@Order(4)
@Component
@Slf4j
public class CoderDictAspect {
private Map<String, String> dictMap = new HashMap<>();
@Resource
private RedisUtil redisUtil;
@Pointcut("@annotation(org.leocoder.thin.common.anno.CoderDictClass)")
public void logPointCut() {
}
/**
* @description [翻译数据]
* 需要处理的数据类型
* 1分页数据R<IPage<T>>
* 2普通列表R<List<T>>
* 3普通数据R<T>
* @author Leocoder
*/
@Around("@annotation(CoderDictClass)")
public Object CoderTypeDictTranslation(final ProceedingJoinPoint proceedingJoinPoint, CoderDictClass CoderDictClass) throws Throwable {
Object proceed = proceedingJoinPoint.proceed();
if (ObjectUtils.isEmpty(proceed)) {
return proceed;
}
if (proceed instanceof List) {
// 数据字典翻译
List<Object> list = (List<Object>) proceed;
CoderDictTranslate(list);
return list;
} else if (proceed instanceof Page) {
// Page 类型处理逻辑
IPage page = (IPage) proceed;
List records = page.getRecords();
// 数据字典翻译
CoderDictTranslate(records);
page.setRecords(records);
// 返回修改后的分页对象
return page;
} else {
// 其他类型处理逻辑或错误处理
return proceed;
}
}
/**
* @description [获取不同类型返回结果数据进行翻译]
* @author Leocoder
*/
private void CoderDictTranslate(Object resultData) {
Object objectData;
// 检查输入的result是否是List或者ArrayList的实例
if (resultData instanceof List) {
// 如果是列表则获取第一个元素
List CoderList = ((List) resultData);
// 如果列表为空则直接返回原始结果
if (CoderList.isEmpty()) {
return;
}
// 获取集合的第一条数据
objectData = CoderList.get(0);
} else {
// 否则直接使用result对象
objectData = resultData;
}
// 获取数据字典key 实体类字段名称
List<CoderDictModel> dictModelList = getCoderDict(objectData.getClass());
// dictModelList.forEach(System.out::println);
// CoderDictModel(dictKey=sys_user_sex, dictValue=sex)
// 如果没有字典映射则直接返回原始结果
if (dictModelList.isEmpty()) {
return;
}
// 获取所有的字典数据
List<SysDictData> dictDataList = listDictCache(dictModelList);
// 如果字典数据是空则直接返回
if (CollectionUtils.isEmpty(dictDataList)) {
return;
}
// 将字典值转换成map形式
for (SysDictData dictData : dictDataList) {
dictMap.put(dictData.getDictType() + "_" + dictData.getDictValue(), dictData.getDictLabel());
}
// 根据对象类型为返回数据的每一个对象赋予字典值
if (resultData instanceof List) {
for (Object entity : (List) resultData) {
assignDictValue(entity, dictModelList, dictMap);
}
} else {
assignDictValue(resultData, dictModelList, dictMap);
}
}
/**
* @description [获取字典数据缓存]
* @author Leocoder
*/
private List<SysDictData> listDictCache(List<CoderDictModel> dictModelList) {
List<SysDictData> dictDataList = new ArrayList<>();
List<String> dictKeyList = getDictKey(dictModelList);
for (String dictKey : dictKeyList) {
Boolean hasKey = redisUtil.hasKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
if(hasKey) {
List<SysDictData> dictDataKeyList = redisUtil.getKey(CoderCacheConstants.DICT_REDIS_KEY + dictKey);
dictDataList.addAll(dictDataKeyList);
}
}
return dictDataList;
}
/**
* @param entity 返回List集合数据循环出来的实体类对象转换字典的核心代码
* @param dictModelList CoderDictModel(dictKey=sys_user_sex, dictValue=sex) 获取实体类的@CoderDict注解值
* @param dictMap 所有字典数据缓存
*/
public void assignDictValue(Object entity, List<CoderDictModel> dictModelList, Map<String, String> dictMap) {
try {
// 遍历每个CoderDictModel对象
for (CoderDictModel dictModel : dictModelList) {
String dictKey = dictModel.getDictKey(); // 获取字典类型
String fieldName = dictModel.getDictValue(); // 获取需要赋值的字段名
Class<?> classData = entity.getClass(); // 获取实体类的类
Field fieldData = classData.getDeclaredField(fieldName); // 获取需要赋值的字段
fieldData.setAccessible(true); // 设置字段可访问性
Object originalValue = fieldData.get(entity); // 获取字段的原始值
if (ObjectUtils.isNotEmpty(originalValue)) {
// 如果原始值不为空则将字典值赋给字段
String dictValue = dictMap.getOrDefault(dictKey + "_" + originalValue, originalValue.toString());
fieldData.set(entity, dictValue);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取实体中所有需要翻译的dictKey返回需要翻译的dictKey列表
*/
private List<String> getDictKey(List<CoderDictModel> CoderDictBos) {
// 创建一个字符串列表用于存储dictKey
List<String> dictKeyList = new ArrayList<>();
// 如果传入的CoderDictBo为空则直接返回空列表
if (CollectionUtils.isEmpty(CoderDictBos)) {
return dictKeyList;
}
for (CoderDictModel dictBo : CoderDictBos) {
// 将每个CoderDictBo对象的key添加到列表中
dictKeyList.add(dictBo.getDictKey());
}
return dictKeyList;
}
/**
* 获取实体类中配置的dictKey 对应的字段名称
*/
private List<CoderDictModel> getCoderDict(Class classData) {
Field[] fields = classData.getDeclaredFields(); // 获取类classData中声明的所有字段
List<CoderDictModel> list = new ArrayList<>(); // 创建一个CoderDictBo列表
CoderDictModel CoderDictBo;
CoderDict CoderDict;
for (Field field : fields) {
if (field.isAnnotationPresent(CoderDict.class)) { // 检查字段是否带有CoderDict注解
CoderDict = field.getAnnotation(CoderDict.class); // 获取CoderDict注解
CoderDictBo = new CoderDictModel(CoderDict.dictKey(), field.getName()); // 创建CoderDictBo对象存储dictKey和字段名
list.add(CoderDictBo);
}
}
// 返回包含配置的dictKey和对应字段内容的列表
return list;
}
}

View File

@ -0,0 +1,21 @@
package org.leocoder.thin.dict.aspect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Leocoder
* @description [CoderDictModel-@CoderDict注解字段]
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class CoderDictModel {
/** 字典Key值 */
private String dictKey;
/** 实体类字段名称 */
private String dictValue;
}

View File

@ -23,6 +23,7 @@
<module>coder-common-thin-limit</module> <module>coder-common-thin-limit</module>
<module>coder-common-thin-oper-logs</module> <module>coder-common-thin-oper-logs</module>
<module>coder-common-thin-oss</module> <module>coder-common-thin-oss</module>
<module>coder-common-thin-dict</module>
</modules> </modules>
</project> </project>

View File

@ -37,6 +37,12 @@
<artifactId>coder-common-thin-oss</artifactId> <artifactId>coder-common-thin-oss</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 字典管理模块 -->
<dependency>
<groupId>org.leocoder.thin</groupId>
<artifactId>coder-common-thin-dict</artifactId>
<version>${revision}</version>
</dependency>
<!-- 定时任务 --> <!-- 定时任务 -->
<!-- <dependency> --> <!-- <dependency> -->
<!-- <groupId>org.leocoder.thin</groupId> --> <!-- <groupId>org.leocoder.thin</groupId> -->

View File

@ -10,6 +10,7 @@ import org.leocoder.thin.oss.annotation.EnableCoderOss;
import org.leocoder.thin.repect.anno.EnableCoderRepeatSubmit; import org.leocoder.thin.repect.anno.EnableCoderRepeatSubmit;
import org.leocoder.thin.resultex.anno.EnableResultEx; import org.leocoder.thin.resultex.anno.EnableResultEx;
import org.leocoder.thin.satoken.anno.EnableCoderSaToken; import org.leocoder.thin.satoken.anno.EnableCoderSaToken;
import org.leocoder.thin.dict.anno.EnableCoderDict;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
@ -29,7 +30,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@EnableResultEx @EnableResultEx
@EnableOperLog @EnableOperLog
@EnableCoderOss @EnableCoderOss
// @EnableCoderDict @EnableCoderDict
@EnableScheduling @EnableScheduling
@Slf4j @Slf4j
@SpringBootApplication(scanBasePackages = "org.leocoder.thin") @SpringBootApplication(scanBasePackages = "org.leocoder.thin")

124
sql/20250926-sys_dict.sql Normal file
View File

@ -0,0 +1,124 @@
-- ====================================================================
-- 字典管理功能数据库脚本
-- 创建时间2025-09-26
-- 作者Leocoder
-- 功能描述:字典类型表和字典数据表的创建及初始化数据
-- ====================================================================
-- ----------------------------
-- 创建字典类型表 sys_dict_type
-- ----------------------------
DROP TABLE IF EXISTS `sys_dict_type`;
CREATE TABLE `sys_dict_type` (
`dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典主键',
`dict_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典名称',
`dict_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典类型',
`dict_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态[0-正常 1-停用]',
`remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`dict_id`) USING BTREE,
UNIQUE KEY `dict_type` (`dict_type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典类型表';
-- ----------------------------
-- 创建字典数据表 sys_dict_data
-- ----------------------------
DROP TABLE IF EXISTS `sys_dict_data`;
CREATE TABLE `sys_dict_data` (
`dict_id` bigint NOT NULL AUTO_INCREMENT COMMENT '字典编码',
`dict_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典类型',
`dict_label` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典名称',
`dict_value` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '字典键值',
`dict_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态[0启用 1停用]',
`dict_tag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'primary' COMMENT 'ElementPlus官方颜色[默认-primary]',
`dict_color` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT 'tags自定义背景颜色[有值会进行覆盖ElementPlus官方颜色](例如16进制或者pink等)',
`sorted` int DEFAULT '0' COMMENT '显示顺序',
`remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '备注',
`create_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`dict_id`) USING BTREE,
KEY `idx_dict_type` (`dict_type`) USING BTREE COMMENT '字典类型索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='字典数据表';
-- ----------------------------
-- 初始化字典类型数据
-- ----------------------------
INSERT INTO `sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `dict_status`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES
(1, '系统开关', 'sys_switch_status', '0', '系统开关列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(2, '用户性别', 'sys_user_sex', '0', '用户性别列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(3, '用户类型', 'sys_user_type', '0', '用户类型列表[1-系统用户2-注册用户3-微信小程序用户]', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(4, '菜单类型', 'sys_menu_type', '0', '菜单类型列表[1-目录2-菜单3-按钮]', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(5, '操作类型', 'sys_oper_type', '0', '操作类型列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(6, '通知类型', 'sys_notice_type', '0', '通知类型列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(7, '标签类型', 'sys_tag_type', '0', 'el-tag的类型列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(8, '通用状态', 'sys_common_status', '0', '通用状态列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(9, '是否标识', 'sys_yes_no', '0', '是否标识列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(10, '文件服务', 'sys_file_service', '0', '文件服务类型列表', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00');
-- ----------------------------
-- 初始化字典数据
-- ----------------------------
INSERT INTO `sys_dict_data` (`dict_id`, `dict_type`, `dict_label`, `dict_value`, `dict_status`, `dict_tag`, `dict_color`, `sorted`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES
-- 系统开关
(1, 'sys_switch_status', '启用', '0', '0', 'primary', '', 1, '正常状态', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(2, 'sys_switch_status', '停用', '1', '0', 'danger', '', 2, '停用状态', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 用户性别
(3, 'sys_user_sex', '', '1', '0', 'primary', '', 1, '性别男', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(4, 'sys_user_sex', '', '2', '0', 'danger', '', 2, '性别女', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(5, 'sys_user_sex', '未知', '3', '0', 'info', '', 3, '性别未知', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 用户类型
(13, 'sys_user_type', '系统用户', '1', '0', 'primary', '', 1, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(14, 'sys_user_type', '注册用户', '2', '0', 'warning', '', 2, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(15, 'sys_user_type', '微信用户', '3', '0', 'success', '', 3, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 菜单类型
(16, 'sys_menu_type', '目录', '1', '0', 'primary', '', 1, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(17, 'sys_menu_type', '菜单', '2', '0', 'warning', '', 2, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(18, 'sys_menu_type', '按钮', '3', '0', 'success', '', 3, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 操作类型
(19, 'sys_oper_type', '其他', '0', '0', 'info', '', 99, '其他操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(20, 'sys_oper_type', '新增', '1', '0', 'info', '', 1, '新增操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(21, 'sys_oper_type', '修改', '2', '0', 'info', '', 2, '修改操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(22, 'sys_oper_type', '删除', '3', '0', 'danger', '', 3, '删除操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(23, 'sys_oper_type', '授权', '4', '0', 'primary', '', 4, '授权操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(24, 'sys_oper_type', '导出', '5', '0', 'warning', '', 5, '导出操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(25, 'sys_oper_type', '导入', '6', '0', 'warning', '', 6, '导入操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(26, 'sys_oper_type', '强退', '7', '0', 'danger', '', 7, '强退操作', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 通知类型
(11, 'sys_notice_type', '通知', '1', '0', 'primary', '', 1, '消息通知', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(12, 'sys_notice_type', '公告', '2', '0', 'warning', '', 2, '消息公告', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 标签类型
(6, 'sys_tag_type', 'primary', '1', '0', 'primary', '', 1, 'el-tag的primary类型', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(7, 'sys_tag_type', 'warning', '2', '0', 'warning', '', 2, 'el-tag的warning类型', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(8, 'sys_tag_type', 'success', '3', '0', 'success', '', 3, 'el-tag的success类型', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(9, 'sys_tag_type', 'danger', '4', '0', 'danger', '', 4, 'el-tag的danger类型', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(10, 'sys_tag_type', 'info', '5', '0', 'info', '', 5, 'el-tag的info类型', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 通用状态
(33, 'sys_common_status', '初始化', '0', '0', 'primary', '', 1, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(34, 'sys_common_status', '成功', '1', '0', 'success', '', 2, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(35, 'sys_common_status', '失败', '2', '0', 'danger', '', 3, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 是否标识
(36, 'sys_yes_no', '', '0', '0', 'primary', '', 1, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(37, 'sys_yes_no', '', '1', '0', 'danger', '', 2, '', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
-- 文件服务
(38, 'sys_file_service', 'LOCAL', '1', '0', 'primary', '', 1, '本地存储', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(39, 'sys_file_service', 'MINIO', '2', '0', 'info', '', 2, 'MINIO存储', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00'),
(40, 'sys_file_service', 'OSS', '3', '0', 'warning', '', 3, '阿里云OSS存储', 'Leocoder', '2025-09-26 00:00:00', 'Leocoder', '2025-09-26 00:00:00');
-- ====================================================================
-- 脚本执行完成
-- 功能:字典管理数据库表和初始数据已创建完成
-- ====================================================================

File diff suppressed because it is too large Load Diff