diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ae43c91 --- /dev/null +++ b/pom.xml @@ -0,0 +1,143 @@ + + + + 4.0.0 + + com.smart + Smart-Parking + 1.0-SNAPSHOT + war + + + UTF-8 + 1.8 + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + net.sourceforge.nekohtml + nekohtml + + + org.springframework.boot + spring-boot-starter-data-jpa + + + mysql + mysql-connector-java + + + cn.hutool + hutool-all + 4.6.10 + + + org.apache.shiro + shiro-spring-boot-web-starter + 1.7.1 + + + org.apache.commons + commons-lang3 + 3.11 + + + com.github.theborakompanioni + thymeleaf-extras-shiro + 2.0.0 + + + org.projectlombok + lombok + 1.16.18 + + + + com.google.guava + guava + 25.1-jre + + + com.github.whvcse + easy-captcha + 1.6.2 + + + + + + org.apache.poi + poi + 4.0.1 + + + org.apache.poi + poi-ooxml + 4.0.1 + + + org.apache.poi + poi-ooxml-schemas + 4.0.1 + + + + org.springframework.boot + spring-boot-starter-integration + + + org.springframework.integration + spring-integration-stream + + + org.springframework.integration + spring-integration-mqtt + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.projectlombok + lombok + 1.18.20 + provided + + + + Smart-Parking + + + org.springframework.boot + spring-boot-maven-plugin + + + org.springframework + springloaded + 1.2.7.RELEASE + + + + + + diff --git a/src/main/java/com/smart/Application.java b/src/main/java/com/smart/Application.java new file mode 100644 index 0000000..ca4dbf1 --- /dev/null +++ b/src/main/java/com/smart/Application.java @@ -0,0 +1,35 @@ +package com.smart; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动类 + * 支付申请地址:https://dwz.cn/mPQmSPss + * 国内领先的支付管理平台:https://paycloud.vip + */ +@EnableAsync // 启用异步方法执行 +@EnableCaching // 启用缓存 +@EnableScheduling // 启用定时任务 +@SpringBootApplication // Spring Boot应用程序注解 +public class Application extends SpringBootServletInitializer { + + private static final Logger logger = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); // 启动Spring Boot应用程序 + logger.info("智能停车场管理平台"); // 打印日志信息 + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Application.class); + } +} diff --git a/src/main/java/com/smart/common/config/AbstractController.java b/src/main/java/com/smart/common/config/AbstractController.java new file mode 100644 index 0000000..ad65180 --- /dev/null +++ b/src/main/java/com/smart/common/config/AbstractController.java @@ -0,0 +1,16 @@ +package com.smart.common.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RestController; + +/** + * 通用Controller + */ +@RestController +public class AbstractController { + + //定义了一个受保护的日志记录器 logger,用于在子类中记录日志信息 + protected Logger logger = LoggerFactory.getLogger(getClass()); + +} \ No newline at end of file diff --git a/src/main/java/com/smart/common/config/BigDecimalSerialize.java b/src/main/java/com/smart/common/config/BigDecimalSerialize.java new file mode 100644 index 0000000..ba09247 --- /dev/null +++ b/src/main/java/com/smart/common/config/BigDecimalSerialize.java @@ -0,0 +1,33 @@ +package com.smart.common.config; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.math.BigDecimal; + +/** + * 金额格式化 + */ +public class BigDecimalSerialize extends JsonSerializer { + // 继承自 Jackson 库中的 JsonSerializer 类,并指定了序列化的数据类型为 BigDecimal + + /** + * 对传入的 BigDecimal 值进行判断和处理 + * @param value 要序列化的 BigDecimal 值 + * @param gen JsonGenerator对象,用于生成JSON数据 + * @param serializerProvider 序列化提供者 + * @throws IOException 当发生I/O异常时抛出 + */ + @Override + public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException { + if (value != null && !"".equals(value)) { + // 如果输入的值不为null且不为空字符串,则进行格式化处理,保留两位小数,并转换为字符串 + gen.writeString(value.setScale(2, BigDecimal.ROUND_HALF_DOWN) + ""); + } else { + // 否则直接将输入的值转换为字符串 + gen.writeString(value + ""); + } + } +} diff --git a/src/main/java/com/smart/common/config/FileConfig.java b/src/main/java/com/smart/common/config/FileConfig.java new file mode 100644 index 0000000..ea72b36 --- /dev/null +++ b/src/main/java/com/smart/common/config/FileConfig.java @@ -0,0 +1,24 @@ +package com.smart.common.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * file映射项目外文件夹 + */ +@Configuration //定义配置信息 +public class FileConfig implements WebMvcConfigurer {//继承,配置Web MVC的相关设置 + + // 从配置文件中读取文件路径 + @Value("${file.path}") + private String filePath; + + // 重写addResourceHandlers方法,用于添加资源处理器 + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 注册一个资源处理器,将/file/**的URL请求映射到指定的文件路径下 + registry.addResourceHandler("/file/**").addResourceLocations("file:" + filePath + "/"); + } +} diff --git a/src/main/java/com/smart/common/config/IndexController.java b/src/main/java/com/smart/common/config/IndexController.java new file mode 100644 index 0000000..c8c82cc --- /dev/null +++ b/src/main/java/com/smart/common/config/IndexController.java @@ -0,0 +1,100 @@ +package com.smart.common.config; + +import com.smart.module.sys.service.SysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +/** + * 通用访问拦截匹配 + */ +@Controller//控制器,处理请求 +public class IndexController { + + @Autowired + private SysConfigService sysConfigService; + + /** + * 登录页面的请求映射方法 + * @param map 存储数据的ModelMap对象 + * @return 返回登录页面的视图名称 + * 处理GET方式请求的/login.html路径,返回登录页面的视图名称 + */ + @GetMapping("login.html") + public String login(ModelMap map) { + Object value = sysConfigService.getByKey("login_title"); + // 如果获取到的值为null,则使用默认值"智能停车场管理平台" + map.addAttribute("login_title", value == null ? "智能停车场管理平台" : value); + + value = sysConfigService.getByKey("smart_name"); + // 如果获取到的值为null,则使用默认值"智能停车场管理平台" + map.addAttribute("smart_name", value == null ? "智能停车场管理平台" : value); + + return "login"; + } + + /** + * 首页的请求映射方法 + * @param map 存储数据的ModelMap对象 + * @return 返回首页的视图名称 + * 处理GET方式请求的/index.html路径,返回首页的视图名称。 + */ + @GetMapping("index.html") + public String index(ModelMap map) { + Object value = sysConfigService.getByKey("smart_name"); + // 如果获取到的值为null,则使用默认值"智能停车场管理平台" + map.addAttribute("smart_name", value == null ? "智能停车场管理平台" : value); + + return "index"; + } + + /** + * 控制台页面的请求映射方法 + * @return 返回控制台页面的视图名称 + * 处理GET方式请求的/console.html路径,返回控制台页面的视图名称。 + */ + @GetMapping("console.html") + public String page() { + return "console"; + } + + /** + * 根据模块、URL路径动态生成页面的请求映射方法 + * @param module 模块名称 + * @param url URL路径 + * @return 返回对应的视图名称 + * 处理GET方式请求的/module/url.html路径,根据模块和URL动态生成页面的视图名称。 + */ + @GetMapping("{module}/{url}.html") + public String page(@PathVariable("module") String module, @PathVariable("url") String url) { + return module + "/" + url; + } + + /** + * 根据模块、子模块、URL路径动态生成页面的请求映射方法 + * @param module 模块名称 + * @param url URL路径 + * @param sub 子模块名称 + * @return 返回对应的视图名称 + * 处理GET方式请求的/module/sub/url.html路径,根据模块、子模块和URL动态生成页面的视图名称。 + */ + @GetMapping("{module}/{sub}/{url}.html") + public String page(@PathVariable("module") String module, @PathVariable("url") String url, @PathVariable("sub") String sub) { + return module + "/" + sub + "/" + url; + } + + /** + * 根据模块、子模块、小子模块、URL路径动态生成页面的请求映射方法 + * @param module 模块名称 + * @param url URL路径 + * @param sub 子模块名称 + * @param smallSub 小子模块名称 + * @return 返回对应的视图名称 + * 处理GET方式请求的/module/sub/smallSub/url.html路径,根据模块、子模块、小子模块和URL动态生成页面的视图名称。 + */ + @GetMapping("{module}/{sub}/{smallSub}/{url}.html") + public String page(@PathVariable("module") String module, @PathVariable("url") String url, @PathVariable("sub") String sub, @PathVariable("smallSub") String smallSub) { + return module + "/" + sub + "/" + smallSub + "/" + url; + } +} \ No newline at end of file diff --git a/src/main/java/com/smart/common/constant/ExcelDataType.java b/src/main/java/com/smart/common/constant/ExcelDataType.java new file mode 100644 index 0000000..5038a0f --- /dev/null +++ b/src/main/java/com/smart/common/constant/ExcelDataType.java @@ -0,0 +1,33 @@ +package com.smart.common.constant; + +/** + * Excel导入导出数据类型枚举 + */ +public enum ExcelDataType { + // 字符串类型 + STRING("String"), + // 数值类型 + INTEGER("Integer"), + // Long类型 + LONG("Long"), + // Double类型 + DOUBLE("Double"), + // BigDecimal类型 + BIG_DECIMAL("BigDecimal"), + // Float类型 + FLOAT("Float"), + // Date类型 + DATE("Date"), + // Timestamp + TIMESTAMP("Timestamp"), + // Macro类型 + MACRO("Macro"), + // Money类型 + MONEY("Money"); + + public String dataType; + + ExcelDataType(String dataType) { + this.dataType = dataType; + } +} diff --git a/src/main/java/com/smart/common/constant/MsgConstant.java b/src/main/java/com/smart/common/constant/MsgConstant.java new file mode 100644 index 0000000..15fc5bb --- /dev/null +++ b/src/main/java/com/smart/common/constant/MsgConstant.java @@ -0,0 +1,22 @@ +package com.smart.common.constant; +/** + * 系统提示静态变量 + */ +public class MsgConstant { + + /** + * 操作成功 + */ + public static final String MSG_OPERATION_SUCCESS = "操作成功!"; + + /** + * 操作失败 + */ + public static final String MSG_OPERATION_FAILED = "操作失败!"; + + /** + * 加载表单数据错误提示 + */ + public static final String MSG_INIT_FORM = "初始化表单数据失败,请重试!"; + +} diff --git a/src/main/java/com/smart/common/constant/SystemConstant.java b/src/main/java/com/smart/common/constant/SystemConstant.java new file mode 100644 index 0000000..b62d0b2 --- /dev/null +++ b/src/main/java/com/smart/common/constant/SystemConstant.java @@ -0,0 +1,199 @@ +package com.smart.common.constant; + +/** + * 系统级静态变量 + */ +public class SystemConstant { + + /** + * 文件分隔符 + */ + public static final String SF_FILE_SEPARATOR = System.getProperty("file.separator"); + + /** + * 数据标识 + */ + public static final String DATA_ROWS = "rows"; + + /** + * 成功 + */ + public static final String SUCCESS = "success"; + /** + * 失败 + */ + public static final String ERROR = "error"; + + /** + * 真 + */ + public static final String TRUE = "true"; + /** + * 假 + */ + public static final String FALSE = "false"; + + + public static final String FILE = "file"; + + /** + * 删除 + */ + public static final Short DELETE_STATUS_YES = 0; + + public static final Short DELETE_STATUS_NO = 1; + + + /** + * 头像:0 默认 1 上传 + */ + public static final Short AVATAR_STATUS_YES = 1; + + public static final Short AVATAR_STATUS_NO = 0; + + /** + * 支付状态 1 :支付 0:未支付 + */ + public static final Short PAY_STATUS_NO = 0; + + public static final Short PAY_STATUS_YES = 1; + + + /** + * 支付类型 0:微信 1:支付宝 + */ + public static final Short PAY_TYPE_WX = 0; + + public static final Short PAY_TYPE_ALI = 1; + + + /** + * 管理员-角色 + */ + public static final String ROLE_ADMIN = "admin"; + + /** + * 机构管理员-角色 + */ + public static final String ROLE_ORG_ADMIN = "orgAdmin"; + + /** + * 是 + */ + public static final String Y = "Y"; + + /** + * 文件存放路径 HTTP + */ + public static final String FILE_PATH = "/file/"; + + /** + * 类型 0:包月车 1:VIP免费车 2:临时 + */ + public static final Short CAR_TYPE_MONTH = 0; + + public static final Short CAR_TYPE_VIP = 1; + + public static final Short CAR_TYPE_TEMP = 2; + + /** + * 菜单类型 + */ + public enum MenuType { + /** + * 目录 + */ + CATALOG(0), + /** + * 菜单 + */ + MENU(1), + /** + * 按钮 + */ + BUTTON(2); + + private final int value; + + MenuType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * 通用字典 + */ + public enum MacroType { + + /** + * 类型 + */ + TYPE(0), + + /** + * 参数 + */ + PARAM(1); + + private final int value; + + MacroType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + } + + /** + * 通用变量,表示可用、禁用、显示、隐藏、是、否 + */ + public enum StatusType { + + /** + * 禁用,隐藏 + */ + DISABLE((short)0), + + /** + * 可用,显示 + */ + ENABLE((short)1), + + /** + * 显示 + */ + SHOW((short)1), + + /** + * 隐藏 + */ + HIDDEN((short)0), + + /** + * 是 + */ + YES((short)1), + + /** + * 否 + */ + NO((short)0); + + private final short value; + + StatusType(short value) { + this.value = value; + } + + public short getValue() { + return value; + } + } +} diff --git a/src/main/java/com/smart/common/dynamicquery/DynamicQuery.java b/src/main/java/com/smart/common/dynamicquery/DynamicQuery.java new file mode 100644 index 0000000..e6ac860 --- /dev/null +++ b/src/main/java/com/smart/common/dynamicquery/DynamicQuery.java @@ -0,0 +1,137 @@ +package com.smart.common.dynamicquery; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +/** + * 动态查询 + */ +public interface DynamicQuery { + /** + * 保存 + * @param entity + */ + void save(Object entity); + + /** + * 更新 + * @param entity + */ + void update(Object entity); + + /** + * 执行原生nativeSql的update,delete操作 + * 传入SQL语句和参数进行占位符替换 + * @param nativeSql + * @param params + * @return + */ + int nativeExecuteUpdate(String nativeSql, Object... params); + + /** + * 执行nativeSql统计查询 + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return 查询结果的条数 + */ + Long nativeQueryCount(String nativeSql, Object... params); + + /** + * 执行nativeSql查询一行 + * @param resultClass 指定查询结果的类型 + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return + */ + T nativeQuerySingleResult(Class resultClass, String nativeSql, Object... params); + + /** + * 执行nativeSql查询 List + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return 由对象组成的List + */ + List query(String nativeSql, Object... params); + + /** + * 执行nativeSql重载形式用于执行原生SQL的查询,并指定查询结果的类型 + * @param resultClass + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return + */ + List query(Class resultClass, String nativeSql, Object... params); + + /** + * 执行nativeSql分页查询 + * @param resultClass 查询结果类型 + * @param pageable 分页数据 + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return 分页对象 + */ + Page nativeQuery(Class resultClass, Pageable pageable, String nativeSql, Object... params); + + /** + * 执行nativeSql分页查询 + * @param resultClass + * @param pageable + * @param nativeSql + * @param params + * @param List + * @return + */ + List nativeQueryPagingList(Class resultClass, Pageable pageable, String nativeSql, Object... params); + + /** + * 查询对象列表,返回List> + * @param nativeSql + * @param params + * @return T + */ + T nativeQueryMap(String nativeSql, Object... params); + + /** + * 查询对象列表,返回List<组合对象> + * @param resultClass + * @param nativeSql + * @param params + * @return + */ + T nativeQueryModel(Class resultClass, String nativeSql, Object... params); + /** + * 查询对象列表,返回List> + * @param nativeSql + * @param params + * @return List + */ + List nativeQueryListMap(String nativeSql, Object... params); + + /** + * 查询对象列表,返回List<组合对象> + * @param resultClass + * @param nativeSql + * @param params + * @return List + */ + List nativeQueryListModel(Class resultClass, String nativeSql, Object... params); + + /** + * 查询对象列表,返回List<组合对象> + * @param resultClass + * @param nativeSql + * @param params + * @return List + */ + List nativeQueryPagingListModel(Class resultClass, Pageable pageable, String nativeSql, Object... params); + + /** + * 执行nativeSql查询 List + * @param nativeSql + * @param params 占位符参数(例如?1)绑定的参数值 + * @return 单个结果对象 + */ + Object querySingleResult(String nativeSql, Object... params); +} diff --git a/src/main/java/com/smart/common/dynamicquery/DynamicQueryImpl.java b/src/main/java/com/smart/common/dynamicquery/DynamicQueryImpl.java new file mode 100644 index 0000000..29a0b44 --- /dev/null +++ b/src/main/java/com/smart/common/dynamicquery/DynamicQueryImpl.java @@ -0,0 +1,179 @@ +package com.smart.common.dynamicquery; + +import org.apache.commons.lang3.StringUtils; +import org.hibernate.query.internal.NativeQueryImpl; +import org.hibernate.transform.Transformers; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.util.List; + +/** + * 查询的实现类 + */ +@Repository +public class DynamicQueryImpl implements DynamicQuery { + + /** + * @PersistenceContext(type = PersistenceContextType.EXTENDED) + * 默认是 PersistenceContextType.TRANSACTION 开启只读事物 + */ + @PersistenceContext + private EntityManager em; // 声明EntityManager对象,用于执行数据库操作 + + public EntityManager getEntityManager() { + return em; + } + + @Override + public void save(Object entity) { + em.persist(entity); // 保存实体对象到数据库 + } + + @Override + public void update(Object entity) { + em.merge(entity); // 更新实体对象到数据库 + } + + private Query createNativeQuery(String sql, Object... params) { + Query q = em.createNativeQuery(sql); // 创建原生SQL查询对象 + if (params != null && params.length > 0) { + for (int i = 0; i < params.length; i++) { + q.setParameter(i + 1, params[i]); // 设置查询参数 + } + } + return q; + } + + @Override + public int nativeExecuteUpdate(String nativeSql, Object... params) { + return createNativeQuery(nativeSql, params).executeUpdate(); // 执行原生SQL更新操作,并返回受影响的行数 + } + + @Override + public T nativeQuerySingleResult(Class resultClass, String nativeSql, Object... params) { + Query q = createNativeQuery(resultClass, nativeSql, params); // 创建原生SQL查询对象 + List list = q.getResultList(); // 获取查询结果列表 + if (list.isEmpty()) { + return null; + } + return list.get(0); // 返回第一个查询结果 + } + + private Query createNativeQuery(Class resultClass, String sql, Object... params) { + Query q; + if (resultClass == null) { + q = em.createNativeQuery(sql); // 创建原生SQL查询对象 + } else { + q = em.createNativeQuery(sql, resultClass); // 创建原生SQL查询对象,并指定结果类型 + } + if (params != null) { + for (int i = 0; i < params.length; i++) { + q.setParameter(i + 1, params[i]); // 设置查询参数 + } + } + return q; + } + + @Override + public List query(String nativeSql, Object... params) { + Query q = createNativeQuery(null, nativeSql, params); // 创建原生SQL查询对象 + return q.getResultList(); // 获取查询结果列表 + } + + @Override + public List query(Class resultClass, String nativeSql, Object... params) { + Query q = createNativeQuery(resultClass, nativeSql, params); // 创建原生SQL查询对象 + return q.getResultList(); // 获取查询结果列表 + } + + @Override + public Long nativeQueryCount(String nativeSql, Object... params) { + nativeSql = StringUtils.substringBefore(nativeSql, "order by"); // 去除查询语句中的排序部分 + Object count = createNativeQuery(nativeSql, params).getSingleResult(); // 执行原生SQL查询,获取结果总数 + return ((Number) count).longValue(); // 将结果转换为Long类型并返回 + } + + @Override + public Page nativeQuery(Class resultClass, Pageable pageable, String nativeSql, Object... params) { + List rows = nativeQueryPagingList(resultClass, pageable, nativeSql, params); // 获取分页查询结果列表 + Long total = nativeQueryCount(nativeSql, params); // 获取查询结果总数 + return new PageImpl<>(rows, pageable, total); // 根据查询结果、分页信息和总数创建Page对象并返回 + } + + @Override + public List nativeQueryPagingList(Class resultClass, Pageable pageable, String nativeSql, + Object... params) { + Integer pageNumber = pageable.getPageNumber(); // 获取当前页码 + Integer pageSize = pageable.getPageSize(); // 获取每页大小 + Integer startPosition = pageNumber * pageSize; // 计算起始位置 + return createNativeQuery(resultClass, nativeSql, params) + .setFirstResult(startPosition).setMaxResults(pageSize) // 设置查询的起始位置和每页大小 + .getResultList(); // 获取查询结果列表 + } + + @Override + public T nativeQueryMap(String nativeSql, Object... params) { + Query q = createNativeQuery(nativeSql, params); // 创建原生SQL查询对象 + q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); // 设置结果转换器为ALIAS_TO_ENTITY_MAP + List list = q.getResultList(); // 获取查询结果列表 + if (list.isEmpty()) { + return null; + } + return list.get(0); // 返回第一个查询结果 + } + + @Override + public T nativeQueryModel(Class resultClass, String nativeSql, Object... params) { + Query q = createNativeQuery(nativeSql, params); // 创建原生SQL查询对象 + q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(resultClass)); // 设置结果转换器为aliasToBean + List list = q.getResultList(); // 获取查询结果列表 + if (list.isEmpty()) { + return null; + } + return list.get(0); // 返回第一个查询结果 + } + + @Override + public List nativeQueryListModel(Class resultClass, + String nativeSql, Object... params) { + Query q = createNativeQuery(nativeSql, params); // 创建原生SQL查询对象 + q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(resultClass)); // 设置结果转换器为aliasToBean + return q.getResultList(); // 获取查询结果列表 + } + + @Override + public List nativeQueryPagingListModel(Class resultClass, Pageable pageable, String nativeSql, Object... params) { + Integer pageNumber = pageable.getPageNumber(); // 获取当前页码 + Integer pageSize = pageable.getPageSize(); // 获取每页大小 + Integer startPosition = pageNumber * pageSize; // 计算起始位置 + Query q = createNativeQuery(nativeSql, params) + .setFirstResult(startPosition).setMaxResults(pageSize); // 设置查询的起始位置和每页大小 + q.unwrap(NativeQueryImpl.class) + .setResultTransformer(Transformers.aliasToBean(resultClass)); // 设置结果转换器为aliasToBean + return q.getResultList(); // 获取查询结果列表 + } + + @Override + public List nativeQueryListMap(String nativeSql, Object... params) { + Query q = createNativeQuery(nativeSql, params); // 创建原生SQL查询对象 + q.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); // 设置结果转换器为ALIAS_TO_ENTITY_MAP + return q.getResultList(); // 获取查询结果列表 + } + + @Override + public Object querySingleResult(String nativeSql, Object... params) { + Query q = createNativeQuery(null, nativeSql, params); // 创建原生SQL查询对象 + List list = q.getResultList(); // 获取查询结果列表 + if(list.size() != 0){ + return list.get(0); // 返回第一个查询结果 + }else{ + return null; // 返回空值 + } + } +} diff --git a/src/main/java/com/smart/common/exception/RrException.java b/src/main/java/com/smart/common/exception/RrException.java new file mode 100644 index 0000000..c9c3b1c --- /dev/null +++ b/src/main/java/com/smart/common/exception/RrException.java @@ -0,0 +1,58 @@ +package com.smart.common.exception; +/** + * 自定义异常 + */ +public class RrException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private String msg; //存储异常的详细信息 + + private int code = 500; //存储异常的错误码,默认为 500 + + //指定的异常信息构造异常对象 + public RrException(String msg) { + super(msg); + this.msg = msg; + } + + //指定的异常信息和原始异常对象构造异常对象 + public RrException(String msg, Throwable e) { + super(msg, e); + this.msg = msg; + } + + //指定的异常信息和错误码构造异常对象 + public RrException(String msg, int code) { + super(msg); + this.msg = msg; + this.code = code; + } + + //指定的异常信息、错误码和原始异常对象构造异常对象 + public RrException(String msg, int code, Throwable e) { + super(msg, e); + this.msg = msg; + this.code = code; + } + + //获取异常的详细信息 + public String getMsg() { + return msg; + } + + //设置异常的详细信息 + public void setMsg(String msg) { + this.msg = msg; + } + + //获取异常的错误码 + public int getCode() { + return code; + } + + //设置异常的错误码 + public void setCode(int code) { + this.code = code; + } +} diff --git a/src/main/java/com/smart/common/exception/RrExceptionHandler.java b/src/main/java/com/smart/common/exception/RrExceptionHandler.java new file mode 100644 index 0000000..411939b --- /dev/null +++ b/src/main/java/com/smart/common/exception/RrExceptionHandler.java @@ -0,0 +1,56 @@ +package com.smart.common.exception; + +import com.smart.common.model.Result; +import org.apache.shiro.authz.AuthorizationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 异常处理器 + */ +@RestControllerAdvice +public class RrExceptionHandler { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * 自定义异常 + */ + @ExceptionHandler(RrException.class) + public Result handleRRException(RrException e){ + Result r = new Result(); + r.put("code", e.getCode()); + r.put("msg", e.getMessage()); + return r; + } + + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException(DuplicateKeyException e){ + logger.error(e.getMessage(), e); + return Result.error("数据库中已存在该记录!"); + } + + @ExceptionHandler(AuthorizationException.class) + public Result handleDuplicateKeyException(AuthorizationException e){ + logger.error(e.getMessage(), e); + return Result.error("抱歉,你没有权限进行操作!"); + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception e){ + logger.error(e.getMessage(), e); + return Result.error(); + } + + /** + * 异常处理参数 + */ + @ExceptionHandler(value = {BindException.class}) + public Result bindException(BindException e) { + return Result.error(e.getAllErrors().toString()); + } +} diff --git a/src/main/java/com/smart/common/model/PageBean.java b/src/main/java/com/smart/common/model/PageBean.java new file mode 100644 index 0000000..3691398 --- /dev/null +++ b/src/main/java/com/smart/common/model/PageBean.java @@ -0,0 +1,83 @@ +package com.smart.common.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * 分页模板 + */ +public class PageBean { + + private List pageData = new ArrayList<>(); // 存储了当前页的数据,默认为空的 ArrayList + private Integer pageSize = Integer.valueOf(10); // 表示每页显示的数据条数,默认为 10 条 + private Integer pageNo = Integer.valueOf(1); // 表示当前页码,默认为第一页,实际使用时需要将其减 1,以符合实际的索引 + private Long totalCount = 0L; //表示总数据条数,默认为 0 + private String description; //存储描述信息的字段 + private String beginTime; //存储开始时间的字段 + private String endTime; //存储结束时间的字段 + + public PageBean(List pageData, Long totalCount) { //初始化分页数据和总数据条数 + this.pageData = pageData; + this.totalCount = totalCount; + } + + public PageBean() { + + } + + public List getPageData() { + return this.pageData; + } //获取当前页的数据 + + public void setPageData(List pageData) { + this.pageData = pageData; + } //设置当前页的数据 + + public Integer getPageSize() { + return this.pageSize; + } //获取每页显示的数据条数 + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } //设置每页显示的数据条数 + + public Long getTotalCount() { + return this.totalCount; + } //获取总数据条数 + + public void setTotalCount(Long totalCount) { + this.totalCount = totalCount; + } //设置总数据条数 + + public Integer getPageNo() { + return this.pageNo - 1; + } //获取当前页码,实际使用时会将其减 1 + + public void setPageNo(Integer pageNo) { + this.pageNo = pageNo; + } //设置当前页码 + + public String getDescription() { + return description; + } //获取描述信息 + + public void setDescription(String description) { + this.description = description; + } //设置描述信息 + + public String getBeginTime() { + return beginTime; + } //获取开始时间 + + public void setBeginTime(String beginTime) { + this.beginTime = beginTime; + } //设置开始时间 + + public String getEndTime() { + return endTime; + } //获取结束时间 + + public void setEndTime(String endTime) { + this.endTime = endTime; + } //设置结束时间 +} \ No newline at end of file diff --git a/src/main/java/com/smart/common/model/Result.java b/src/main/java/com/smart/common/model/Result.java new file mode 100644 index 0000000..64663e7 --- /dev/null +++ b/src/main/java/com/smart/common/model/Result.java @@ -0,0 +1,69 @@ +package com.smart.common.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * 页面响应 + */ +public class Result extends HashMap { + + private static final long serialVersionUID = 1L; + + public Result() { + put("code", 0); + } + + public static Result error() { + return error(500, "未知异常,请联系管理员"); + } + + public static Result error(String msg) { + return error(500, msg); + } + + public static Result error(int code, String msg) { + Result r = new Result(); + r.put("code", code); + r.put("msg", msg); + return r; + } + + public static Result ok(Object msg) { + Result r = new Result(); + r.put("msg", msg); + return r; + } + + public static Result ok(Object msg,Object data) { + Result r = new Result(); + r.put("msg", msg); + r.put("data", data); + return r; + } + + public static Result ok(Map map) { + Result r = new Result(); + r.putAll(map); + return r; + } + + public static Result ok() { + Result r = new Result(); + r.put("msg", "操作成功"); + return r; + } + + public static Boolean isOk(Result r) { + if(r.get("code").toString().equals("0")){ + return true; + } + return false; + } + + @Override + public Result put(String key, Object value) { + super.put(key, value); + return this; + } +} \ No newline at end of file diff --git a/src/main/java/com/smart/common/mqtt/MqttGateway.java b/src/main/java/com/smart/common/mqtt/MqttGateway.java new file mode 100644 index 0000000..61051ac --- /dev/null +++ b/src/main/java/com/smart/common/mqtt/MqttGateway.java @@ -0,0 +1,15 @@ +package com.smart.common.mqtt; + +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.handler.annotation.Header; + +// 使用MessagingGateway注解将接口标记为消息网关 +@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") +public interface MqttGateway { + + // 定义了一个发送消息到MQTT的方法 + // @Header注解用于指定要发送的消息所属的主题(topic) + void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic); + +} diff --git a/src/main/java/com/smart/common/mqtt/MqttSenderConfig.java b/src/main/java/com/smart/common/mqtt/MqttSenderConfig.java new file mode 100644 index 0000000..de96b2a --- /dev/null +++ b/src/main/java/com/smart/common/mqtt/MqttSenderConfig.java @@ -0,0 +1,73 @@ +package com.smart.common.mqtt; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.IntegrationComponentScan; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHandler; + +@Slf4j +@Configuration +@IntegrationComponentScan +public class MqttSenderConfig { + + @Value("${spring.mqtt.username}") + private String username; + @Value("${spring.mqtt.password}") + private String password; + @Value("${spring.mqtt.url}") + private String hostUrl; + @Value("${spring.mqtt.client.id}") + private String clientId; + @Value("${spring.mqtt.default.topic}") + private String defaultTopic; + @Value("${spring.mqtt.default.completionTimeout}") + private int completionTimeout; + + // 配置MQTT连接选项 + @Bean + public MqttConnectOptions getMqttConnectOptions() { + MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); + mqttConnectOptions.setCleanSession(true); + mqttConnectOptions.setConnectionTimeout(10); + mqttConnectOptions.setKeepAliveInterval(90); + mqttConnectOptions.setAutomaticReconnect(true); + mqttConnectOptions.setUserName(username); + mqttConnectOptions.setPassword(password.toCharArray()); + mqttConnectOptions.setServerURIs(new String[]{hostUrl}); + mqttConnectOptions.setKeepAliveInterval(2); + return mqttConnectOptions; + } + + // 创建MqttPahoClientFactory实例,用于创建MqttClient + @Bean + public MqttPahoClientFactory mqttClientFactory() { + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + factory.setConnectionOptions(getMqttConnectOptions()); + return factory; + } + + // 配置消息处理器,用于发送消息到MQTT + @Bean + @ServiceActivator(inputChannel = "mqttOutboundChannel") + public MessageHandler mqttOutbound() { + MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, mqttClientFactory()); + messageHandler.setAsync(true); + messageHandler.setDefaultTopic(defaultTopic); + return messageHandler; + } + + // 配置消息通道,用于接收要发送的消息 + @Bean + public MessageChannel mqttOutboundChannel() { + return new DirectChannel(); + } +} diff --git a/src/main/java/com/smart/common/util/CommonUtils.java b/src/main/java/com/smart/common/util/CommonUtils.java new file mode 100644 index 0000000..a44d5c9 --- /dev/null +++ b/src/main/java/com/smart/common/util/CommonUtils.java @@ -0,0 +1,121 @@ +package com.smart.common.util; + +import cn.hutool.core.date.DateUtil; +import com.smart.common.constant.MsgConstant; +import com.smart.common.constant.SystemConstant; +import com.smart.common.model.Result; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * 通用工具类 + */ +public class CommonUtils { + + /** + * 判断对象是否为空 + * + * @param obj + * @return + */ + public static boolean isNullOrEmpty(Object obj) { + if (obj == null) { + return true; + } + return false; + } + + /** + * 查询详情提示 + * + * @param data + * @return + */ + public static Result msg(Object data) { + if (isNullOrEmpty(data)) { + return Result.error(MsgConstant.MSG_INIT_FORM); // 如果数据为空,返回带有错误消息的结果对象 + } + return Result.ok(data); // 如果数据不为空,返回包含数据的成功结果对象 + } + + /** + * 返回数据 + * + * @param data + * @return + */ + public static Result msgNotCheckNull(Object data) { + return Result.ok().put(SystemConstant.DATA_ROWS, data); // 直接返回一个成功的结果对象,包含传入的数据 + } + + /** + * 使用正则表达式去掉多余的小数点和0 + * + * @param s + * @return + */ + public static String subZeroAndDot(String s) { + if (s.indexOf(".") > 0) { // 如果字符串中包含小数点 + s = s.replaceAll("0+?$", ""); // 去掉末尾多余的0 + s = s.replaceAll("[.]$", ""); // 如果最后一位是小数点,也去掉 + } + return s; + } + + /** + * 下载图片 + * + * @param imgUrl 图片的URL地址 + * @param imgPath 下载保存的本地路径 + */ + public static void downLoadImg(String imgUrl, String imgPath) { + BufferedImage bufferedImage; + try { + URL url = new URL(imgUrl); // 创建URL对象 + URLConnection urlConnection = url.openConnection(); // 打开URL连接 + HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; // 强制转换为HttpURLConnection类型 + httpURLConnection.connect(); // 连接URL + if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { // 如果连接成功 + InputStream inputStream = httpURLConnection.getInputStream(); // 获取输入流 + bufferedImage = ImageIO.read(inputStream); // 读取图片 + ImageIO.write(bufferedImage, "png", new File(imgPath)); // 将图片写入本地文件 + } else { + System.out.println("连接失败"); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 创建多级文件夹 + * + * @param filePath 文件路径 + * @return 最终的父级文件夹 + */ + public static File createParentFile(String filePath) { + File parentFile = new File(filePath + SystemConstant.SF_FILE_SEPARATOR + DateUtil.thisYear()); // 创建年份父级文件夹 + if (!parentFile.exists()) { // 如果父级文件夹不存在 + parentFile.mkdirs(); // 创建父级文件夹 + } + parentFile = new File(parentFile, (DateUtil.thisMonth() + 1) + ""); // 创建月份父级文件夹 + if (!parentFile.exists()) { // 如果父级文件夹不存在 + parentFile.mkdirs(); // 创建父级文件夹 + } + parentFile = new File(parentFile, DateUtil.thisDayOfMonth() + ""); // 创建日期父级文件夹 + if (!parentFile.exists()) { // 如果父级文件夹不存在 + parentFile.mkdirs(); // 创建父级文件夹 + } + return parentFile; // 返回最终的父级文件夹 + } +} diff --git a/src/main/java/com/smart/common/util/DateUtils.java b/src/main/java/com/smart/common/util/DateUtils.java new file mode 100644 index 0000000..57f0a01 --- /dev/null +++ b/src/main/java/com/smart/common/util/DateUtils.java @@ -0,0 +1,99 @@ +package com.smart.common.util; + +import java.sql.Timestamp; +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"; + + /** + * 格式化日期为指定的字符串格式 + * + * @param date 要格式化的日期 + * @return 格式化后的日期字符串 + */ + public static String format(Date date) { + return format(date, DATE_PATTERN); // 使用默认日期格式进行格式化 + } + + /** + * 格式化日期为指定的字符串格式 + * + * @param date 要格式化的日期 + * @param pattern 指定的日期格式 + * @return 格式化后的日期字符串 + */ + 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 指定的日期格式 + * @return 格式化后的日期字符串 + */ + public static String format(Timestamp date, String pattern) { + if (date != null) { + SimpleDateFormat df = new SimpleDateFormat(pattern); // 创建指定格式的日期格式化对象 + return df.format(date); // 格式化日期并返回字符串 + } + return null; + } + + /** + * 解析字符串为日期对象 + * + * @param str 要解析的日期字符串 + * @param pattern 指定的日期格式 + * @return 解析后的日期对象 + */ + public static Date parse(String str, String pattern) { + if (str != null) { + SimpleDateFormat df = new SimpleDateFormat(pattern); // 创建指定格式的日期格式化对象 + try { + return df.parse(str); // 解析日期字符串并返回日期对象 + } catch (ParseException e) { + e.printStackTrace(); + } + } + return null; + } + + /** + * 获取当前时间戳 + * + * @return 当前时间戳 + */ + public static Timestamp getTimestamp() { + return new Timestamp(System.currentTimeMillis()); // 创建当前时间的时间戳对象 + } + + /** + * 获取当前时间 + * + * @return 当前时间的字符串表示 + */ + public static String getTime() { + return format(new Timestamp(System.currentTimeMillis()), DATE_TIME_PATTERN); // 使用默认日期时间格式格式化当前时间 + } + + public static void main(String[] args) { + System.out.println(System.currentTimeMillis()); // 打印当前系统时间的毫秒数 + } +} diff --git a/src/main/java/com/smart/common/util/ExcelExport.java b/src/main/java/com/smart/common/util/ExcelExport.java new file mode 100644 index 0000000..38d435a --- /dev/null +++ b/src/main/java/com/smart/common/util/ExcelExport.java @@ -0,0 +1,1281 @@ +package com.smart.common.util; + +import com.smart.common.constant.ExcelDataType; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFCellStyle; +import org.apache.poi.hssf.usermodel.HSSFRichTextString; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.RegionUtil; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.*; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.sql.Timestamp; +import java.util.List; +import java.util.*; + +/** + * 导出Excel文件(导出“XLSX”格式,支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion) + */ +public class ExcelExport { + + private static Logger log = LoggerFactory.getLogger(ExcelExport.class); + + /** + * 单元格靠左对齐 + */ + public static final Integer CELL_ALIGN_LEFT = 1; + /** + * 单元格居中对齐 + */ + public static final Integer CELL_ALIGN_CENTER = 2; + /** + * 单元格靠右对齐 + */ + public static final Integer CELL_ALIGN_RIGHT = 3; + + /** + * 工作薄对象,用于创建和操作Excel + */ + private SXSSFWorkbook wb; + + /** + * 工作表对象,用于表示Excel中的一个工作表 + */ + private Sheet sheet; + + /** + * 样式列表,保存不同样式的单元格样式对象 + */ + private Map styles; + + /** + * 当前行号,用于记录写入数据的行号 + */ + private int rownum; + + /** + * 导出模板工作薄对象,类型为Workbook,用于读取Excel文件模板 + */ + private Workbook workbook; + + /** + * 构造函数 + * @param fileName + * 导出文件模板,读取第一个工作表 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelExport(String fileName, int headerNum) throws InvalidFormatException, IOException { + this(new File(fileName), headerNum); + } + + /** + * 构造函数 + * @param file + * 导出文件模板对象,读取第一个工作表 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelExport(File file, int headerNum) throws InvalidFormatException, IOException { + this(file, headerNum, 0); + } + + /** + * 构造函数 + * @param fileName + * 导出文件模板 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @param sheetIndex + * 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelExport(String fileName, int headerNum, int sheetIndex) throws InvalidFormatException, IOException { + this(new File(fileName), headerNum, sheetIndex); + } + + /** + * 构造函数 + * + * 导出文件模板对象 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @param sheetIndex + * 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelExport(File file, int headerNum, int sheetIndex) throws InvalidFormatException, IOException { + this(file.getName(), new FileInputStream(file), headerNum, sheetIndex); + } + + /** + * 构造函数 + * + * 导出文件模板对象 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @param sheetIndex + * 工作表编号 + * @throws IOException + */ + public ExcelExport(String fileName, InputStream is, int headerNum, int sheetIndex) + throws IOException { + if (StringUtils.isBlank(fileName)) { + throw new RuntimeException("导入文档为空!"); + } else if (fileName.toLowerCase().endsWith("xls")) { + this.workbook = new HSSFWorkbook(is); + } else if (fileName.toLowerCase().endsWith("xlsx")) { + this.workbook = new XSSFWorkbook(is); + } else if (fileName.toLowerCase().endsWith("xlsm")) { + this.workbook = new XSSFWorkbook(is); + } else { + throw new RuntimeException("文档格式不正确!"); + } + if (this.workbook.getNumberOfSheets() < sheetIndex) { + throw new RuntimeException("文档中没有工作表!"); + } + this.sheet = this.workbook.getSheetAt(sheetIndex); + if (this.sheet.getLastRowNum() < 0) { + throw new RuntimeException("文档模板错误!"); + } + this.rownum = headerNum + 1; + this.styles = createStyles(workbook); + log.debug("Initialize success."); + } + + /** + * 构造函数 -- SXSSFWorkbook + * @param fileName + * @param headerNum + * @param sheetIndex + * @param batchType + * @throws IOException + */ + public ExcelExport(String fileName, int headerNum, int sheetIndex, String batchType) + throws IOException { + if (StringUtils.isBlank(fileName)) { + throw new RuntimeException("导出文档为空!"); + } else { + InputStream is = new FileInputStream(new File(fileName)); + XSSFWorkbook workb = new XSSFWorkbook(is); + this.wb = new SXSSFWorkbook(workb, 1000); + if (this.wb.getNumberOfSheets() < sheetIndex) { + throw new RuntimeException("文档中没有工作表!"); + } + this.sheet = this.wb.getSheetAt(sheetIndex); + this.rownum = headerNum + 1; + this.styles = createStyles(wb); + log.debug("Initialize success."); + } + } + + /** + * 构造函数 + * + * @param title + * 表格标题,传“空值”,表示无标题 + * @param headerMap + * 表头数组 + */ + public ExcelExport(String title, Map headerMap) { + List headerList = new ArrayList(); + headerList.add("序号"); + for (String key : headerMap.keySet()) { + headerList.add(headerMap.get(key)); + } + initialize(title, headerList); + } + + /** + * 构造函数 + * + * @param title + * 表格标题,传“空值”,表示无标题 + * @param headerList + * 表头列表 + */ + public ExcelExport(String title, List headerList) { + // 自动增加序号列 + headerList.add(0, "序号"); + initialize(title, headerList); + } + + /** + * 初始化函数 + * + * @param title + * 表格标题,传“空值”,表示无标题 + * @param headerList + * 表头列表 + */ + private void initialize(String title, List headerList) { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet("Export"); + this.styles = createStyles(wb); + // Create title + if (StringUtils.isNotBlank(title)) { + Row titleRow = sheet.createRow(rownum++); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), + headerList.size() - 1)); + } + // Create header + if (headerList == null) { + throw new RuntimeException("headerList not null!"); + } + Row headerRow = sheet.createRow(rownum++); + headerRow.setHeightInPoints(16); + for (int i = 0; i < headerList.size(); i++) { + Cell cell = headerRow.createCell(i); + cell.setCellStyle(styles.get("header")); + String[] ss = StringUtils.split(headerList.get(i), "**", 2); + if (ss.length == 2) { + cell.setCellValue(ss[0]); + Comment comment = this.sheet.createDrawingPatriarch() + .createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6)); + comment.setString(new XSSFRichTextString(ss[1])); + cell.setCellComment(comment); + } else { + cell.setCellValue(headerList.get(i)); + } + sheet.autoSizeColumn(i); + } + for (int i = 0; i < headerList.size(); i++) { + int colWidth = sheet.getColumnWidth(i) * 2; + sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth); + } + log.debug("Initialize success."); + } + + /** + * 创建表格样式 + * + * @param wb + * 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) { + Map styles = new HashMap(6); + + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.LEFT); + styles.put("data1", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + styles.put("data2", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.RIGHT); + styles.put("data3", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + // style.setWrapText(true); + style.setAlignment(HorizontalAlignment.CENTER); + style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(IndexedColors.WHITE.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + return styles; + } + + /** + * 添加一行 + * + * @return 行对象 + */ + public Row addRow() { + return sheet.createRow(rownum++); + } + + /** + * 添加一个单元格 + * + * @param row + * 添加的行 + * @param column + * 添加列号 + * @param val + * 添加值 + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val) { + return this.addCell(row, column, val, 0, Class.class); + } + + /** + * 添加一个单元格 + * + * @param row + * 添加的行 + * @param column + * 添加列号 + * @param val + * 添加值 + * @param align + * 对齐方式(1:靠左;2:居中;3:靠右) + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val, int align) { + return this.addCell(row, column, val, 0, Class.class); + } + + /** + * 添加一个单元格 + * + * @param row + * 添加的行 + * @param column + * 添加列号 + * @param val + * 添加值 + * @param align + * 对齐方式(1:靠左;2:居中;3:靠右) + * @return 单元格对象 + */ + public Cell addCell(Row row, int column, Object val, int align, Class fieldType) { + Cell cell = row.createCell(column); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + try { + if (val == null) { + cell.setCellValue(""); + } else if (val instanceof String) { + cell.setCellValue((String) val); + } else if (val instanceof Integer) { + cell.setCellValue((String) val); + } else if (val instanceof Short) { + cell.setCellValue((Short) val); + } else if (val instanceof Long) { + cell.setCellValue((Long) val); + } else if (val instanceof Double) { + cell.setCellValue((Double) val); + } else if (val instanceof Float) { + cell.setCellValue((Float) val); + } else if (val instanceof Timestamp) { + DataFormat format = null; + if (wb != null) { + format = wb.createDataFormat(); + } else { + format = workbook.createDataFormat(); + } + style.setDataFormat(format.getFormat("yyyy-MM-dd HH:mm:ss")); + cell.setCellValue((Date) val); + } else if (val instanceof Date) { + DataFormat format = null; + if (wb != null) { + format = wb.createDataFormat(); + } else { + format = workbook.createDataFormat(); + } + style.setDataFormat(format.getFormat("yyyy-MM-dd")); + cell.setCellValue((Date) val); + } else { + if (fieldType != Class.class) { + cell.setCellValue((String) fieldType.getMethod("setValue", Object.class).invoke(null, val)); + } else { + cell.setCellValue((String) Class + .forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), + "fieldtype." + val.getClass().getSimpleName() + "Type")) + .getMethod("setValue", Object.class).invoke(null, val)); + } + } + } catch (Exception ex) { + log.debug("Set cell value [" + row.getRowNum() + "," + column + "] error: " + ex.toString()); + cell.setCellValue(val.toString()); + } + cell.setCellStyle(style); + return cell; + } + + /** + * 获取行对象 + * + * @param rownum + * @return + */ + public Row getRow(int rownum) { + return this.sheet.getRow(rownum); + } + + /** + * 获取最后一个数据行号 + * + * @return + */ + public int getLastDataRowNum() { + return this.sheet.getLastRowNum() + 1; + } + + /** + * 设置单元格的值 + * + * @param row + * 设置的行 + * @param column + * 设置的列号 + * @param val + * 设置的值 + * 对齐方式(1:靠左;2:居中;3:靠右) + * @return + */ + public void setCellValue(Row row, int column, Object val) { + Cell cell = row.getCell(column); + try { + if (val == null) { + cell.setCellValue(""); + } else if (val instanceof String) { + cell.setCellValue((String) val); + } else if (val instanceof Integer) { + cell.setCellValue((Integer) val); + } else if (val instanceof Long) { + cell.setCellValue((Long) val); + } else if (val instanceof Double) { + cell.setCellValue((Double) val); + } else if (val instanceof Float) { + cell.setCellValue((Float) val); + } else if (val instanceof Date) { + cell.setCellValue((Date) val); + } else { + cell.setCellValue((String) Class + .forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(), + "fieldtype." + val.getClass().getSimpleName() + "Type")) + .getMethod("setValue", Object.class).invoke(null, val)); + } + } catch (Exception ex) { + log.debug("Set cell value [" + row.getRowNum() + "," + column + "] error: " + ex.toString()); + cell.setCellValue(val.toString()); + } + } + + /** + * + * 设置公式 + * + * @param row + * 设置的行 + * @param column + * 设置的列号 + * @param val + * 设置的值 + */ + public void setCellFormula(Row row, int column, Object val) { + Cell cell = row.getCell(column); + if (null == cell) { + cell = row.createCell(column); + } + try { + if (val == null) { + cell.setCellValue(""); + } else if (val instanceof String) { + cell.setCellFormula((String) val); + } + } catch (Exception ex) { + log.debug("Set cell formula [" + row.getRowNum() + "," + column + "] error: " + ex.toString()); + cell.setCellValue(val.toString()); + } + } + + /** + * + * 合并单元格 + * + * @param startX + * 起始行数 + * @param endX + * 结束行数 + * @param startY + * 起始列数 + * @param endY + * 结束列数 + * @param value + * void 合并后单元格塞值 + * @param flag + * + */ + public void mergeRegion(int startX, int endX, int startY, int endY, String value, boolean flag) { + CellRangeAddress cra = new CellRangeAddress(startX, endX, startY, endY); + // 在sheet里增加合并单元格 + sheet.addMergedRegion(cra); + // 塞值 + Row row = sheet.getRow(startX); + if (null == row) { + row = sheet.createRow(startX); + } + // 设置背景色 边框 字号 + Cell cell = row.createCell(startY); + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short) 14); + if (flag) { + style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + font.setFontHeightInPoints((short) 10); + } + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setAlignment(HorizontalAlignment.CENTER); + + style.setFont(font); + cell.setCellStyle(style); + RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet); + setCellValue(row, startY, value); + } + + /** + * + * 合并单元格并赋值 + * + * @param startX + * 起始行数 + * @param endX + * 结束行数 + * @param startY + * 起始列数 + * @param endY + * 结束列数 + * @param value + * void 合并后单元格塞值 + */ + public void mergeRegion(int startX, int endX, int startY, int endY, String value) { + CellRangeAddress cra = new CellRangeAddress(startX, endX, startY, endY); + // 在sheet里增加合并单元格 + sheet.addMergedRegion(cra); + // 塞值 + Row row = sheet.getRow(startX); + if (null == row) { + row = sheet.createRow(startX); + } + // 设置格式 + Cell cell = row.createCell(startY); + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short) 10); + style.setFont(font); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setAlignment(HorizontalAlignment.CENTER); + cell.setCellStyle(style); + RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet); + RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet); + setCellValue(row, startY, value); + } + + /** + * 设置内容为强制换行 + * + */ + public void setWrapText(int rowNum, int columnNum, String excelType) { + if ("xls".equals(excelType)) { + HSSFCellStyle cellStyle = (HSSFCellStyle) workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short) 10); + cellStyle.setFont(font); + cellStyle.setWrapText(true); + cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + cellStyle.setAlignment(HorizontalAlignment.CENTER); + cellStyle.setBorderRight(BorderStyle.THIN); + cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderLeft(BorderStyle.THIN); + cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderTop(BorderStyle.THIN); + cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderBottom(BorderStyle.THIN); + cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + Cell cell = getRow(rowNum).getCell(columnNum); + cell.setCellStyle(cellStyle); + cell.setCellValue(new HSSFRichTextString(cell.getStringCellValue())); + } else { + XSSFCellStyle cellStyle = (XSSFCellStyle) workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short) 10); + cellStyle.setFont(font); + cellStyle.setWrapText(true); + cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + cellStyle.setAlignment(HorizontalAlignment.CENTER); + cellStyle.setBorderRight(BorderStyle.THIN); + cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderLeft(BorderStyle.THIN); + cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderTop(BorderStyle.THIN); + cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderBottom(BorderStyle.THIN); + cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + Cell cell = getRow(rowNum).getCell(columnNum); + cell.setCellStyle(cellStyle); + cell.setCellValue(new XSSFRichTextString(cell.getStringCellValue())); + } + + } + + /** + * 设置单元格是否锁定 + * + */ + public void setCellLocked(int rowNum, int columnNum, boolean locked) { + CellStyle cellStyle = workbook.createCellStyle(); + cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + cellStyle.setBorderRight(BorderStyle.THIN); + cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderLeft(BorderStyle.THIN); + cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderTop(BorderStyle.THIN); + cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + cellStyle.setBorderBottom(BorderStyle.THIN); + cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + Font dataFont = workbook.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + cellStyle.setFont(dataFont); + cellStyle.setAlignment(HorizontalAlignment.CENTER); + // 设置为文本格式 + DataFormat format = workbook.createDataFormat(); + cellStyle.setDataFormat(format.getFormat("@")); + cellStyle.setLocked(locked); + Cell cell = getRow(rowNum).getCell(columnNum); + cell.setCellStyle(cellStyle); + } + + /** + * 添加数据List + * + * @param list + * 数据列表 + * @param headerMap + * 列表头LinkedHashMap key实体类字段 value表头列 + * @param alignMap + * 对齐方式Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * + */ + public ExcelExport setDataList(List list, Map headerMap, Map alignMap) { + int num = 0; + for (E e : list) { + int colunm = 0; + Row row = addRow(); + num++; + addCell(row, colunm++, String.valueOf(num), 2, Class.class); + StringBuilder sb = new StringBuilder(); + for (String col : headerMap.keySet()) { + Object val; + int align = 0; + try { + val = Reflections.invokeGetter(e, col); + } catch (Exception ex) { + log.debug(ex.toString()); + val = ""; + } + if ((alignMap != null) && (alignMap.size() > 0)) { + for (String alignCol : alignMap.keySet()) { + if (col.equals(alignCol)) { + align = alignMap.get(alignCol).intValue(); + } + } + } + addCell(row, colunm++, val, align, Class.class); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + + return this; + } + + /** + * 添加数据List(可设置单元格格式) + * + * @param list + * 数据列表 + * 列表头LinkedHashMap key实体类字段 value表头列 + * 对齐方式Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param typeMap + * 单元格格式 + */ + public ExcelExport setDataList(List> list, Map dataMap, boolean hasRowNum, + String str, Map typeMap) { + int num = 1; + for (Map m : list) { + if ((m != null) && (m.size() > 0)) { + int colunm = 0; + Row row = addRow(); + row.setHeightInPoints(25); + if (hasRowNum) { + addCell(row, colunm++, Integer.valueOf(num++), 2, Class.class); + } + StringBuilder sb = new StringBuilder(); + for (String col : dataMap.keySet()) { + Object val = null; + int align = 0; + try { + val = m.get(col); + } catch (Exception ex) { + log.debug(ex.toString()); + if ("null".equals(str)) { + val = null; + } else { + val = ""; + } + } + if (dataMap.get(col) != null) { + align = dataMap.get(col).intValue(); + } + Cell cell; + if (null != val) { + cell = addCell(row, colunm++, val, align, Class.class); + } else { + cell = row.createCell(colunm++); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + cell.setCellStyle(style); + } + if (typeMap != null && !typeMap.isEmpty()) { + if (typeMap.containsKey(col)) { + CellStyle cellStyle = workbook.createCellStyle(); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + cellStyle.cloneStyleFrom(style); + // 设置单元格格式为"文本" + DataFormat format = workbook.createDataFormat(); + cellStyle.setDataFormat(format.getFormat("@")); + cell.setCellStyle(cellStyle); + } + } + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + } + return this; + + } + + /** + * 添加数据List(可设置单元格格式) + * + * @param list + * 数据列表 + * 列表头LinkedHashMap key实体类字段 value表头列 + * 对齐方式Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param typeMap + * 单元格格式 + */ + public ExcelExport setDataList(List> list, Map headerMap, + Map alignMap, boolean hasRowNum, Map typeMap) { + int num = 1; + for (Map m : list) { + if ((m != null) && (m.size() > 0)) { + int colunm = 0; + Row row = addRow(); + if (hasRowNum) { + addCell(row, colunm++, String.valueOf(num), 2, Class.class); + } + StringBuilder sb = new StringBuilder(); + for (String col : headerMap.keySet()) { + Object val; + int align = 0; + try { + val = m.get(col); + } catch (Exception ex) { + log.debug(ex.toString()); + val = ""; + } + if ((alignMap != null) && (alignMap.size() > 0)) { + for (String alignCol : alignMap.keySet()) { + if (col.equals(alignCol)) { + align = alignMap.get(alignCol).intValue(); + } + } + } + Cell cell = addCell(row, colunm++, val, align, Class.class); + if (typeMap != null && !typeMap.isEmpty()) { + if (typeMap.containsKey(col)) { + CellStyle cellStyle = workbook.createCellStyle(); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + cellStyle.cloneStyleFrom(style); + DataFormat df = workbook.createDataFormat(); + if(ExcelDataType.MONEY.dataType.equals(typeMap.get(col))) { + cellStyle.setDataFormat(df.getFormat("#,##0.00")); + if(val != null && StringUtils.isNoneBlank(val.toString())) { + cell.setCellValue(Double.parseDouble(val.toString())); + } + } + cell.setCellStyle(cellStyle); + } + } + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + } + return this; + + } + + /** + * 添加数据(List>) + * + * @param list + * 数据列表 + * @param headerMap + * 列表头LinkedHashMap key实体类字段 value表头列 + * @param alignMap + * 对齐方式Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param str + * 区分标记 + * + */ + public ExcelExport setDataList(List> list, Map headerMap, + Map alignMap, String str) { + int num = 1; + for (Map m : list) { + if ((m != null) && (m.size() > 0)) { + int colunm = 0; + Row row = addRow(); + addCell(row, colunm++, String.valueOf(num), 2, Class.class); + StringBuilder sb = new StringBuilder(); + for (String col : headerMap.keySet()) { + Object val = null; + int align = 0; + try { + val = m.get(col); + } catch (Exception ex) { + log.debug(ex.toString()); + val = ""; + } + if ((alignMap != null) && (alignMap.size() > 0)) { + for (String alignCol : alignMap.keySet()) { + if (col.equals(alignCol)) { + align = alignMap.get(alignCol).intValue(); + } + } + } + addCell(row, colunm++, val, align, Class.class); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + } + return this; + } + + /** + * 添加数据(List>) + * + * @param list + * 数据列表 + * @param headerMap + * 列表头LinkedHashMap key实体类字段 value表头列 + * @param alignMap + * 对齐方式Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param hasRowNum + * 是否包含序号列(序号列在第一列) + * + */ + public ExcelExport setDataList(List> list, Map headerMap, + Map alignMap, boolean hasRowNum) { + int num = 1; + for (Map m : list) { + if ((m != null) && (m.size() > 0)) { + int colnum = 0; + Row row = addRow(); + if (hasRowNum) { + addCell(row, colnum++, String.valueOf(num), 2, Class.class); + } + StringBuilder sb = new StringBuilder(); + for (String col : headerMap.keySet()) { + Object val; + int align = 0; + try { + val = m.get(col); + } catch (Exception ex) { + log.debug(ex.toString()); + val = ""; + } + if ((alignMap != null) && (alignMap.size() > 0)) { + for (String alignCol : alignMap.keySet()) { + if (col.equals(alignCol)) { + align = alignMap.get(alignCol).intValue(); + } + } + } + addCell(row, colnum++, val, align, Class.class); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + } + return this; + } + + /** + * 添加数据List(只针对数据递增的模板文件) + * + * @param list + * 数据列表 + * @param dataMap + * 数据Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param hasRowNum + * 是否包含序号列(序号列在第一列) + * + */ + public ExcelExport setDataList(List list, Map dataMap, boolean hasRowNum) { + int num = 0; + for (E e : list) { + int colnum = 0; + Row row = addRow(); + row.setHeightInPoints(25); + num++; + if (hasRowNum) { + addCell(row, colnum++, String.valueOf(num), 2, Class.class); + } + StringBuilder sb = new StringBuilder(); + for (String col : dataMap.keySet()) { + Object val; + int align = 0; + try { + val = Reflections.invokeGetter(e, col); + } catch (Exception ex) { + log.debug(ex.toString()); + val = ""; + } + if (dataMap.get(col) != null) { + align = dataMap.get(col).intValue(); + } + addCell(row, colnum++, val, align, Class.class); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + + return this; + } + + /** + * 添加数据(List> 只针对数据递增的模板文件) + * + * @param list + * 数据列表 + * @param dataMap + * 数据Map key实体类字段 value 对齐方式(1:靠左;2:居中;3:靠右) + * @param hasRowNum + * 是否包含序号列(序号列在第一列) + * @param str + * 区分标记 + * + */ + public ExcelExport setDataList(List> list, Map dataMap, boolean hasRowNum, + String str) { + int num = 1; + for (Map m : list) { + if ((m != null) && (m.size() > 0)) { + int colnum = 0; + Row row = addRow(); + row.setHeightInPoints(25); + if (hasRowNum) { + addCell(row, colnum++, Integer.valueOf(num++), 2, Class.class); + } + StringBuilder sb = new StringBuilder(); + for (String col : dataMap.keySet()) { + Object val; + int align = 0; + try { + val = m.get(col); + } catch (Exception ex) { + log.debug(ex.toString()); + if ("null".equals(str)) { + val = null; + } else { + val = ""; + } + } + if (dataMap.get(col) != null) { + align = dataMap.get(col).intValue(); + } + if (null != val) { + addCell(row, colnum++, val, align, Class.class); + } else { + Cell cell = row.createCell(colnum++); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + cell.setCellStyle(style); + } + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + log.debug(new StringBuilder().append("Write success: [").append(row.getRowNum()).append("] ") + .append(sb.toString()).toString()); + } + } + return this; + } + + /** + * 创建并设置单元格样式--可加背景色--XSSF格式 + * + * @param row + * @param cellAlignCenter + * @param color void + */ + public CellStyle handleCellStyle(Row row, int column, Integer cellAlignCenter, Color color) { + Cell cell = null == row.getCell(column) ? row.createCell(column) : row.getCell(column); + // 复制单元格样式 + CellStyle cellStyle = copyCellStyle(row, column, cellAlignCenter); + // 设置背景色 + XSSFCellStyle styleTemp = ((XSSFCellStyle) cellStyle); + if (null != color) { + styleTemp.setFillForegroundColor(new XSSFColor((CTColor) color)); + } + styleTemp.setFillPattern(FillPatternType.SOLID_FOREGROUND); + cell.setCellStyle(styleTemp); + return styleTemp; + } + + /** + * 创建并设置单元格样式--可加背景色--HSSF格式 + * + * @param row + * @param column + * @param cellAlignCenter + * @param index 颜色索引值 + */ + public CellStyle handleCellStyle(Row row, int column, Integer cellAlignCenter, Short index) { + Cell cell = null == row.getCell(column) ? row.createCell(column) : row.getCell(column); + // 复制单元格样式 + CellStyle cellStyle = copyCellStyle(row, column, cellAlignCenter); + + // 设置背景色 + HSSFCellStyle styleTemp = ((HSSFCellStyle) cellStyle); + if (null != index) { + styleTemp.setFillForegroundColor(index); + } + styleTemp.setFillPattern(FillPatternType.SOLID_FOREGROUND); + cell.setCellStyle(styleTemp); + return cellStyle; + } + + /** + * 复制某个单元格样式 + * @param row + * + * @param row + * @param align void + * + */ + public CellStyle copyCellStyle(Row row, int column, Integer align) { + CellStyle cellStyle = workbook.createCellStyle(); + CellStyle style = styles.get("data" + (align >= 1 && align <= 3 ? align : "")); + cellStyle.cloneStyleFrom(style); + return cellStyle; + } + + /** + * 输出数据流 + * + * @param os + * 输出数据流 + */ + public ExcelExport write(OutputStream os) throws IOException { + wb.write(os); + return this; + } + + /** + * 输出到客户端 + * + * @param fileName + * 输出文件名 + */ + public ExcelExport write(HttpServletResponse response, HttpServletRequest request, String fileName) + throws IOException { + response.reset(); + response.setCharacterEncoding("UTF-8"); + String agent = request.getHeader("User-Agent"); + boolean isMSIE = (agent != null && agent.indexOf("MSIE") != -1); + // 处理部分IE无法识别的问题 + Boolean flag = agent.indexOf("like Gecko") > 0; + if (isMSIE || flag) { + fileName = URLEncoder.encode(fileName, "UTF-8"); + } else { + fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1"); + } + response.setContentType("application/octet-stream; charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + write(response.getOutputStream()); + return this; + } + + /** + * 输出到文件 + * + * 输出文件名 + */ + public ExcelExport writeFile(String name) throws FileNotFoundException, IOException { + FileOutputStream os = null; + try { + os = new FileOutputStream(name); + this.write(os); + } catch (Exception e) { + if (null != os) { + os.close(); + } + } + return this; + } + + /** + * 清理临时文件 + */ + public ExcelExport dispose() { + wb.dispose(); + return this; + } + + /** + * 输出数据流(只针对于模板文件) + * + * @param os + * 输出数据流 + */ + public ExcelExport writeTemplate(OutputStream os) throws IOException { + workbook.write(os); + os.flush(); + os.close(); + return this; + } + + /** + * 输出到客户端(只针对于模板文件) + * + * @param fileName + * 输出文件名 + */ + public ExcelExport writeTemplate(HttpServletResponse response, HttpServletRequest request, String fileName) + throws IOException { + response.reset(); + response.setCharacterEncoding("UTF-8"); + String agent = request.getHeader("User-Agent"); + boolean isMSIE = (agent != null && agent.indexOf("MSIE") != -1); + // 处理部分IE无法识别的问题 + Boolean flag = agent.indexOf("like Gecko") > 0; + if (isMSIE || flag) { + fileName = URLEncoder.encode(fileName, "UTF-8"); + } else { + fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1"); + } + response.setContentType("application/octet-stream; charset=utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + writeTemplate(response.getOutputStream()); + return this; + } + + /** + * 输出到文件(只针对于模板文件) + * + * 输出文件名 + */ + public ExcelExport writeTemplateFile(String name) throws IOException { + FileOutputStream os = null; + try { + os = new FileOutputStream(name); + this.writeTemplate(os); + } catch (Exception e) { + if (null != os) { + os.close(); + } + } + return this; + } + + /** + * 获取sheet + * + * @param headerNum + * @param sheetIndex + * @throws InvalidFormatException + * @throws IOException void + * + */ + public void getSheetAt(int headerNum, int sheetIndex) { + if (this.workbook.getNumberOfSheets() < sheetIndex) { + throw new RuntimeException("文档中没有工作表!"); + } + this.sheet = this.workbook.getSheetAt(sheetIndex); + if (this.sheet.getLastRowNum() <= 0) { + throw new RuntimeException("文档模板错误!"); + } + this.rownum = headerNum + 1; + this.styles = createStyles(workbook); + log.debug("Initialize success."); + } + + public Workbook getWorkbook() { + return this.workbook; + } + + public Sheet getSheet() { + return this.sheet; + } + +} diff --git a/src/main/java/com/smart/common/util/ExcelImport.java b/src/main/java/com/smart/common/util/ExcelImport.java new file mode 100644 index 0000000..b0daa6d --- /dev/null +++ b/src/main/java/com/smart/common/util/ExcelImport.java @@ -0,0 +1,509 @@ +package com.smart.common.util; + +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.smart.common.constant.ExcelDataType; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; + + +/** + * 导入Excel文件(支持“XLS”和“XLSX”格式) + */ +public class ExcelImport { + + private static Logger log = LoggerFactory.getLogger(ExcelImport.class); + + /** + * 是否将数值类型数据使用string,防止数值类型为科学计数法 + */ + private boolean double2String = true; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 标题行号 + */ + private int headerNum; + + /** + * 构造函数,将Excel文件加载到工作薄对象中 + * @param fileName 导入文件名 + * @param headerNum 标题行号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(String fileName, int headerNum) throws InvalidFormatException, IOException { + this(new File(fileName), headerNum); + } + + /** + * 构造函数,将Excel文件加载到工作薄对象中 + * @param file 导入文件对象 + * @param headerNum 标题行号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(File file, int headerNum) throws InvalidFormatException, IOException { + this(file, headerNum, 0); + } + + /** + * 构造函数,将Excel文件加载到工作薄对象中 + * @param fileName 导入文件名 + * @param headerNum 标题行号 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(String fileName, int headerNum, int sheetIndex) throws InvalidFormatException, IOException { + this(new File(fileName), headerNum, sheetIndex); + } + + /** + * 构造函数,将Excel文件加载到工作薄对象中 + * @param file 导入文件对象 + * @param headerNum 标题行号 + * @param sheetIndex 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(File file, int headerNum, int sheetIndex) throws InvalidFormatException, IOException { + this(file.getName(), new FileInputStream(file), headerNum, sheetIndex); + } + + + /** + * 构造函数,将Excel文件加载到工作薄对象中 + * @param multipartFile 导入文件对象 + * @param headerNum 标题行号 + * @param sheetIndex 工作表编号 + * @param double2String 是否将数值类型数据使用string,防止数值类型为科学计数法 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(MultipartFile multipartFile, int headerNum, int sheetIndex, boolean double2String) + throws InvalidFormatException, IOException { + this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex); + this.double2String = double2String; + } + + /** + * 构造函数 + * + * 导入文件对象 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @param sheetIndex + * 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(MultipartFile multipartFile, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex); + } + + /** + * 构造函数 + * + * 导入文件对象 + * @param headerNum + * 标题行号,数据行号=标题行号+1 + * @param sheetIndex + * 工作表编号 + * @throws InvalidFormatException + * @throws IOException + */ + public ExcelImport(String fileName, InputStream is, int headerNum, int sheetIndex) + throws InvalidFormatException, IOException { + if (StringUtils.isBlank(fileName)) { + throw new RuntimeException("导入文档为空!"); + } else if (fileName.toLowerCase().endsWith("xls")) { + this.wb = new HSSFWorkbook(is); + } else if (fileName.toLowerCase().endsWith("xlsx")) { + this.wb = new XSSFWorkbook(is); + } else if (fileName.toLowerCase().endsWith("xlsm")) { + this.wb = new XSSFWorkbook(is); + } else { + throw new RuntimeException("文档格式不正确!"); + } + if (this.wb.getNumberOfSheets() < sheetIndex) { + throw new RuntimeException("文档中没有工作表!"); + } + this.sheet = this.wb.getSheetAt(sheetIndex); + if (this.sheet.getLastRowNum() < 0) { + throw new RuntimeException("文档中无数据!"); + } + this.headerNum = headerNum; + is.close(); + log.debug("Initialize success."); + } + + /** + * 获取行对象 + * + * @param rownum + * @return + */ + public Row getRow(int rownum) { + return this.sheet.getRow(rownum); + } + + /** + * 获取数据行号 + * + * @return + */ + public int getDataRowNum() { + return headerNum + 1; + } + + /** + * 获取最后一个数据行号 + * + * @return + */ + public int getLastDataRowNum() { + return this.sheet.getLastRowNum() + 1; + } + + /** + * 获取最后一个列号 + * + * @return + */ + public int getLastCellNum() { + return this.getRow(headerNum).getLastCellNum(); + } + + /** + * 获取单元格值 + * + * @param row + * 获取的行 + * @param column + * 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) { + Object val = ""; + try { + Cell cell = row.getCell(column); + if (cell != null) { + if (cell.getCellType() == CellType.NUMERIC) { + short format = cell.getCellStyle().getDataFormat(); + SimpleDateFormat sdf; + if (format == 184) { // 日期 + sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } else { // 不是日期格式 + // 根据数据来判断是否使用转换 + val = cell.getNumericCellValue(); + // 如果数据包含E并且需要转换,则转换 + if (double2String && String.valueOf(val).contains("E")) { + val = new java.text.DecimalFormat("0").format(cell.getNumericCellValue()); + } + return val; + } + double value = cell.getNumericCellValue(); + Date date = DateUtil.getJavaDate(value); + if (date == null || "".equals(date)) { + val = ""; + } + try { + val = sdf.format(date); + } catch (Exception e) { + e.printStackTrace(); + val = ""; + } + } else if (cell.getCellType() == CellType.STRING) { + val = cell.getStringCellValue(); + } else if (cell.getCellType() == CellType.FORMULA) { + try { + val = cell.getStringCellValue(); + } catch (Exception e) { + val = cell.getCellFormula(); + } + } else if (cell.getCellType() == CellType.BOOLEAN) { + val = cell.getBooleanCellValue(); + } else if (cell.getCellType() == CellType.ERROR) { + val = cell.getErrorCellValue(); + } + } + } catch (Exception e) { + return val; + } + return val; + } + + /** + * 获取某几列数据 + */ + + public List> getDataListByColumn(Map headerMap, int[] columnNumber) { + List dataList = new ArrayList>(); + for (int i = getDataRowNum(); i < getLastDataRowNum(); i++) { + Row row = getRow(i); + StringBuilder sb = new StringBuilder(); + Map e = new HashMap<>(); + int number = 0; + for (String entityName : headerMap.keySet()) { + Object val = getCellValue(row, columnNumber[number++]); + if (StringUtils.isNotBlank(val.toString())) { + String valType = headerMap.get(entityName); + try { + if (ExcelDataType.STRING.dataType.equals(valType)) { + String s = String.valueOf(val.toString()); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + val = String.valueOf(val.toString()); + } + val = StringUtils.trim((String) val); + } else if (ExcelDataType.INTEGER.dataType.equals(valType)) { + val = Double.valueOf(val.toString()).intValue(); + } else if (ExcelDataType.LONG.dataType.equals(valType)) { + val = Double.valueOf(val.toString()).longValue(); + } else if (ExcelDataType.DOUBLE.dataType.equals(valType)) { + val = Double.valueOf(val.toString()); + } else if (ExcelDataType.BIG_DECIMAL.dataType.equals(valType)) { + val = BigDecimal.valueOf(Double.valueOf(val.toString())); + } else if (ExcelDataType.FLOAT.dataType.equals(valType)) { + val = Float.valueOf(val.toString()); + } else if (ExcelDataType.TIMESTAMP.dataType.equals(valType)) { + if (val.toString().split("-").length > 1) { + val = Timestamp.valueOf(val.toString()); + } else { + val = DateUtil.getJavaDate((Double) val); + val = new Timestamp(((Date) val).getTime()); + } + } else if (ExcelDataType.DATE.dataType.equals(valType)) { + val = DateUtil.getJavaDate((Double) val); + } + } catch (Exception ex) { + log.info("Get cell value [" + i + "," + number + + "] error: " + ex.toString()); + + val = null; + } + if (val == null) { + val = ""; + } + e.put(entityName, val); + }else{ + e.put(entityName, ""); + } + sb.append(val).append(", "); + } + dataList.add(e); + } + return dataList; + } + + + /** + * 获取导入数据列表 + * + * @param cls + * 导入对象类型 + * @param headerMap + * 表头对象LinkedHashMap 字段名,字段类型 + * @param macroMap + * 字典对象Map 字段名,字典类型 + * @return + * @throws InstantiationException + * @throws IllegalAccessException + * List + */ + public List getDataList(Class cls, Map headerMap, Map macroMap) + throws InstantiationException, IllegalAccessException { + List dataList = new ArrayList(); + for (int i = getDataRowNum(); i < getLastDataRowNum(); i++) { + Object e = cls.newInstance(); + int column = 0; + Row row = getRow(i); + StringBuilder sb = new StringBuilder(); + for (String entityName : headerMap.keySet()) { + Object val = getCellValue(row, column++); + if (StringUtils.isNotBlank(val.toString())) { + + String valType = headerMap.get(entityName); + try { + if (ExcelDataType.STRING.dataType.equals(valType)) { + String s = String.valueOf(val.toString()); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + val = String.valueOf(val.toString()); + } + val = StringUtils.trim((String) val).replaceAll("\\u00A0+", ""); + + } else if (ExcelDataType.INTEGER.dataType.equals(valType)) { + val = Integer.valueOf(Double.valueOf(val.toString()).intValue()); + } else if (ExcelDataType.LONG.dataType.equals(valType)) { + val = Long.valueOf(Double.valueOf(val.toString()).longValue()); + } else if (ExcelDataType.DOUBLE.dataType.equals(valType)) { + val = Double.valueOf(val.toString()); + } else if (ExcelDataType.BIG_DECIMAL.dataType.equals(valType)) { + val = BigDecimal.valueOf(Double.valueOf(val.toString())); + } else if (ExcelDataType.FLOAT.dataType.equals(valType)) { + val = Float.valueOf(val.toString()); + } else if (ExcelDataType.TIMESTAMP.dataType.equals(valType)) { + if(val.toString().split("-").length > 1) { + val = Timestamp.valueOf(val.toString()); + }else { + val = DateUtil.getJavaDate(((Double) val).doubleValue()); + val = new Timestamp(((Date)val).getTime()); + } + }else if (ExcelDataType.DATE.dataType.equals(valType)) { + val = DateUtil.getJavaDate(((Double) val).doubleValue()); + } + } catch (Exception ex) { + log.info(new StringBuilder().append("Get cell value [").append(i).append(",").append(column) + .append("] error: ").append(ex.toString()).toString()); + + val = null; + } + Reflections.invokeSetter(e, entityName, val); + } + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + dataList.add(e); + } + return dataList; + } + + /** + * 获取导入数据列表 + * + * @param headerMap + * 表头对象LinkedHashMap 字段名,字段类型 + * @param macroMap + * 字典对象Map 字段名,字典类型 + * @return + * @throws InstantiationException + * @throws IllegalAccessException + * List> + */ + public List> getDataList(Map headerMap, Map macroMap) { + List dataList = new ArrayList(); + for (int i = getDataRowNum(); i < getLastDataRowNum(); i++) { + int column = 0; + Row row = getRow(i); + StringBuilder sb = new StringBuilder(); + Map valMap = new HashMap(headerMap.size()); + for (String entityName : headerMap.keySet()) { + Object val = getCellValue(row, column++); + if (val != null) { + String valType = headerMap.get(entityName); + try { + if (ExcelDataType.STRING.dataType.equals(valType)) { + String s = String.valueOf(val.toString()); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + val = String.valueOf(val.toString()); + } + } else if (ExcelDataType.INTEGER.dataType.equals(valType)) { + val = Integer.valueOf(Double.valueOf(val.toString()).intValue()); + } else if (ExcelDataType.LONG.dataType.equals(valType)) { + val = Long.valueOf(Double.valueOf(val.toString()).longValue()); + } else if (ExcelDataType.DOUBLE.dataType.equals(valType)) { + val = Double.valueOf(val.toString()); + } else if (ExcelDataType.BIG_DECIMAL.dataType.equals(valType)) { + val = BigDecimal.valueOf(Double.valueOf(val.toString())); + } else if (ExcelDataType.FLOAT.dataType.equals(valType)) { + val = Float.valueOf(val.toString()); + } else if (ExcelDataType.DATE.dataType.equals(valType)) { + val = DateUtil.getJavaDate(((Double) val).doubleValue()); + } + } catch (Exception ex) { + log.debug(new StringBuilder().append("Get cell value [").append(i).append(",").append(column) + .append("] error: ").append(ex.toString()).toString()); + + val = ""; + } + } + valMap.put(entityName, val); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + dataList.add(valMap); + log.debug(new StringBuilder().append("Read success: [").append(i).append("] ").append(sb.toString()) + .toString()); + } + return dataList; + } + + /** + * 获取导入数据列表 + * + * @param headerList + * 数据字段集合 + * @return + * @throws InstantiationException + * @throws IllegalAccessException + * List> + */ + public List> getDataList(List headerList) { + List dataList = new ArrayList(); + for (int i = getDataRowNum(); i < getLastDataRowNum(); i++) { + int column = 0; + Row row = getRow(i); + StringBuilder sb = new StringBuilder(); + Map valMap = new HashMap(headerList.size()); + for (String key : headerList) { + Object val = getCellValue(row, column++); + try { + val = String.valueOf(val.toString()); + } catch (Exception ex) { + log.debug(new StringBuilder().append("Get cell value [").append(i).append(",").append(column) + .append("] error: ").append(ex.toString()).toString()); + + val = ""; + } + valMap.put(key, val.toString()); + sb.append(new StringBuilder().append(val).append(", ").toString()); + } + dataList.add(valMap); + log.debug(new StringBuilder().append("Read success: [").append(i).append("] ").append(sb.toString()) + .toString()); + } + return dataList; + } + + /** + * 导入测试 + */ + public static void main(String[] args) throws Throwable { + ExcelImport ei = new ExcelImport("C:\\Users\\Re11a\\Desktop\\scrap.xlsx", 1); + for (int i = 0; i < ei.getLastDataRowNum(); i++) { + Row row = ei.getRow(i); + for (int j = ei.getDataRowNum(); j < ei.getLastCellNum(); j++) { + Object val = ei.getCellValue(row, j); + System.out.print(val + ", "); + } + System.out.print("\n"); + } + } +} diff --git a/src/main/java/com/smart/common/util/GuavaCacheUtil.java b/src/main/java/com/smart/common/util/GuavaCacheUtil.java new file mode 100644 index 0000000..48c6b70 --- /dev/null +++ b/src/main/java/com/smart/common/util/GuavaCacheUtil.java @@ -0,0 +1,57 @@ +package com.smart.common.util; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * 本地缓存工具类 + */ +public class GuavaCacheUtil { + + public static final String TOKEN_PREFIX = "token_"; // 缓存键的前缀,用于区分不同类型的缓存 + + /** + * 使用 Guava 缓存库创建一个本地缓存,过期时间为12小时,并采用LRU算法进行缓存管理 + */ + private static LoadingCache localCache = + CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000) + .expireAfterAccess(2, TimeUnit.HOURS) + .build(new CacheLoader() { + @Override + public String load(String key) { + return ""; // 在缓存中找不到对应值时的加载行为,默认返回空字符串 + } + }); + + /** + * 设置缓存 + * + * @param key 缓存键 + * @param value 缓存值 + */ + public static void setKey(String key, String value) { + key = TOKEN_PREFIX + key; // 添加缓存键前缀,用于区分不同类型的缓存 + localCache.put(key, value); // 将键值对放入缓存中 + } + + /** + * 获取缓存 + * + * @param key 缓存键 + * @return 缓存值 + */ + public static String getKey(String key) { + String value = null; + try { + key = TOKEN_PREFIX + key; // 添加缓存键前缀,用于区分不同类型的缓存 + value = localCache.get(key); // 根据键从缓存中获取对应的值 + } catch (ExecutionException e) { + e.printStackTrace(); + } + return value; // 返回缓存值 + } +} diff --git a/src/main/java/com/smart/common/util/IPUtils.java b/src/main/java/com/smart/common/util/IPUtils.java new file mode 100644 index 0000000..c7cc7ea --- /dev/null +++ b/src/main/java/com/smart/common/util/IPUtils.java @@ -0,0 +1,54 @@ +package com.smart.common.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * 获取客户端IP地址的工具类 + */ +public class IPUtils { + + private static Logger logger = LoggerFactory.getLogger(IPUtils.class); + + /** + * 获取客户端IP地址 + * 如果使用了反向代理软件如 Nginx,使用 request.getRemoteAddr() 方法无法获取真实的客户端IP地址 + * 在多级反向代理的情况下,X-Forwarded-For 头部包含了一串 IP 地址,其中第一个非 unknown 的 IP 地址即为真实IP地址 + */ + public static String getIpAddr() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String ip = null; + try { + ip = request.getHeader("x-forwarded-for"); // 获取 X-Forwarded-For 头部的值,可能包含多个 IP 地址 + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); // 获取 Proxy-Client-IP 头部的值 + } + if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); // 获取 WL-Proxy-Client-IP 头部的值 + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); // 获取 HTTP_CLIENT_IP 头部的值 + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); // 获取 HTTP_X_FORWARDED_FOR 头部的值 + } + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); // 获取客户端的真实 IP 地址 + } + } catch (Exception e) { + logger.error("IPUtils ERROR ", e); // 输出错误日志 + } + // 如果使用了代理,则获取第一个IP地址 + if (!StringUtils.isEmpty(ip) && ip.length() > 15) { + if (ip.indexOf(",") > 0) { + ip = ip.substring(0, ip.indexOf(",")); // 如果包含多个 IP 地址,则获取第一个IP地址 + } + } + return ip; // 返回获取到的客户端IP地址 + } +} diff --git a/src/main/java/com/smart/common/util/MD5Utils.java b/src/main/java/com/smart/common/util/MD5Utils.java new file mode 100644 index 0000000..db7f12b --- /dev/null +++ b/src/main/java/com/smart/common/util/MD5Utils.java @@ -0,0 +1,41 @@ +package com.smart.common.util; + +import org.apache.shiro.crypto.hash.SimpleHash; +import org.apache.shiro.util.ByteSource; + +/** + * MD5加密工具类 + */ +public class MD5Utils { + + private static final String SALT = "123qwedcxzaq"; // 盐值,用于增加密码的复杂度 + + private static final String ALGORITHM_NAME = "md5"; // 加密算法名称 + + private static final int HASH_ITERATIONS = 2; // 迭代次数,增加加密强度 + + /** + * 使用MD5算法对密码进行加密 + * @param password 待加密的密码 + * @return 加密后的密码 + */ + public static String encrypt(String password) { + String newPassword = new SimpleHash(ALGORITHM_NAME, password, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex(); + return newPassword; + } + + /** + * 使用MD5算法对密码进行加密,使用用户名作为盐值 + * @param username 用户名 + * @param password 待加密的密码 + * @return 加密后的密码 + */ + public static String encrypt(String username, String password) { + String newPassword = new SimpleHash(ALGORITHM_NAME, password, ByteSource.Util.bytes(username + SALT), HASH_ITERATIONS).toHex(); + return newPassword; + } + + public static void main(String[] args) { + System.out.println(encrypt("admin","admin")); // 打印示例调用,使用默认的盐值和迭代次数进行密码加密 + } +} diff --git a/src/main/java/com/smart/common/util/OrderUtils.java b/src/main/java/com/smart/common/util/OrderUtils.java new file mode 100644 index 0000000..28212cf --- /dev/null +++ b/src/main/java/com/smart/common/util/OrderUtils.java @@ -0,0 +1,38 @@ +package com.smart.common.util; + +import org.apache.commons.lang3.RandomStringUtils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 订单编码生成器,生成26位数字编码, + * 生成规则:1位支付类型 + 17位时间戳 + 8位随机数字 + */ +public class OrderUtils { + + /** + * 生成时间戳 + */ + private static String getDateTime() { + DateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); // 设置日期格式为年月日时分秒毫秒 + return sdf.format(new Date()); // 获取当前时间并转换为指定格式的字符串 + } + + /** + * 生成订单单号编码 + */ + public static String getOrderNo() { + return getDateTime() + RandomStringUtils.randomNumeric(8); // 生成17位时间戳 + 8位随机数字的订单编码 + } + + /** + * 生成订单单号编码,带有园区管理ID + * @param parkManageId 园区管理ID + * @return 带有园区管理ID的订单编码 + */ + public static String getOrderNo(Long parkManageId) { + return parkManageId + getDateTime() + RandomStringUtils.randomNumeric(8); // 生成带有园区管理ID的订单编码 + } +} diff --git a/src/main/java/com/smart/common/util/Reflections.java b/src/main/java/com/smart/common/util/Reflections.java new file mode 100644 index 0000000..508b4ee --- /dev/null +++ b/src/main/java/com/smart/common/util/Reflections.java @@ -0,0 +1,315 @@ +package com.smart.common.util; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.Assert; + +import java.lang.reflect.*; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + */ +public class Reflections { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(Reflections.class); + + /** + * 调用Getter方法. 支持多级,如:对象名.对象名.方法 + */ + public static Object invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, + new Object[] {}); + } + return object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, + Object value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, + new Object[] {}); + } else { + String setterMethodName = SETTER_PREFIX + + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, + new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + public static Object getFieldValue(final Object obj, final String fieldName) { + Field field = getAccessibleField(obj, fieldName); + + if (field == null) { + throw new IllegalArgumentException("Could not find field [" + + fieldName + "] on target [" + obj + "]"); + } + + Object result = null; + try { + result = field.get(obj); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, + final Object value) { + Field field = getAccessibleField(obj, fieldName); + + if (field == null) { + throw new IllegalArgumentException("Could not find field [" + + fieldName + "] on target [" + obj + "]"); + } + + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常:{}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. 同时匹配方法名+参数类型, + */ + public static Object invokeMethod(final Object obj, + final String methodName, final Class[] parameterTypes, + final Object[] args) { + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + throw new IllegalArgumentException("Could not find method [" + + methodName + "] on target [" + obj + "]"); + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + public static Object invokeMethodByName(final Object obj, + final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName); + if (method == null) { + return ""; + } + + try { + return method.invoke(obj, args); + } catch (Exception e) { + throw convertReflectionExceptionToUnchecked(e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, + final String fieldName) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass + .getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } catch (NoSuchFieldException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... + * args) + */ + public static Method getAccessibleMethod(final Object obj, + final String methodName, final Class... parameterTypes) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType + .getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, + parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + // Method不在当前类定义,继续向上转型 + continue;// new add + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null. 只匹配函数名。 + * + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... + * args) + */ + public static Method getAccessibleMethodByName(final Object obj, + final String methodName) { + Validate.notNull(obj, "object can't be null"); + Validate.notBlank(methodName, "methodName can't be blank"); + + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType + .getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName)) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier + .isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) + || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier + .isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 如无法找到, 返回Object.class. eg. + * public UserDao extends HibernateDao + * + * @param clazz + * The class to introspect + * @return the first generic declaration, or Object.class if cannot be + * determined + */ + public static Class getClassGenricType(final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class. + * + * 如public UserDao extends HibernateDao + * + * @param clazz + * clazz The class to introspect + * @param index + * the Index of the generic ddeclaration,start from 0. + * @return the index generic declaration, or Object.class if cannot be + * determined + */ + public static Class getClassGenricType(final Class clazz, final int index) { + + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + logger.warn(clazz.getSimpleName() + + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + logger.warn("Index: " + index + ", Size of " + + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + logger.warn(clazz.getSimpleName() + + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) { + Assert.notNull(instance, "Instance must not be null"); + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked( + Exception e) { + if (e instanceof IllegalAccessException + || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException( + ((InvocationTargetException) e).getTargetException()); + } else if (e instanceof RuntimeException) { + return (RuntimeException) e; + } + return new RuntimeException("Unexpected Checked Exception.", e); + } +} diff --git a/src/main/java/com/smart/common/util/ShiroUtils.java b/src/main/java/com/smart/common/util/ShiroUtils.java new file mode 100644 index 0000000..056a497 --- /dev/null +++ b/src/main/java/com/smart/common/util/ShiroUtils.java @@ -0,0 +1,86 @@ +package com.smart.common.util; + +import com.smart.module.sys.entity.SysUser; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; + +/** + * Shiro工具类 + */ +public class ShiroUtils { + + /** + * 获取当前会话的 Session 对象 + * @return 当前会话的 Session 对象 + */ + public static Session getSession() { + return SecurityUtils.getSubject().getSession(); + } + + /** + * 获取当前用户的 Subject 对象 + * @return 当前用户的 Subject 对象 + */ + public static Subject getSubject() { + return SecurityUtils.getSubject(); + } + + /** + * 获取当前用户的 SysUser 对象 + * @return 当前用户的 SysUser 对象 + */ + public static SysUser getUserEntity() { + return (SysUser) SecurityUtils.getSubject().getPrincipal(); + } + + /** + * 获取当前用户的 ID + * @return 当前用户的 ID + */ + public static Long getUserId() { + return getUserEntity().getUserId(); + } + + /** + * 设置会话属性 + * @param key 属性键 + * @param value 属性值 + */ + public static void setSessionAttribute(Object key, Object value) { + getSession().setAttribute(key, value); + } + + /** + * 获取会话属性值 + * @param key 属性键 + * @return 属性值 + */ + public static Object getSessionAttribute(Object key) { + return getSession().getAttribute(key); + } + + /** + * 检查当前用户是否已登录 + * @return 如果已登录,返回 true;否则,返回 false + */ + public static boolean isLogin() { + return SecurityUtils.getSubject().getPrincipal() != null; + } + + /** + * 登出当前用户 + */ + public static void logout() { + SecurityUtils.getSubject().logout(); + } + + /** + * 检查当前用户是否拥有指定角色 + * @param roleSign 角色账号 + * @return 如果拥有指定角色,返回 true;否则,返回 false + */ + public static boolean isHasRole(String roleSign) { + return SecurityUtils.getSubject().hasRole(roleSign); + } +} diff --git a/src/main/java/com/smart/common/util/SpringUtils.java b/src/main/java/com/smart/common/util/SpringUtils.java new file mode 100644 index 0000000..5a22845 --- /dev/null +++ b/src/main/java/com/smart/common/util/SpringUtils.java @@ -0,0 +1,94 @@ +package com.smart.common.util; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * 工具类 + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor { + + /** + * Spring应用上下文环境的 Bean 工厂 + */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { + SpringUtils.beanFactory = beanFactory; + } + + /** + * 获取对象 + * + * @param name Bean 的名称 + * @return Object 对应名称的 Bean 实例 + * @throws org.springframework.beans.BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为 requiredType 的对象 + * + * @param clz Bean 的类型 + * @return 对应类型的 Bean 实例 + * @throws org.springframework.beans.BeansException + */ + public static T getBean(Class clz) throws BeansException { + @SuppressWarnings("unchecked") + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 判断 Bean 工厂中是否包含给定名称的 Bean + * + * @param name Bean 的名称 + * @return boolean 如果包含给定名称的 Bean,则返回 true + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断给定名称的 Bean 是否是单例 + * + * @param name Bean 的名称 + * @return boolean 如果指定名称的 Bean 是单例,则返回 true + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * 获取给定名称的 Bean 的类型 + * + * @param name Bean 的名称 + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 获取给定名称的 Bean 的别名 + * + * @param name Bean 的名称 + * @return String[] Bean 的别名数组 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + +} diff --git a/src/main/java/com/smart/common/util/SslUtils.java b/src/main/java/com/smart/common/util/SslUtils.java new file mode 100644 index 0000000..5616c13 --- /dev/null +++ b/src/main/java/com/smart/common/util/SslUtils.java @@ -0,0 +1,58 @@ +package com.smart.common.util; + +import javax.net.ssl.*; +import java.security.cert.X509Certificate; + +/** + * 开发环境 忽略 SSL 验证 + */ +public class SslUtils { + + private static void trustAllHttpsCertificates() throws Exception { + TrustManager[] trustAllCerts = new TrustManager[1]; + TrustManager tm = new miTM(); + trustAllCerts[0] = tm; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, null); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } + + static class miTM implements TrustManager,X509TrustManager { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public boolean isServerTrusted(X509Certificate[] certs) { + return true; + } + + public boolean isClientTrusted(X509Certificate[] certs) { + return true; + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + return; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + return; + } + } + + /** + * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用 + * @throws Exception + */ + public static void ignoreSsl() throws Exception{ + HostnameVerifier hv = (urlHostName, session) -> { + System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost()); + return true; + }; + trustAllHttpsCertificates(); + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } + +} \ No newline at end of file diff --git a/src/main/java/com/smart/module/sys/entity/SysConfig.java b/src/main/java/com/smart/module/sys/entity/SysConfig.java new file mode 100644 index 0000000..4a15759 --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysConfig.java @@ -0,0 +1,67 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * sys_config 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_config") // JPA注解,映射到数据库中的sys_config表 +public class SysConfig extends PageBean implements Serializable { + + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "id", nullable = false, length = 20) // JPA注解,映射到数据库表的id字段,非空且长度为20 + private Long id; + + /** + * 键 + */ + @Column(name = "config_key", length = 50) // JPA注解,映射到数据库表的config_key字段,长度为50 + private String key; + + /** + * 值 + */ + @Column(name = "config_value", length = 2000) // JPA注解,映射到数据库表的config_value字段,长度为2000 + private String value; + + /** + * 状态,0:隐藏 1:显示 + */ + @Column(name = "status", length = 4) // JPA注解,映射到数据库表的status字段,长度为4 + private Short status; + + /** + * 备注 + */ + @Column(name = "config_remark", length = 500) // JPA注解,映射到数据库表的config_remark字段,长度为500 + private String remark; + + /** + * 创建时间 + */ + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,用于日期格式的转换 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,用于日期格式的转换 + private Timestamp gmtModified; + + /** + * 创建用户id + */ + @Column(name = "user_id_create") // JPA注解,映射到数据库表的user_id_create字段 + private Long userIdCreate; +} diff --git a/src/main/java/com/smart/module/sys/entity/SysInterface.java b/src/main/java/com/smart/module/sys/entity/SysInterface.java new file mode 100644 index 0000000..343b9de --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysInterface.java @@ -0,0 +1,79 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * sys_interface 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_interface") // JPA注解,映射到数据库中的sys_interface表 +public class SysInterface extends PageBean implements Serializable { + + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + private Long id; + + /** + * 类型 + */ + @Column(name = "type") // JPA注解,映射到数据库表的type字段 + private String type; + + /** + * 描述 + */ + @Column(name = "description") // JPA注解,映射到数据库表的description字段 + private String description; + + /** + * 验证Token + */ + @Column(name = "token") // JPA注解,映射到数据库表的token字段 + private String token; + + /** + * 参数 + */ + @Column(name = "params") // JPA注解,映射到数据库表的params字段 + private String params; + + /** + * 状态,0:禁用 1:启用 + */ + @Column(name = "status") // JPA注解,映射到数据库表的status字段 + private Short status; + + /** + * 查询语句 + */ + @Lob // JPA注解,表示将该字段映射为大型对象类型 + @Column(name = "query", columnDefinition="TEXT") // JPA注解,映射到数据库表的query字段,并指定为TEXT类型 + private String query; + + /** + * 创建用户id + */ + @Column(name = "user_id_create") // JPA注解,映射到数据库表的user_id_create字段 + private Long userIdCreate; + + /** + * 创建时间 + */ + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,用于日期格式的转换 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,用于日期格式的转换 + private Timestamp gmtModified; +} diff --git a/src/main/java/com/smart/module/sys/entity/SysLandingRecords.java b/src/main/java/com/smart/module/sys/entity/SysLandingRecords.java new file mode 100644 index 0000000..2d4d5bd --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysLandingRecords.java @@ -0,0 +1,55 @@ +package com.smart.module.sys.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * sys_landing_records 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_landing_records") // JPA注解,映射到数据库中的sys_landing_records表 +public class SysLandingRecords implements Serializable { + + /** + * 主键 + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "id", nullable = false, length = 11) // JPA注解,映射到数据库表的id字段,设置非空约束和长度限制 + private Integer id; + + /** + * 用户ID + */ + @Column(name = "user_id", nullable = false, length = 20) // JPA注解,映射到数据库表的user_id字段,设置非空约束和长度限制 + private Long userId; + + /** + * 最近登录时间 + */ + @Column(name = "login_date", nullable = false) // JPA注解,映射到数据库表的login_date字段,设置非空约束 + private Timestamp loginDate; + + /** + * 最近登录地点 + */ + @Column(name = "place", nullable = false, length = 10) // JPA注解,映射到数据库表的place字段,设置非空约束和长度限制 + private String place; + + /** + * 最近登录IP + */ + @Column(name = "ip", nullable = false, length = 15) // JPA注解,映射到数据库表的ip字段,设置非空约束和长度限制 + private String ip; + + /** + * 登录方式 + */ + @Column(name = "login_way", nullable = false, length = 10) // JPA注解,映射到数据库表的login_way字段,设置非空约束和长度限制 + private String loginWay; + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysLog.java b/src/main/java/com/smart/module/sys/entity/SysLog.java new file mode 100644 index 0000000..36696fb --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysLog.java @@ -0,0 +1,93 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * sys_log 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_log") // JPA注解,映射到数据库中的sys_log表 +public class SysLog extends PageBean implements Serializable { + + /** + * 主键 + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "id", nullable = false, length = 20) // JPA注解,映射到数据库表的id字段,设置非空约束和长度限制 + private Long id; + + /** + * 用户id + */ + @Column(name = "user_id", length = 20) // JPA注解,映射到数据库表的user_id字段,设置长度限制 + private Long userId; + + /** + * 用户名 + */ + @Column(name = "username", length = 50) // JPA注解,映射到数据库表的username字段,设置长度限制 + private String username; + + /** + * 用户操作 + */ + @Column(name = "operation", length = 50) // JPA注解,映射到数据库表的operation字段,设置长度限制 + private String operation; + + /** + * 响应时间 + */ + @Column(name = "time", length = 11) // JPA注解,映射到数据库表的time字段,设置长度限制 + private Integer time; + + /** + * 请求方法 + */ + @Column(name = "method", length = 200) // JPA注解,映射到数据库表的method字段,设置长度限制 + private String method; + + /** + * 请求参数 + */ + @Column(name = "params") // JPA注解,映射到数据库表的params字段 + private String params; + + /** + * IP地址 + */ + @Column(name = "ip", length = 64) // JPA注解,映射到数据库表的ip字段,设置长度限制 + private String ip; + + /** + * 访问方式 0:PC 1:手机 2:未知 + */ + @Column(name = "device_type", nullable = false, length = 4) // JPA注解,映射到数据库表的device_type字段,设置非空约束和长度限制 + private Short deviceType; + + /** + * 类型 0: 一般日志记录 1: 异常错误日志 + */ + @Column(name = "log_type", nullable = false, length = 4) // JPA注解,映射到数据库表的log_type字段,设置非空约束和长度限制 + private Short logType; + + /** + * 异常详细信息 + */ + @Column(name = "exception_detail") // JPA注解,映射到数据库表的exception_detail字段 + private String exceptionDetail; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + private Timestamp gmtCreate; +} diff --git a/src/main/java/com/smart/module/sys/entity/SysMenu.java b/src/main/java/com/smart/module/sys/entity/SysMenu.java new file mode 100644 index 0000000..490c5f5 --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysMenu.java @@ -0,0 +1,95 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.List; + +/** + * sys_menu 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_menu") // JPA注解,映射到数据库中的sys_menu表 +public class SysMenu extends PageBean implements Serializable { + + /** + * 菜单id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "menu_id", nullable = false, length = 20) // JPA注解,映射到数据库表的menu_id字段,设置非空约束和长度限制 + private Long menuId; + + /** + * 父菜单ID,一级菜单为0 + */ + @Column(name = "parent_id", length = 20) // JPA注解,映射到数据库表的parent_id字段,设置长度限制 + private Long parentId; + + /** + * 菜单名称 + */ + @Column(name = "name", length = 50) // JPA注解,映射到数据库表的name字段,设置长度限制 + private String name; + + /** + * 菜单URL + */ + @Column(name = "url", length = 200) // JPA注解,映射到数据库表的url字段,设置长度限制 + private String url; + + /** + * 授权(多个用逗号分隔,如:user:list,user:create) + */ + @Column(name = "perms", length = 500) // JPA注解,映射到数据库表的perms字段,设置长度限制 + private String perms; + + /** + * 类型 0:目录 1:菜单 2:按钮 + */ + @Column(name = "type", length = 11) // JPA注解,映射到数据库表的type字段,设置长度限制 + private Integer type; + + /** + * 菜单图标 + */ + @Column(name = "icon", length = 50) // JPA注解,映射到数据库表的icon字段,设置长度限制 + private String icon; + + /** + * 排序 + */ + @Column(name = "order_num", length = 11) // JPA注解,映射到数据库表的order_num字段,设置长度限制 + private Integer orderNum; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + private Timestamp gmtModified; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private Boolean spread = false; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private String parentName; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private Boolean isParent; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private List list; + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysOrg.java b/src/main/java/com/smart/module/sys/entity/SysOrg.java new file mode 100644 index 0000000..2ad2a8c --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysOrg.java @@ -0,0 +1,104 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; + +/** + * sys_org 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_org") // JPA注解,映射到数据库中的sys_org表 +public class SysOrg extends PageBean implements Serializable { + + /** + * 机构id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "org_id", nullable = false, length = 20) // JPA注解,映射到数据库表的org_id字段,设置非空约束和长度限制 + private Long orgId; + + /** + * 上级机构ID,一级机构为0 + */ + @Column(name = "parent_id", length = 20) // JPA注解,映射到数据库表的parent_id字段,设置长度限制 + private Long parentId; + + /** + * 机构编码 + */ + @Column(name = "code", length = 100) // JPA注解,映射到数据库表的code字段,设置长度限制 + private String code; + + /** + * 机构名称 + */ + @Column(name = "name", length = 100) // JPA注解,映射到数据库表的name字段,设置长度限制 + private String name; + + /** + * 机构名称(全称) + */ + @Column(name = "full_name", length = 100) // JPA注解,映射到数据库表的full_name字段,设置长度限制 + private String fullName; + + /** + * 机构负责人 + */ + @Column(name = "director", length = 100) // JPA注解,映射到数据库表的director字段,设置长度限制 + private String director; + + /** + * 联系邮箱 + */ + @Column(name = "email", length = 100) // JPA注解,映射到数据库表的email字段,设置长度限制 + private String email; + + /** + * 联系电话 + */ + @Column(name = "phone", length = 100) // JPA注解,映射到数据库表的phone字段,设置长度限制 + private String phone; + + /** + * 地址 + */ + @Column(name = "address", length = 200) // JPA注解,映射到数据库表的address字段,设置长度限制 + private String address; + + /** + * 排序 + */ + @Column(name = "order_num", length = 11) // JPA注解,映射到数据库表的order_num字段,设置长度限制 + private Integer orderNum; + + /** + * 可用标识 1:可用 0:不可用 + */ + @Column(name = "status", length = 4) // JPA注解,映射到数据库表的status字段,设置长度限制 + private Short status; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + private Timestamp gmtModified; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private String parentName; + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysRole.java b/src/main/java/com/smart/module/sys/entity/SysRole.java new file mode 100644 index 0000000..6ef0bd9 --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysRole.java @@ -0,0 +1,78 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.List; + +/** + * sys_role 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_role") // JPA注解,映射到数据库中的sys_role表 +public class SysRole extends PageBean implements Serializable { + + /** + * 角色id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "role_id", nullable = false, length = 20) // JPA注解,映射到数据库表的role_id字段,设置非空约束和长度限制 + private Long roleId; + + /** + * 所属机构 + */ + @Column(name = "org_id") // JPA注解,映射到数据库表的org_id字段 + private Long orgId; + + /** + * 角色名称 + */ + @Column(name = "role_name", length = 100) // JPA注解,映射到数据库表的role_name字段,设置长度限制 + private String roleName; + + /** + * 角色账号 + */ + @Column(name = "role_sign", length = 100) // JPA注解,映射到数据库表的role_sign字段,设置长度限制 + private String roleSign; + + /** + * 备注 + */ + @Column(name = "remark", length = 100) // JPA注解,映射到数据库表的remark字段,设置长度限制 + private String remark; + + /** + * 创建用户id + */ + @Column(name = "user_id_create") // JPA注解,映射到数据库表的user_id_create字段 + private Long userIdCreate; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定时间格式化的模式和时区 + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + private Timestamp gmtModified; + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private List menuIdList; // 角色拥有的菜单ID列表 + + @Transient // JPA注解,表示该属性不映射到数据库表中 + private List orgIdList; // 角色所属机构ID列表 + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysRoleMenu.java b/src/main/java/com/smart/module/sys/entity/SysRoleMenu.java new file mode 100644 index 0000000..b43924e --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysRoleMenu.java @@ -0,0 +1,36 @@ +package com.smart.module.sys.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * sys_role_menu 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_role_menu") // JPA注解,映射到数据库中的sys_role_menu表 +public class SysRoleMenu implements Serializable { + + /** + * 记录id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "id", nullable = false, length = 20) // JPA注解,映射到数据库表的id字段,设置非空约束和长度限制 + private Long id; // 记录的id + + /** + * 角色ID + */ + @Column(name = "role_id", length = 20) // JPA注解,映射到数据库表的role_id字段,设置长度限制 + private Long roleId; // 角色的id + + /** + * 菜单ID + */ + @Column(name = "menu_id", length = 20) // JPA注解,映射到数据库表的menu_id字段,设置长度限制 + private Long menuId; // 菜单的id + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysRoleOrg.java b/src/main/java/com/smart/module/sys/entity/SysRoleOrg.java new file mode 100644 index 0000000..fe68f5f --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysRoleOrg.java @@ -0,0 +1,36 @@ +package com.smart.module.sys.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * sys_role_org 实体类 + */ +@Data +@Entity +@Table(name = "sys_role_org") +public class SysRoleOrg implements Serializable{ + /** + * 记录id + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false, length = 20) + private Long id; + + /** + * 角色ID + */ + @Column(name = "role_id", length = 20) + private Long roleId; + + /** + * 机构ID + */ + @Column(name = "org_id", length = 20) + private Long orgId; + +} + diff --git a/src/main/java/com/smart/module/sys/entity/SysUser.java b/src/main/java/com/smart/module/sys/entity/SysUser.java new file mode 100644 index 0000000..c30cc6d --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysUser.java @@ -0,0 +1,132 @@ +package com.smart.module.sys.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.smart.common.model.PageBean; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.List; + +/** + * sys_user 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_user") // JPA注解,映射到数据库中的sys_user表 +public class SysUser extends PageBean implements Serializable { + + /** + * 用户id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "user_id", nullable = false, length = 20) // JPA注解,映射到数据库表的user_id字段,设置非空约束和长度限制 + private Long userId; + + /** + * 所属机构 + */ + @Column(name = "org_id", nullable = false, length = 20) // JPA注解,映射到数据库表的org_id字段,设置非空约束和长度限制 + private Long orgId; + + /** + * 用户名 + */ + @Column(name = "username", nullable = false, length = 50) // JPA注解,映射到数据库表的username字段,设置非空约束和长度限制 + private String username; + + /** + * 密码 + */ + @Column(name = "password", nullable = false, length = 50) // JPA注解,映射到数据库表的password字段,设置非空约束和长度限制 + private String password; + + /** + * 姓名(昵称) + */ + @Column(name = "nickname", length = 50) // JPA注解,映射到数据库表的nickname字段,设置长度限制 + private String nickname; + + /** + * 邮箱 + */ + @Column(name = "email", length = 100) // JPA注解,映射到数据库表的email字段,设置长度限制 + private String email; + + /** + * 手机号 + */ + @Column(name = "mobile", length = 100) // JPA注解,映射到数据库表的mobile字段,设置长度限制 + private String mobile; + + /** + * 状态 0:禁用,1:正常 + */ + @Column(name = "status", length = 4) // JPA注解,映射到数据库表的status字段,设置长度限制 + private Short status; + + /** + * 头像上传 0:未上传 1:上传 + */ + @Column(name = "avatar_status", nullable = false, length = 4) // JPA注解,映射到数据库表的avatar_status字段,设置非空约束和长度限制 + private Short avatarStatus; + + /** + * 备注 + */ + @Column(name = "remark", length = 500) // JPA注解,映射到数据库表的remark字段,设置长度限制 + private String remark; + + /** + * 创建用户id + */ + @Column(name = "user_id_create") // JPA注解,映射到数据库表的user_id_create字段 + private Long userIdCreate; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定日期格式和时区 + @Column(name = "gmt_create") // JPA注解,映射到数据库表的gmt_create字段 + private Timestamp gmtCreate; + + /** + * 修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // Jackson注解,指定日期格式和时区 + @Column(name = "gmt_modified") // JPA注解,映射到数据库表的gmt_modified字段 + private Timestamp gmtModified; + + /** + * 是否修改过初始密码 + */ + @Column(name = "is_modify_pwd", length = 4) // JPA注解,映射到数据库表的is_modify_pwd字段,设置长度限制 + private Short isModifyPwd; + + /** + * 机构名称 + */ + @Transient // JPA注解,表示该属性不会映射到数据库表 + private String orgName; + + /** + * 角色ID + */ + @Transient // JPA注解,表示该属性不会映射到数据库表 + private List roleIdList; + + /** + * 角色名称 + */ + @Transient // JPA注解,表示该属性不会映射到数据库表 + private List roleNameList; + + /** + * 旧密码 + */ + @Transient // JPA注解,表示该属性不会映射到数据库表 + private String oldPassword; + +} diff --git a/src/main/java/com/smart/module/sys/entity/SysUserRole.java b/src/main/java/com/smart/module/sys/entity/SysUserRole.java new file mode 100644 index 0000000..b530199 --- /dev/null +++ b/src/main/java/com/smart/module/sys/entity/SysUserRole.java @@ -0,0 +1,36 @@ +package com.smart.module.sys.entity; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * sys_user_role 实体类 + */ +@Data // Lombok注解,自动生成getter、setter等方法 +@Entity // JPA注解,表示该类是一个实体类 +@Table(name = "sys_user_role") // JPA注解,映射到数据库中的sys_user_role表 +public class SysUserRole implements Serializable { + + /** + * 记录id + */ + @Id // JPA注解,标识该字段为主键 + @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA注解,指定主键生成策略为自增长 + @Column(name = "id", nullable = false, length = 20) // JPA注解,映射到数据库表的id字段,设置非空约束和长度限制 + private Long id; + + /** + * 用户ID + */ + @Column(name = "user_id", length = 20) // JPA注解,映射到数据库表的user_id字段,设置长度限制 + private Long userId; + + /** + * 角色ID + */ + @Column(name = "role_id", length = 20) // JPA注解,映射到数据库表的role_id字段,设置长度限制 + private Long roleId; + +} diff --git a/src/main/java/com/smart/module/sys/repository/SysConfigRepository.java b/src/main/java/com/smart/module/sys/repository/SysConfigRepository.java new file mode 100644 index 0000000..e769a92 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysConfigRepository.java @@ -0,0 +1,14 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_user Repository +*/ +@Repository //CRUD +public interface SysConfigRepository extends JpaRepository { + +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysInterfaceRepository.java b/src/main/java/com/smart/module/sys/repository/SysInterfaceRepository.java new file mode 100644 index 0000000..3283394 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysInterfaceRepository.java @@ -0,0 +1,16 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysInterface; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_interface Repository +*/ +@Repository //CRUD +public interface SysInterfaceRepository extends JpaRepository { + + SysInterface getByType(String type); + +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysLandingRecordsRepository.java b/src/main/java/com/smart/module/sys/repository/SysLandingRecordsRepository.java new file mode 100644 index 0000000..b51b963 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysLandingRecordsRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysLandingRecords; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_landing_records Repository +*/ +@Repository //CRUD +public interface SysLandingRecordsRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysLogRepository.java b/src/main/java/com/smart/module/sys/repository/SysLogRepository.java new file mode 100644 index 0000000..07ac4ff --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysLogRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysLog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_log Repository +*/ +@Repository //CRUD +public interface SysLogRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysMenuRepository.java b/src/main/java/com/smart/module/sys/repository/SysMenuRepository.java new file mode 100644 index 0000000..5173048 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysMenuRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysMenu; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_menu Repository +*/ +@Repository //CRUD +public interface SysMenuRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysOrgRepository.java b/src/main/java/com/smart/module/sys/repository/SysOrgRepository.java new file mode 100644 index 0000000..e8a591b --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysOrgRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysOrg; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_org Repository +*/ +@Repository //CRUD +public interface SysOrgRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysRoleMenuRepository.java b/src/main/java/com/smart/module/sys/repository/SysRoleMenuRepository.java new file mode 100644 index 0000000..b5dcf01 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysRoleMenuRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysRoleMenu; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_role_menu Repository +*/ +@Repository //CRUD +public interface SysRoleMenuRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysRoleOrgRepository.java b/src/main/java/com/smart/module/sys/repository/SysRoleOrgRepository.java new file mode 100644 index 0000000..e0797ea --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysRoleOrgRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysRoleOrg; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_role_org Repository +*/ +@Repository //CRUD +public interface SysRoleOrgRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysRoleRepository.java b/src/main/java/com/smart/module/sys/repository/SysRoleRepository.java new file mode 100644 index 0000000..40e8b7d --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysRoleRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysRole; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_role Repository +*/ +@Repository //CRUD +public interface SysRoleRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysUserRepository.java b/src/main/java/com/smart/module/sys/repository/SysUserRepository.java new file mode 100644 index 0000000..b41f060 --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysUserRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysUser; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_user Repository +*/ +@Repository //CRUD +public interface SysUserRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/repository/SysUserRoleRepository.java b/src/main/java/com/smart/module/sys/repository/SysUserRoleRepository.java new file mode 100644 index 0000000..0f2aa7f --- /dev/null +++ b/src/main/java/com/smart/module/sys/repository/SysUserRoleRepository.java @@ -0,0 +1,13 @@ +package com.smart.module.sys.repository; + +import com.smart.module.sys.entity.SysUserRole; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * sys_user_role Repository +*/ +@Repository //CRUD +public interface SysUserRoleRepository extends JpaRepository { +} + diff --git a/src/main/java/com/smart/module/sys/service/SysConfigService.java b/src/main/java/com/smart/module/sys/service/SysConfigService.java new file mode 100644 index 0000000..a16d141 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysConfigService.java @@ -0,0 +1,45 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysConfig; + +/** + * 配置管理 + */ +public interface SysConfigService { + + /** + * 保存配置信息 + * @param config 配置对象 + * @return 返回保存结果 + */ + Result save(SysConfig config); + + /** + * 根据ID获取配置信息 + * @param id 配置ID + * @return 返回配置信息结果 + */ + Result get(Long id); + + /** + * 根据ID删除配置信息 + * @param id 配置ID + * @return 返回删除结果 + */ + Result delete(Long id); + + /** + * 获取配置信息列表 + * @param config 查询条件 + * @return 返回配置信息列表结果 + */ + Result list(SysConfig config); + + /** + * 根据配置键获取配置值 + * @param key 配置键 + * @return 返回配置值 + */ + Object getByKey(String key); +} diff --git a/src/main/java/com/smart/module/sys/service/SysInterfaceService.java b/src/main/java/com/smart/module/sys/service/SysInterfaceService.java new file mode 100644 index 0000000..5c3604a --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysInterfaceService.java @@ -0,0 +1,45 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysInterface; + +/** + * 接口管理 + */ +public interface SysInterfaceService { + + /** + * 保存接口信息 + * @param entity 接口对象 + * @return 返回保存结果 + */ + Result save(SysInterface entity); + + /** + * 根据ID获取接口信息 + * @param id 接口ID + * @return 返回接口信息结果 + */ + Result get(Long id); + + /** + * 根据ID删除接口信息 + * @param id 接口ID + * @return 返回删除结果 + */ + Result delete(Long id); + + /** + * 获取接口信息列表 + * @param entity 查询条件 + * @return 返回接口信息列表结果 + */ + Result list(SysInterface entity); + + /** + * 根据条件查询接口信息 + * @param entity 查询条件 + * @return 返回接口信息查询结果 + */ + Result query(SysInterface entity); +} diff --git a/src/main/java/com/smart/module/sys/service/SysLogService.java b/src/main/java/com/smart/module/sys/service/SysLogService.java new file mode 100644 index 0000000..d318fd6 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysLogService.java @@ -0,0 +1,23 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysLog; + +/** + * 日志管理 + */ +public interface SysLogService { + + /** + * 日志查询 + * @param log 日志对象,用于指定查询条件 + * @return 返回日志查询结果 + */ + Result list(SysLog log); + + /** + * 日志保存 + * @param log 日志对象,要保存的日志信息 + */ + void save(SysLog log); +} diff --git a/src/main/java/com/smart/module/sys/service/SysMenuService.java b/src/main/java/com/smart/module/sys/service/SysMenuService.java new file mode 100644 index 0000000..1b5699b --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysMenuService.java @@ -0,0 +1,48 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysMenu; + +import java.util.List; + +/** + * 菜单管理 + */ +public interface SysMenuService { + + /** + * 获取菜单列表 + * @param menu 菜单对象,用于指定查询条件 + * @return 返回菜单查询结果 + */ + Result list(SysMenu menu); + + /** + * 根据父菜单ID获取子菜单列表 + * @param parentId 父菜单ID + * @return 返回子菜单列表 + */ + List select(Long parentId); + + /** + * 根据菜单ID删除菜单 + * @param menuId 菜单ID + * @return 返回删除操作结果 + */ + Result delete(Long menuId); + + /** + * 根据用户ID获取菜单列表 + * @param userId 用户ID + * @return 返回菜单列表 + */ + List getByUserId(Long userId); + + /** + * 调整菜单顺序 + * @param parentId 父菜单ID + * @param menuId 菜单ID + * @return 返回调整顺序的操作结果 + */ + Result drop(Long parentId, Long menuId); +} diff --git a/src/main/java/com/smart/module/sys/service/SysOrgService.java b/src/main/java/com/smart/module/sys/service/SysOrgService.java new file mode 100644 index 0000000..10cfb83 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysOrgService.java @@ -0,0 +1,38 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysOrg; + +/** + * 机构管理 + */ +public interface SysOrgService { + + /** + * 获取机构列表 + * @param org 机构对象,用于指定查询条件 + * @return 返回机构查询结果 + */ + Result list(SysOrg org); + + /** + * 根据父机构ID获取子机构列表 + * @param parentId 父机构ID + * @return 返回子机构列表 + */ + Result select(Long parentId); + + /** + * 保存机构信息 + * @param org 机构对象 + * @return 返回保存操作结果 + */ + Result save(SysOrg org); + + /** + * 根据机构ID删除机构 + * @param orgId 机构ID + * @return 返回删除操作结果 + */ + Result delete(Long orgId); +} diff --git a/src/main/java/com/smart/module/sys/service/SysRoleService.java b/src/main/java/com/smart/module/sys/service/SysRoleService.java new file mode 100644 index 0000000..766980c --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysRoleService.java @@ -0,0 +1,65 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysRole; + +/** + * 角色管理 + */ +public interface SysRoleService { + + /** + * 获取角色列表 + * @param role 角色对象,用于指定查询条件 + * @return 返回角色查询结果 + */ + Result list(SysRole role); + + /** + * 获取所有角色列表 + * @return 返回所有角色列表 + */ + Result select(); + + /** + * 保存角色信息 + * @param role 角色对象 + * @return 返回保存操作结果 + */ + Result save(SysRole role); + + /** + * 根据角色ID删除角色 + * @param roleId 角色ID + * @return 返回删除操作结果 + */ + Result delete(Long roleId); + + /** + * 根据角色ID获取菜单列表 + * @param roleId 角色ID + * @return 返回菜单列表 + */ + Result getMenu(Long roleId); + + /** + * 保存角色菜单关联信息 + * @param role 角色对象 + * @return 返回保存操作结果 + */ + Result saveMenu(SysRole role); + + /** + * 根据角色ID获取机构列表 + * @param roleId 角色ID + * @return 返回机构列表 + */ + Result getOrg(Long roleId); + + /** + * 保存角色机构关联信息 + * @param role 角色对象 + * @return 返回保存操作结果 + */ + Result saveOrg(SysRole role); +} diff --git a/src/main/java/com/smart/module/sys/service/SysUserService.java b/src/main/java/com/smart/module/sys/service/SysUserService.java new file mode 100644 index 0000000..b382bc8 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/SysUserService.java @@ -0,0 +1,92 @@ +package com.smart.module.sys.service; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysUser; + +import java.util.List; + +/** + * 用户管理 + */ +public interface SysUserService { + + /** + * 保存用户 + * + * @param user 用户对象,包含需要保存的用户信息 + * @return 返回保存操作结果 + */ + Result save(SysUser user); + + /** + * 根据用户ID获取用户 + * + * @param userId 用户ID + * @return 返回查询到的用户信息 + */ + Result get(Long userId); + + /** + * 根据用户ID删除用户 + * + * @param userId 用户ID + * @return 返回删除操作结果 + */ + Result delete(Long userId); + + /** + * 根据用户名获取用户 + * + * @param username 用户名 + * @return 返回查询到的用户信息 + */ + SysUser getUser(String username); + + /** + * 用户列表 + * + * @param user 用户对象,用于指定查询条件 + * @return 返回用户查询结果 + */ + Result list(SysUser user); + + /** + * 根据用户ID获取用户角色 + * + * @param userId 用户ID + * @return 返回用户所拥有的角色列表 + */ + List listUserRoles(Long userId); + + /** + * 根据用户ID获取用户权限 + * + * @param userId 用户ID + * @return 返回用户所拥有的权限列表 + */ + List listUserPerms(Long userId); + + /** + * 更新用户密码 + * + * @param user 用户对象,包含需要更新的用户密码信息 + * @return 返回更新操作结果 + */ + Result updatePwd(SysUser user); + + /** + * 更新用户信息 + * + * @param user 用户对象,包含需要更新的用户信息 + * @return 返回更新操作结果 + */ + Result update(SysUser user); + + /** + * 根据角色获取用户信息 + * + * @param roleSign 角色账号 + * @return 返回符合角色账号的用户列表 + */ + List listUserByRole(String roleSign); +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysConfigServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..78871b7 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,121 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysConfig; +import com.smart.module.sys.repository.SysConfigRepository; +import com.smart.module.sys.service.SysConfigService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service // 表明该类是一个Service组件 +public class SysConfigServiceImpl implements SysConfigService { + + @Autowired // 自动装配DynamicQuery对象 + private DynamicQuery dynamicQuery; + @Autowired // 自动装配SysConfigRepository对象 + private SysConfigRepository sysConfigRepository; + + /** + * 保存配置信息 + * @param config + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) // 添加事务注解,当出现异常时进行回滚 + public Result save(SysConfig config) { + String nativeSql = "SELECT * FROM sys_config WHERE config_key=?"; // SQL查询语句,根据config_key查询配置信息 + SysConfig sysConfig = dynamicQuery.nativeQuerySingleResult( + SysConfig.class,nativeSql,new Object[]{config.getKey()}); // 使用DynamicQuery执行原生SQL查询语句得到结果 + if(sysConfig!=null){ + if(!config.getId().equals(sysConfig.getId())){ + return Result.error("配置键重复"); + } + }else{ + config.setGmtCreate(DateUtils.getTimestamp()); // 设置配置信息的创建时间 + } + config.setUserIdCreate(ShiroUtils.getUserId()); // 设置创建配置信息的用户ID + sysConfigRepository.saveAndFlush(config); // 保存配置信息并立即刷新到数据库 + return Result.ok("保存成功"); + } + + /** + * 根据ID获取配置信息 + * @param id + * @return + */ + @Override + public Result get(Long id) { + SysConfig config = sysConfigRepository.getOne(id); // 根据ID从数据库中获取配置信息 + return Result.ok(config); + } + + /** + * 根据ID删除配置信息 + * @param id + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Result delete(Long id) { + sysConfigRepository.deleteById(id); // 根据ID删除配置信息 + return Result.ok("删除成功"); + } + + /** + * 获取配置信息列表 + * @param config + * @return + */ + @Override + public Result list(SysConfig config) { + String nativeSql = "SELECT COUNT(*) FROM sys_config "; // 统计配置信息的总数 + nativeSql += common(config); // 根据条件构建通用的SQL语句片段 + Long count = dynamicQuery.nativeQueryCount(nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到结果 + PageBean data = new PageBean<>(); + if(count>0){ + nativeSql = "SELECT * FROM sys_config "; // 查询配置信息列表 + nativeSql += common(config); // 根据条件构建通用的SQL语句片段 + nativeSql += "ORDER BY gmt_create desc"; // 按创建时间降序排序 + Pageable pageable = PageRequest.of(config.getPageNo(),config.getPageSize()); // 创建分页请求对象 + List list = dynamicQuery.nativeQueryPagingList(SysConfig.class,pageable,nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到分页结果 + data = new PageBean(list,count); // 构建分页数据对象 + } + return Result.ok(data); + } + + /** + * 配置键获取配置值 + * @param key + * @return + */ + @Override + public Object getByKey(String key) { + String nativeSql = "SELECT config_value FROM sys_config WHERE config_key=?"; // 根据config_key查询配置值 + return dynamicQuery.querySingleResult(nativeSql,new Object[]{key}); // 使用DynamicQuery执行原生SQL查询语句得到结果 + } + + /** + * 模糊查询 + * @param config + * @return + */ + public String common(SysConfig config){ + String description = config.getDescription(); // 获取配置信息的描述 + String commonSql = ""; + if(StringUtils.isNotBlank(description)){ + commonSql += "WHERE config_key like '"+description+"%' "; // 根据描述模糊查询config_key字段 + commonSql += "OR config_value like '"+description+"%' "; // 根据描述模糊查询config_value字段 + } + return commonSql; + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysInterfaceServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysInterfaceServiceImpl.java new file mode 100644 index 0000000..f9844cf --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysInterfaceServiceImpl.java @@ -0,0 +1,133 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.constant.SystemConstant; +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysInterface; +import com.smart.module.sys.repository.SysInterfaceRepository; +import com.smart.module.sys.service.SysInterfaceService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +@Service // 表明该类是一个Service组件 +public class SysInterfaceServiceImpl implements SysInterfaceService { + + @Autowired // 自动装配DynamicQuery对象 + private DynamicQuery dynamicQuery; + @Autowired // 自动装配SysInterfaceRepository对象 + private SysInterfaceRepository sysInterfaceRepository; + + /** + * 保存接口信息 + * @param entity + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) // 添加事务注解,当出现异常时进行回滚 + public Result save(SysInterface entity) { + String nativeSql = "SELECT * FROM sys_interface WHERE type=?"; + SysInterface sysInterface = dynamicQuery.nativeQuerySingleResult( + SysInterface.class,nativeSql,new Object[]{entity.getType()}); + if(sysInterface!=null){ + if(!entity.getId().equals(sysInterface.getId())){ + return Result.error("配置类型重复"); + } + }else{ + entity.setGmtCreate(DateUtils.getTimestamp()); // 设置接口信息的创建时间 + } + entity.setUserIdCreate(ShiroUtils.getUserId()); // 设置创建接口信息的用户ID + sysInterfaceRepository.saveAndFlush(entity); // 保存接口信息并立即刷新到数据库 + return Result.ok("保存成功"); + } + + /** + * 根据ID获取接口信息 + * @param id + * @return + */ + @Override + public Result get(Long id) { + SysInterface entity = sysInterfaceRepository.getOne(id); // 根据ID从数据库中获取接口信息 + return Result.ok(entity); + } + + /** + * 根据ID删除接口信息 + * @param id + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Result delete(Long id) { + sysInterfaceRepository.deleteById(id); // 根据ID删除接口信息 + return Result.ok("删除成功"); + } + + /** + * 获取接口信息列表 + * @param entity + * @return + */ + @Override + public Result list(SysInterface entity) { + String nativeSql = "SELECT COUNT(*) FROM sys_interface "; // 统计接口信息的总数 + nativeSql += common(entity); // 根据条件构建通用的SQL语句片段 + Long count = dynamicQuery.nativeQueryCount(nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到结果 + PageBean data = new PageBean<>(); + if(count>0){ + nativeSql = "SELECT * FROM sys_interface "; // 查询接口信息列表 + nativeSql += common(entity); // 根据条件构建通用的SQL语句片段 + nativeSql += "ORDER BY gmt_create desc"; // 按创建时间降序排序 + Pageable pageable = PageRequest.of(entity.getPageNo(),entity.getPageSize()); // 创建分页请求对象 + List list = dynamicQuery.nativeQueryPagingList(SysInterface.class,pageable,nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到分页结果 + data = new PageBean(list,count); // 构建分页数据对象 + } + return Result.ok(data); + } + + /** + * 根据接口类型获取接口数据 + * @param entity + * @return + */ + @Override + @Transactional(readOnly = true) + public Result query(SysInterface entity) { + SysInterface sysInterface = sysInterfaceRepository.getByType(entity.getType()); // 根据类型从数据库中获取接口信息 + if(sysInterface!=null){ + if(sysInterface.getStatus().shortValue() == SystemConstant.StatusType.ENABLE.getValue()){ // 判断接口是否启用 + String query = sysInterface.getQuery(); // 获取接口的查询语句 + Object[] params = null; + if(StringUtils.isNotBlank(entity.getParams())){ + params = entity.getParams().split(";"); // 解析参数字符串为数组 + } + List> list = dynamicQuery.nativeQueryListMap(query,params); // 使用DynamicQuery执行原生SQL查询语句得到结果 + return Result.ok(list); + }else{ + return Result.error("接口已被禁用"); + } + }else{ + return Result.error("接口类型不存在"); + } + } + + public String common(SysInterface entity){ + String description = entity.getDescription(); // 获取接口信息的描述 + String commonSql = ""; + if(StringUtils.isNotBlank(description)){ + commonSql += "WHERE type like '"+description+"%' "; // 根据描述模糊查询type字段 + commonSql += "OR name like '"+description+"%' "; // 根据描述模糊查询name字段 + } + return commonSql; + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysLogServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysLogServiceImpl.java new file mode 100644 index 0000000..6a261cc --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysLogServiceImpl.java @@ -0,0 +1,50 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysLog; +import com.smart.module.sys.service.SysLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service // 表明该类是一个Service组件 +public class SysLogServiceImpl implements SysLogService { + + @Autowired // 自动装配DynamicQuery对象 + private DynamicQuery dynamicQuery; + + /** + * 获取日志信息列表 + * @param log + * @return + */ + @Override + public Result list(SysLog log) { + String nativeSql = "SELECT COUNT(*) FROM sys_log "; // 统计日志信息的总数 + Long count = dynamicQuery.nativeQueryCount(nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到结果 + PageBean data = new PageBean<>(); + if(count>0){ + nativeSql = "SELECT * FROM sys_log ORDER BY gmt_create desc"; // 查询日志信息列表 + Pageable pageable = PageRequest.of(log.getPageNo(),log.getPageSize()); // 创建分页请求对象 + List list = dynamicQuery.nativeQueryPagingList(SysLog.class,pageable,nativeSql); // 使用DynamicQuery执行原生SQL查询语句得到分页结果 + data = new PageBean(list,count); // 构建分页数据对象 + } + return Result.ok(data); + } + + /** + * 保存日志信息 + * @param log + */ + @Override + @Transactional(rollbackFor=Exception.class) // 添加事务注解,当出现异常时进行回滚 + public void save(SysLog log) { + dynamicQuery.save(log); // 保存日志信息到数据库 + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysMenuServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..dd59627 --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,130 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.constant.SystemConstant; +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysMenu; +import com.smart.module.sys.service.SysMenuService; +import com.smart.module.sys.service.SysUserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class SysMenuServiceImpl implements SysMenuService { + + @Autowired + private DynamicQuery dynamicQuery; // 动态查询接口 + @Autowired + private SysUserService sysUserService; // 用户服务接口 + + @Override + public Result list(SysMenu menu) { + // 构建原生SQL语句,查询指定父菜单ID下的菜单列表 + String nativeSql = "SELECT * FROM sys_menu WHERE parent_id=?"; + String description = menu.getDescription(); + if(StringUtils.isNotBlank(description)){ + // 如果有传入描述信息,则添加模糊查询条件 + nativeSql +=" AND name like '"+description+"%'"; + } + // 根据排序号进行升序排序 + nativeSql += " ORDER BY order_num"; + // 执行动态查询,返回菜单列表 + List menuList = dynamicQuery.query(SysMenu.class, nativeSql, new Object[]{menu.getParentId()}); + // 返回查询结果 + return Result.ok(menuList); + } + + @Override + public List select(Long parentId) { + // 构建原生SQL语句,查询指定父菜单ID下的菜单列表 + String nativeSql = "SELECT * FROM sys_menu "; + if(parentId!=null){ + // 如果有传入父菜单ID,则添加条件 + nativeSql += " WHERE parent_id="+parentId; + } + // 根据排序号进行升序排序 + nativeSql += " ORDER BY order_num"; + // 执行动态查询,返回菜单列表 + return dynamicQuery.query(SysMenu.class, nativeSql); + } + + @Override + @Transactional(rollbackFor=Exception.class) + public Result delete(Long menuId) { + // 构建原生SQL语句,根据菜单ID查询菜单信息 + String nativeSql = "SELECT * FROM sys_menu WHERE menu_id = ?"; + // 使用动态查询工具查询数据库并获得单个查询结果 + SysMenu menu = dynamicQuery.nativeQuerySingleResult(SysMenu.class, nativeSql, new Object[]{menuId}); + + if(menu.getType().equals(SystemConstant.MenuType.CATALOG.getValue())){ + // 如果菜单类型是目录,则查询是否有下级目录、菜单或按钮 + nativeSql = "SELECT COUNT(*) FROM sys_menu WHERE parent_id=?"; + // 查询下级目录、菜单或者按钮的数量 + Long count = dynamicQuery.nativeQueryCount(nativeSql, new Object[]{menuId}); + if(count>0){ + return Result.error("请先删除下级目录、菜单或者按钮"); + } + }else if(menu.getType().equals(SystemConstant.MenuType.MENU.getValue())){ + /** + * 删除下级按钮 + */ + // 如果菜单类型是菜单,则删除下级按钮 + nativeSql = "DELETE FROM sys_menu WHERE parent_id = ?"; + dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{menuId}); + } + /** + * 删除自身 + */ + // 删除自身菜单项 + nativeSql = "DELETE FROM sys_menu WHERE menu_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{menuId}); + + return Result.ok("删除成功"); + } + + @Override + public List getByUserId(Long userId) { + // 构建原生SQL语句,根据用户ID查询用户拥有的菜单列表 + String nativeSql = "SELECT DISTINCT m.* FROM sys_menu m "; + nativeSql +="LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id "; + nativeSql +="LEFT JOIN sys_role r ON r.role_id = rm.role_id "; + nativeSql +="WHERE r.role_id IN (SELECT role_id FROM sys_user_role WHERE user_id=?) "; + // SQL查询语句,根据用户ID查询用户拥有的菜单权限并按排序号 + nativeSql +="AND m.TYPE = 0 ORDER BY m.order_num"; + // 执行动态查询,返回菜单列表 + List list = dynamicQuery.query(SysMenu.class, nativeSql, new Object[]{userId}); + + list.stream().forEach(menu->{ + // 遍历菜单列表,查询每个菜单的子菜单列表 + String subSql = "SELECT DISTINCT m.* FROM sys_menu m "; + subSql +="LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id "; + subSql +="LEFT JOIN sys_role r ON r.role_id = rm.role_id "; + subSql +="WHERE r.role_id IN (SELECT role_id FROM sys_user_role WHERE user_id=?) "; + subSql +="AND m.parent_id = ? ORDER BY m.order_num"; + // 执行动态查询,返回子菜单列表 + List subList = dynamicQuery.query(SysMenu.class, subSql, new Object[]{userId,menu.getMenuId()}); + // 设置子菜单列表到父菜单对象中 + menu.setList(subList); + }); + // 返回查询结果 + return list; + } + + @Override + @Transactional(rollbackFor=Exception.class) + public Result drop(Long parentId, Long menuId) { + // 构建原生SQL语句,更新菜单的父菜单ID + String nativeSql = "UPDATE sys_menu SET parent_id=? WHERE menu_id=?"; + // 执行更新操作,将菜单的父菜单ID更新为指定的parentId + int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{parentId,menuId}); + if(count==0){ + return Result.error(); + }else{ + return Result.ok("更新成功"); + } + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysOrgServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysOrgServiceImpl.java new file mode 100644 index 0000000..6a6ce5d --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysOrgServiceImpl.java @@ -0,0 +1,95 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.module.sys.entity.SysOrg; +import com.smart.module.sys.repository.SysOrgRepository; +import com.smart.module.sys.service.SysOrgService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class SysOrgServiceImpl implements SysOrgService { + + @Autowired + private DynamicQuery dynamicQuery; // 动态查询工具类 + @Autowired + private SysOrgRepository sysOrgRepository; // 机构数据访问接口 + + @Override + public Result list(SysOrg org) { + String nativeSql = "SELECT COUNT(*) FROM sys_org WHERE parent_id=? "; // SQL查询语句,根据父机构ID查询子机构数量 + String description = org.getDescription(); + if(StringUtils.isNotBlank(description)){ + nativeSql +="AND name like '"+description+"%'"; // 根据描述进行模糊查询 + } + Long count = dynamicQuery.nativeQueryCount(nativeSql,new Object[]{org.getParentId()}); // 查询符合条件的子机构数量 + PageBean data = new PageBean<>(); + if(count>0){ + nativeSql = "SELECT * FROM sys_org WHERE parent_id=? "; // SQL查询语句,根据父机构ID查询子机构列表 + if(StringUtils.isNotBlank(description)){ + nativeSql +="AND name like '"+description+"%' "; // 根据描述进行模糊查询 + } + nativeSql +="ORDER BY gmt_create desc"; // 按创建时间倒序排列 + Pageable pageable = PageRequest.of(org.getPageNo(),org.getPageSize()); // 构造分页查询参数 + List list = + dynamicQuery.nativeQueryPagingList(SysOrg.class,pageable,nativeSql,new Object[]{org.getParentId()}); // 分页查询子机构列表 + data = new PageBean(list,count); // 构造分页结果 + } + return Result.ok(data); // 返回查询结果 + } + + @Override + public Result select(Long parentId) { + String nativeSql = "SELECT * FROM sys_org"; // SQL查询语句,查询机构列表 + if(parentId!=null){ + nativeSql += " WHERE parent_id="+parentId; // 根据父机构ID查询子机构列表 + } + nativeSql += " ORDER BY order_num desc"; // 按排序号倒序排列 + List list = dynamicQuery.query(SysOrg.class,nativeSql); // 查询机构列表 + return Result.ok(list); // 返回查询结果 + } + + @Override + @Transactional(rollbackFor=Exception.class) + public Result save(SysOrg org) { + String nativeSql = "SELECT * FROM sys_org WHERE code=?"; // SQL查询语句,根据机构代码查询机构信息 + SysOrg sysOrg = dynamicQuery.nativeQuerySingleResult( + SysOrg.class,nativeSql,new Object[]{org.getCode()}); // 查询符合条件的机构 + if(sysOrg==null){ + org.setGmtCreate(DateUtils.getTimestamp()); // 设置机构的创建时间 + }else{ + if(!sysOrg.getOrgId().equals(org.getOrgId())){ + return Result.error("机构代码重复"); // 若机构代码已存在且不是当前编辑的机构,则返回错误提示 + } + } + org.setGmtModified(DateUtils.getTimestamp()); // 设置机构的修改时间 + if(org.getParentId()==null){ + org.setParentId(0L); // 若父机构ID为空,则设置为顶级机构 + } + sysOrgRepository.saveAndFlush(org); // 保存机构信息到数据库 + return Result.ok("保存成功"); // 返回保存成功的结果 + } + + @Override + @Transactional(rollbackFor=Exception.class) + public Result delete(Long orgId) { + String nativeSql = "SELECT COUNT(*) FROM sys_org WHERE parent_id=?"; // SQL查询语句,根据机构ID查询子机构数量 + Long count = dynamicQuery.nativeQueryCount(nativeSql,new Object[]{orgId}); // 查询下级机构数量 + if(count>0){ + return Result.error("请先删除下级机构"); // 若存在下级机构,则返回错误提示 + }else{ + nativeSql = "DELETE FROM sys_org WHERE org_id=?"; // SQL删除语句,根据机构ID删除机构信息 + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{orgId}); // 删除机构信息 + } + return Result.ok("删除成功"); // 返回删除成功的结果 + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysRoleServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..5029a4d --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,154 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysRole; +import com.smart.module.sys.entity.SysRoleMenu; +import com.smart.module.sys.entity.SysRoleOrg; +import com.smart.module.sys.repository.SysRoleRepository; +import com.smart.module.sys.service.SysRoleService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class SysRoleServiceImpl implements SysRoleService { + + @Autowired + private DynamicQuery dynamicQuery; // 动态SQL查询工具 + + @Autowired + private SysRoleRepository sysRoleRepository; // 角色仓库 + + // 查询角色列表并分页 + @Override + public Result list(SysRole role) { + String nativeSql = "SELECT COUNT(*) FROM sys_role "; + nativeSql += common(role); // 构建通用SQL查询条件 + Long count = dynamicQuery.nativeQueryCount(nativeSql); // 统计记录总数 + PageBean data = new PageBean<>(); // 创建一个分页数据对象 + if(count>0){ + nativeSql = "SELECT * FROM sys_role "; + nativeSql += common(role); // 构建通用SQL查询条件 + nativeSql += "ORDER BY gmt_create desc "; + Pageable pageable = PageRequest.of(role.getPageNo(),role.getPageSize()); // 构建分页参数 + List list = dynamicQuery.nativeQueryPagingList(SysRole.class, pageable, nativeSql); // 分页查询角色列表 + data = new PageBean(list, count); // 将分页数据放入PageBean对象中 + } + return Result.ok(data); // 返回结果 + } + + // 构建通用SQL查询条件 + public String common(SysRole role){ + String description = role.getDescription(); + String commonSql = ""; + if(StringUtils.isNotBlank(description)){ + commonSql += "WHERE role_name like '"+description+"%' "; + commonSql += "OR role_sign like '"+description+"%' "; + } + return commonSql; + } + + // 查询所有角色 + @Override + public Result select() { + String nativeSql = "SELECT * FROM sys_role"; + List list = dynamicQuery.query(SysRole.class, nativeSql, new Object[]{}); + return Result.ok(list); + } + + // 保存角色信息 + @Override + @Transactional + public Result save(SysRole role) { + String nativeSql = "SELECT * FROM sys_role WHERE role_sign=?"; + SysRole sysRole = dynamicQuery.nativeQuerySingleResult(SysRole.class, nativeSql, new Object[]{role.getRoleSign()}); + if(sysRole!=null){ + if(!sysRole.getRoleSign().equals(role.getRoleSign())){ + return Result.error("角色账号重复"); + } + }else{ + role.setGmtCreate(DateUtils.getTimestamp()); // 设置创建时间 + } + role.setGmtModified(DateUtils.getTimestamp()); // 设置修改时间 + role.setOrgId((long)-1); // 设置组织ID + role.setUserIdCreate(ShiroUtils.getUserId()); // 设置创建人的用户ID + sysRoleRepository.saveAndFlush(role); // 保存角色信息 + return Result.ok("保存成功"); + } + + // 删除角色 + @Override + @Transactional(rollbackFor=Exception.class) + public Result delete(Long roleId) { + String nativeSql = "DELETE FROM sys_role_menu WHERE role_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{roleId}); // 删除角色与菜单的关联记录 + nativeSql = "DELETE FROM sys_role WHERE role_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{roleId}); // 删除角色记录 + nativeSql = "DELETE FROM sys_user_role WHERE role_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{roleId}); // 删除用户与角色的关联记录 + return Result.ok("删除成功"); + } + + // 获取角色所关联的菜单信息 + @Override + public Result getMenu(Long roleId) { + String nativeSql = "SELECT menu_id FROM sys_role_menu WHERE role_id=?"; + List list = dynamicQuery.query(nativeSql,new Object[]{roleId}); + return Result.ok(list); + } + + // 保存角色与菜单的关联关系 + @Override + @Transactional(rollbackFor=Exception.class) + public Result saveMenu(SysRole role) { + Long roleId = role.getRoleId(); + String nativeSql = "DELETE FROM sys_role_menu WHERE role_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{roleId}); // 先删除角色与菜单的关联记录 + List menuIdList = role.getMenuIdList(); + if(menuIdList!=null){ + menuIdList.forEach(menuId->{ + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setMenuId(menuId); + roleMenu.setRoleId(roleId); + dynamicQuery.save(roleMenu); // 保存角色与菜单的关联记录 + }); + } + return Result.ok("保存成功"); + } + + // 获取角色所关联的组织信息 + @Override + public Result getOrg(Long roleId) { + String nativeSql = "SELECT org_id FROM sys_role_org WHERE role_id=?"; + List list = dynamicQuery.query(nativeSql,new Object[]{roleId}); + return Result.ok(list); + } + + // 保存角色与组织的关联关系 + @Override + @Transactional(rollbackFor=Exception.class) + public Result saveOrg(SysRole role) { + Long roleId = role.getRoleId(); + String nativeSql = "DELETE FROM sys_role_org WHERE role_id=?"; + dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{roleId}); // 先删除角色与组织的关联记录 + List orgIdList = role.getOrgIdList(); + if(orgIdList!=null){ + orgIdList.forEach(orgId->{ + SysRoleOrg roleOrg = new SysRoleOrg(); + roleOrg.setOrgId(orgId); + roleOrg.setRoleId(roleId); + dynamicQuery.save(roleOrg); // 保存角色与组织的关联记录 + }); + } + return Result.ok("保存成功"); + } +} diff --git a/src/main/java/com/smart/module/sys/service/impl/SysUserServiceImpl.java b/src/main/java/com/smart/module/sys/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..f7dd4ca --- /dev/null +++ b/src/main/java/com/smart/module/sys/service/impl/SysUserServiceImpl.java @@ -0,0 +1,206 @@ +package com.smart.module.sys.service.impl; + +import com.smart.common.constant.SystemConstant; +import com.smart.common.dynamicquery.DynamicQuery; +import com.smart.common.model.PageBean; +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.common.util.MD5Utils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysOrg; +import com.smart.module.sys.entity.SysUser; +import com.smart.module.sys.entity.SysUserRole; +import com.smart.module.sys.repository.SysUserRepository; +import com.smart.module.sys.service.SysUserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service // 声明为Service组件 +public class SysUserServiceImpl implements SysUserService { + + + @Autowired + private DynamicQuery dynamicQuery; // 使用动态查询工具类自动注入 + @Autowired + private SysUserRepository sysUserRepository; // 使用用户仓库自动注入 + + + @Override + @Transactional(rollbackFor = Exception.class) // 声明事务注解,发生异常时进行回滚 + public Result save(SysUser user) { // 保存用户 + String nativeSql = "SELECT * FROM sys_user WHERE username=?"; // 执行原生SQL查询,根据用户名查询用户 + SysUser sysUser = dynamicQuery.nativeQuerySingleResult( + SysUser.class, nativeSql, new Object[]{user.getUsername()}); // 执行原生SQL查询,并返回单个结果 + if (sysUser != null) { // 如果查询到了用户 + if (!sysUser.getUserId().equals(user.getUserId())) { // 判断查询到的用户ID是否和当前用户ID相同 + return Result.error("用户名重复"); // 返回错误结果,提示用户名重复 + } + } else { + user.setGmtCreate(DateUtils.getTimestamp()); // 设置用户的创建时间为当前时间 + user.setPassword(MD5Utils.encrypt(user.getUsername(), user.getPassword())); // 对用户密码进行加密存储 + user.setAvatarStatus(SystemConstant.AVATAR_STATUS_NO); // 设置用户的头像状态为未上传 + } + user.setGmtModified(DateUtils.getTimestamp()); // 设置用户的修改时间为当前时间 + user.setUserIdCreate(ShiroUtils.getUserId()); // 设置用户的创建者ID为当前登录用户ID + sysUserRepository.saveAndFlush(user); // 保存用户信息到数据库 + nativeSql = "DELETE FROM sys_user_role WHERE user_id=?"; // 执行原生SQL删除操作,删除用户角色关联表中的用户角色 + dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{user.getUserId()}); // 执行原生SQL更新操作 + List roleList = user.getRoleIdList(); // 获取用户所属角色ID列表 + if (roleList != null) { // 如果角色ID列表不为空 + roleList.forEach(roleId -> { // 遍历角色ID列表 + SysUserRole userRole = new SysUserRole(); // 创建用户角色对象 + userRole.setUserId(user.getUserId()); // 设置用户角色关联表中的用户ID + userRole.setRoleId(Long.parseLong(roleId.toString())); // 设置用户角色关联表中的角色ID + dynamicQuery.save(userRole); // 保存用户角色关联信息到数据库 + }); + } + return Result.ok("保存成功"); // 返回成功结果,提示保存成功 + } + + + @Override + public Result get(Long userId) { // 获取用户信息 + /** + * 用户信息 + */ + String nativeSql = "SELECT * FROM sys_user WHERE user_id=?"; // 执行原生SQL查询,根据用户ID查询用户 + SysUser user = dynamicQuery.nativeQuerySingleResult( + SysUser.class, nativeSql, new Object[]{userId}); // 执行原生SQL查询,并返回单个结果 + /** + * 机构信息 + */ + nativeSql = "SELECT * FROM sys_org WHERE org_id=?"; // 执行原生SQL查询,根据机构ID查询机构信息 + SysOrg sysOrg = dynamicQuery.nativeQuerySingleResult( + SysOrg.class, nativeSql, new Object[]{user.getOrgId()}); // 执行原生SQL查询,并返回单个结果 + if (sysOrg != null) { // 如果查询到了机构信息 + user.setOrgName(sysOrg.getName()); // 设置用户的机构名称 + } + /** + * 角色信息 + */ + nativeSql = "SELECT role_id FROM sys_user_role WHERE user_id=?"; // 执行原生SQL查询,根据用户ID查询角色ID列表 + List roleIdList = dynamicQuery.query(nativeSql, new Object[]{userId}); // 执行原生SQL查询,并返回多个结果 + user.setRoleIdList(roleIdList); // 设置用户的角色ID列表 + nativeSql = "SELECT role_name FROM sys_role WHERE role_id IN (SELECT role_id FROM sys_user_role WHERE user_id=?)"; // 执行原生SQL查询,根据用户ID查询角色名称列表 + List roleNameList = dynamicQuery.query(nativeSql, new Object[]{userId}); // 执行原生SQL查询,并返回多个结果 + user.setRoleNameList(roleNameList); // 设置用户的角色名称列表 + return Result.ok(user); // 返回成功结果,并携带用户信息 + } + + + @Override + @Transactional(rollbackFor = Exception.class) // 声明事务注解,发生异常时进行回滚 + public Result delete(Long userId) { // 删除用户 + String nativeSql = "DELETE FROM sys_user_role WHERE user_id=?"; // 执行原生SQL删除操作,删除用户角色关联表中的用户角色 + dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{userId}); // 执行原生SQL更新操作 + nativeSql = "DELETE FROM sys_user WHERE user_id=?"; // 执行原生SQL删除操作,删除用户信息 + dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{userId}); // 执行原生SQL更新操作 + return Result.ok("删除成功"); // 返回成功结果,提示删除成功 + } + + + @Override + public SysUser getUser(String username) { // 根据用户名获取用户信息 + String nativeSql = "SELECT * FROM sys_user u WHERE username = ?"; // 执行原生SQL查询,根据用户名查询用户 + return dynamicQuery.nativeQuerySingleResult(SysUser.class, nativeSql, new Object[]{username}); // 执行原生SQL查询,并返回单个结果 + } + + + @Override + public Result list(SysUser user) { // 获取用户列表 + String nativeSql = "SELECT COUNT(*) FROM sys_user "; // 执行原生SQL查询,统计用户数量 + nativeSql += common(user); // 拼接查询条件 + Long count = dynamicQuery.nativeQueryCount(nativeSql); // 执行原生SQL查询,并返回统计结果 + PageBean data = new PageBean<>(); // 创建分页对象 + if (count > 0) { // 如果用户数量大于0 + nativeSql = "SELECT * FROM sys_user "; // 执行原生SQL查询,根据条件查询用户列表 + nativeSql += common(user); // 拼接查询条件 + nativeSql += "ORDER BY gmt_create desc"; // 根据创建时间降序排序 + Pageable pageable = PageRequest.of(user.getPageNo(), user.getPageSize()); // 创建分页请求对象 + List list = dynamicQuery.nativeQueryPagingList(SysUser.class, pageable, nativeSql); // 执行原生SQL分页查询 + data = new PageBean(list, count); // 封装查询结果到分页对象中 + } + return Result.ok(data); // 返回成功结果,并携带分页数据 + } + + public String common(SysUser user) { // 拼接查询条件 + String description = user.getDescription(); // 获取用户描述 + String commonSql = ""; // 初始化查询条件字符串 + if (StringUtils.isNotBlank(description)) { // 如果用户描述不为空 + commonSql += "WHERE username like '" + description + "%' "; // 拼接用户名模糊查询条件 + commonSql += "OR nickname like '" + description + "%' "; // 拼接昵称模糊查询条件 + } + return commonSql; // 返回查询条件字符串 + } + + + @Override + public List listUserRoles(Long userId) { + // 查询用户角色的原生SQL语句 + String nativeSql = "SELECT r.role_sign FROM sys_user u "; + nativeSql += " LEFT JOIN sys_user_role ur ON u.user_id = ur.user_id"; + nativeSql += " LEFT JOIN sys_role r ON r.role_id = ur.role_id"; + nativeSql += " WHERE u.user_id = ?"; + // 执行原生SQL查询,获取用户角色列表 + List list = dynamicQuery.query(nativeSql, new Object[]{userId}); + return list; + } + + + @Override + public List listUserPerms(Long userId) { + String nativeSql = "SELECT DISTINCT m.perms FROM sys_user_role ur"; + nativeSql += " LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id"; + nativeSql += " LEFT JOIN sys_menu m ON rm.menu_id = m.menu_id"; + nativeSql += " WHERE ur.user_id = ?"; + List list = dynamicQuery.query(nativeSql, new Object[]{userId}); + return list; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Result updatePwd(SysUser user) { + // 将密码进行加密处理 + String password = MD5Utils.encrypt(user.getUsername(), user.getPassword()); + // 更新用户密码的原生SQL语句 + String nativeSql = "UPDATE sys_user SET password=? WHERE user_id=?"; + // 执行原生SQL更新,更新用户密码 + int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{password, user.getUserId()}); + if (count == 1) { + return Result.ok("修改成功"); + } else { + return Result.ok("修改失败"); + } + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public Result update(SysUser user) { + // 更新用户信息的原生SQL语句 + String nativeSql = "UPDATE sys_user SET nickname=?,email=?,mobile=? WHERE user_id=?"; + Object[] params = new Object[]{user.getNickname(), user.getEmail(), user.getMobile(), user.getUserId()}; + // 执行原生SQL更新,更新用户信息 + int count = dynamicQuery.nativeExecuteUpdate(nativeSql, params); + if (count == 1) { + return Result.ok("更新成功"); + } else { + return Result.ok("更新失败"); + } + } + + + @Override + public List listUserByRole(String roleSign) { + // 根据角色账号查询用户列表的原生SQL语句 + // 该方法未实现,直接返回null + return null; + } +} + diff --git a/src/main/java/com/smart/module/sys/web/ConfigController.java b/src/main/java/com/smart/module/sys/web/ConfigController.java new file mode 100644 index 0000000..dfd89f9 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/ConfigController.java @@ -0,0 +1,57 @@ +package com.smart.module.sys.web; // 定义了该类所在的包名 + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysConfig; +import com.smart.module.sys.service.SysConfigService; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 参数设置 + */ +@RestController // 声明该类为一个 RESTful 风格的控制器 +@RequestMapping("/sys/config") // 指定基本的请求路径为 "/sys/config" +public class ConfigController { + + @Autowired + private SysConfigService sysConfigService; // 使用自动装配特性将 SysConfigService 注入到该类中 + + /** + * 参数列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + public Result list(SysConfig config){ // 接收一个 SysConfig 对象作为参数 + return sysConfigService.list(config); // 调用 SysConfigService 的 list 方法,传入 config 参数,并返回结果 + } + + /** + * 获取 + */ + @PostMapping("/get") // 处理 HTTP POST 请求,请求路径为 "/get" + public Result get(Long id){ // 接收一个 Long 类型的参数 id + return sysConfigService.get(id); // 调用 SysConfigService 的 get 方法,传入 id 参数,并返回结果 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysConfig config){ // 使用 RequestBody 注解将请求体中的数据绑定到 SysConfig 对象上 + return sysConfigService.save(config); // 调用 SysConfigService 的 save 方法,传入 config 参数,并返回结果 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result delete(Long id){ // 接收一个 Long 类型的参数 id + return sysConfigService.delete(id); // 调用 SysConfigService 的 delete 方法,传入 id 参数,并返回结果 + } + +} diff --git a/src/main/java/com/smart/module/sys/web/InterfaceController.java b/src/main/java/com/smart/module/sys/web/InterfaceController.java new file mode 100644 index 0000000..bcec406 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/InterfaceController.java @@ -0,0 +1,129 @@ +package com.smart.module.sys.web; + +import cn.hutool.core.io.FileUtil; +import com.smart.common.model.Result; +import com.smart.common.util.ExcelExport; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysInterface; +import com.smart.module.sys.service.SysInterfaceService; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ClassUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.*; + + +/** + * 接口配置 + */ +@Slf4j // 使用 lombok 注解,自动生成日志相关代码 +@RestController // 声明该类为一个 RESTful 风格的控制器 +@RequestMapping("/sys/interface") // 指定基本的请求路径为 "/sys/interface" +public class InterfaceController { + + @Autowired + private SysInterfaceService sysInterfaceService; // 使用自动装配特性将 SysInterfaceService 注入到该类中 + + /** + * 接口列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result list(SysInterface entity){ // 接收一个 SysInterface 对象作为参数 + return sysInterfaceService.list(entity); // 调用 SysInterfaceService 的 list 方法,传入 entity 参数,并返回结果 + } + + /** + * 获取 + */ + @PostMapping("/get") // 处理 HTTP POST 请求,请求路径为 "/get" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result get(Long id){ // 接收一个 Long 类型的参数 id + return sysInterfaceService.get(id); // 调用 SysInterfaceService 的 get 方法,传入 id 参数,并返回结果 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysInterface entity){ // 使用 RequestBody 注解将请求体中的数据绑定到 SysInterface 对象上 + return sysInterfaceService.save(entity); // 调用 SysInterfaceService 的 save 方法,传入 entity 参数,并返回结果 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result delete(Long id){ // 接收一个 Long 类型的参数 id + return sysInterfaceService.delete(id); // 调用 SysInterfaceService 的 delete 方法,传入 id 参数,并返回结果 + } + + /** + * 查询 + */ + @PostMapping("/query") // 处理 HTTP POST 请求,请求路径为 "/query" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result query(SysInterface entity){ // 接收一个 SysInterface 对象作为参数 + return sysInterfaceService.query(entity); // 调用 SysInterfaceService 的 query 方法,传入 entity 参数,并返回结果 + } + + /** + * 查询根据机构 + */ + @PostMapping("/queryByOrgId") // 处理 HTTP POST 请求,请求路径为 "/queryByOrgId" + @RequiresRoles("admin") // 需要具有 "admin" 角色才能访问该方法 + public Result queryByOrgId(SysInterface entity){ // 接收一个 SysInterface 对象作为参数 + entity.setParams(ShiroUtils.getUserEntity().getOrgId().toString()); // 获取当前用户的机构ID,并设置到 entity 的 params 属性中 + return sysInterfaceService.query(entity); // 调用 SysInterfaceService 的 query 方法,传入 entity 参数,并返回结果 + } + + /** + * 导出任务 + * @param request + * @param response + * @return + */ + @RequestMapping(value = "export") // 处理 HTTP 请求,请求路径为 "/export" + @RequiresRoles(value={"admin","orgAdmin"}) // 需要具有 "admin" 或 "orgAdmin" 角色才能访问该方法 + public String export(HttpServletRequest request, HttpServletResponse response) { + try { + List> list = new ArrayList<>(); // 创建一个空的列表 + Map listMap = list.get(0); // 获取列表中的第一个元素 + /** + * 数据 + */ + Map dataMap = new LinkedHashMap<>(); // 创建一个有序的键值对映射表 + /** + * 表头 可以自定义 AS + */ + Map headerMap = new LinkedHashMap<>(); // 创建一个有序的键值对映射表,用于存储表头信息 + for (String key : listMap.keySet()) { // 遍历列表中第一个元素的键集合 + headerMap.put(key,key); // 将键和键作为表头信息存储到headerMap中 + dataMap.put(key, ExcelExport.CELL_ALIGN_LEFT); // 将键和 ExcelExport.CELL_ALIGN_LEFT 存储到dataMap中 + } + list.add(0, headerMap); // 将headerMap作为第一个元素添加到列表中 + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); // 创建日期格式化对象 + InputStream stream = ClassUtils.getDefaultClassLoader() + .getResourceAsStream("static/file/taskTemplate.xlsx"); // 获取资源文件 static/file/taskTemplate.xlsx 的输入流 + ExcelExport excelExport = new ExcelExport( + FileUtil.writeFromStream(stream, new File("file/taskTemplate.xlsx")), -1); // 创建 ExcelExport 对象 + excelExport.setDataList(list,dataMap, false, "").writeTemplate(response, request, + "任务查询信息-" + sdf.format(new Date()) + ".xlsx"); // 使用 ExcelExport 对象导出数据到Excel文件,并写入响应中供下载 + } catch (Exception e) { + log.error("任务查询信息导出Excel出错", e); // 打印错误日志 + } + return null; // 返回空字符串 + } +} \ No newline at end of file diff --git a/src/main/java/com/smart/module/sys/web/LogController.java b/src/main/java/com/smart/module/sys/web/LogController.java new file mode 100644 index 0000000..b0b5539 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/LogController.java @@ -0,0 +1,29 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysLog; +import com.smart.module.sys.service.SysLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 日志管理 + */ +@RestController // 声明该类为一个 RESTful 风格的控制器 +@RequestMapping("/sys/log") // 指定基本的请求路径为 "/sys/log" +public class LogController { + + @Autowired + private SysLogService sysLogService; // 使用自动装配特性将 SysLogService 注入到该类中 + + /** + * 日志列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + public Result list(SysLog log){ // 接收一个 SysLog 对象作为参数 + return sysLogService.list(log); // 调用 SysLogService 的 list 方法,传入 log 参数,并返回结果 + } + +} diff --git a/src/main/java/com/smart/module/sys/web/LoginController.java b/src/main/java/com/smart/module/sys/web/LoginController.java new file mode 100644 index 0000000..d8abf76 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/LoginController.java @@ -0,0 +1,110 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.common.util.DateUtils; +import com.smart.common.util.IPUtils; +import com.smart.common.util.MD5Utils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysLog; +import com.smart.module.sys.entity.SysUser; +import com.smart.module.sys.service.SysLogService; +import com.smart.module.sys.service.SysUserService; +import com.wf.captcha.utils.CaptchaUtil; +import org.apache.shiro.authc.IncorrectCredentialsException; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.subject.Subject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 登录 + */ +@Controller // 声明该类为一个控制器类 +@RequestMapping("/sys") // 指定基本的请求路径为 "/sys" +public class LoginController { + + @Autowired + private SysUserService sysUserService; // 使用自动装配特性将 SysUserService 注入到该类中 + @Autowired + private SysLogService sysLogService; // 使用自动装配特性将 SysLogService 注入到该类中 + + /** + * 登录 + * @param username 用户名 + * @param password 密码 + * @return 返回结果信息 + */ + @PostMapping("/login") // 处理 HTTP POST 请求,请求路径为 "/login" + @ResponseBody // 将方法返回的对象转换为 JSON 格式的响应体 + public Result login(String username, String password, String verCode, + boolean rememberMe, HttpServletRequest request){ + try{ + if (CaptchaUtil.ver(verCode, request)) { // 验证验证码是否匹配 + Subject subject = ShiroUtils.getSubject(); // 获取当前用户的主体对象 + password = MD5Utils.encrypt(username, password); // 对密码进行加密 + UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); // 创建令牌对象 + subject.login(token); // 登录操作,交由 Shiro 框架处理 + saveLog(); // 保存登录日志 + } else { + CaptchaUtil.clear(request); // 清除验证码 + return Result.error(400, "验证码错误"); // 返回错误结果 + } + } catch (UnknownAccountException e) { + return Result.error("账户不存在"); // 返回错误结果 + } catch (IncorrectCredentialsException e) { + return Result.error("密码不正确"); // 返回错误结果 + } + return Result.ok("登录成功"); // 返回成功结果 + } + + /** + * 异步保存登录日志 + */ + @Async // 标记为异步方法 + public void saveLog(){ + SysLog log = new SysLog(); // 创建系统日志对象 + SysUser user = ShiroUtils.getUserEntity(); // 获取当前登录用户信息 + log.setUserId(user.getUserId()); // 设置用户ID + log.setUsername(user.getNickname()); // 设置用户名 + log.setGmtCreate(DateUtils.getTimestamp()); // 设置创建时间 + log.setOperation("登录"); // 设置操作类型为登录 + log.setMethod(""); + log.setParams(""); + log.setExceptionDetail(""); + log.setTime(10); + log.setIp(IPUtils.getIpAddr()); // 获取客户端IP地址 + log.setDeviceType((short)0); + log.setLogType((short)0); + sysLogService.save(log); // 调用系统日志服务保存日志信息 + } + + /** + * 退出登录 + * @return 重定向到登录页面 + */ + @GetMapping("/logout") // 处理 HTTP GET 请求,请求路径为 "/logout" + public String logout(){ + ShiroUtils.logout(); // 执行登出操作 + return "redirect:/login.html"; // 重定向到登录页面 + } + + /** + * 生成验证码 + * @param request 请求对象 + * @param response 响应对象 + * @throws Exception 异常 + */ + @RequestMapping("captcha") // 处理 HTTP请求,请求路径为 "/captcha" + public void generate(HttpServletRequest request, HttpServletResponse response) throws Exception { + CaptchaUtil.out(request, response); // 生成验证码 + } +} diff --git a/src/main/java/com/smart/module/sys/web/MemberController.java b/src/main/java/com/smart/module/sys/web/MemberController.java new file mode 100644 index 0000000..bb8be76 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/MemberController.java @@ -0,0 +1,66 @@ +package com.smart.module.sys.web; + + +import com.smart.common.model.Result; +import com.smart.common.util.MD5Utils; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysUser; +import com.smart.module.sys.service.SysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + + +/** + * 个人设置 + */ +@RestController // 声明该类为一个控制器类,同时每个方法的返回值都会被转换成 JSON 格式的响应体 +@RequestMapping("/member") // 指定基本的请求路径为 "/member" +public class MemberController { + + @Autowired + private SysUserService sysUserService; // 使用自动装配特性将 SysUserService 注入到该类中 + + /** + * 修改密码 + * @param user 用户信息 + * @return 返回结果信息 + */ + @PostMapping("/updatePwd") // 处理 HTTP POST 请求,请求路径为 "/updatePwd" + public Result updatePwd(@RequestBody SysUser user){ + SysUser entity = ShiroUtils.getUserEntity(); // 获取当前登录用户信息 + String password = MD5Utils.encrypt(entity.getUsername(),user.getOldPassword()); // 对旧密码进行加密 + if(entity.getPassword().equals(password)){ // 判断旧密码是否正确 + entity.setPassword(user.getPassword()); // 更新用户的密码 + return sysUserService.updatePwd(entity); // 调用系统用户服务更新密码 + }else{ + return Result.error("原密码不正确"); // 返回错误结果 + } + } + + /** + * 获取当前用户信息 + * @return 当前用户信息 + */ + @PostMapping("/info") // 处理 HTTP POST 请求,请求路径为 "/info" + public Result info(){ + return sysUserService.get(ShiroUtils.getUserId()); // 调用系统用户服务获取当前用户信息 + } + + /** + * 更新用户信息 + * @param user 用户信息 + * @return 返回结果信息 + */ + @PostMapping("/update") // 处理 HTTP POST 请求,请求路径为 "/update" + public Result update(@RequestBody SysUser user){ + SysUser entity = ShiroUtils.getUserEntity(); // 获取当前登录用户信息 + entity.setEmail(user.getEmail()); // 更新用户的邮箱 + entity.setMobile(user.getMobile()); // 更新用户的手机号码 + entity.setNickname(user.getNickname()); // 更新用户的昵称 + return sysUserService.update(user); // 调用系统用户服务更新用户信息 + } +} \ No newline at end of file diff --git a/src/main/java/com/smart/module/sys/web/MenuController.java b/src/main/java/com/smart/module/sys/web/MenuController.java new file mode 100644 index 0000000..83355f1 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/MenuController.java @@ -0,0 +1,85 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysMenu; +import com.smart.module.sys.repository.SysMenuRepository; +import com.smart.module.sys.service.SysMenuService; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 菜单管理 + */ +@RestController // 声明该类为一个控制器类,同时每个方法的返回值都会被转换成 JSON 格式的响应体 +@RequestMapping("/sys/menu") // 指定基本的请求路径为 "/sys/menu" +public class MenuController { + + @Autowired + private SysMenuService sysMenuService; // 使用自动装配特性将 SysMenuService 注入到该类中 + + @Autowired + private SysMenuRepository sysMenuRepository; // 使用自动装配特性将 SysMenuRepository 注入到该类中 + + /** + * 列表 + */ + @RequestMapping("/list") // 处理 HTTP 请求,请求路径为 "/list" + public Result list(SysMenu menu){ + return sysMenuService.list(menu); // 调用系统菜单服务获取菜单列表 + } + + /** + * 树结构 + */ + @RequestMapping("/select") // 处理 HTTP 请求,请求路径为 "/select" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result select(Long parentId){ + List list = sysMenuService.select(parentId); // 获取指定父菜单的子菜单列表 + return Result.ok(list); // 返回结果列表 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysMenu menu){ + sysMenuRepository.saveAndFlush(menu); // 保存更新菜单信息 + return Result.ok("保存成功"); // 返回保存成功的结果 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result delete(Long menuId){ + return sysMenuService.delete(menuId); // 调用系统菜单服务删除指定菜单 + } + + + /** + * 获取菜单 + */ + @RequestMapping("/getByUser") // 处理 HTTP 请求,请求路径为 "/getByUser" + public List getByUser(){ + return sysMenuService.getByUserId(ShiroUtils.getUserId()); // 获取当前登录用户的菜单列表 + } + + + /** + * 列表 + */ + @RequestMapping("/drop") // 处理 HTTP 请求,请求路径为 "/drop" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result drop(Long parentId,Long menuId){ + return sysMenuService.drop(parentId,menuId); // 获取指定父菜单下的子菜单及子菜单的子菜单列表 + } +} diff --git a/src/main/java/com/smart/module/sys/web/OrgController.java b/src/main/java/com/smart/module/sys/web/OrgController.java new file mode 100644 index 0000000..bd4ebf6 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/OrgController.java @@ -0,0 +1,56 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysOrg; +import com.smart.module.sys.service.SysOrgService; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 机构管理 + */ +@RestController // 声明该类为一个控制器类,同时每个方法的返回值都会被转换成 JSON 格式的响应体 +@RequestMapping("/sys/org") // 指定基本的请求路径为 "/sys/org" +public class OrgController { + + @Autowired + private SysOrgService sysOrgService; // 使用自动装配特性将 SysOrgService 注入到该类中 + + /** + * 机构列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + public Result list(SysOrg sysOrg){ + return sysOrgService.list(sysOrg); // 调用系统机构服务获取机构列表 + } + + /** + * 树结构 + */ + @RequestMapping("/select") // 处理 HTTP 请求,请求路径为 "/select" + public Result select(Long parentId){ + return sysOrgService.select(parentId); // 获取指定父机构的子机构列表 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysOrg org){ + return sysOrgService.save(org); // 保存更新机构信息 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result delete(Long orgId){ + return sysOrgService.delete(orgId); // 调用系统机构服务删除指定机构 + } +} diff --git a/src/main/java/com/smart/module/sys/web/RoleController.java b/src/main/java/com/smart/module/sys/web/RoleController.java new file mode 100644 index 0000000..5acdfdf --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/RoleController.java @@ -0,0 +1,98 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.module.sys.entity.SysRole; +import com.smart.module.sys.service.SysRoleService; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 角色管理 + */ +@RestController // 声明该类为一个控制器类,同时每个方法的返回值都会被转换成 JSON 格式的响应体 +@RequestMapping("/sys/role") // 指定基本的请求路径为 "/sys/role" +public class RoleController { + + @Autowired + private SysRoleService sysRoleService; // 使用自动装配特性将 SysRoleService 注入到该类中 + + /** + * 角色列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + public Result list(SysRole role){ + return sysRoleService.list(role); // 调用系统角色服务获取角色列表 + } + + /** + * 角色选择 + */ + @PostMapping("/select") // 处理 HTTP POST 请求,请求路径为 "/select" + public Result select(){ + return sysRoleService.select(); // 获取所有角色列表 + } + + /** + * 角色选择 + */ + @PostMapping("/selectByUser") // 处理 HTTP POST 请求,请求路径为 "/selectByUser" + public Result selectByUser(){ + return sysRoleService.select(); // 根据用户获取对应的角色列表 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysRole role){ + return sysRoleService.save(role); // 保存更新角色信息 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result delete(Long roleId){ + return sysRoleService.delete(roleId); // 调用系统角色服务删除指定角色 + } + + /** + * 根据角色ID获取菜单 + */ + @PostMapping("/getMenu") // 处理 HTTP POST 请求,请求路径为 "/getMenu" + public Result getMenu(Long roleId){ + return sysRoleService.getMenu(roleId); // 获取指定角色的菜单列表 + } + + /** + * 根据角色保存菜单 + */ + @PostMapping("/saveMenu") // 处理 HTTP POST 请求,请求路径为 "/saveMenu" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result saveMenu(@RequestBody SysRole role){ + return sysRoleService.saveMenu(role); // 保存指定角色的菜单权限信息 + } + + /** + * 根据角色ID获取机构 + */ + @PostMapping("/getOrg") // 处理 HTTP POST 请求,请求路径为 "/getOrg" + public Result getOrg(Long roleId){ + return sysRoleService.getOrg(roleId); // 获取指定角色的机构列表 + } + + /** + * 根据角色保存机构 + */ + @PostMapping("/saveOrg") // 处理 HTTP POST 请求,请求路径为 "/saveOrg" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result saveOrg(@RequestBody SysRole role){ + return sysRoleService.saveOrg(role); // 保存指定角色的机构权限信息 + } +} diff --git a/src/main/java/com/smart/module/sys/web/UserController.java b/src/main/java/com/smart/module/sys/web/UserController.java new file mode 100644 index 0000000..9e6b586 --- /dev/null +++ b/src/main/java/com/smart/module/sys/web/UserController.java @@ -0,0 +1,83 @@ +package com.smart.module.sys.web; + +import com.smart.common.model.Result; +import com.smart.common.util.ShiroUtils; +import com.smart.module.sys.entity.SysUser; +import com.smart.module.sys.service.SysUserService; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 用户管理 + */ +@RestController // 声明该类为一个控制器类,同时每个方法的返回值都会被转换成 JSON 格式的响应体 +@RequestMapping("/sys/user") // 指定基本的请求路径为 "/sys/user" +public class UserController { + + @Autowired + private SysUserService sysUserService; // 使用自动装配特性将 SysUserService 注入到该类中 + + /** + * 用户列表 + */ + @PostMapping("/list") // 处理 HTTP POST 请求,请求路径为 "/list" + public Result list(SysUser user){ + return sysUserService.list(user); // 调用系统用户服务获取用户列表 + } + + /** + * 获取 + */ + @PostMapping("/get") // 处理 HTTP POST 请求,请求路径为 "/get" + public Result get(Long userId){ + return sysUserService.get(userId); // 根据用户ID获取用户信息 + } + + /** + * 保存 + */ + @PostMapping("/save") // 处理 HTTP POST 请求,请求路径为 "/save" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result save(@RequestBody SysUser user){ + return sysUserService.save(user); // 保存更新用户信息 + } + + /** + * 删除 + */ + @PostMapping("/delete") // 处理 HTTP POST 请求,请求路径为 "/delete" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result delete(Long userId){ + return sysUserService.delete(userId); // 删除指定用户 + } + + /** + * 修改密码 + */ + @PostMapping("/updatePwd") // 处理 HTTP POST 请求,请求路径为 "/updatePwd" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result updatePwd(SysUser user){ + return sysUserService.updatePwd(user); // 修改用户密码 + } + + /** + * 获取当前用户信息 + */ + @PostMapping("/info") // 处理 HTTP POST 请求,请求路径为 "/info" + public Result info(){ + return sysUserService.get(ShiroUtils.getUserId()); // 获取当前用户的信息 + } + + /** + * 更新用户信息 + */ + @PostMapping("/update") // 处理 HTTP POST 请求,请求路径为 "/update" + @RequiresRoles("admin") // 需要 "admin" 角色才能访问该方法 + public Result update(@RequestBody SysUser user){ + return sysUserService.update(user); // 更新用户信息 + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..e1b2aa9 --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,68 @@ +server.servlet.context-path=/ +# ??????????????????? + +server.port=8080 +# ?????????? + +server.session-timeout=60 +# ?????? + +server.tomcat.max-threads=100 +# Tomcat???????? + +server.tomcat.uri-encoding=UTF-8 +# URI???? + +server.servlet.session.tracking-modes = cookie +server.servlet.session.cookie.http-only =true +# ??cookie???????????cookie?http-only???true??????? + +spring.servlet.multipart.enabled =true +spring.servlet.multipart.max-request-size=20MB +spring.servlet.multipart.max-file-size=20MB +# ??????????????????????????20MB + +spring.mvc.static-path-pattern=/** +# ??????????????????????? + +spring.aop.proxy-target-class=true +# ??CGLIB??????AOP + +spring.thymeleaf.mode=HTML +# Thymeleaf?????????????HTML?? + +spring.devtools.livereload.enabled=true +# ???????????? + +spring.thymeleaf.cache=false +spring.thymeleaf.cache-period=0 +spring.thymeleaf.template.cache=false +# ??Thymeleaf??????? + +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/smart-parking?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +spring.datasource.username=root +spring.datasource.password=SASAKIDS +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# ??????????URL????????????? + +spring.jpa.database = MYSQL +# ?????????????MySQL + +spring.jpa.show-sql = false +spring.jpa.open-in-view = false +# ????SQL?????OpenInView????? + +spring.jpa.hibernate.ddl-auto = update +# Hibernate?DDL?????????????? + +spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +# Hibernate?????????????????????????? + +spring.datasource.hikari.minimum-idle=3 +spring.datasource.hikari.maximum-pool-size=10 +spring.datasource.hikari.max-lifetime =30000 +# ?????????????????????????????????? + +spring.datasource.hikari.connection-test-query=SELECT 1 +# ?????????????????? diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..caf4dfc --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + profiles: + active: dev \ No newline at end of file