currentModelClass();
+
+ /**
+ *
+ * 插入一条记录(选择字段,策略插入)
+ *
+ *
+ * @param entity 实体对象
+ */
+ boolean insert(T entity);
+
+ /**
+ *
+ * 插入(批量),该方法不支持 Oracle、SQL Server
+ *
+ *
+ * @param entityList 实体对象集合
+ */
+ boolean insertBatch(Collection entityList);
+
+ /**
+ *
+ * 插入(批量),该方法不支持 Oracle、SQL Server
+ *
+ *
+ * @param entityList 实体对象集合
+ * @param batchSize 插入批次数量
+ */
+ boolean insertBatch(Collection entityList, int batchSize);
+
+ /**
+ *
+ * 根据 ID 选择修改
+ *
+ *
+ * @param entity 实体对象
+ */
+ boolean updateById(T entity);
+
+ /**
+ *
+ * 根据 whereEntity 条件,更新记录
+ *
+ *
+ * @param entity 实体对象
+ * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
+ */
+ boolean update(T entity, Wrapper updateWrapper);
+
+ /**
+ *
+ * 根据ID 批量更新
+ *
+ *
+ * @param entityList 实体对象集合
+ */
+ boolean updateBatchById(Collection entityList);
+
+ /**
+ *
+ * 根据ID 批量更新
+ *
+ *
+ * @param entityList 实体对象集合
+ * @param batchSize 更新批次数量
+ */
+ boolean updateBatchById(Collection entityList, int batchSize);
+
+ /**
+ *
+ * 根据 ID 查询
+ *
+ *
+ * @param id 主键ID
+ */
+ T selectById(Serializable id);
+
+ /**
+ *
+ * 根据 ID 删除
+ *
+ *
+ * @param id 主键ID
+ */
+ boolean deleteById(Serializable id);
+
+ /**
+ *
+ * 删除(根据ID 批量删除)
+ *
+ *
+ * @param idList 主键ID列表
+ */
+ boolean deleteBatchIds(Collection extends Serializable> idList);
+}
diff --git a/common/src/main/java/io/common/service/CrudService.java b/common/src/main/java/io/common/service/CrudService.java
new file mode 100644
index 0000000..05836b8
--- /dev/null
+++ b/common/src/main/java/io/common/service/CrudService.java
@@ -0,0 +1,25 @@
+package io.common.service;
+
+import io.common.page.PageData;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CRUD基础服务接口
+ */
+public interface CrudService extends BaseService {
+
+ PageData page(Map params);
+
+ List list(Map params);
+
+ D get(Long id);
+
+ void save(D dto);
+
+ void update(D dto);
+
+ void delete(Long[] ids);
+
+}
diff --git a/common/src/main/java/io/common/service/impl/BaseServiceImpl.java b/common/src/main/java/io/common/service/impl/BaseServiceImpl.java
new file mode 100644
index 0000000..e10a7ea
--- /dev/null
+++ b/common/src/main/java/io/common/service/impl/BaseServiceImpl.java
@@ -0,0 +1,213 @@
+
+
+package io.common.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import io.common.constant.Constant;
+import io.common.page.PageData;
+import io.common.service.BaseService;
+import io.common.utils.ConvertUtils;
+import org.apache.ibatis.binding.MapperMethod;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.session.SqlSession;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * 基础服务类,所有Service都要继承
+ *
+
+ */
+public abstract class BaseServiceImpl, T> implements BaseService {
+ @Autowired
+ protected M baseDao;
+ protected Log log = LogFactory.getLog(getClass());
+
+ /**
+ * 获取分页对象
+ * @param params 分页查询参数
+ * @param defaultOrderField 默认排序字段
+ * @param isAsc 排序方式
+ */
+ protected IPage getPage(Map params, String defaultOrderField, boolean isAsc) {
+ //分页参数
+ long curPage = 1;
+ long limit = 10;
+
+ if (params.get(Constant.PAGE) != null) {
+ curPage = Long.parseLong((String) params.get(Constant.PAGE));
+ }
+ if (params.get(Constant.LIMIT) != null) {
+ limit = Long.parseLong((String) params.get(Constant.LIMIT));
+ }
+
+ //分页对象
+ Page page = new Page<>(curPage, limit);
+
+ //分页参数
+ params.put(Constant.PAGE, page);
+
+ //排序字段
+ String orderField = (String) params.get(Constant.ORDER_FIELD);
+ String order = (String) params.get(Constant.ORDER);
+
+ //前端字段排序
+ if (StrUtil.isNotBlank(orderField) && StrUtil.isNotBlank(order)) {
+ if (Constant.ASC.equalsIgnoreCase(order)) {
+ return page.addOrder(OrderItem.asc(orderField));
+ } else {
+ return page.addOrder(OrderItem.desc(orderField));
+ }
+ }
+
+ //没有排序字段,则不排序
+ if (StrUtil.isBlank(defaultOrderField)) {
+ return page;
+ }
+
+ //默认排序
+ if (isAsc) {
+ page.addOrder(OrderItem.asc(defaultOrderField));
+ } else {
+ page.addOrder(OrderItem.desc(defaultOrderField));
+ }
+
+ return page;
+ }
+
+ protected PageData getPageData(List> list, long total, Class target) {
+ List targetList = ConvertUtils.sourceToTarget(list, target);
+
+ return new PageData<>(targetList, total);
+ }
+
+ protected PageData getPageData(IPage page, Class target) {
+ return getPageData(page.getRecords(), page.getTotal(), target);
+ }
+
+ protected void paramsToLike(Map params, String... likes) {
+ for (String like : likes) {
+ String val = (String) params.get(like);
+ if (StrUtil.isNotBlank(val)) {
+ params.put(like, "%" + val + "%");
+ } else {
+ params.put(like, null);
+ }
+ }
+ }
+
+ /**
+ *
+ * 判断数据库操作是否成功
+ *
+ *
+ * 注意!! 该方法为 Integer 判断,不可传入 int 基本类型
+ *
+ *
+ * @param result 数据库操作返回影响条数
+ * @return boolean
+ */
+ protected static boolean retBool(Integer result) {
+ return SqlHelper.retBool(result);
+ }
+
+ protected Class currentMapperClass() {
+ return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
+ }
+
+ @Override
+ public Class currentModelClass() {
+ return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1);
+ }
+
+ protected String getSqlStatement(SqlMethod sqlMethod) {
+ return SqlHelper.getSqlStatement(this.currentMapperClass(), sqlMethod);
+ }
+
+ @Override
+ public boolean insert(T entity) {
+ return BaseServiceImpl.retBool(baseDao.insert(entity));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean insertBatch(Collection entityList) {
+ return insertBatch(entityList, 100);
+ }
+
+ /**
+ * 批量插入
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean insertBatch(Collection entityList, int batchSize) {
+ String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
+ return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
+ }
+
+ /**
+ * 执行批量操作
+ */
+ protected boolean executeBatch(Collection list, int batchSize, BiConsumer consumer) {
+ return SqlHelper.executeBatch(this.currentModelClass(), this.log, list, batchSize, consumer);
+ }
+
+
+ @Override
+ public boolean updateById(T entity) {
+ return BaseServiceImpl.retBool(baseDao.updateById(entity));
+ }
+
+ @Override
+ public boolean update(T entity, Wrapper updateWrapper) {
+ return BaseServiceImpl.retBool(baseDao.update(entity, updateWrapper));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateBatchById(Collection entityList) {
+ return updateBatchById(entityList, 30);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean updateBatchById(Collection entityList, int batchSize) {
+ String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID);
+ return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
+ MapperMethod.ParamMap param = new MapperMethod.ParamMap<>();
+ param.put(Constants.ENTITY, entity);
+ sqlSession.update(sqlStatement, param);
+ });
+ }
+
+ @Override
+ public T selectById(Serializable id) {
+ return baseDao.selectById(id);
+ }
+
+ @Override
+ public boolean deleteById(Serializable id) {
+ return SqlHelper.retBool(baseDao.deleteById(id));
+ }
+
+ @Override
+ public boolean deleteBatchIds(Collection extends Serializable> idList) {
+ return SqlHelper.retBool(baseDao.deleteBatchIds(idList));
+ }
+}
diff --git a/common/src/main/java/io/common/service/impl/CrudServiceImpl.java b/common/src/main/java/io/common/service/impl/CrudServiceImpl.java
new file mode 100644
index 0000000..e1822ee
--- /dev/null
+++ b/common/src/main/java/io/common/service/impl/CrudServiceImpl.java
@@ -0,0 +1,74 @@
+
+
+package io.common.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
+import io.common.page.PageData;
+import io.common.service.CrudService;
+import io.common.utils.ConvertUtils;
+import org.springframework.beans.BeanUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CRUD基础服务类
+ *
+
+ */
+public abstract class CrudServiceImpl, T, D> extends BaseServiceImpl implements CrudService {
+
+ protected Class currentDtoClass() {
+ return (Class)ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2);
+ }
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, null, false),
+ getWrapper(params)
+ );
+
+ return getPageData(page, currentDtoClass());
+ }
+
+ @Override
+ public List list(Map params) {
+ List entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, currentDtoClass());
+ }
+
+ public abstract QueryWrapper getWrapper(Map params);
+
+ @Override
+ public D get(Long id) {
+ T entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, currentDtoClass());
+ }
+
+ @Override
+ public void save(D dto) {
+ T entity = ConvertUtils.sourceToTarget(dto, currentModelClass());
+ insert(entity);
+
+ //copy主键值到dto
+ BeanUtils.copyProperties(entity, dto);
+ }
+
+ @Override
+ public void update(D dto) {
+ T entity = ConvertUtils.sourceToTarget(dto, currentModelClass());
+ updateById(entity);
+ }
+
+ @Override
+ public void delete(Long[] ids) {
+ baseDao.deleteBatchIds(Arrays.asList(ids));
+ }
+}
diff --git a/common/src/main/java/io/common/utils/ConvertUtils.java b/common/src/main/java/io/common/utils/ConvertUtils.java
new file mode 100644
index 0000000..3eae250
--- /dev/null
+++ b/common/src/main/java/io/common/utils/ConvertUtils.java
@@ -0,0 +1,51 @@
+package io.common.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 转换工具类
+ *
+ */
+public class ConvertUtils {
+ private static Logger logger = LoggerFactory.getLogger(ConvertUtils.class);
+
+ public static T sourceToTarget(Object source, Class target){
+ if(source == null){
+ return null;
+ }
+ T targetObject = null;
+ try {
+ targetObject = target.newInstance();
+ BeanUtils.copyProperties(source, targetObject);
+ } catch (Exception e) {
+ logger.error("convert error ", e);
+ }
+
+ return targetObject;
+ }
+
+ public static List sourceToTarget(Collection> sourceList, Class target){
+ if(sourceList == null){
+ return null;
+ }
+
+ List targetList = new ArrayList<>(sourceList.size());
+ try {
+ for(Object source : sourceList){
+ T targetObject = target.newInstance();
+ BeanUtils.copyProperties(source, targetObject);
+ targetList.add(targetObject);
+ }
+ }catch (Exception e){
+ logger.error("convert error ", e);
+ }
+
+ return targetList;
+ }
+}
diff --git a/common/src/main/java/io/common/utils/DateUtils.java b/common/src/main/java/io/common/utils/DateUtils.java
new file mode 100644
index 0000000..0c8de70
--- /dev/null
+++ b/common/src/main/java/io/common/utils/DateUtils.java
@@ -0,0 +1,55 @@
+package io.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 日期处理
+ *
+
+ */
+public class DateUtils {
+ /** 时间格式(yyyy-MM-dd) */
+ public final static String DATE_PATTERN = "yyyy-MM-dd";
+ /** 时间格式(yyyy-MM-dd HH:mm:ss) */
+ public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+ /**
+ * 日期格式化 日期格式为:yyyy-MM-dd
+ * @param date 日期
+ * @return 返回yyyy-MM-dd格式日期
+ */
+ public static String format(Date date) {
+ return format(date, DATE_PATTERN);
+ }
+
+ /**
+ * 日期格式化 日期格式为:yyyy-MM-dd
+ * @param date 日期
+ * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN
+ * @return 返回yyyy-MM-dd格式日期
+ */
+ public static String format(Date date, String pattern) {
+ if (date != null) {
+ SimpleDateFormat df = new SimpleDateFormat(pattern);
+ return df.format(date);
+ }
+ return null;
+ }
+
+ /**
+ * 日期解析
+ * @param date 日期
+ * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN
+ * @return 返回Date
+ */
+ public static Date parse(String date, String pattern) {
+ try {
+ return new SimpleDateFormat(pattern).parse(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/common/src/main/java/io/common/utils/HttpContextUtils.java b/common/src/main/java/io/common/utils/HttpContextUtils.java
new file mode 100644
index 0000000..b52b073
--- /dev/null
+++ b/common/src/main/java/io/common/utils/HttpContextUtils.java
@@ -0,0 +1,55 @@
+package io.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Http
+ *
+
+ */
+public class HttpContextUtils {
+
+ public static HttpServletRequest getHttpServletRequest() {
+ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if(requestAttributes == null){
+ return null;
+ }
+
+ return ((ServletRequestAttributes) requestAttributes).getRequest();
+ }
+
+ public static Map getParameterMap(HttpServletRequest request) {
+ Enumeration parameters = request.getParameterNames();
+
+ Map params = new HashMap<>();
+ while (parameters.hasMoreElements()) {
+ String parameter = parameters.nextElement();
+ String value = request.getParameter(parameter);
+ if (StrUtil.isNotBlank(value)) {
+ params.put(parameter, value);
+ }
+ }
+
+ return params;
+ }
+
+ public static String getDomain(){
+ HttpServletRequest request = getHttpServletRequest();
+ StringBuffer url = request.getRequestURL();
+ return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+ }
+
+ public static String getOrigin(){
+ HttpServletRequest request = getHttpServletRequest();
+ return request.getHeader(HttpHeaders.ORIGIN);
+ }
+}
diff --git a/common/src/main/java/io/common/utils/IpUtils.java b/common/src/main/java/io/common/utils/IpUtils.java
new file mode 100644
index 0000000..18d2f5c
--- /dev/null
+++ b/common/src/main/java/io/common/utils/IpUtils.java
@@ -0,0 +1,50 @@
+package io.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * IP地址
+ *
+
+ */
+public class IpUtils {
+ private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
+
+ /**
+ * 获取IP地址
+ *
+ * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
+ * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
+ */
+ public static String getIpAddr(HttpServletRequest request) {
+ String unknown = "unknown";
+ String ip = null;
+ try {
+ ip = request.getHeader("x-forwarded-for");
+ if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (StrUtil.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_CLIENT_IP");
+ }
+ if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+ }
+ if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ } catch (Exception e) {
+ logger.error("IPUtils ERROR ", e);
+ }
+
+ return ip;
+ }
+
+}
diff --git a/common/src/main/java/io/common/utils/JsonUtils.java b/common/src/main/java/io/common/utils/JsonUtils.java
new file mode 100644
index 0000000..8e03106
--- /dev/null
+++ b/common/src/main/java/io/common/utils/JsonUtils.java
@@ -0,0 +1,68 @@
+package io.common.utils;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JSON 工具类
+ *
+
+ */
+public class JsonUtils {
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ public static String toJsonString(Object object) {
+ try {
+ return objectMapper.writeValueAsString(object);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(String text, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(text, clazz);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(byte[] bytes, Class clazz) {
+ if (ArrayUtil.isEmpty(bytes)) {
+ return null;
+ }
+ try {
+ return objectMapper.readValue(bytes, clazz);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static T parseObject(String text, TypeReference typeReference) {
+ try {
+ return objectMapper.readValue(text, typeReference);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static List parseArray(String text, Class clazz) {
+ if (StrUtil.isEmpty(text)) {
+ return new ArrayList<>();
+ }
+ try {
+ return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/common/src/main/java/io/common/utils/Result.java b/common/src/main/java/io/common/utils/Result.java
new file mode 100644
index 0000000..58491cc
--- /dev/null
+++ b/common/src/main/java/io/common/utils/Result.java
@@ -0,0 +1,76 @@
+package io.common.utils;
+
+import io.common.exception.ErrorCode;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+
+/**
+ * 响应数据
+ *
+
+ * @since 1.0.0
+ */
+@Schema(title = "响应")
+public class Result implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 编码:0表示成功,其他值表示失败
+ */
+ @Schema(title = "编码:0表示成功,其他值表示失败")
+ private int code = 0;
+ /**
+ * 消息内容
+ */
+ @Schema(title = "消息内容")
+ private String msg = "success";
+ /**
+ * 响应数据
+ */
+ @Schema(title = "响应数据")
+ private T data;
+
+ public Result ok(T data) {
+ this.setData(data);
+ return this;
+ }
+ public boolean success(){
+ return code == 0;
+ }
+
+ public Result error(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ return this;
+ }
+
+ public Result error(String msg) {
+ this.code = ErrorCode.INTERNAL_SERVER_ERROR;
+ this.msg = msg;
+ return this;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public T getData() {
+ return data;
+ }
+
+ public void setData(T data) {
+ this.data = data;
+ }
+}
diff --git a/common/src/main/java/io/common/utils/SpringContextUtils.java b/common/src/main/java/io/common/utils/SpringContextUtils.java
new file mode 100644
index 0000000..5b22d04
--- /dev/null
+++ b/common/src/main/java/io/common/utils/SpringContextUtils.java
@@ -0,0 +1,47 @@
+package io.common.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring Context 工具类
+ *
+
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+ public static ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ SpringContextUtils.applicationContext = applicationContext;
+ }
+
+ public static Object getBean(String name) {
+ return applicationContext.getBean(name);
+ }
+
+ public static T getBean(Class requiredType) {
+ return applicationContext.getBean(requiredType);
+ }
+
+ public static T getBean(String name, Class requiredType) {
+ return applicationContext.getBean(name, requiredType);
+ }
+
+ public static boolean containsBean(String name) {
+ return applicationContext.containsBean(name);
+ }
+
+ public static boolean isSingleton(String name) {
+ return applicationContext.isSingleton(name);
+ }
+
+ public static Class extends Object> getType(String name) {
+ return applicationContext.getType(name);
+ }
+
+}
diff --git a/common/src/main/java/io/common/utils/TreeNode.java b/common/src/main/java/io/common/utils/TreeNode.java
new file mode 100644
index 0000000..97e982a
--- /dev/null
+++ b/common/src/main/java/io/common/utils/TreeNode.java
@@ -0,0 +1,26 @@
+package io.common.utils;
+
+import lombok.Data;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 树节点,所有需要实现树节点的,都需要继承该类
+ */
+ @Data
+public class TreeNode implements Serializable {
+ private static final long serialVersionUID = 1L;
+ /**
+ * 主键
+ */
+ private Long id;
+ /**
+ * 上级ID
+ */
+ private Long pid;
+ /**
+ * 子节点列表
+ */
+ private List children = new ArrayList<>();
+}
diff --git a/common/src/main/java/io/common/utils/TreeUtils.java b/common/src/main/java/io/common/utils/TreeUtils.java
new file mode 100644
index 0000000..4cf33b8
--- /dev/null
+++ b/common/src/main/java/io/common/utils/TreeUtils.java
@@ -0,0 +1,72 @@
+package io.common.utils;
+
+import io.common.validator.AssertUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 树形结构工具类,如:菜单、部门等
+ *
+
+ * @since 1.0.0
+ */
+public class TreeUtils {
+
+ /**
+ * 根据pid,构建树节点
+ */
+ public static List build(List treeNodes, Long pid) {
+ //pid不能为空
+ AssertUtils.isNull(pid, "pid");
+
+ List treeList = new ArrayList<>();
+ for(T treeNode : treeNodes) {
+ if (pid.equals(treeNode.getPid())) {
+ treeList.add(findChildren(treeNodes, treeNode));
+ }
+ }
+
+ return treeList;
+ }
+
+ /**
+ * 查找子节点
+ */
+ private static T findChildren(List treeNodes, T rootNode) {
+ for(T treeNode : treeNodes) {
+ if(rootNode.getId().equals(treeNode.getPid())) {
+ rootNode.getChildren().add(findChildren(treeNodes, treeNode));
+ }
+ }
+ return rootNode;
+ }
+
+ /**
+ * 构建树节点
+ */
+ public static List build(List treeNodes) {
+ List result = new ArrayList<>();
+
+ //list转map
+ Map nodeMap = new LinkedHashMap<>(treeNodes.size());
+ for(T treeNode : treeNodes){
+ nodeMap.put(treeNode.getId(), treeNode);
+ }
+
+ for(T node : nodeMap.values()) {
+ T parent = nodeMap.get(node.getPid());
+ if(parent != null && !(node.getId().equals(parent.getId()))){
+ parent.getChildren().add(node);
+ continue;
+ }
+
+ result.add(node);
+ }
+
+ return result;
+ }
+
+}
diff --git a/common/src/main/java/io/common/validator/AssertUtils.java b/common/src/main/java/io/common/validator/AssertUtils.java
new file mode 100644
index 0000000..bb26520
--- /dev/null
+++ b/common/src/main/java/io/common/validator/AssertUtils.java
@@ -0,0 +1,90 @@
+package io.common.validator;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ArrayUtil;
+import io.common.exception.ErrorCode;
+import io.common.exception.RenException;
+import cn.hutool.core.util.StrUtil;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 校验工具类
+ *
+
+ * @since 1.0.0
+ */
+public class AssertUtils {
+
+ public static void isBlank(String str, String... params) {
+ isBlank(str, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isBlank(String str, Integer code, String... params) {
+ if(code == null){
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (StrUtil.isBlank(str)) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isNull(Object object, String... params) {
+ isNull(object, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isNull(Object object, Integer code, String... params) {
+ if(code == null){
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if (object == null) {
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isArrayEmpty(Object[] array, String... params) {
+ isArrayEmpty(array, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isArrayEmpty(Object[] array, Integer code, String... params) {
+ if(code == null){
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if(ArrayUtil.isEmpty(array)){
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isListEmpty(List> list, String... params) {
+ isListEmpty(list, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isListEmpty(List> list, Integer code, String... params) {
+ if(code == null){
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if(CollUtil.isEmpty(list)){
+ throw new RenException(code, params);
+ }
+ }
+
+ public static void isMapEmpty(Map map, String... params) {
+ isMapEmpty(map, ErrorCode.NOT_NULL, params);
+ }
+
+ public static void isMapEmpty(Map map, Integer code, String... params) {
+ if(code == null){
+ throw new RenException(ErrorCode.NOT_NULL, "code");
+ }
+
+ if(MapUtil.isEmpty(map)){
+ throw new RenException(code, params);
+ }
+ }
+}
diff --git a/common/src/main/java/io/common/validator/ValidatorUtils.java b/common/src/main/java/io/common/validator/ValidatorUtils.java
new file mode 100644
index 0000000..902325e
--- /dev/null
+++ b/common/src/main/java/io/common/validator/ValidatorUtils.java
@@ -0,0 +1,47 @@
+package io.common.validator;
+
+import io.common.exception.RenException;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator;
+
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * hibernate-validator校验工具类
+ * 参考文档:http://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/
+ * @since 1.0.0
+ */
+public class ValidatorUtils {
+
+ private static ResourceBundleMessageSource getMessageSource() {
+ ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
+ bundleMessageSource.setDefaultEncoding("UTF-8");
+ bundleMessageSource.setBasenames("i18n/validation");
+ return bundleMessageSource;
+ }
+
+ /**
+ * 校验对象
+ * @param object 待校验对象
+ * @param groups 待校验的组
+ */
+ public static void validateEntity(Object object, Class>... groups)
+ throws RenException {
+ Locale.setDefault(LocaleContextHolder.getLocale());
+ Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
+ new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
+ .buildValidatorFactory().getValidator();
+
+ Set> constraintViolations = validator.validate(object, groups);
+ if (!constraintViolations.isEmpty()) {
+ ConstraintViolation