commit edb0a89b5ecaf8bd3d491b61b4d357e3535bfcfc Author: chenyouting <514333061@qq.com> Date: Thu Jul 10 14:26:11 2025 +0800 初始化提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa3ee97 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml + +.flattened-pom.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1393e7a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2019 fys-Vue-Plus + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9fce2f --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ + +
+ +- - - +## 平台简介 + +[![码云Gitee](https://gitee.com/dromara/fys-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/fys-Vue-Plus) +[![GitHub](https://img.shields.io/github/stars/dromara/fys-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/fys-Vue-Plus) +[![Star](https://gitcode.com/dromara/fys-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/fys-Vue-Plus) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/fys-Vue-Plus/blob/5.X/LICENSE) +[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=fys-Vue-Plus) +
+[![fys-Vue-Plus](https://img.shields.io/badge/fys_Vue_Plus-5.4.0-success.svg)](https://gitee.com/dromara/fys-Vue-Plus) +[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]() +[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() +[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() + +> Dromara fys-Vue-Plus 是重写 fys-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) + +> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可
+活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 + +> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) + +> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)
+> 成员前端项目地址: 基于vben5 [fys-plus-vben5](https://gitee.com/dapppp/fys-plus-vben5)
+> 成员前端项目地址: 基于soybean [fys-plus-soybean](https://gitee.com/xlsea/fys-plus-soybean)
+> 成员项目地址: 删除多租户与工作流 [fys-Vue-Plus-Single](https://gitee.com/ColorDreams/fys-Vue-Plus-Single)
+ +> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) + +## 赞助商 + +MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
+CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/fys-JFlow
+数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
+引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
+**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/**
+Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
+[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) + +# 本框架与fys的功能差异 + +| 功能 | 本框架 | fys | +|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| +| 前端项目 | 采用 Vue3 + TS + ElementPlus 重写 | 基于Vue2/Vue3 + JS | +| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 | +| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 | +| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat | +| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 | +| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验
角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 | +| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 | +| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer
可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 | +| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 | +| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具
支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan
支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐
连接池采用 common-pool Bug多经常性出问题 | +| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能
例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 | +| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多
例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL | +| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 | +| 数据分页 | 采用 Mybatis-Plus 分页插件
框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 | +| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤
只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展
生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 | +| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件
支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 | +| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密
支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 | +| 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 | +| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译
支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | +| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库
通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源
支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | +| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | +| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 | +| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 | +| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | +| SSE推送 | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步 | 无 | +| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | +| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 | +| 分布式锁 | 采用 Lock4j 底层基于 Redisson | 无 | +| 分布式任务调度 | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 | +| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储
支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | +| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 | +| 短信 | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用 | 不支持 | +| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | +| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | +| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | +| Excel框架 | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | +| 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | +| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | +| 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | +| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗
用了它即可实时查看请求经过的每一处每一个节点 | 无 | +| 代码生成器 | 只需设计好表结构 一键生成所有crud代码与页面
降低80%的开发量 把精力都投入到业务设计上
框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 | +| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生jar部署 其他环境需手动下载安装 自行搭建 | +| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 | +| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 | +| 代码单例测试 | 提供单例测试 使用方式编写方法与maven多环境单测插件 | 只提供基础功能 其他需自行编写扩展 | +| Demo案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 | + + +## 本框架与fys的业务差异 + +| 业务 | 功能说明 | 本框架 | fys | +|--------|----------------------------------------------------------------------|-----|------------------| +| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 | +| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 | +| 客户端管理 | 系统内对接的所有客户端管理 如: pc端、小程序端等
支持动态授权登录方式 如: 短信登录、密码登录等 支持动态控制token时效 | 支持 | 无 | +| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | +| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | +| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | +| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | +| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | +| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | +| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | +| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | +| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | +| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | +| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | +| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | +| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | +| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | +| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 | +| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | +| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | +| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | +| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | + +## 参考文档 + +使用框架前请仔细阅读文档重点注意事项 +
+>[初始化项目 必看](https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/init) +>>[https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/init) +> +>[专栏与视频 入门必看](https://plus-doc.dromara.org/#/common/column) +>>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column) +> +>[部署项目 必看](https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/deploy) +>>[https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/fys-vue-plus/quickstart/deploy) +> +>[如何加群](https://plus-doc.dromara.org/#/common/add_group) +>>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group) +> +>[参考文档 Wiki](https://plus-doc.dromara.org) +>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org) + +## 软件架构图 + +![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png "Plus部署架构图.png") + +## 如何参与贡献 + +[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution) + +## 捐献作者 +作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭 + + + +## 演示图例 + +| | | +|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| +| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1735829153637063344/3c21fd4c_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1735829181303499815/4522cefa_1419627.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1735829377205259767/76a705d7_1419627.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1722959592856812900/e2d0d342_1419627.png "屏幕截图") | +| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") | + + + + + + + + + + + + diff --git a/fys-admin/Dockerfile b/fys-admin/Dockerfile new file mode 100644 index 0000000..6afba6f --- /dev/null +++ b/fys-admin/Dockerfile @@ -0,0 +1,31 @@ +# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ +FROM bellsoft/liberica-openjdk-debian:17.0.11-cds +#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds +#FROM findepi/graalvm:java17-native + +LABEL maintainer="Lion Li" + +RUN mkdir -p /fys/server/logs \ + /fys/server/temp \ + /fys/skywalking/agent + +WORKDIR /fys/server + +ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" + +EXPOSE ${SERVER_PORT} +# 暴露 snail job 客户端端口 用于定时任务调度中心通信 +EXPOSE ${SNAIL_PORT} + +ADD ./target/fys-admin.jar ./app.jar + +SHELL ["/bin/bash", "-c"] + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -Dsnail-job.port=${SNAIL_PORT} \ + # 应用名称 如果想区分集群节点监控 改成不同的名称即可 + #-Dskywalking.agent.service_name=fys-server \ + #-javaagent:/fys/skywalking/agent/skywalking-agent.jar \ + -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ + -jar app.jar + diff --git a/fys-admin/pom.xml b/fys-admin/pom.xml new file mode 100644 index 0000000..e706cd4 --- /dev/null +++ b/fys-admin/pom.xml @@ -0,0 +1,172 @@ + + + + fys-vue-plus + com.fuyuanshen + ${revision} + + 4.0.0 + jar + fys-admin + + + web服务入口 + + + + + + + com.mysql + mysql-connector-j + + + + + + + + + + + + + + + + + + + + + + + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-social + + + + com.fuyuanshen + fys-common-ratelimiter + + + + com.fuyuanshen + fys-common-mail + + + + + com.fuyuanshen + fys-customer + + + + + com.fuyuanshen + fys-equipment + + + + com.fuyuanshen + fys-system + + + + com.fuyuanshen + fys-job + + + + + com.fuyuanshen + fys-generator + + + + + com.fuyuanshen + fys-demo + + + + + com.fuyuanshen + fys-workflow + + + + + com.fuyuanshen + fys-app + + + + + de.codecentric + spring-boot-admin-starter-client + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + + + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + false + ${project.artifactId} + + + + + + diff --git a/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java b/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java new file mode 100644 index 0000000..4f755e3 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/DromaraApplication.java @@ -0,0 +1,22 @@ +package com.fuyuanshen; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +/** + * 启动程序 + * + * @author Lion Li + */ +@SpringBootApplication +public class DromaraApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(DromaraApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(♥◠‿◠)ノ゙ fys-Vue-Plus启动成功 ლ(´ڡ`ლ)゙"); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/DromaraServletInitializer.java b/fys-admin/src/main/java/com/fuyuanshen/DromaraServletInitializer.java new file mode 100644 index 0000000..b1dae01 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/DromaraServletInitializer.java @@ -0,0 +1,18 @@ +package com.fuyuanshen; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author Lion Li + */ +public class DromaraServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(DromaraApplication.class); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java new file mode 100644 index 0000000..f761ea6 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppAuthController.java @@ -0,0 +1,173 @@ +package com.fuyuanshen.app.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.exception.NotLoginException; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fuyuanshen.app.model.AppSmsLoginBody; +import com.fuyuanshen.app.service.AppLoginService; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.model.RegisterBody; +import com.fuyuanshen.common.core.domain.model.SmsLoginBody; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.bo.SysTenantBo; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.service.ISysClientService; +import com.fuyuanshen.system.service.ISysConfigService; +import com.fuyuanshen.system.service.ISysTenantService; +import com.fuyuanshen.web.domain.vo.LoginTenantVo; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.domain.vo.TenantListVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysRegisterService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.core.factory.SmsFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.net.URL; +import java.util.List; + +/** + * APP认证 + * + * @author Lion Li + */ +@Slf4j +@SaIgnore +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/auth") +public class AppAuthController { + + private final AppLoginService loginService; + private final SysRegisterService registerService; + private final ISysConfigService configService; + private final ISysTenantService tenantService; + private final ISysClientService clientService; + + + /** + * 登录方法 + * + * @return 结果 + */ +// @ApiEncrypt + @PostMapping("/login") + public R login(@RequestBody AppSmsLoginBody appSmsLoginBody) { +// SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); + ValidatorUtils.validate(appSmsLoginBody); + SmsLoginBody loginBody = new SmsLoginBody(); + loginBody.setPhonenumber(appSmsLoginBody.getPhonenumber()); + loginBody.setSmsCode(appSmsLoginBody.getSmsCode()); + loginBody.setTenantId(appSmsLoginBody.getTenantId()); + loginBody.setClientId("ca839698e245d60aa2f0e59bd52b34f8"); + loginBody.setGrantType("appSms"); + // 授权类型和客户端id + String clientId = loginBody.getClientId(); + String grantType = loginBody.getGrantType(); + SysClientVo client = clientService.queryByClientId(clientId); + // 查询不到 client 或 client 内不包含 grantType + if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { + log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); + return R.fail(MessageUtils.message("auth.grant.type.error")); + } else if (!SystemConstants.NORMAL.equals(client.getStatus())) { + return R.fail(MessageUtils.message("auth.grant.type.blocked")); + } + // 校验租户 + loginService.checkTenant(loginBody.getTenantId()); + // 登录 + String body = JsonUtils.toJsonString(loginBody); + LoginVo loginVo = IAuthStrategy.login(body, client, grantType); + return R.ok(loginVo); + } + + + + + /** + * 退出登录 + */ + @PostMapping("/logout") + public R logout() { +// loginService.logout(); + return R.ok("退出成功"); + } + + /** + * 用户注册 + */ + @ApiEncrypt + @PostMapping("/register") + public R register(@Validated @RequestBody RegisterBody user) { + if (!configService.selectRegisterEnabled(user.getTenantId())) { + return R.fail("当前系统没有开启注册功能!"); + } + registerService.register(user); + return R.ok(); + } + + /** + * 登录页面租户下拉框 + * + * @return 租户列表 + */ + @GetMapping("/tenant/list") + public R tenantList(HttpServletRequest request) throws Exception { + // 返回对象 + LoginTenantVo result = new LoginTenantVo(); + boolean enable = TenantHelper.isEnable(); + result.setTenantEnabled(enable); + // 如果未开启租户这直接返回 + if (!enable) { + return R.ok(result); + } + + List tenantList = tenantService.queryList(new SysTenantBo()); + List voList = MapstructUtils.convert(tenantList, TenantListVo.class); + try { + // 如果只超管返回所有租户 + if (LoginHelper.isSuperAdmin()) { + result.setVoList(voList); + return R.ok(result); + } + } catch (NotLoginException ignored) { + } + + // 获取域名 + String host; + String referer = request.getHeader("referer"); + if (StringUtils.isNotBlank(referer)) { + // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试 + host = referer.split("//")[1].split("/")[0]; + } else { + host = new URL(request.getRequestURL().toString()).getHost(); + } + // 根据域名进行筛选 + List list = StreamUtils.filter(voList, vo -> + StringUtils.equalsIgnoreCase(vo.getDomain(), host)); + result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); + return R.ok(result); + } + + + @GetMapping("/test") + public void testSend() { + // 在创建完SmsBlend实例后,再未手动调用注销的情况下框架会持有该实例,可以直接通过指定configId来获取想要的配置,如果你想使用 + // 负载均衡形式获取实例,只要使用getSmsBlend的无参重载方法即可,如果你仅有一个配置,也可以使用该方法 + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + + SmsResponse smsResponse = smsBlend.sendMessage("18656573389", "123"); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java new file mode 100644 index 0000000..1344160 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppDeviceController.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.app.controller; + +import com.fuyuanshen.app.domain.dto.APPReNameDTO; +import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo; +import com.fuyuanshen.app.service.equipment.APPDeviceService; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.service.DeviceService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * APP 设备信息管理 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/device") +public class AppDeviceController extends BaseController { + + private final DeviceService deviceService; + + + private final APPDeviceService appDeviceService; + + /** + * 查询设备列表 + */ + @GetMapping("/list") + public TableDataInfo list(DeviceQueryCriteria bo, PageQuery pageQuery) { + + return deviceService.queryAppDeviceList(bo,pageQuery); + } + + /** + * 绑定设备 + */ + @PostMapping("/bind") + public R bind(@RequestBody AppDeviceBo bo) { + return toAjax(deviceService.bindDevice(bo)); + } + + + /** + * 解绑设备 + */ + @DeleteMapping("/unBind") + public R unBind(Long id) { + return toAjax(deviceService.unBindDevice(id)); + } + + /** + * 查询设备类型列表 + */ + @GetMapping(value = "/typeList") + public R> getTypeList() { + List typeList = appDeviceService.getTypeList(); + return R.ok(typeList); + } + + /** + * 重命名设备 + * @param reNameDTO + * @return + */ + @PostMapping(value = "/reName") + public R reName(@Validated @RequestBody APPReNameDTO reNameDTO) { + appDeviceService.reName(reNameDTO); + return R.ok("重命名成功!!!"); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppFileController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppFileController.java new file mode 100644 index 0000000..acf52d3 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppFileController.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.app.controller; + +import com.fuyuanshen.app.domain.bo.AppBusinessFileBo; +import com.fuyuanshen.app.domain.dto.AppFileDto; +import com.fuyuanshen.app.domain.vo.AppFileVo; +import com.fuyuanshen.app.service.AppFileService; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.web.core.BaseController; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * APP 文件管理 + * @date 2025-06-27 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/file") +public class AppFileController extends BaseController { + + private final AppFileService appFileService; + + /** + * 查询文件列表 + */ + @GetMapping("/list") + public R> list(AppBusinessFileBo bo) { + return R.ok(appFileService.list(bo)); + } + + /** + * 上传文件 + */ + @PostMapping("/upload") + public R upload(@Validated @ModelAttribute AppFileDto bo) { + return toAjax(appFileService.add(bo)); + } + + + /** + * 文件删除 + */ + @DeleteMapping("/delete") + public R delete(Long[] ids) { + if(ids == null || ids.length == 0){ + return R.fail("请选择要删除的文件"); + } + return toAjax(appFileService.delete(ids)); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppOperationVideoController.java b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppOperationVideoController.java new file mode 100644 index 0000000..919873b --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/controller/AppOperationVideoController.java @@ -0,0 +1,71 @@ +package com.fuyuanshen.app.controller; + +import com.fuyuanshen.app.domain.bo.AppOperationVideoBo; +import com.fuyuanshen.app.domain.vo.AppOperationVideoVo; +import com.fuyuanshen.app.service.IAppOperationVideoService; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.model.AppLoginUser; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.common.web.core.BaseController; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * APP 操作视频管理 + * @date 2025-06-27 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/app/operationVideo") +public class AppOperationVideoController extends BaseController { + + private final IAppOperationVideoService appOperationVideoService; + + /** + * 查询操作视频列表 + */ + @GetMapping("/listOperationVideos") + public List listOperationVideos(AppOperationVideoBo bo) { + return appOperationVideoService.queryList(bo); + } + + /** + * 查询操作视频详情信息 + */ + @GetMapping("/getOperationVideoInfo") + public AppOperationVideoVo getOperationVideoInfo(AppOperationVideoBo bo) { + return appOperationVideoService.queryById(bo.getId()); + } + + /** + * 操作视频添加 + */ + @PostMapping("/addOperationVideo") + public R addOperationVideo(@RequestBody AppOperationVideoBo bo) { + AppLoginUser loginUser = AppLoginHelper.getLoginUser(); + if (loginUser != null) { + bo.setCreateBy(loginUser.getUserId()); + } + return toAjax(appOperationVideoService.insertByBo(bo)); + } + + /** + * 操作视频编辑 + */ + @PostMapping("/editOperationVideo") + public R editOperationVideo(@RequestBody AppOperationVideoBo bo) { + return toAjax(appOperationVideoService.updateByBo(bo)); + } + + /** + * 操作视频删除 + */ + @DeleteMapping("/deleteOperationVideo") + public R deleteOperationVideo(Long[] ids) { + return toAjax(appOperationVideoService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppFileDto.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppFileDto.java new file mode 100644 index 0000000..9eafe36 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppFileDto.java @@ -0,0 +1,21 @@ +package com.fuyuanshen.app.domain.dto; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Data +public class AppFileDto { + + private Long deviceId; + + /** + * 文件类型(1:操作说明,2:产品参数) + */ + private Long fileType; + + /** + * 文件 + */ + private MultipartFile file; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppVideoDto.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppVideoDto.java new file mode 100644 index 0000000..b7ddc02 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/dto/AppVideoDto.java @@ -0,0 +1,8 @@ +package com.fuyuanshen.app.domain.dto; + +import lombok.Data; + +@Data +public class AppVideoDto { + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/domain/vo/DeviceVo.java b/fys-admin/src/main/java/com/fuyuanshen/app/domain/vo/DeviceVo.java new file mode 100644 index 0000000..653ba03 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/domain/vo/DeviceVo.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.app.domain.vo; + +import lombok.Data; + +@Data +public class DeviceVo { + + private String deviceId; + + private String deviceName; + + private String deviceType; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/model/AppSmsLoginBody.java b/fys-admin/src/main/java/com/fuyuanshen/app/model/AppSmsLoginBody.java new file mode 100644 index 0000000..3c20512 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/model/AppSmsLoginBody.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.app.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class AppSmsLoginBody { + /** + * 手机号 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + + /** + * 短信code + */ + @NotBlank(message = "{sms.code.not.blank}") + private String smsCode; + + /** + * 租户ID + */ + @NotBlank(message = "租户ID不能为空") + private String tenantId; +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppFileService.java b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppFileService.java new file mode 100644 index 0000000..3fbd0eb --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppFileService.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.app.service; + +import com.fuyuanshen.app.domain.bo.AppBusinessFileBo; +import com.fuyuanshen.app.domain.dto.AppFileDto; +import com.fuyuanshen.app.domain.vo.AppBusinessFileVo; +import com.fuyuanshen.app.domain.vo.AppFileVo; +import com.fuyuanshen.common.oss.core.OssClient; +import com.fuyuanshen.common.oss.factory.OssFactory; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import com.fuyuanshen.system.service.ISysOssService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 登录校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class AppFileService { + + private final ISysOssService sysOssService; + + private final IAppBusinessFileService appBusinessFileService; + + public List list(AppBusinessFileBo bo) { + bo.setCreateBy(AppLoginHelper.getUserId()); + return appBusinessFileService.queryAppFileList(bo); + } + + public Boolean add(AppFileDto bo) { + + // 上传文件 + SysOssVo upload = sysOssService.upload(bo.getFile()); + + if (upload == null) { + return false; + } + + AppBusinessFileBo appBusinessFileBo = new AppBusinessFileBo(); + appBusinessFileBo.setFileId(upload.getOssId()); + appBusinessFileBo.setBusinessId(bo.getDeviceId()); + appBusinessFileBo.setFileType(bo.getFileType()); + appBusinessFileBo.setCreateBy(AppLoginHelper.getUserId()); + + return appBusinessFileService.insertByBo(appBusinessFileBo); + } + + public Boolean delete(Long[] ids) { + AppBusinessFileBo bo = new AppBusinessFileBo(); + bo.setCreateBy(AppLoginHelper.getUserId()); + bo.setIds(List.of(ids)); + List appBusinessFileVos = appBusinessFileService.queryList(bo); + List fileIds = appBusinessFileVos.stream().map(AppBusinessFileVo::getFileId).toList(); + List list = sysOssService.listByIds(fileIds); + for (SysOssVo sysOss : list) { + OssClient storage = OssFactory.instance(sysOss.getService()); + storage.delete(sysOss.getUrl()); + } + return appBusinessFileService.deleteWithValidByIds(List.of(ids), true); + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/app/service/AppLoginService.java b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppLoginService.java new file mode 100644 index 0000000..bd92c2b --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/app/service/AppLoginService.java @@ -0,0 +1,188 @@ +package com.fuyuanshen.app.service; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fuyuanshen.app.domain.vo.AppRoleVo; +import com.fuyuanshen.app.domain.vo.AppUserVo; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.dto.RoleDTO; +import com.fuyuanshen.common.core.domain.model.AppLoginUser; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.log.event.LogininforEvent; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.exception.TenantException; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.service.ISysTenantService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.*; +import java.util.function.Supplier; + +/** + * 登录校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class AppLoginService { + + @Value("${user.password.maxRetryCount}") + private Integer maxRetryCount; + + @Value("${user.password.lockTime}") + private Integer lockTime; + + private final ISysTenantService tenantService; + private final IAppRoleService roleService; + + + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = "app_pwd_err_cnt:"; + + + /** + * 退出登录 + */ + public void logout() { + try { + AppLoginUser loginUser = AppLoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + return; + } + if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { + // 超级管理员 登出清除动态租户 + TenantHelper.clearDynamic(); + } + recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); + } catch (NotLoginException ignored) { + } finally { + try { + StpUtil.logout(); + } catch (NotLoginException ignored) { + } + } + } + + /** + * 记录登录信息 + * + * @param tenantId 租户ID + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + */ + public void recordLogininfor(String tenantId, String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + + /** + * 构建登录用户 + */ + public AppLoginUser buildLoginUser(AppUserVo user) { + AppLoginUser loginUser = new AppLoginUser(); + Long userId = user.getUserId(); + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(userId); + loginUser.setDeptId(user.getDeptId()); + loginUser.setUsername(user.getUserName()); + loginUser.setNickname(user.getNickName()); + loginUser.setUserType(user.getUserType()); + Set perms = new HashSet<>(); + loginUser.setMenuPermission(perms); + loginUser.setRolePermission(perms); + loginUser.setRoles(new ArrayList<>()); +/* List roles = appRoleService.selectRolesByUserId(userId); + loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));*/ + return loginUser; + } + + /** + * 登录校验 + */ + public void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { + String errorKey = PWD_ERR_CNT_KEY + username; + String loginFail = Constants.LOGIN_FAIL; + + // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) + int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); + // 锁定时间内登录 则踢出 + if (errorNumber >= maxRetryCount) { + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } + + if (supplier.get()) { + // 错误次数递增 + errorNumber++; + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); + // 达到规定错误次数 则锁定登录 + if (errorNumber >= maxRetryCount) { + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } else { + // 未达到规定错误次数 + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); + throw new UserException(loginType.getRetryLimitCount(), errorNumber); + } + } + + // 登录成功 清空错误次数 + RedisUtils.deleteObject(errorKey); + } + + /** + * 校验租户 + * + * @param tenantId 租户ID + */ + public void checkTenant(String tenantId) { + if (!TenantHelper.isEnable()) { + return; + } + if (StringUtils.isBlank(tenantId)) { + throw new TenantException("tenant.number.not.blank"); + } + if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + return; + } + SysTenantVo tenant = tenantService.queryByTenantId(tenantId); + if (ObjectUtil.isNull(tenant)) { + log.info("登录租户:{} 不存在.", tenantId); + throw new TenantException("tenant.not.exists"); + } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) { + log.info("登录租户:{} 已被停用.", tenantId); + throw new TenantException("tenant.blocked"); + } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) + && new Date().after(tenant.getExpireTime())) { + log.info("登录租户:{} 已超过有效期.", tenantId); + throw new TenantException("tenant.expired"); + } + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/AuthController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/AuthController.java new file mode 100644 index 0000000..382a20c --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/AuthController.java @@ -0,0 +1,239 @@ +package com.fuyuanshen.web.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.model.LoginBody; +import com.fuyuanshen.common.core.domain.model.RegisterBody; +import com.fuyuanshen.common.core.domain.model.SocialLoginBody; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.social.config.properties.SocialLoginConfigProperties; +import com.fuyuanshen.common.social.config.properties.SocialProperties; +import com.fuyuanshen.common.social.utils.SocialUtils; +import com.fuyuanshen.common.sse.dto.SseMessageDto; +import com.fuyuanshen.common.sse.utils.SseMessageUtils; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.bo.SysTenantBo; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.service.ISysClientService; +import com.fuyuanshen.system.service.ISysConfigService; +import com.fuyuanshen.system.service.ISysSocialService; +import com.fuyuanshen.system.service.ISysTenantService; +import com.fuyuanshen.web.domain.vo.LoginTenantVo; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.domain.vo.TenantListVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import com.fuyuanshen.web.service.SysRegisterService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 认证 + * + * @author Lion Li + */ +@Slf4j +@SaIgnore +@RequiredArgsConstructor +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final SocialProperties socialProperties; + private final SysLoginService loginService; + private final SysRegisterService registerService; + private final ISysConfigService configService; + private final ISysTenantService tenantService; + private final ISysSocialService socialUserService; + private final ISysClientService clientService; + private final ScheduledExecutorService scheduledExecutorService; + + + /** + * 登录方法 + * + * @param body 登录信息 + * @return 结果 + */ + @ApiEncrypt + @PostMapping("/login") + public R login(@RequestBody String body) { + LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); + ValidatorUtils.validate(loginBody); + // 授权类型和客户端id + String clientId = loginBody.getClientId(); + String grantType = loginBody.getGrantType(); + SysClientVo client = clientService.queryByClientId(clientId); + // 查询不到 client 或 client 内不包含 grantType + if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { + log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); + return R.fail(MessageUtils.message("auth.grant.type.error")); + } else if (!SystemConstants.NORMAL.equals(client.getStatus())) { + return R.fail(MessageUtils.message("auth.grant.type.blocked")); + } + // 校验租户 + loginService.checkTenant(loginBody.getTenantId()); + // 登录 + LoginVo loginVo = IAuthStrategy.login(body, client, grantType); + + Long userId = LoginHelper.getUserId(); + scheduledExecutorService.schedule(() -> { + SseMessageDto dto = new SseMessageDto(); + dto.setMessage("欢迎登录fys-Vue-Plus后台管理系统"); + dto.setUserIds(List.of(userId)); + SseMessageUtils.publishMessage(dto); + }, 5, TimeUnit.SECONDS); + return R.ok(loginVo); + } + + /** + * 获取跳转URL + * + * @param source 登录来源 + * @return 结果 + */ + @GetMapping("/binding/{source}") + public R authBinding(@PathVariable("source") String source, + @RequestParam String tenantId, @RequestParam String domain) { + SocialLoginConfigProperties obj = socialProperties.getType().get(source); + if (ObjectUtil.isNull(obj)) { + return R.fail(source + "平台账号暂不支持"); + } + AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties); + Map map = new HashMap<>(); + map.put("tenantId", tenantId); + map.put("domain", domain); + map.put("state", AuthStateUtils.createState()); + String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8)); + return R.ok("操作成功", authorizeUrl); + } + + /** + * 前端回调绑定授权(需要token) + * + * @param loginBody 请求体 + * @return 结果 + */ + @PostMapping("/social/callback") + public R socialCallback(@RequestBody SocialLoginBody loginBody) { + // 校验token + StpUtil.checkLogin(); + // 获取第三方登录信息 + AuthResponse response = SocialUtils.loginAuth( + loginBody.getSource(), loginBody.getSocialCode(), + loginBody.getSocialState(), socialProperties); + AuthUser authUserData = response.getData(); + // 判断授权响应是否成功 + if (!response.ok()) { + return R.fail(response.getMsg()); + } + loginService.socialRegister(authUserData); + return R.ok(); + } + + + /** + * 取消授权(需要token) + * + * @param socialId socialId + */ + @DeleteMapping(value = "/unlock/{socialId}") + public R unlockSocial(@PathVariable Long socialId) { + // 校验token + StpUtil.checkLogin(); + Boolean rows = socialUserService.deleteWithValidById(socialId); + return rows ? R.ok() : R.fail("取消授权失败"); + } + + + /** + * 退出登录 + */ + @PostMapping("/logout") + public R logout() { + loginService.logout(); + return R.ok("退出成功"); + } + + /** + * 用户注册 + */ + @ApiEncrypt + @PostMapping("/register") + public R register(@Validated @RequestBody RegisterBody user) { + if (!configService.selectRegisterEnabled(user.getTenantId())) { + return R.fail("当前系统没有开启注册功能!"); + } + registerService.register(user); + return R.ok(); + } + + /** + * 登录页面租户下拉框 + * + * @return 租户列表 + */ + @GetMapping("/tenant/list") + public R tenantList(HttpServletRequest request) throws Exception { + // 返回对象 + LoginTenantVo result = new LoginTenantVo(); + boolean enable = TenantHelper.isEnable(); + result.setTenantEnabled(enable); + // 如果未开启租户这直接返回 + if (!enable) { + return R.ok(result); + } + + List tenantList = tenantService.queryList(new SysTenantBo()); + List voList = MapstructUtils.convert(tenantList, TenantListVo.class); + try { + // 如果只超管返回所有租户 + if (LoginHelper.isSuperAdmin()) { + result.setVoList(voList); + return R.ok(result); + } + } catch (NotLoginException ignored) { + } + + // 获取域名 + String host; + String referer = request.getHeader("referer"); + if (StringUtils.isNotBlank(referer)) { + // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试 + host = referer.split("//")[1].split("/")[0]; + } else { + host = new URL(request.getRequestURL().toString()).getHost(); + } + // 根据域名进行筛选 + List list = StreamUtils.filter(voList, vo -> + StringUtils.equalsIgnoreCase(vo.getDomain(), host)); + result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); + return R.ok(result); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/CaptchaController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/CaptchaController.java new file mode 100644 index 0000000..bb066b9 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/CaptchaController.java @@ -0,0 +1,158 @@ +package com.fuyuanshen.web.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.captcha.AbstractCaptcha; +import cn.hutool.captcha.generator.CodeGenerator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; +import com.fuyuanshen.common.mail.config.properties.MailProperties; +import com.fuyuanshen.common.mail.utils.MailUtils; +import com.fuyuanshen.common.ratelimiter.annotation.RateLimiter; +import com.fuyuanshen.common.ratelimiter.enums.LimitType; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.web.config.properties.CaptchaProperties; +import com.fuyuanshen.common.web.enums.CaptchaType; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.core.factory.SmsFactory; +import com.fuyuanshen.web.domain.vo.CaptchaVo; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; +import java.util.LinkedHashMap; + +/** + * 验证码操作处理 + * + * @author Lion Li + */ +@SaIgnore +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +public class CaptchaController { + + private final CaptchaProperties captchaProperties; + private final MailProperties mailProperties; + + /** + * 短信验证码 + * + * @param phonenumber 用户手机号 + */ + @RateLimiter(key = "#phonenumber", time = 60, count = 1) + @GetMapping("/resource/sms/code") + public R smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { + String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + // 验证码模板id 自行处理 (查数据库或写死均可) + String templateId = ""; + LinkedHashMap map = new LinkedHashMap<>(1); + map.put("code", code); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); + if (!smsResponse.isSuccess()) { + log.error("验证码短信发送异常 => {}", smsResponse); + return R.fail(smsResponse.getData().toString()); + } + return R.ok(); + } + + /** + * 邮箱验证码 + * + * @param email 邮箱 + */ + @GetMapping("/resource/email/code") + public R emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { + if (!mailProperties.getEnabled()) { + return R.fail("当前系统没有开启邮箱功能!"); + } + SpringUtils.getAopProxy(this).emailCodeImpl(email); + return R.ok(); + } + + /** + * 邮箱验证码 + * 独立方法避免验证码关闭之后仍然走限流 + */ + @RateLimiter(key = "#email", time = 60, count = 1) + public void emailCodeImpl(String email) { + String key = GlobalConstants.CAPTCHA_CODE_KEY + email; + String code = RandomUtil.randomNumbers(4); + RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + try { + MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); + } catch (Exception e) { + log.error("验证码短信发送异常 => {}", e.getMessage()); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 生成验证码 + */ + @GetMapping("/auth/code") + public R getCode() { + boolean captchaEnabled = captchaProperties.getEnable(); + if (!captchaEnabled) { + CaptchaVo captchaVo = new CaptchaVo(); + captchaVo.setCaptchaEnabled(false); + return R.ok(captchaVo); + } + return R.ok(SpringUtils.getAopProxy(this).getCodeImpl()); + } + + /** + * 生成验证码 + * 独立方法避免验证码关闭之后仍然走限流 + */ + @RateLimiter(time = 60, count = 10, limitType = LimitType.IP) + public CaptchaVo getCodeImpl() { + // 保存验证码信息 + String uuid = IdUtil.simpleUUID(); + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; + // 生成验证码 + CaptchaType captchaType = captchaProperties.getType(); + boolean isMath = CaptchaType.MATH == captchaType; + Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); + CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); + AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); + captcha.setGenerator(codeGenerator); + captcha.createCode(); + // 如果是数学验证码,使用SpEL表达式处理验证码结果 + String code = captcha.getCode(); + if (isMath) { + ExpressionParser parser = new SpelExpressionParser(); + Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); + code = exp.getValue(String.class); + } + + log.info("图片验证码:{}", code); + log.info("图片验证码uuid:{}", uuid); + + RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); + CaptchaVo captchaVo = new CaptchaVo(); + captchaVo.setUuid(uuid); + captchaVo.setImg(captcha.getImageBase64()); + return captchaVo; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/controller/IndexController.java b/fys-admin/src/main/java/com/fuyuanshen/web/controller/IndexController.java new file mode 100644 index 0000000..30d8774 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/controller/IndexController.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.web.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 首页 + * + * @author Lion Li + */ +@SaIgnore +@RequiredArgsConstructor +@RestController +public class IndexController { + + /** + * 访问首页,提示语 + */ + @GetMapping("/") + public String index() { + return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName()); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/CaptchaVo.java b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/CaptchaVo.java new file mode 100644 index 0000000..f4c51c4 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/CaptchaVo.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.web.domain.vo; + +import lombok.Data; + +/** + * 验证码信息 + * + * @author Michelle.Chung + */ +@Data +public class CaptchaVo { + + /** + * 是否开启验证码 + */ + private Boolean captchaEnabled = true; + + private String uuid; + + /** + * 验证码图片 + */ + private String img; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginTenantVo.java b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginTenantVo.java new file mode 100644 index 0000000..5cc8bcc --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginTenantVo.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.web.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 登录租户对象 + * + * @author Michelle.Chung + */ +@Data +public class LoginTenantVo { + + /** + * 租户开关 + */ + private Boolean tenantEnabled; + + /** + * 租户对象列表 + */ + private List voList; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginVo.java b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginVo.java new file mode 100644 index 0000000..63223ab --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/LoginVo.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.web.domain.vo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * 登录验证信息 + * + * @author Michelle.Chung + */ +@Data +public class LoginVo { + + /** + * 授权令牌 + */ + @JsonProperty("access_token") + private String accessToken; + + /** + * 刷新令牌 + */ + @JsonProperty("refresh_token") + private String refreshToken; + + /** + * 授权令牌 access_token 的有效期 + */ + @JsonProperty("expire_in") + private Long expireIn; + + /** + * 刷新令牌 refresh_token 的有效期 + */ + @JsonProperty("refresh_expire_in") + private Long refreshExpireIn; + + /** + * 应用id + */ + @JsonProperty("client_id") + private String clientId; + + /** + * 令牌权限 + */ + private String scope; + + /** + * 用户 openid + */ + private String openid; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/TenantListVo.java b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/TenantListVo.java new file mode 100644 index 0000000..c90c26c --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/domain/vo/TenantListVo.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.web.domain.vo; + +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +/** + * 租户列表 + * + * @author Lion Li + */ +@Data +@AutoMapper(target = SysTenantVo.class) +public class TenantListVo { + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 域名 + */ + private String domain; + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java b/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java new file mode 100644 index 0000000..9105a5d --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/listener/UserActionListener.java @@ -0,0 +1,163 @@ +package com.fuyuanshen.web.listener; + +import cn.dev33.satoken.listener.SaTokenListener; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.convert.Convert; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.CacheConstants; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.ip.AddressUtils; +import com.fuyuanshen.common.log.event.LogininforEvent; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * 用户行为 侦听器的实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Component +@Slf4j +public class UserActionListener implements SaTokenListener { + + private final SysLoginService loginService; + + /** + * 每次登录时触发 + */ + @Override + public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { + UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = ServletUtils.getClientIP(); + UserOnlineDTO dto = new UserOnlineDTO(); + dto.setIpaddr(ip); + dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + dto.setBrowser(userAgent.getBrowser().getName()); + dto.setOs(userAgent.getOs().getName()); + dto.setLoginTime(System.currentTimeMillis()); + dto.setTokenId(tokenValue); + String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); + String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY); + dto.setUserName(username); + dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); + dto.setDeviceType(loginParameter.getDeviceType()); + dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); + TenantHelper.dynamic(tenantId, () -> { + if(loginParameter.getTimeout() == -1) { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); + } else { + RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); + } + }); + // 记录登录日志 + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(Constants.LOGIN_SUCCESS); + logininforEvent.setMessage(MessageUtils.message("user.login.success")); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + // 更新登录信息 + loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); + log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次注销时触发 + */ + @Override + public void doLogout(String loginType, Object loginId, String tokenValue) { + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); + log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被踢下线时触发 + */ + @Override + public void doKickout(String loginType, Object loginId, String tokenValue) { + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); + log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被顶下线时触发 + */ + @Override + public void doReplaced(String loginType, Object loginId, String tokenValue) { + String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); + TenantHelper.dynamic(tenantId, () -> { + RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); + }); + log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); + } + + /** + * 每次被封禁时触发 + */ + @Override + public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { + } + + /** + * 每次被解封时触发 + */ + @Override + public void doUntieDisable(String loginType, Object loginId, String service) { + } + + /** + * 每次打开二级认证时触发 + */ + @Override + public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCloseSafe(String loginType, String tokenValue, String service) { + } + + /** + * 每次创建Session时触发 + */ + @Override + public void doCreateSession(String id) { + } + + /** + * 每次注销Session时触发 + */ + @Override + public void doLogoutSession(String id) { + } + + /** + * 每次Token续期时触发 + */ + @Override + public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { + } +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/IAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/IAuthStrategy.java new file mode 100644 index 0000000..27889f6 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/IAuthStrategy.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.web.service; + + +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.web.domain.vo.LoginVo; + +/** + * 授权策略 + * + * @author Michelle.Chung + */ +public interface IAuthStrategy { + + String BASE_NAME = "AuthStrategy"; + + /** + * 登录 + * + * @param body 登录对象 + * @param client 授权管理视图对象 + * @param grantType 授权类型 + * @return 登录验证信息 + */ + static LoginVo login(String body, SysClientVo client, String grantType) { + // 授权类型和客户端id + String beanName = grantType + BASE_NAME; + if (!SpringUtils.containsBean(beanName)) { + throw new ServiceException("授权类型不正确!"); + } + IAuthStrategy instance = SpringUtils.getBean(beanName); + return instance.login(body, client); + } + + /** + * 登录 + * + * @param body 登录对象 + * @param client 授权管理视图对象 + * @return 登录验证信息 + */ + LoginVo login(String body, SysClientVo client); + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/SysLoginService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/SysLoginService.java new file mode 100644 index 0000000..0d55a4e --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/SysLoginService.java @@ -0,0 +1,256 @@ +package com.fuyuanshen.web.service; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.lock.annotation.Lock4j; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthUser; +import com.fuyuanshen.common.core.constant.CacheConstants; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.dto.PostDTO; +import com.fuyuanshen.common.core.domain.dto.RoleDTO; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.log.event.LogininforEvent; +import com.fuyuanshen.common.mybatis.helper.DataPermissionHelper; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.exception.TenantException; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.bo.SysSocialBo; +import com.fuyuanshen.system.domain.vo.*; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.system.service.*; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.Date; +import java.util.List; +import java.util.function.Supplier; + +/** + * 登录校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLoginService { + + @Value("${user.password.maxRetryCount}") + private Integer maxRetryCount; + + @Value("${user.password.lockTime}") + private Integer lockTime; + + private final ISysTenantService tenantService; + private final ISysPermissionService permissionService; + private final ISysSocialService sysSocialService; + private final ISysRoleService roleService; + private final ISysDeptService deptService; + private final ISysPostService postService; + private final SysUserMapper userMapper; + + + /** + * 绑定第三方用户 + * + * @param authUserData 授权响应实体 + */ + @Lock4j + public void socialRegister(AuthUser authUserData) { + String authId = authUserData.getSource() + authUserData.getUuid(); + // 第三方用户信息 + SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class); + BeanUtil.copyProperties(authUserData.getToken(), bo); + Long userId = LoginHelper.getUserId(); + bo.setUserId(userId); + bo.setAuthId(authId); + bo.setOpenId(authUserData.getUuid()); + bo.setUserName(authUserData.getUsername()); + bo.setNickName(authUserData.getNickname()); + List checkList = sysSocialService.selectByAuthId(authId); + if (CollUtil.isNotEmpty(checkList)) { + throw new ServiceException("此三方账号已经被绑定!"); + } + // 查询是否已经绑定用户 + SysSocialBo params = new SysSocialBo(); + params.setUserId(userId); + params.setSource(bo.getSource()); + List list = sysSocialService.queryList(params); + if (CollUtil.isEmpty(list)) { + // 没有绑定用户, 新增用户信息 + sysSocialService.insertByBo(bo); + } else { + // 更新用户信息 + bo.setId(list.get(0).getId()); + sysSocialService.updateByBo(bo); + // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断 + // throw new ServiceException("此平台账号已经被绑定!"); + } + } + + + /** + * 退出登录 + */ + public void logout() { + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + return; + } + if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { + // 超级管理员 登出清除动态租户 + TenantHelper.clearDynamic(); + } + recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); + } catch (NotLoginException ignored) { + } finally { + try { + StpUtil.logout(); + } catch (NotLoginException ignored) { + } + } + } + + /** + * 记录登录信息 + * + * @param tenantId 租户ID + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + */ + public void recordLogininfor(String tenantId, String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + + /** + * 构建登录用户 + */ + public LoginUser buildLoginUser(SysUserVo user) { + LoginUser loginUser = new LoginUser(); + Long userId = user.getUserId(); + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(userId); + loginUser.setDeptId(user.getDeptId()); + loginUser.setUsername(user.getUserName()); + loginUser.setNickname(user.getNickName()); + loginUser.setUserType(user.getUserType()); + // 用户级别 + loginUser.setUserLevel(user.getUserLevel()); + // pid + loginUser.setPid(user.getPid()); + + loginUser.setMenuPermission(permissionService.getMenuPermission(userId)); + loginUser.setRolePermission(permissionService.getRolePermission(userId)); + if (ObjectUtil.isNotNull(user.getDeptId())) { + Opt deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); + loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); + loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); + } + List roles = roleService.selectRolesByUserId(userId); + List posts = postService.selectPostsByUserId(userId); + loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); + loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class)); + return loginUser; + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId, String ip) { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(ip); + sysUser.setLoginDate(DateUtils.getNowDate()); + sysUser.setUpdateBy(userId); + DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser)); + } + + /** + * 登录校验 + */ + public void checkLogin(LoginType loginType, String tenantId, String username, Supplier supplier) { + String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; + String loginFail = Constants.LOGIN_FAIL; + + // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) + int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); + // 锁定时间内登录 则踢出 + if (errorNumber >= maxRetryCount) { + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } + + if (supplier.get()) { + // 错误次数递增 + errorNumber++; + RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); + // 达到规定错误次数 则锁定登录 + if (errorNumber >= maxRetryCount) { + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); + throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); + } else { + // 未达到规定错误次数 + recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); + throw new UserException(loginType.getRetryLimitCount(), errorNumber); + } + } + + // 登录成功 清空错误次数 + RedisUtils.deleteObject(errorKey); + } + + /** + * 校验租户 + * + * @param tenantId 租户ID + */ + public void checkTenant(String tenantId) { + if (!TenantHelper.isEnable()) { + return; + } + if (StringUtils.isBlank(tenantId)) { + throw new TenantException("tenant.number.not.blank"); + } + if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + return; + } + SysTenantVo tenant = tenantService.queryByTenantId(tenantId); + if (ObjectUtil.isNull(tenant)) { + log.info("登录租户:{} 不存在.", tenantId); + throw new TenantException("tenant.not.exists"); + } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) { + log.info("登录租户:{} 已被停用.", tenantId); + throw new TenantException("tenant.blocked"); + } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) + && new Date().after(tenant.getExpireTime())) { + log.info("登录租户:{} 已超过有效期.", tenantId); + throw new TenantException("tenant.expired"); + } + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/SysRegisterService.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/SysRegisterService.java new file mode 100644 index 0000000..e424114 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/SysRegisterService.java @@ -0,0 +1,115 @@ +package com.fuyuanshen.web.service; + +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.domain.model.RegisterBody; +import com.fuyuanshen.common.core.enums.UserType; +import com.fuyuanshen.common.core.exception.user.CaptchaException; +import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.log.event.LogininforEvent; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.common.web.config.properties.CaptchaProperties; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.system.service.ISysUserService; +import org.springframework.stereotype.Service; + +/** + * 注册校验方法 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRegisterService { + + private final ISysUserService userService; + private final SysUserMapper userMapper; + private final CaptchaProperties captchaProperties; + + /** + * 注册 + */ + public void register(RegisterBody registerBody) { + String tenantId = registerBody.getTenantId(); + String username = registerBody.getUsername(); + String password = registerBody.getPassword(); + // 校验用户类型是否存在 + String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); + + boolean captchaEnabled = captchaProperties.getEnable(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid()); + } + SysUserBo sysUser = new SysUserBo(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(BCrypt.hashpw(password)); + sysUser.setUserType(userType); + + boolean exist = TenantHelper.dynamic(tenantId, () -> { + return userMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getUserName, sysUser.getUserName())); + }); + if (exist) { + throw new UserException("user.register.save.error", username); + } + boolean regFlag = userService.registerUser(sysUser, tenantId); + if (!regFlag) { + throw new UserException("user.register.error"); + } + recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + public void validateCaptcha(String tenantId, String username, String code, String uuid) { + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + /** + * 记录登录信息 + * + * @param tenantId 租户ID + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * @return + */ + private void recordLogininfor(String tenantId, String username, String status, String message) { + LogininforEvent logininforEvent = new LogininforEvent(); + logininforEvent.setTenantId(tenantId); + logininforEvent.setUsername(username); + logininforEvent.setStatus(status); + logininforEvent.setMessage(message); + logininforEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(logininforEvent); + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/AppSmsAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/AppSmsAuthStrategy.java new file mode 100644 index 0000000..f185e77 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/AppSmsAuthStrategy.java @@ -0,0 +1,130 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fuyuanshen.app.domain.AppUser; +import com.fuyuanshen.app.domain.vo.AppUserVo; +import com.fuyuanshen.app.mapper.AppUserMapper; +import com.fuyuanshen.app.service.AppLoginService; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.AppLoginUser; +import com.fuyuanshen.common.core.domain.model.SmsLoginBody; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +import java.util.Date; + +/** + * 短信认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service("appSms" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class AppSmsAuthStrategy implements IAuthStrategy { + + private final AppLoginService loginService; + private final AppUserMapper appUserMapper; + private final RestClient.Builder builder; + + @Override + public LoginVo login(String body, SysClientVo client) { + SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); + ValidatorUtils.validate(loginBody); + String tenantId = loginBody.getTenantId(); + String phonenumber = loginBody.getPhonenumber(); + String smsCode = loginBody.getSmsCode(); + AppLoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + loginService.checkLogin(LoginType.SMS, tenantId, phonenumber, () -> !validateSmsCode(tenantId, phonenumber, smsCode)); + AppUserVo user = loadUserByPhonenumber(phonenumber); + if (ObjectUtil.isNull(user)) { + //新增Appuser + addAppUser(tenantId, phonenumber); + + user = appUserMapper.selectVoOne(new LambdaQueryWrapper().eq(AppUser::getPhonenumber, phonenumber)); +// loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists", phonenumber)); +// throw new UserException("user.not.exists", phonenumber); + } + + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(AppLoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + AppLoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + return loginVo; + } + + private void addAppUser(String tenantId, String phonenumber) { + AppUser appUser = new AppUser(); + appUser.setPhonenumber(phonenumber); + appUser.setUserName(phonenumber); + appUser.setStatus("0"); + appUser.setLoginDate(new Date()); + appUser.setLoginIp(ServletUtils.getClientIP()); + appUser.setTenantId(tenantId); + appUserMapper.insert(appUser); + } + + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); + } + + private AppUserVo loadUserByPhonenumber(String phonenumber) { + AppUserVo user = appUserMapper.selectVoOne(new LambdaQueryWrapper().eq(AppUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); +// throw new UserException("user.not.exists", phonenumber); + // 新增AppUser用户 + return null; + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + return user; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/EmailAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/EmailAuthStrategy.java new file mode 100644 index 0000000..612d6ca --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/EmailAuthStrategy.java @@ -0,0 +1,102 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.EmailLoginBody; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 邮件认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service("email" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class EmailAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public LoginVo login(String body, SysClientVo client) { + EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); + ValidatorUtils.validate(loginBody); + String tenantId = loginBody.getTenantId(); + String email = loginBody.getEmail(); + String emailCode = loginBody.getEmailCode(); + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByEmail(email); + loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + LoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + return loginVo; + } + + /** + * 校验邮箱验证码 + */ + private boolean validateEmailCode(String tenantId, String email, String emailCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); + if (StringUtils.isBlank(code)) { + loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(emailCode); + } + + private SysUserVo loadUserByEmail(String email) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, email)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", email); + throw new UserException("user.not.exists", email); + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", email); + throw new UserException("user.blocked", email); + } + return user; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/PasswordAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/PasswordAuthStrategy.java new file mode 100644 index 0000000..dee3300 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/PasswordAuthStrategy.java @@ -0,0 +1,123 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.domain.model.PasswordLoginBody; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.user.CaptchaException; +import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.common.web.config.properties.CaptchaProperties; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 密码认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service("password" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class PasswordAuthStrategy implements IAuthStrategy { + + private final CaptchaProperties captchaProperties; + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public LoginVo login(String body, SysClientVo client) { + PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); + ValidatorUtils.validate(loginBody); + String tenantId = loginBody.getTenantId(); + String username = loginBody.getUsername(); + String password = loginBody.getPassword(); + String code = loginBody.getCode(); + String uuid = loginBody.getUuid(); + + boolean captchaEnabled = captchaProperties.getEnable(); + // 验证码开关 + if (captchaEnabled) { + validateCaptcha(tenantId, username, code, uuid); + } + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByUsername(username); + loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); + // 此处可根据登录用户的数据不同 自行创建 loginUser + return loginService.buildLoginUser(user); + }); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + LoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + return loginVo; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + */ + private void validateCaptcha(String tenantId, String username, String code, String uuid) { + String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); + String captcha = RedisUtils.getCacheObject(verifyKey); + RedisUtils.deleteObject(verifyKey); + if (captcha == null) { + loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) { + loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); + throw new CaptchaException(); + } + } + + private SysUserVo loadUserByUsername(String username) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, username)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", username); + throw new UserException("user.not.exists", username); + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new UserException("user.blocked", username); + } + return user; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SmsAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SmsAuthStrategy.java new file mode 100644 index 0000000..6d49d72 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SmsAuthStrategy.java @@ -0,0 +1,102 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.domain.model.SmsLoginBody; +import com.fuyuanshen.common.core.enums.LoginType; +import com.fuyuanshen.common.core.exception.user.CaptchaExpireException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 短信认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service("sms" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class SmsAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + private final SysUserMapper userMapper; + + @Override + public LoginVo login(String body, SysClientVo client) { + SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); + ValidatorUtils.validate(loginBody); + String tenantId = loginBody.getTenantId(); + String phonenumber = loginBody.getPhonenumber(); + String smsCode = loginBody.getSmsCode(); + LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { + SysUserVo user = loadUserByPhonenumber(phonenumber); + loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + LoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + return loginVo; + } + + /** + * 校验短信验证码 + */ + private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { + String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); + if (StringUtils.isBlank(code)) { + loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); + throw new CaptchaExpireException(); + } + return code.equals(smsCode); + } + + private SysUserVo loadUserByPhonenumber(String phonenumber) { + SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", phonenumber); + throw new UserException("user.not.exists", phonenumber); + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", phonenumber); + throw new UserException("user.blocked", phonenumber); + } + return user; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SocialAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SocialAuthStrategy.java new file mode 100644 index 0000000..857a185 --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/SocialAuthStrategy.java @@ -0,0 +1,131 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.http.Method; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.domain.model.SocialLoginBody; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.exception.user.UserException; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.social.config.properties.SocialProperties; +import com.fuyuanshen.common.social.utils.SocialUtils; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysSocialVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.system.service.ISysSocialService; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +/** + * 第三方授权策略 + * + * @author thiszhc is 三三 + */ +@Slf4j +@Service("social" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class SocialAuthStrategy implements IAuthStrategy { + + private final SocialProperties socialProperties; + private final ISysSocialService sysSocialService; + private final SysUserMapper userMapper; + private final SysLoginService loginService; + + /** + * 登录-第三方授权登录 + * + * @param body 登录信息 + * @param client 客户端信息 + */ + @Override + public LoginVo login(String body, SysClientVo client) { + SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class); + ValidatorUtils.validate(loginBody); + AuthResponse response = SocialUtils.loginAuth( + loginBody.getSource(), loginBody.getSocialCode(), + loginBody.getSocialState(), socialProperties); + if (!response.ok()) { + throw new ServiceException(response.getMsg()); + } + AuthUser authUserData = response.getData(); + if ("GITEE".equals(authUserData.getSource())) { + // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖 + HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/fys-Vue-Plus") + .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) + .executeAsync(); + HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/fys-Cloud-Plus") + .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) + .executeAsync(); + } + + List list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid()); + if (CollUtil.isEmpty(list)) { + throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); + } + SysSocialVo social; + if (TenantHelper.isEnable()) { + Optional opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId())); + if (opt.isEmpty()) { + throw new ServiceException("对不起,你没有权限登录当前租户!"); + } + social = opt.get(); + } else { + social = list.get(0); + } + LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> { + SysUserVo user = loadUser(social.getUserId()); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + return loginService.buildLoginUser(user); + }); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + LoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + return loginVo; + } + + private SysUserVo loadUser(Long userId) { + SysUserVo user = userMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", ""); + throw new UserException("user.not.exists", ""); + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", ""); + throw new UserException("user.blocked", ""); + } + return user; + } + +} diff --git a/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/XcxAuthStrategy.java b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/XcxAuthStrategy.java new file mode 100644 index 0000000..82d907b --- /dev/null +++ b/fys-admin/src/main/java/com/fuyuanshen/web/service/impl/XcxAuthStrategy.java @@ -0,0 +1,111 @@ +package com.fuyuanshen.web.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.request.AuthWechatMiniProgramRequest; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.model.XcxLoginBody; +import com.fuyuanshen.common.core.domain.model.XcxLoginUser; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.web.domain.vo.LoginVo; +import com.fuyuanshen.web.service.IAuthStrategy; +import com.fuyuanshen.web.service.SysLoginService; +import org.springframework.stereotype.Service; + +/** + * 小程序认证策略 + * + * @author Michelle.Chung + */ +@Slf4j +@Service("xcx" + IAuthStrategy.BASE_NAME) +@RequiredArgsConstructor +public class XcxAuthStrategy implements IAuthStrategy { + + private final SysLoginService loginService; + + @Override + public LoginVo login(String body, SysClientVo client) { + XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); + ValidatorUtils.validate(loginBody); + // xcxCode 为 小程序调用 wx.login 授权后获取 + String xcxCode = loginBody.getXcxCode(); + // 多个小程序识别使用 + String appid = loginBody.getAppid(); + + // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid + AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder() + .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥") + .ignoreCheckRedirectUri(true).ignoreCheckState(true).build()); + AuthCallback authCallback = new AuthCallback(); + authCallback.setCode(xcxCode); + AuthResponse resp = authRequest.login(authCallback); + String openid, unionId; + if (resp.ok()) { + AuthToken token = resp.getData().getToken(); + openid = token.getOpenId(); + // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。 + unionId = token.getUnionId(); + } else { + throw new ServiceException(resp.getMsg()); + } + // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 + SysUserVo user = loadUserByOpenid(openid); + // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 + XcxLoginUser loginUser = new XcxLoginUser(); + loginUser.setTenantId(user.getTenantId()); + loginUser.setUserId(user.getUserId()); + loginUser.setUsername(user.getUserName()); + loginUser.setNickname(user.getNickName()); + loginUser.setUserType(user.getUserType()); + loginUser.setClientKey(client.getClientKey()); + loginUser.setDeviceType(client.getDeviceType()); + loginUser.setOpenid(openid); + + SaLoginParameter model = new SaLoginParameter(); + model.setDeviceType(client.getDeviceType()); + // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 + // 例如: 后台用户30分钟过期 app用户1天过期 + model.setTimeout(client.getTimeout()); + model.setActiveTimeout(client.getActiveTimeout()); + model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); + // 生成token + LoginHelper.login(loginUser, model); + + LoginVo loginVo = new LoginVo(); + loginVo.setAccessToken(StpUtil.getTokenValue()); + loginVo.setExpireIn(StpUtil.getTokenTimeout()); + loginVo.setClientId(client.getClientId()); + loginVo.setOpenid(openid); + return loginVo; + } + + private SysUserVo loadUserByOpenid(String openid) { + // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 + // todo 自行实现 userService.selectUserByOpenid(openid); + SysUserVo user = new SysUserVo(); + if (ObjectUtil.isNull(user)) { + log.info("登录用户:{} 不存在.", openid); + // todo 用户不存在 业务逻辑自行实现 + } else if (SystemConstants.DISABLE.equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", openid); + // todo 用户已被停用 业务逻辑自行实现 + } + return user; + } + +} diff --git a/fys-admin/src/main/resources/application-dev.yml b/fys-admin/src/main/resources/application-dev.yml new file mode 100644 index 0000000..8e300e7 --- /dev/null +++ b/fys-admin/src/main/resources/application-dev.yml @@ -0,0 +1,308 @@ +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: false + url: http://localhost:9090/admin + instance: + service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} + username: @monitor.username@ + password: @monitor.password@ + +--- # snail-job 配置 +snail-job: + enabled: false + # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group: "fys_group" + # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表 + token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 17888 + # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 + namespace: ${spring.profiles.active} + # 随主应用端口漂移 + port: 2${server.port} + # 客户端ip指定 + host: + # RPC类型: netty, grpc + rpc-type: grpc + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://47.120.79.150:3306/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: root + password: Jq_123456# +# # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: 47.120.79.150 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 2 + # redis 密码必须配置 + password: xhYc_djkl382^#780! + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl.enabled: false + +# redisson 配置 +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 4 + # Netty线程池数量 + nettyThreads: 8 + # 单节点配置 + singleServerConfig: + # 客户端名称 不能用中文 + clientName: fys-Vue-Plus + # 最小空闲连接数 + connectionMinimumIdleSize: 8 + # 连接池大小 + connectionPoolSize: 32 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 +sms: + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 com.fuyuanshen.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: LTAI5tJdDNpZootsPQ5hdELx + # 称为accessSecret有些称之为apiSecret + access-key-secret: mU4WtffcCXpHPz5tLwQpaGtLsJXONt + #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置 + template-id: SMS_322180518 + #模板变量 上述模板的变量 + templateName: code + signature: 湖北星汉研创科技 +# sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + + +--- # 三方授权 +justauth: + # 前端外网访问地址 + address: http://localhost:80 + type: + maxkey: + # maxkey 服务器地址 + # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 + server-url: http://sso.maxkey.top + client-id: 876892492581044224 + client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 + redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [openid, email, phone, profile] + qq: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=qq + union-id: false + weibo: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=weibo + gitee: + client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 + client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac + redirect-uri: ${justauth.address}/social-callback?source=gitee + dingtalk: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=dingtalk + baidu: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=baidu + csdn: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=csdn + coding: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=coding + coding-group-name: xx + oschina: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=oschina + alipay_wallet: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet + alipay-public-key: MIIB**************DAQAB + wechat_open: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_open + wechat_mp: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_mp + wechat_enterprise: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise + agent-id: 1000002 + gitlab: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea + + +# 文件存储路径 +file: + mac: + path: ~/file/ + avatar: ~/avatar/ + linux: + path: /home/eladmin/file/ + avatar: /home/eladmin/avatar/ + windows: + path: C:\eladmin\file\ + avatar: C:\eladmin\avatar\ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + device: + pic: C:\eladmin\file\ #设备图片存储路径 + ip: http://fuyuanshen.com:81/ #服务器地址 + app_avatar: + pic: C:\eladmin\file\ #设备图片存储路径 + #ip: http://fuyuanshen.com:81/ #服务器地址 + ip: https://fuyuanshen.com/ #服务器地址 +# MQTT配置 +mqtt: + username: admin + password: #YtvpSfCNG + url: tcp://47.120.79.150:2883 + subClientId: fys_subClient + subTopic: worker/alert/#,worker/location/# + pubTopic: worker/location + pubClientId: fys_pubClient \ No newline at end of file diff --git a/fys-admin/src/main/resources/application-jingquan.yml b/fys-admin/src/main/resources/application-jingquan.yml new file mode 100644 index 0000000..811dcfd --- /dev/null +++ b/fys-admin/src/main/resources/application-jingquan.yml @@ -0,0 +1,304 @@ +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: false + url: http://localhost:9090/admin + instance: + service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} + username: @monitor.username@ + password: @monitor.password@ + +--- # snail-job 配置 +snail-job: + enabled: false + # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group: "fys_group" + # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表 + token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 17888 + # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 + namespace: ${spring.profiles.active} + # 随主应用端口漂移 + port: 2${server.port} + # 客户端ip指定 + host: + # RPC类型: netty, grpc + rpc-type: grpc + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://47.120.79.150:3306/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: root + password: Jq_123456# +# # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: 47.120.79.150 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 2 + # redis 密码必须配置 + password: xhYc_djkl382^#780! + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl.enabled: false + +# redisson 配置 +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 4 + # Netty线程池数量 + nettyThreads: 8 + # 单节点配置 + singleServerConfig: + # 客户端名称 不能用中文 + clientName: fys-Vue-Plus + # 最小空闲连接数 + connectionMinimumIdleSize: 8 + # 连接池大小 + connectionPoolSize: 32 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 +sms: + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 com.fuyuanshen.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + + +--- # 三方授权 +justauth: + # 前端外网访问地址 + address: http://localhost:80 + type: + maxkey: + # maxkey 服务器地址 + # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 + server-url: http://sso.maxkey.top + client-id: 876892492581044224 + client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 + redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [openid, email, phone, profile] + qq: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=qq + union-id: false + weibo: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=weibo + gitee: + client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 + client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac + redirect-uri: ${justauth.address}/social-callback?source=gitee + dingtalk: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=dingtalk + baidu: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=baidu + csdn: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=csdn + coding: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=coding + coding-group-name: xx + oschina: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=oschina + alipay_wallet: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet + alipay-public-key: MIIB**************DAQAB + wechat_open: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_open + wechat_mp: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_mp + wechat_enterprise: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise + agent-id: 1000002 + gitlab: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea + + +# 文件存储路径 +file: + mac: + path: ~/file/ + avatar: ~/avatar/ + linux: + path: /home/eladmin/file/ + avatar: /home/eladmin/avatar/ + windows: + path: C:\eladmin\file\ + avatar: C:\eladmin\avatar\ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + device: + pic: C:\eladmin\file\ #设备图片存储路径 + ip: http://fuyuanshen.com:81/ #服务器地址 + app_avatar: + pic: C:\eladmin\file\ #设备图片存储路径 + #ip: http://fuyuanshen.com:81/ #服务器地址 + ip: https://fuyuanshen.com/ #服务器地址 +# MQTT配置 +mqtt: + username: admin + password: fys123456 + url: tcp://47.107.152.87:1883 + subClientId: fys_subClient_01 + subTopic: worker/alert/#,worker/location/# + pubTopic: worker/location + pubClientId: fys_pubClient_01 \ No newline at end of file diff --git a/fys-admin/src/main/resources/application-prod.yml b/fys-admin/src/main/resources/application-prod.yml new file mode 100644 index 0000000..45255db --- /dev/null +++ b/fys-admin/src/main/resources/application-prod.yml @@ -0,0 +1,308 @@ +--- # 临时文件存储位置 避免临时文件被系统清理报错 +spring.servlet.multipart.location: /fys/server/temp + +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: true + url: http://localhost:9090/admin + instance: + service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} + username: @monitor.username@ + password: @monitor.password@ + +--- # snail-job 配置 +snail-job: + enabled: true + # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group: "fys_group" + # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表 + token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 17888 + # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 + namespace: ${spring.profiles.active} + # 随主应用端口漂移 + port: 2${server.port} + # 客户端ip指定 + host: + # RPC类型: netty, grpc + rpc-type: grpc + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: false + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://localhost:3306/fys_vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: root + password: Jz_5623_cl1 +# # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: localhost + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 1 + # redis 密码必须配置 + password: re_fs_11520631 + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl.enabled: false + +# redisson 配置 +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 16 + # Netty线程池数量 + nettyThreads: 32 + # 单节点配置 + singleServerConfig: + # 客户端名称 不能用中文 + clientName: fys-Vue-Plus + # 最小空闲连接数 + connectionMinimumIdleSize: 32 + # 连接池大小 + connectionPoolSize: 64 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 +sms: + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 com.fuyuanshen.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + +--- # 三方授权 +justauth: + # 前端外网访问地址 + address: http://localhost:80 + type: + maxkey: + # maxkey 服务器地址 + # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 + server-url: http://sso.maxkey.top + client-id: 876892492581044224 + client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 + redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [ openid, email, phone, profile ] + qq: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=qq + union-id: false + weibo: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=weibo + gitee: + client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 + client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac + redirect-uri: ${justauth.address}/social-callback?source=gitee + dingtalk: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=dingtalk + baidu: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=baidu + csdn: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=csdn + coding: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=coding + coding-group-name: xx + oschina: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=oschina + alipay_wallet: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet + alipay-public-key: MIIB**************DAQAB + wechat_open: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_open + wechat_mp: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_mp + wechat_enterprise: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise + agent-id: 1000002 + gitlab: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea + + +# MQTT配置 +mqtt: + username: admin + password: #YtvpSfCNG + url: tcp://47.120.79.150:2883 + subClientId: fys_subClient + subTopic: worker/alert/#,worker/location/# + pubTopic: worker/location + pubClientId: fys_pubClient + + +# 文件存储路径 +file: + mac: + path: ~/file/ + avatar: ~/avatar/ + linux: + path: /home/eladmin/file/ + avatar: /home/eladmin/avatar/ + windows: + path: C:\eladmin\file\ + avatar: C:\eladmin\avatar\ + # 文件大小 /M + maxSize: 100 + avatarMaxSize: 5 + device: + pic: C:\eladmin\file\ #设备图片存储路径 + ip: http://fuyuanshen.com:81/ #服务器地址 + app_avatar: + pic: C:\eladmin\file\ #设备图片存储路径 + #ip: http://fuyuanshen.com:81/ #服务器地址 + ip: https://fuyuanshen.com/ #服务器地址 \ No newline at end of file diff --git a/fys-admin/src/main/resources/application-test.yml b/fys-admin/src/main/resources/application-test.yml new file mode 100644 index 0000000..8f699b3 --- /dev/null +++ b/fys-admin/src/main/resources/application-test.yml @@ -0,0 +1,272 @@ +--- # 监控中心配置 +spring.boot.admin.client: + # 增加客户端开关 + enabled: false + url: http://localhost:9090/admin + instance: + service-host-type: IP + metadata: + username: ${spring.boot.admin.client.username} + userpassword: ${spring.boot.admin.client.password} + username: @monitor.username@ + password: @monitor.password@ + +--- # snail-job 配置 +snail-job: + enabled: false + # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 + group: "fys_group" + # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表 + token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" + server: + host: 127.0.0.1 + port: 17888 + # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 + namespace: ${spring.profiles.active} + # 随主应用端口漂移 + port: 2${server.port} + # 客户端ip指定 + host: + # RPC类型: netty, grpc + rpc-type: grpc + +--- # 数据源配置 +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content + dynamic: + # 性能分析插件(有性能损耗 不建议生产环境使用) + p6spy: true + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + # 严格模式 匹配不到数据源则报错 + strict: true + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driverClassName: com.mysql.cj.jdbc.Driver + # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 + # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) + url: jdbc:mysql://localhost:3306/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: root + password: root +# # 从库数据源 +# slave: +# lazy: true +# type: ${spring.datasource.type} +# driverClassName: com.mysql.cj.jdbc.Driver +# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true +# username: +# password: +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: jdbc:oracle:thin:@//localhost:1521/XE +# username: ROOT +# password: root +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true +# username: root +# password: root +# sqlserver: +# type: ${spring.datasource.type} +# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true +# username: SA +# password: root + hikari: + # 最大连接池数量 + maxPoolSize: 20 + # 最小空闲线程数量 + minIdle: 10 + # 配置获取连接等待超时的时间 + connectionTimeout: 30000 + # 校验超时时间 + validationTimeout: 5000 + # 空闲连接存活最大时间,默认10分钟 + idleTimeout: 600000 + # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 + maxLifetime: 1800000 + # 多久检查一次连接的活性 + keepaliveTime: 30000 + +--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) +spring.data: + redis: + # 地址 + host: 123.207.99.140 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 2 + # redis 密码必须配置 + password: ccxx11234 + # 连接超时时间 + timeout: 10s + # 是否开启ssl + ssl.enabled: false + +# redisson 配置 +redisson: + # redis key前缀 + keyPrefix: + # 线程池数量 + threads: 4 + # Netty线程池数量 + nettyThreads: 8 + # 单节点配置 + singleServerConfig: + # 客户端名称 不能用中文 + clientName: fys-Vue-Plus + # 最小空闲连接数 + connectionMinimumIdleSize: 8 + # 连接池大小 + connectionPoolSize: 32 + # 连接空闲超时,单位:毫秒 + idleConnectionTimeout: 10000 + # 命令等待超时,单位:毫秒 + timeout: 3000 + # 发布和订阅连接池大小 + subscriptionConnectionPoolSize: 50 + +--- # mail 邮件发送 +mail: + enabled: false + host: smtp.163.com + port: 465 + # 是否需要用户名密码验证 + auth: true + # 发送方,遵循RFC-822标准 + from: xxx@163.com + # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) + user: xxx@163.com + # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) + pass: xxxxxxxxxx + # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 + starttlsEnable: true + # 使用SSL安全连接 + sslEnable: true + # SMTP超时时长,单位毫秒,缺省值不超时 + timeout: 0 + # Socket连接超时值,单位毫秒,缺省值不超时 + connectionTimeout: 0 + +--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 +# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 +sms: + # 配置源类型用于标定配置来源(interface,yaml) + config-type: yaml + # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 + restricted: true + # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 + minute-max: 1 + # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 + account-max: 30 + # 以下配置来自于 com.fuyuanshen.sms4j.provider.config.BaseConfig类中 + blends: + # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 + # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 + config1: + # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: alibaba + # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 + access-key-id: 您的accessKey + # 称为accessSecret有些称之为apiSecret + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + config2: + # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 + supplier: tencent + access-key-id: 您的accessKey + access-key-secret: 您的accessKeySecret + signature: 您的短信签名 + sdk-app-id: 您的sdkAppId + + +--- # 三方授权 +justauth: + # 前端外网访问地址 + address: http://localhost:80 + type: + maxkey: + # maxkey 服务器地址 + # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 + server-url: http://sso.maxkey.top + client-id: 876892492581044224 + client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 + redirect-uri: ${justauth.address}/social-callback?source=maxkey + topiam: + # topiam 服务器地址 + server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol + client-id: 449c4*********937************759 + client-secret: ac7***********1e0************28d + redirect-uri: ${justauth.address}/social-callback?source=topiam + scopes: [openid, email, phone, profile] + qq: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=qq + union-id: false + weibo: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=weibo + gitee: + client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 + client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac + redirect-uri: ${justauth.address}/social-callback?source=gitee + dingtalk: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=dingtalk + baidu: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=baidu + csdn: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=csdn + coding: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=coding + coding-group-name: xx + oschina: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=oschina + alipay_wallet: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet + alipay-public-key: MIIB**************DAQAB + wechat_open: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_open + wechat_mp: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_mp + wechat_enterprise: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise + agent-id: 1000002 + gitlab: + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitlab + gitea: + # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204 + # gitea 服务器地址 + server-url: https://demo.gitea.com + client-id: 10**********6 + client-secret: 1f7d08**********5b7**********29e + redirect-uri: ${justauth.address}/social-callback?source=gitea diff --git a/fys-admin/src/main/resources/application.yml b/fys-admin/src/main/resources/application.yml new file mode 100644 index 0000000..3744835 --- /dev/null +++ b/fys-admin/src/main/resources/application.yml @@ -0,0 +1,288 @@ +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8000 + servlet: + # 应用的访问路径 + context-path: / + # undertow 配置 + undertow: + # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 + max-http-post-size: -1 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + # 每块buffer的空间大小,越小的空间被利用越充分 + buffer-size: 512 + # 是否分配的直接内存 + direct-buffers: true + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 8 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 256 + +captcha: + # 是否启用验证码校验 + enable: true + # 验证码类型 math 数组计算 char 字符验证 + type: MATH + # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 + category: CIRCLE + # 数字验证码位数 + numberLength: 1 + # 字符验证码长度 + charLength: 4 + +# 日志配置 +logging: + level: + com.fuyuanshen: @logging.level@ + org.springframework: warn + org.mybatis.spring.mapper: error + org.apache.fury: warn + com.fuyuanshen.web.service.impl.ClientServiceImpl: debug + config: classpath:logback-plus.xml + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + application: + name: fys-Vue-Plus + threads: + # 开启虚拟线程 仅jdk21可用 + virtual: + enabled: false + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: @profiles.active@ + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + mvc: + # 设置静态资源路径 防止所有请求都去查静态资源 + static-path-pattern: /static/** + format: + date-time: yyyy-MM-dd HH:mm:ss + jackson: + # 日期格式化 + date-format: yyyy-MM-dd HH:mm:ss + serialization: + # 格式化输出 + indent_output: false + # 忽略无法转换的对象 + fail_on_empty_beans: false + deserialization: + # 允许对象忽略json中不存在的属性 + fail_on_unknown_properties: false + +# Sa-Token配置 +sa-token: + # token名称 (同时也是cookie名称) + token-name: Authorization + # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) + is-concurrent: true + # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) + is-share: false + # jwt秘钥 + jwt-secret-key: abcdefghijklmnopqrstuvwxyz + +# security配置 +security: + # 排除路径 + excludes: + - /*.html + - /**/*.html + - /**/*.css + - /**/*.js + - /favicon.ico + - /error + - /*/api-docs + - /*/api-docs/** + - /warm-flow-ui/config + +# 多租户配置 +tenant: + # 是否开启 + enable: true + # 排除表 + excludes: + - sys_menu + - sys_tenant + - sys_tenant_package + - sys_role_dept + - sys_role_menu + - sys_user_post + - sys_user_role + - sys_client + - sys_oss_config + - app_menu + - app_user_role + - app_role_menu + +# MyBatisPlus配置 +# https://baomidou.com/config/ +mybatis-plus: + # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效 + enableLogicDelete: false + # 多包名使用 例如 com.fuyuanshen.**.mapper,org.xxx.**.mapper + mapperPackage: com.fuyuanshen.**.mapper + # 对应的 XML 文件位置 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.fuyuanshen.**.domain + global-config: + dbConfig: + # 主键类型 + # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID + # 如需改为自增 需要将数据库表全部设置为自增 + idType: ASSIGN_ID + +# 数据加密 +mybatis-encryptor: + # 是否开启加密 + enable: false + # 默认加密算法 + algorithm: BASE64 + # 编码方式 BASE64/HEX。默认BASE64 + encode: BASE64 + # 安全秘钥 对称算法的秘钥 如:AES,SM4 + password: + # 公私钥 非对称算法的公私钥 如:SM2,RSA + publicKey: + privateKey: + +# api接口加密 +api-decrypt: + # 是否开启全局接口加密 + enabled: true + # AES 加密头标识 + headerFlag: encrypt-key + # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 + # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= + publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ== + # 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 + # 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== + privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= + +springdoc: + api-docs: + # 是否开启接口文档 + enabled: true + info: + # 标题 + title: '标题:fys-Vue-Plus多租户管理系统_接口文档' + # 描述 + description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' + # 版本 + version: '版本号: ${fys.version}' + # 作者信息 + contact: + name: Lion Li + email: crazylionli@163.com + url: https://gitee.com/dromara/fys-Vue-Plus + components: + # 鉴权方式配置 + security-schemes: + apiKey: + type: APIKEY + in: HEADER + name: ${sa-token.token-name} + #这里定义了两个分组,可定义多个,也可以不定义 + group-configs: + - group: 1.演示模块 + packages-to-scan: com.fuyuanshen.demo + - group: 2.通用模块 + packages-to-scan: com.fuyuanshen.web + - group: 3.系统模块 + packages-to-scan: com.fuyuanshen.system + - group: 4.代码生成模块 + packages-to-scan: com.fuyuanshen.generator + - group: 5.工作流模块 + packages-to-scan: com.fuyuanshen.workflow + - group: 设备管理模块 + packages-to-scan: com.fuyuanshen.equipment + - group: 客户管理模块 + packages-to-scan: com.fuyuanshen.customer + - group: APP模块 + packages-to-scan: com.fuyuanshen.app + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludeUrls: + - /system/notice + +# 全局线程池相关配置 +# 如使用JDK21请直接使用虚拟线程 不要开启此配置 +thread-pool: + # 是否开启线程池 + enabled: false + # 队列最大长度 + queueCapacity: 128 + # 线程池维护线程所允许的空闲时间 + keepAliveSeconds: 300 + +--- # 分布式锁 lock4j 全局配置 +lock4j: + # 获取分布式锁超时时间,默认为 3000 毫秒 + acquire-timeout: 3000 + # 分布式锁的超时时间,默认为 30 秒 + expire: 30000 + +--- # Actuator 监控端点的配置项 +management: + endpoints: + web: + exposure: + include: '*' + endpoint: + health: + show-details: ALWAYS + logfile: + external-file: ./logs/sys-console.log + +--- # 默认/推荐使用sse推送 +sse: + enabled: true + path: /resource/sse + +--- # websocket +websocket: + # 如果关闭 需要和前端开关一起关闭 + enabled: false + # 路径 + path: /resource/websocket + # 设置访问源地址 + allowedOrigins: '*' + +--- # warm-flow工作流配置 +warm-flow: + # 是否开启工作流,默认true + enabled: true + # 是否开启设计器ui + ui: true + # 默认Authorization,如果有多个token,用逗号分隔 + token-name: ${sa-token.token-name},clientid + # 流程状态对应的三元色 + chart-status-color: + ## 未办理 + - 62,62,62 + ## 待办理 + - 255,205,23 + ## 已办理 + - 157,255,0 diff --git a/fys-admin/src/main/resources/banner.txt b/fys-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..21b1126 --- /dev/null +++ b/fys-admin/src/main/resources/banner.txt @@ -0,0 +1,8 @@ +Application Version: ${revision} +Spring Boot Version: ${spring-boot.version} +__________ _____.___.__ ____ ____ __________.__ +\______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______ + | _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/ + | | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \ + |____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ > + \/ \/ \/ \/ diff --git a/fys-admin/src/main/resources/i18n/messages.properties b/fys-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..cce11c8 --- /dev/null +++ b/fys-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,61 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +auth.grant.type.error=认证权限类型错误 +auth.grant.type.blocked=认证权限类型已禁用 +auth.grant.type.not.blank=认证权限类型不能为空 +auth.clientid.not.blank=认证客户端id不能为空 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序[code]不能为空 +social.source.not.blank=第三方登录平台[source]不能为空 +social.code.not.blank=第三方登录平台[code]不能为空 +social.state.not.blank=第三方登录平台[state]不能为空 +##租户 +tenant.number.not.blank=租户编号不能为空 +tenant.not.exists=对不起, 您的租户不存在,请联系管理员 +tenant.blocked=对不起,您的租户已禁用,请联系管理员 +tenant.expired=对不起,您的租户已过期,请联系管理员 diff --git a/fys-admin/src/main/resources/i18n/messages_en_US.properties b/fys-admin/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..f948c4a --- /dev/null +++ b/fys-admin/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,61 @@ +#错误消息 +not.null=* Required fill in +user.jcaptcha.error=Captcha error +user.jcaptcha.expire=Captcha invalid +user.not.exists=Sorry, your account: {0} does not exist +user.password.not.match=User does not exist/Password error +user.password.retry.limit.count=Password input error {0} times +user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes +user.password.delete=Sorry, your account:{0} has been deleted +user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator +role.blocked=Role disabled,please contact administrators +user.logout.success=Exit successful +length.not.valid=The length must be between {min} and {max} characters +user.username.not.blank=Username cannot be blank +user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number +user.username.length.valid=Account length must be between {min} and {max} characters +user.password.not.blank=Password cannot be empty +user.password.length.valid=Password length must be between {min} and {max} characters +user.password.not.valid=* 5-50 characters +user.email.not.valid=Mailbox format error +user.email.not.blank=Mailbox cannot be blank +user.phonenumber.not.blank=Phone number cannot be blank +user.mobile.phone.number.not.valid=Phone number format error +user.login.success=Login successful +user.register.success=Register successful +user.register.save.error=Failed to save user {0}, The registered account already exists +user.register.error=Register failed, please contact system administrator +user.notfound=Please login again +user.forcelogout=The administrator is forced to exit,please login again +user.unknown.error=Unknown error, please login again +auth.grant.type.error=Auth grant type error +auth.grant.type.blocked=Auth grant type disabled +auth.grant.type.not.blank=Auth grant type cannot be blank +auth.clientid.not.blank=Auth clientid cannot be blank +##文件上传消息 +upload.exceed.maxSize=The uploaded file size exceeds the limit file size!
the maximum allowed file size is:{0}MB! +upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters +##权限 +no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}] +no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}] +no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}] +no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}] +no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}] +no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}] +repeat.submit.message=Repeat submit is not allowed, please try again later +rate.limiter.message=Visit too frequently, please try again later +sms.code.not.blank=Sms code cannot be blank +sms.code.retry.limit.count=Sms code input error {0} times +sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes +email.code.not.blank=Email code cannot be blank +email.code.retry.limit.count=Email code input error {0} times +email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes +xcx.code.not.blank=Mini program [code] cannot be blank +social.source.not.blank=Social login platform [source] cannot be blank +social.code.not.blank=Social login platform [code] cannot be blank +social.state.not.blank=Social login platform [state] cannot be blank +##租户 +tenant.number.not.blank=Tenant number cannot be blank +tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator +tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator +tenant.expired=Sorry, your tenant has expired. Please contact the administrator. diff --git a/fys-admin/src/main/resources/i18n/messages_zh_CN.properties b/fys-admin/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..cce11c8 --- /dev/null +++ b/fys-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,61 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=对不起, 您的账号:{0} 不存在. +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号:{0} 已被删除 +user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +user.logout.success=退出成功 +length.not.valid=长度必须在{min}到{max}个字符之间 +user.username.not.blank=用户名不能为空 +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.username.length.valid=账户长度必须在{min}到{max}个字符之间 +user.password.not.blank=用户密码不能为空 +user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 +user.password.not.valid=* 5-50个字符 +user.email.not.valid=邮箱格式错误 +user.email.not.blank=邮箱不能为空 +user.phonenumber.not.blank=用户手机号不能为空 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.register.save.error=保存用户 {0} 失败,注册账号已存在 +user.register.error=注册失败,请联系系统管理人员 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 +auth.grant.type.error=认证权限类型错误 +auth.grant.type.blocked=认证权限类型已禁用 +auth.grant.type.not.blank=认证权限类型不能为空 +auth.clientid.not.blank=认证客户端id不能为空 +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] +repeat.submit.message=不允许重复提交,请稍候再试 +rate.limiter.message=访问过于频繁,请稍候再试 +sms.code.not.blank=短信验证码不能为空 +sms.code.retry.limit.count=短信验证码输入错误{0}次 +sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 +email.code.not.blank=邮箱验证码不能为空 +email.code.retry.limit.count=邮箱验证码输入错误{0}次 +email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 +xcx.code.not.blank=小程序[code]不能为空 +social.source.not.blank=第三方登录平台[source]不能为空 +social.code.not.blank=第三方登录平台[code]不能为空 +social.state.not.blank=第三方登录平台[state]不能为空 +##租户 +tenant.number.not.blank=租户编号不能为空 +tenant.not.exists=对不起, 您的租户不存在,请联系管理员 +tenant.blocked=对不起,您的租户已禁用,请联系管理员 +tenant.expired=对不起,您的租户已过期,请联系管理员 diff --git a/fys-admin/src/main/resources/ip2region.xdb b/fys-admin/src/main/resources/ip2region.xdb new file mode 100644 index 0000000..7052c05 Binary files /dev/null and b/fys-admin/src/main/resources/ip2region.xdb differ diff --git a/fys-admin/src/main/resources/logback-plus.xml b/fys-admin/src/main/resources/logback-plus.xml new file mode 100644 index 0000000..afb8124 --- /dev/null +++ b/fys-admin/src/main/resources/logback-plus.xml @@ -0,0 +1,129 @@ + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + ${log.path}/sys-console.log + + + + + + + + ${log.pattern} + utf-8 + + + + INFO + + + + + + ${log.path}/sys-info.log + + + + + + + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + + + + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + 0 + + 512 + + + + + + + + 0 + + 512 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fys-admin/src/test/java/com/fuyuanshen/test/AssertUnitTest.java b/fys-admin/src/test/java/com/fuyuanshen/test/AssertUnitTest.java new file mode 100644 index 0000000..47248ee --- /dev/null +++ b/fys-admin/src/test/java/com/fuyuanshen/test/AssertUnitTest.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.test; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * 断言单元测试案例 + * + * @author Lion Li + */ +@DisplayName("断言单元测试案例") +public class AssertUnitTest { + + @DisplayName("测试 assertEquals 方法") + @Test + public void testAssertEquals() { + Assertions.assertEquals("666", new String("666")); + Assertions.assertNotEquals("666", new String("666")); + } + + @DisplayName("测试 assertSame 方法") + @Test + public void testAssertSame() { + Object obj = new Object(); + Object obj1 = obj; + Assertions.assertSame(obj, obj1); + Assertions.assertNotSame(obj, obj1); + } + + @DisplayName("测试 assertTrue 方法") + @Test + public void testAssertTrue() { + Assertions.assertTrue(true); + Assertions.assertFalse(true); + } + + @DisplayName("测试 assertNull 方法") + @Test + public void testAssertNull() { + Assertions.assertNull(null); + Assertions.assertNotNull(null); + } + +} diff --git a/fys-admin/src/test/java/com/fuyuanshen/test/DemoUnitTest.java b/fys-admin/src/test/java/com/fuyuanshen/test/DemoUnitTest.java new file mode 100644 index 0000000..33177e1 --- /dev/null +++ b/fys-admin/src/test/java/com/fuyuanshen/test/DemoUnitTest.java @@ -0,0 +1,70 @@ +package com.fuyuanshen.test; + +import com.fuyuanshen.common.web.config.properties.CaptchaProperties; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.concurrent.TimeUnit; + +/** + * 单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件 +@DisplayName("单元测试案例") +public class DemoUnitTest { + + @Autowired + private CaptchaProperties captchaProperties; + + @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") + @Test + public void testTest() { + System.out.println(captchaProperties); + } + + @Disabled + @DisplayName("测试 @Disabled 注解") + @Test + public void testDisabled() { + System.out.println(captchaProperties); + } + + @Timeout(value = 2L, unit = TimeUnit.SECONDS) + @DisplayName("测试 @Timeout 注解") + @Test + public void testTimeout() throws InterruptedException { + Thread.sleep(3000); + System.out.println(captchaProperties); + } + + + @DisplayName("测试 @RepeatedTest 注解") + @RepeatedTest(3) + public void testRepeatedTest() { + System.out.println(666); + } + + @BeforeAll + public static void testBeforeAll() { + System.out.println("@BeforeAll =================="); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + @AfterAll + public static void testAfterAll() { + System.out.println("@AfterAll =================="); + } + +} diff --git a/fys-admin/src/test/java/com/fuyuanshen/test/ParamUnitTest.java b/fys-admin/src/test/java/com/fuyuanshen/test/ParamUnitTest.java new file mode 100644 index 0000000..cd5f0c8 --- /dev/null +++ b/fys-admin/src/test/java/com/fuyuanshen/test/ParamUnitTest.java @@ -0,0 +1,72 @@ +package com.fuyuanshen.test; + +import com.fuyuanshen.common.core.enums.UserType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +/** + * 带参数单元测试案例 + * + * @author Lion Li + */ +@DisplayName("带参数单元测试案例") +public class ParamUnitTest { + + @DisplayName("测试 @ValueSource 注解") + @ParameterizedTest + @ValueSource(strings = {"t1", "t2", "t3"}) + public void testValueSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @NullSource 注解") + @ParameterizedTest + @NullSource + public void testNullSource(String str) { + System.out.println(str); + } + + @DisplayName("测试 @EnumSource 注解") + @ParameterizedTest + @EnumSource(UserType.class) + public void testEnumSource(UserType type) { + System.out.println(type.getUserType()); + } + + @DisplayName("测试 @MethodSource 注解") + @ParameterizedTest + @MethodSource("getParam") + public void testMethodSource(String str) { + System.out.println(str); + } + + public static Stream getParam() { + List list = new ArrayList<>(); + list.add("t1"); + list.add("t2"); + list.add("t3"); + return list.stream(); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/fys-admin/src/test/java/com/fuyuanshen/test/TagUnitTest.java b/fys-admin/src/test/java/com/fuyuanshen/test/TagUnitTest.java new file mode 100644 index 0000000..b4b0e50 --- /dev/null +++ b/fys-admin/src/test/java/com/fuyuanshen/test/TagUnitTest.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.test; + +import org.junit.jupiter.api.*; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * 标签单元测试案例 + * + * @author Lion Li + */ +@SpringBootTest +@DisplayName("标签单元测试案例") +public class TagUnitTest { + + @Tag("dev") + @DisplayName("测试 @Tag dev") + @Test + public void testTagDev() { + System.out.println("dev"); + } + + @Tag("prod") + @DisplayName("测试 @Tag prod") + @Test + public void testTagProd() { + System.out.println("prod"); + } + + @Tag("local") + @DisplayName("测试 @Tag local") + @Test + public void testTagLocal() { + System.out.println("local"); + } + + @Tag("exclude") + @DisplayName("测试 @Tag exclude") + @Test + public void testTagExclude() { + System.out.println("exclude"); + } + + @BeforeEach + public void testBeforeEach() { + System.out.println("@BeforeEach =================="); + } + + @AfterEach + public void testAfterEach() { + System.out.println("@AfterEach =================="); + } + + +} diff --git a/fys-common/fys-common-bom/pom.xml b/fys-common/fys-common-bom/pom.xml new file mode 100644 index 0000000..d9eea50 --- /dev/null +++ b/fys-common/fys-common-bom/pom.xml @@ -0,0 +1,185 @@ + + + 4.0.0 + + com.fuyuanshen + fys-common-bom + ${revision} + pom + + + fys-common-bom common依赖项 + + + + 5.4.0 + + + + + + + com.fuyuanshen + fys-common-core + ${revision} + + + + + com.fuyuanshen + fys-common-doc + ${revision} + + + + + com.fuyuanshen + fys-common-excel + ${revision} + + + + + com.fuyuanshen + fys-common-idempotent + ${revision} + + + + + com.fuyuanshen + fys-common-job + ${revision} + + + + + com.fuyuanshen + fys-common-log + ${revision} + + + + + com.fuyuanshen + fys-common-mail + ${revision} + + + + + com.fuyuanshen + fys-common-mybatis + ${revision} + + + + + com.fuyuanshen + fys-common-oss + ${revision} + + + + + com.fuyuanshen + fys-common-ratelimiter + ${revision} + + + + + com.fuyuanshen + fys-common-redis + ${revision} + + + + + com.fuyuanshen + fys-common-satoken + ${revision} + + + + + com.fuyuanshen + fys-common-security + ${revision} + + + + + com.fuyuanshen + fys-common-sms + ${revision} + + + + com.fuyuanshen + fys-common-social + ${revision} + + + + + com.fuyuanshen + fys-common-web + ${revision} + + + + + com.fuyuanshen + fys-common-translation + ${revision} + + + + + com.fuyuanshen + fys-common-sensitive + ${revision} + + + + + com.fuyuanshen + fys-common-json + ${revision} + + + + + com.fuyuanshen + fys-common-encrypt + ${revision} + + + + + com.fuyuanshen + fys-common-tenant + ${revision} + + + + + com.fuyuanshen + fys-common-websocket + ${revision} + + + + + com.fuyuanshen + fys-common-sse + ${revision} + + + + + + diff --git a/fys-common/fys-common-core/pom.xml b/fys-common/fys-common-core/pom.xml new file mode 100644 index 0000000..1ba5b46 --- /dev/null +++ b/fys-common/fys-common-core/pom.xml @@ -0,0 +1,143 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-core + + + fys-common-core 核心模块 + + + + 5.8.35 + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-aop + + + + + org.apache.commons + commons-lang3 + + + + + jakarta.servlet + jakarta.servlet-api + + + + cn.hutool + hutool-core + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + cn.hutool + hutool-http + + + + cn.hutool + hutool-extra + + + + + org.apache.poi + poi + 5.4.0 + + + org.apache.poi + poi-ooxml + 5.4.0 + + + xerces + xercesImpl + 2.12.2 + + + org.apache.poi + poi-scratchpad + 5.4.0 + + + org.apache.poi + poi-ooxml-full + 5.4.0 + + + + org.projectlombok + lombok + + + + + org.springframework.boot + spring-boot-configuration-processor + + + + org.springframework.boot + spring-boot-properties-migrator + runtime + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + + + + + org.lionsoul + ip2region + + + com.baomidou + mybatis-plus-core + 3.5.12 + compile + + + + + diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ApplicationConfig.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ApplicationConfig.java new file mode 100644 index 0000000..caea7ec --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ApplicationConfig.java @@ -0,0 +1,17 @@ +package com.fuyuanshen.common.core.config; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * 程序注解配置 + * + * @author Lion Li + */ +@AutoConfiguration +@EnableAspectJAutoProxy +@EnableAsync(proxyTargetClass = true) +public class ApplicationConfig { + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/AsyncConfig.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/AsyncConfig.java new file mode 100644 index 0000000..bda1b42 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/AsyncConfig.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.common.core.config; + +import cn.hutool.core.util.ArrayUtil; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.core.task.VirtualThreadTaskExecutor; +import org.springframework.scheduling.annotation.AsyncConfigurer; + +import java.util.Arrays; +import java.util.concurrent.Executor; + +/** + * 异步配置 + *

+ * 如果未使用虚拟线程则生效 + * + * @author Lion Li + */ +@AutoConfiguration +public class AsyncConfig implements AsyncConfigurer { + + /** + * 自定义 @Async 注解使用系统线程池 + */ + @Override + public Executor getAsyncExecutor() { + if(SpringUtils.isVirtual()) { + return new VirtualThreadTaskExecutor("async-"); + } + return SpringUtils.getBean("scheduledExecutorService"); + } + + /** + * 异步执行异常处理 + */ + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + throwable.printStackTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Exception message - ").append(throwable.getMessage()) + .append(", Method name - ").append(method.getName()); + if (ArrayUtil.isNotEmpty(objects)) { + sb.append(", Parameter value - ").append(Arrays.toString(objects)); + } + throw new ServiceException(sb.toString()); + }; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ThreadPoolConfig.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ThreadPoolConfig.java new file mode 100644 index 0000000..f80c1eb --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ThreadPoolConfig.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.common.core.config; + +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import com.fuyuanshen.common.core.config.properties.ThreadPoolProperties; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.Threads; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.task.VirtualThreadTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author Lion Li + **/ +@Slf4j +@AutoConfiguration +@EnableConfigurationProperties(ThreadPoolProperties.class) +public class ThreadPoolConfig { + + /** + * 核心线程数 = cpu 核心数 + 1 + */ + private final int core = Runtime.getRuntime().availableProcessors() + 1; + + private ScheduledExecutorService scheduledExecutorService; + + @Bean(name = "threadPoolTaskExecutor") + @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") + public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(core); + executor.setMaxPoolSize(core * 2); + executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); + executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + // daemon 必须为 true + BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true); + if (SpringUtils.isVirtual()) { + builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); + } else { + builder.namingPattern("schedule-pool-%d"); + } + ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, + builder.build(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + this.scheduledExecutorService = scheduledThreadPoolExecutor; + return scheduledThreadPoolExecutor; + } + + /** + * 销毁事件 + */ + @PreDestroy + public void destroy() { + try { + log.info("====关闭后台任务任务线程池===="); + Threads.shutdownAndAwaitTermination(scheduledExecutorService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ValidatorConfig.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ValidatorConfig.java new file mode 100644 index 0000000..7369a77 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/ValidatorConfig.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.common.core.config; + +import jakarta.validation.Validator; +import org.hibernate.validator.HibernateValidator; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +import java.util.Properties; + +/** + * 校验框架配置类 + * + * @author Lion Li + */ +@AutoConfiguration(before = ValidationAutoConfiguration.class) +public class ValidatorConfig { + + /** + * 配置校验框架 快速失败模式 + */ + @Bean + public Validator validator(MessageSource messageSource) { + try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) { + // 国际化 + factoryBean.setValidationMessageSource(messageSource); + // 设置使用 HibernateValidator 校验器 + factoryBean.setProviderClass(HibernateValidator.class); + Properties properties = new Properties(); + // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误 + properties.setProperty("hibernate.validator.fail_fast", "true"); + factoryBean.setValidationProperties(properties); + // 加载配置 + factoryBean.afterPropertiesSet(); + return factoryBean.getValidator(); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/properties/ThreadPoolProperties.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/properties/ThreadPoolProperties.java new file mode 100644 index 0000000..c2532e6 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/config/properties/ThreadPoolProperties.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.core.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 线程池 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "thread-pool") +public class ThreadPoolProperties { + + /** + * 是否开启线程池 + */ + private boolean enabled; + + /** + * 队列最大长度 + */ + private int queueCapacity; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private int keepAliveSeconds; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..9d73f2a --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheConstants.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 缓存的key 常量 + * + * @author Lion Li + */ +public interface CacheConstants { + + /** + * 在线用户 redis key + */ + String ONLINE_TOKEN_KEY = "online_tokens:"; + + /** + * 参数管理 cache key + */ + String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + String SYS_DICT_KEY = "sys_dict:"; + + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheNames.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheNames.java new file mode 100644 index 0000000..2519212 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/CacheNames.java @@ -0,0 +1,89 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 缓存组名称常量 + *

+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local + *

+ * ttl 过期时间 如果设置为0则不过期 默认为0 + * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 + * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + * local 默认开启本地缓存为1 关闭本地缓存为0 + *

+ * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0 + * + * @author Lion Li + */ +public interface CacheNames { + + /** + * 演示案例 + */ + String DEMO_CACHE = "demo:cache#60s#10m#20"; + + /** + * 系统配置 + */ + String SYS_CONFIG = "sys_config"; + + /** + * 数据字典 + */ + String SYS_DICT = "sys_dict"; + + /** + * 数据字典类型 + */ + String SYS_DICT_TYPE = "sys_dict_type"; + + /** + * 租户 + */ + String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; + + /** + * 客户端 + */ + String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; + + /** + * 用户账户 + */ + String SYS_USER_NAME = "sys_user_name#30d"; + + /** + * 用户名称 + */ + String SYS_NICKNAME = "sys_nickname#30d"; + + /** + * 部门 + */ + String SYS_DEPT = "sys_dept#30d"; + + /** + * OSS内容 + */ + String SYS_OSS = "sys_oss#30d"; + + /** + * 角色自定义权限 + */ + String SYS_ROLE_CUSTOM = "sys_role_custom#30d"; + + /** + * 部门及以下权限 + */ + String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d"; + + /** + * OSS配置 + */ + String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config"; + + /** + * 在线用户 + */ + String ONLINE_TOKEN = "online_tokens"; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/Constants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/Constants.java new file mode 100644 index 0000000..fe50dc5 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/Constants.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 通用常量信息 + * + * @author fys + */ +public interface Constants { + + /** + * UTF-8 字符集 + */ + String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + String GBK = "GBK"; + + /** + * www主域 + */ + String WWW = "www."; + + /** + * http请求 + */ + String HTTP = "http://"; + + /** + * https请求 + */ + String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + String FAIL = "1"; + + /** + * 登录成功 + */ + String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + String LOGOUT = "Logout"; + + /** + * 注册 + */ + String REGISTER = "Register"; + + /** + * 登录失败 + */ + String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + Integer CAPTCHA_EXPIRATION = 2; + + /** + * 顶级父级id + */ + Long TOP_PARENT_ID = 0L; + +} + diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java new file mode 100644 index 0000000..18257d9 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/GlobalConstants.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 全局的key常量 (业务无关的key) + * + * @author Lion Li + */ +public interface GlobalConstants { + + /** + * 全局 redis key (业务无关的key) + */ + String GLOBAL_REDIS_KEY = "global:"; + + /** + * 验证码 redis key + */ + String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:"; + + /** + * 防重提交 redis key + */ + String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:"; + + /** + * 限流 redis key + */ + String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; + + /** + * 三方认证 redis key + */ + String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/HttpStatus.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..f7ba342 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 返回状态码 + * + * @author Lion Li + */ +public interface HttpStatus { + /** + * 操作成功 + */ + int SUCCESS = 200; + + /** + * 对象创建成功 + */ + int CREATED = 201; + + /** + * 请求已经被接受 + */ + int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + int MOVED_PERM = 301; + + /** + * 重定向 + */ + int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + int BAD_REQUEST = 400; + + /** + * 未授权 + */ + int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + int ERROR = 500; + + /** + * 接口未实现 + */ + int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + int WARN = 601; +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/RegexConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/RegexConstants.java new file mode 100644 index 0000000..b4716e0 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/RegexConstants.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + *

+ * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ + * + * @author Feng + */ +public interface RegexConstants extends RegexPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; + + /** + * 权限标识必须符合以下格式: + * 1. 标准格式:xxx:yyy:zzz + * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*` + * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*` + * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*` + * 2. 允许空字符串(""),表示没有权限标识 + */ + String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$"; + + /** + * 身份证号码(后6位) + */ + String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + + /** + * QQ号码 + */ + String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; + + /** + * 邮政编码 + */ + String POSTAL_CODE = "^[1-9]\\d{5}$"; + + /** + * 注册账号 + */ + String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; + + /** + * 通用状态(0表示正常,1表示停用) + */ + String STATUS = "^[01]$"; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/ResponseMessageConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/ResponseMessageConstants.java new file mode 100644 index 0000000..a571cb0 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/ResponseMessageConstants.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 响应消息常量类 + * + * @author: 默苍璃 + * @date: 2025-06-2117:21 + */ +public class ResponseMessageConstants { + + /** + * 删除操作成功提示 + */ + public static final String DELETE_SUCCESS = "删除成功!"; + + /** + * 新增操作成功提示 + */ + public static final String SAVE_SUCCESS = "新增成功!"; + + /** + * 更新操作成功提示 + */ + public static final String UPDATE_SUCCESS = "更新成功!"; + + // 可根据业务需求继续扩展其他常用提示信息 + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/SystemConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/SystemConstants.java new file mode 100644 index 0000000..51b8df7 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/SystemConstants.java @@ -0,0 +1,80 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 系统常量信息 + * + * @author Lion Li + */ +public interface SystemConstants { + + /** + * 正常状态 + */ + String NORMAL = "0"; + + /** + * 异常状态 + */ + String DISABLE = "1"; + + /** + * 是否为系统默认(是) + */ + String YES = "Y"; + + /** + * 是否为系统默认(否) + */ + String NO = "N"; + + /** + * 是否菜单外链(是) + */ + String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + String NO_FRAME = "1"; + + /** + * 菜单类型(目录) + */ + String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + String INNER_LINK = "InnerLink"; + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 根部门祖级列表 + */ + String ROOT_DEPT_ANCESTORS = "0"; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/TenantConstants.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/TenantConstants.java new file mode 100644 index 0000000..43839b2 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/constant/TenantConstants.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.common.core.constant; + +/** + * 租户常量信息 + * + * @author Lion Li + */ +public interface TenantConstants { + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 超级管理员角色 roleKey + */ + String SUPER_ADMIN_ROLE_KEY = "superadmin"; + + /** + * 租户管理员角色 roleKey + */ + String TENANT_ADMIN_ROLE_KEY = "admin"; + + /** + * 富源晟内部员工 roleKey + */ + String FYS_ROLE_KEY = "fel"; + + /** + * 租户管理员角色名称 + */ + String TENANT_ADMIN_ROLE_NAME = "管理员"; + + /** + * 默认租户ID + */ + String DEFAULT_TENANT_ID = "000000"; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/PageResult.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/PageResult.java new file mode 100644 index 0000000..2d46205 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/PageResult.java @@ -0,0 +1,24 @@ +package com.fuyuanshen.common.core.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 分页结果封装类 + * @author Zheng Jie + * @date 2018-11-23 + * @param + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PageResult implements Serializable { + + private List rows; + + private long total; +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/R.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/R.java new file mode 100644 index 0000000..9eb8074 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/R.java @@ -0,0 +1,110 @@ +package com.fuyuanshen.common.core.domain; + +import com.fuyuanshen.common.core.constant.HttpStatus; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class R implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(String msg, T data) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(String msg, T data) { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static R warn(String msg) { + return restResult(null, HttpStatus.WARN, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static R warn(String msg, T data) { + return restResult(data, HttpStatus.WARN, msg); + } + + private static R restResult(T data, int code, String msg) { + R r = new R<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + return r; + } + + public static Boolean isError(R ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/ResponseVO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/ResponseVO.java new file mode 100644 index 0000000..ed130c1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/ResponseVO.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.common.core.domain; + +import lombok.Data; + +/** + * @Description: 返回体 + * @Author: WY + * @Date: 2025/6/2 + **/ +@Data +public class ResponseVO { + + private Integer code; // 成功:0 失败:-1 + private String msg; + private T data; + + public ResponseVO(Integer code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + public ResponseVO(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + // 静态方法,用于创建成功的响应 + public static ResponseVO success() { + return new ResponseVO<>(0, "操作成功", null); + } + + public static ResponseVO success(T data) { + return new ResponseVO<>(0, "操作成功", data); + } + + public static ResponseVO success(String msg, T data) { + return new ResponseVO<>(0, msg, data); + } + + // 静态方法,用于创建失败的响应 + public static ResponseVO fail(String msg) { + return new ResponseVO<>(-1, msg, null); + } + + public static ResponseVO fail(String msg, T data) { + return new ResponseVO<>(-1, msg, data); + } + + // 链式方法 - 添加泛型支持 + public ResponseVO withData(R data) { + return new ResponseVO<>(this.code, this.msg, data); + } + + public ResponseVO withMsg(String msg) { + this.msg = msg; + return this; + } + + public ResponseVO withCode(Integer code) { + this.code = code; + return this; + } +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/CompleteTaskDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/CompleteTaskDTO.java new file mode 100644 index 0000000..42e2444 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/CompleteTaskDTO.java @@ -0,0 +1,71 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 办理任务请求对象 + * + * @author may + */ +@Data +public class CompleteTaskDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + private Long taskId; + + /** + * 附件id + */ + private String fileId; + + /** + * 抄送人员 + */ + private List flowCopyList; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 办理意见 + */ + private String message; + + /** + * 消息通知 + */ + private String notice; + + /** + * 流程变量 + */ + private Map variables; + + /** + * 扩展变量(此处为逗号分隔的ossId) + */ + private String ext; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DeptDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DeptDTO.java new file mode 100644 index 0000000..e6c8d4b --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DeptDTO.java @@ -0,0 +1,36 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 部门 + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class DeptDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String deptName; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictDataDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictDataDTO.java new file mode 100644 index 0000000..d9d7a2b --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictDataDTO.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典数据DTO + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class DictDataDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典标签 + */ + private String dictLabel; + + /** + * 字典键值 + */ + private String dictValue; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictTypeDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictTypeDTO.java new file mode 100644 index 0000000..ccbc20d --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/DictTypeDTO.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典类型DTO + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class DictTypeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典主键 + */ + private Long dictId; + + /** + * 字典名称 + */ + private String dictName; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/FlowCopyDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/FlowCopyDTO.java new file mode 100644 index 0000000..caef884 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/FlowCopyDTO.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 抄送 + * + * @author may + */ +@Data +public class FlowCopyDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名称 + */ + private String userName; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/OssDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/OssDTO.java new file mode 100644 index 0000000..616d4bb --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/OssDTO.java @@ -0,0 +1,46 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * OSS对象 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class OssDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 对象存储主键 + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/PostDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/PostDTO.java new file mode 100644 index 0000000..51946ad --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/PostDTO.java @@ -0,0 +1,46 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 岗位 + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class PostDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + private Long postId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 岗位编码 + */ + private String postCode; + + /** + * 岗位名称 + */ + private String postName; + + /** + * 岗位类别编码 + */ + private String postCategory; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/RoleDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/RoleDTO.java new file mode 100644 index 0000000..8a82964 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/RoleDTO.java @@ -0,0 +1,42 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色 + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class RoleDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) + */ + private String dataScope; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessDTO.java new file mode 100644 index 0000000..6d872f6 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessDTO.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.common.core.domain.dto; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 启动流程对象 + * + * @author may + */ +@Data +public class StartProcessDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 业务唯一值id + */ + private String businessId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessReturnDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessReturnDTO.java new file mode 100644 index 0000000..ae87462 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/StartProcessReturnDTO.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.core.domain.dto; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 启动流程返回对象 + * + * @author Lion Li + */ +@Data +public class StartProcessReturnDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + private Long processInstanceId; + + /** + * 任务id + */ + private Long taskId; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/TaskAssigneeDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/TaskAssigneeDTO.java new file mode 100644 index 0000000..85de311 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/TaskAssigneeDTO.java @@ -0,0 +1,101 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 任务受让人 + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class TaskAssigneeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总大小 + */ + private Long total = 0L; + + /** + * + */ + private List list; + + public TaskAssigneeDTO(Long total, List list) { + this.total = total; + this.list = list; + } + + /** + * 将源列表转换为 TaskHandler 列表 + * + * @param 通用类型 + * @param sourceList 待转换的源列表 + * @param storageId 提取 storageId 的函数 + * @param handlerCode 提取 handlerCode 的函数 + * @param handlerName 提取 handlerName 的函数 + * @param groupName 提取 groupName 的函数 + * @param createTimeMapper 提取 createTime 的函数 + * @return 转换后的 TaskHandler 列表 + */ + public static List convertToHandlerList( + List sourceList, + Function storageId, + Function handlerCode, + Function handlerName, + Function groupName, + Function createTimeMapper) { + return sourceList.stream() + .map(item -> new TaskHandler( + String.valueOf(storageId.apply(item)), + handlerCode.apply(item), + handlerName.apply(item), + groupName != null ? String.valueOf(groupName.apply(item)) : null, + createTimeMapper.apply(item) + )).collect(Collectors.toList()); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class TaskHandler { + + /** + * 主键 + */ + private String storageId; + + /** + * 权限编码 + */ + private String handlerCode; + + /** + * 权限名称 + */ + private String handlerName; + + /** + * 权限分组 + */ + private String groupName; + + /** + * 创建时间 + */ + private Date createTime; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserDTO.java new file mode 100644 index 0000000..a5a30d1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserDTO.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 用户 + * + * @author Michelle.Chung + */ +@Data +@NoArgsConstructor +public class UserDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserOnlineDTO.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserOnlineDTO.java new file mode 100644 index 0000000..08500f1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/dto/UserOnlineDTO.java @@ -0,0 +1,72 @@ +package com.fuyuanshen.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 当前在线会话 + * + * @author fys + */ + +@Data +@NoArgsConstructor +public class UserOnlineDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessDeleteEvent.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessDeleteEvent.java new file mode 100644 index 0000000..4f746ca --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessDeleteEvent.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 删除流程监听 + * + * @author AprilWind + */ +@Data +public class ProcessDeleteEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 业务id + */ + private String businessId; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessEvent.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessEvent.java new file mode 100644 index 0000000..94f197f --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessEvent.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; + +/** + * 总体流程监听 + * + * @author may + */ +@Data +public class ProcessEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 业务id + */ + private String businessId; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 流程状态 + */ + private String status; + + /** + * 办理参数 + */ + private Map params; + + /** + * 当为true时为申请人节点办理 + */ + private Boolean submit; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessTaskEvent.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessTaskEvent.java new file mode 100644 index 0000000..76b0bb5 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/event/ProcessTaskEvent.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程任务监听 + * + * @author may + */ +@Data +public class ProcessTaskEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 任务id + */ + private Long taskId; + + /** + * 业务id + */ + private String businessId; + + /** + * 流程状态 + */ + private String status; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/AppLoginUser.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/AppLoginUser.java new file mode 100644 index 0000000..78c0e8a --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/AppLoginUser.java @@ -0,0 +1,148 @@ +package com.fuyuanshen.common.core.domain.model; + +import com.fuyuanshen.common.core.domain.dto.PostDTO; +import com.fuyuanshen.common.core.domain.dto.RoleDTO; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class AppLoginUser implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门类别编码 + */ + private String deptCategory; + + /** + * 部门名 + */ + private String deptName; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户类型 + */ + private String userType; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 菜单权限 + */ + private Set menuPermission; + + /** + * 角色权限 + */ + private Set rolePermission; + + /** + * 用户名 + */ + private String username; + + /** + * 用户昵称 + */ + private String nickname; + + /** + * 角色对象 + */ + private List roles; + + /** + * 岗位对象 + */ + private List posts; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 获取登录id + */ + public String getLoginId() { + if (userType == null) { + throw new IllegalArgumentException("用户类型不能为空"); + } + if (userId == null) { + throw new IllegalArgumentException("用户ID不能为空"); + } + return userType + ":" + userId; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/EmailLoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/EmailLoginBody.java new file mode 100644 index 0000000..f9bf216 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/EmailLoginBody.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 邮件登录对象 + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class EmailLoginBody extends LoginBody { + + /** + * 邮箱 + */ + @NotBlank(message = "{user.email.not.blank}") + @Email(message = "{user.email.not.valid}") + private String email; + + /** + * 邮箱code + */ + @NotBlank(message = "{email.code.not.blank}") + private String emailCode; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..bc567cc --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginBody.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户登录对象 + * + * @author Lion Li + */ + +@Data +public class LoginBody implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端id + */ + @NotBlank(message = "{auth.clientid.not.blank}") + private String clientId; + + /** + * 授权类型 + */ + @NotBlank(message = "{auth.grant.type.not.blank}") + private String grantType; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginUser.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..2f6260b --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/LoginUser.java @@ -0,0 +1,155 @@ +package com.fuyuanshen.common.core.domain.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.domain.dto.PostDTO; +import com.fuyuanshen.common.core.domain.dto.RoleDTO; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class LoginUser implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户ID + */ + private Long userId; + private Long pid; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门类别编码 + */ + private String deptCategory; + + /** + * 部门名 + */ + private String deptName; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户类型 + */ + private String userType; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 菜单权限 + */ + private Set menuPermission; + + /** + * 角色权限 + */ + private Set rolePermission; + + /** + * 用户名 + */ + private String username; + + /** + * 用户昵称 + */ + private String nickname; + + /** + * 角色对象 + */ + private List roles; + + /** + * 岗位对象 + */ + private List posts; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 用户等级 + */ + private Byte userLevel; + + /** + * 获取登录id + */ + public String getLoginId() { + if (userType == null) { + throw new IllegalArgumentException("用户类型不能为空"); + } + if (userId == null) { + throw new IllegalArgumentException("用户ID不能为空"); + } + return userType + ":" + userId; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/PasswordLoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/PasswordLoginBody.java new file mode 100644 index 0000000..1ba0e32 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/PasswordLoginBody.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * 密码登录对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PasswordLoginBody extends LoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.username.not.blank}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") + private String username; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") + private String password; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/RegisterBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..f111926 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/RegisterBody.java @@ -0,0 +1,33 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * 用户注册对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RegisterBody extends LoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.username.not.blank}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") + private String username; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") + private String password; + + private String userType; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SmsLoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SmsLoginBody.java new file mode 100644 index 0000000..82a3312 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SmsLoginBody.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 短信登录对象 + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class SmsLoginBody extends LoginBody { + + /** + * 手机号 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + + /** + * 短信code + */ + @NotBlank(message = "{sms.code.not.blank}") + private String smsCode; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SocialLoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SocialLoginBody.java new file mode 100644 index 0000000..e165eb5 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/SocialLoginBody.java @@ -0,0 +1,35 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 三方登录对象 + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class SocialLoginBody extends LoginBody { + + /** + * 第三方登录平台 + */ + @NotBlank(message = "{social.source.not.blank}") + private String source; + + /** + * 第三方登录code + */ + @NotBlank(message = "{social.code.not.blank}") + private String socialCode; + + /** + * 第三方登录socialState + */ + @NotBlank(message = "{social.state.not.blank}") + private String socialState; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/TaskAssigneeBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/TaskAssigneeBody.java new file mode 100644 index 0000000..8c3ab81 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/TaskAssigneeBody.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.common.core.domain.model; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务受让人 + * + * @author AprilWind + */ +@Data +@NoArgsConstructor +public class TaskAssigneeBody implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 权限编码 + */ + private String handlerCode; + + /** + * 权限名称 + */ + private String handlerName; + + /** + * 权限分组 + */ + private String groupId; + + /** + * 开始时间 + */ + private String beginTime; + + /** + * 结束时间 + */ + private String endTime; + + /** + * 当前页 + */ + private Integer pageNum = 1; + + /** + * 每页显示条数 + */ + private Integer pageSize = 10; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginBody.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginBody.java new file mode 100644 index 0000000..06a1245 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginBody.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 三方登录对象 + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class XcxLoginBody extends LoginBody { + + /** + * 小程序id(多个小程序时使用) + */ + private String appid; + + /** + * 小程序code + */ + @NotBlank(message = "{xcx.code.not.blank}") + private String xcxCode; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginUser.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginUser.java new file mode 100644 index 0000000..96eed6f --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/domain/model/XcxLoginUser.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 小程序登录用户身份权限 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class XcxLoginUser extends LoginUser { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * openid + */ + private String openid; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/BusinessStatusEnum.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/BusinessStatusEnum.java new file mode 100644 index 0000000..f92197d --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/BusinessStatusEnum.java @@ -0,0 +1,215 @@ +package com.fuyuanshen.common.core.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 业务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + + /** + * 已完成 + */ + FINISH("finish", "已完成"), + + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + + /** + * 已退回 + */ + BACK("back", "已退回"), + + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + private static final Map STATUS_MAP = Arrays.stream(BusinessStatusEnum.values()) + .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity())); + + /** + * 根据状态获取对应的 BusinessStatusEnum 枚举 + * + * @param status 业务状态码 + * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null + */ + public static BusinessStatusEnum getByStatus(String status) { + // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null + return STATUS_MAP.get(status); + } + + /** + * 根据状态获取对应的业务状态描述信息 + * + * @param status 业务状态码 + * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + BusinessStatusEnum statusEnum = STATUS_MAP.get(status); + return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY; + } + + /** + * 判断是否为指定的状态之一:草稿、已撤销或已退回 + * + * @param status 要检查的状态 + * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false + */ + public static boolean isDraftOrCancelOrBack(String status) { + return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status); + } + + /** + * 判断是否为撤销,退回,作废,终止 + * + * @param status status + * @return 结果 + */ + public static boolean initialState(String status) { + return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status); + } + + /** + * 获取运行中的实例状态列表 + * + * @return 包含运行中实例状态的不可变列表 + * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态) + */ + public static List runningStatus() { + return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status); + } + + /** + * 获取结束实例的状态列表 + * + * @return 包含结束实例状态的不可变列表 + * (包含 FINISH、INVALID 和 TERMINATION 状态) + */ + public static List finishStatus() { + return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/DeviceType.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/DeviceType.java new file mode 100644 index 0000000..4bf21c5 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/DeviceType.java @@ -0,0 +1,39 @@ +package com.fuyuanshen.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DeviceType { + + /** + * pc端 + */ + PC("pc"), + + /** + * app端 + */ + APP("app"), + + /** + * 小程序端 + */ + XCX("xcx"), + + /** + * 第三方社交登录平台 + */ + SOCIAL("social"); + + /** + * 设备标识 + */ + private final String device; +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/FormatsType.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/FormatsType.java new file mode 100644 index 0000000..f880140 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/FormatsType.java @@ -0,0 +1,146 @@ +package com.fuyuanshen.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.utils.StringUtils; + +/* + * 日期格式 + * "yyyy":4位数的年份,例如:2023年表示为"2023"。 + * "yy":2位数的年份,例如:2023年表示为"23"。 + * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。 + * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。 + * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。 + * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。 + * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。 + * "E":星期的缩写,例如:星期三表示为"Wed"。 + * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。 + * 时间格式 + * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。 + * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。 + * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。 + * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。 + * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。 + */ + +/** + * 日期格式与时间格式枚举 + */ +@Getter +@AllArgsConstructor +public enum FormatsType { + + /** + * 例如:2023年表示为"23" + */ + YY("yy"), + + /** + * 例如:2023年表示为"2023" + */ + YYYY("yyyy"), + + /** + * 例例如,2023年7月可以表示为 "2023-07" + */ + YYYY_MM("yyyy-MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22" + */ + YYYY_MM_DD("yyyy-MM-dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30" + */ + YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), + + /** + * 例如:下午3点30分45秒,表示为 "15:30:45" + */ + HH_MM_SS("HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023/07" + */ + YYYY_MM_SLASH("yyyy/MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22" + */ + YYYY_MM_DD_SLASH("yyyy/MM/dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023.07" + */ + YYYY_MM_DOT("yyyy.MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22" + */ + YYYY_MM_DD_DOT("yyyy.MM.dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30" + */ + YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"), + + /** + * 例如,2023年7月可以表示为 "202307" + */ + YYYYMM("yyyyMM"), + + /** + * 例如,2023年7月22日可以表示为 "20230722" + */ + YYYYMMDD("yyyyMMdd"), + + /** + * 例如,2023年7月22日下午3点可以表示为 "2023072215" + */ + YYYYMMDDHH("yyyyMMddHH"), + + /** + * 例如,2023年7月22日下午3点30分可以表示为 "202307221530" + */ + YYYYMMDDHHMM("yyyyMMddHHmm"), + + /** + * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045" + */ + YYYYMMDDHHMMSS("yyyyMMddHHmmss"); + + /** + * 时间格式 + */ + private final String timeFormat; + + public static FormatsType getFormatsType(String str) { + for (FormatsType value : values()) { + if (StringUtils.contains(str, value.getTimeFormat())) { + return value; + } + } + throw new RuntimeException("'FormatsType' not found By " + str); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/LoginType.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/LoginType.java new file mode 100644 index 0000000..84f99b1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/LoginType.java @@ -0,0 +1,44 @@ +package com.fuyuanshen.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum LoginType { + + /** + * 密码登录 + */ + PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), + + /** + * 短信登录 + */ + SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + + /** + * 邮箱登录 + */ + EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), + + /** + * 小程序登录 + */ + XCX("", ""); + + /** + * 登录重试超出限制提示 + */ + final String retryLimitExceed; + + /** + * 登录重试限制计数提示 + */ + final String retryLimitCount; +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserStatus.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserStatus.java new file mode 100644 index 0000000..6ddd8d7 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户状态 + * + * @author fys + */ +@Getter +@AllArgsConstructor +public enum UserStatus { + /** + * 正常 + */ + OK("0", "正常"), + /** + * 停用 + */ + DISABLE("1", "停用"), + /** + * 删除 + */ + DELETED("2", "删除"); + + private final String code; + private final String info; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserType.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserType.java new file mode 100644 index 0000000..bb93e36 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/enums/UserType.java @@ -0,0 +1,39 @@ +package com.fuyuanshen.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.utils.StringUtils; + +/** + * 用户类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum UserType { + + /** + * 后台系统用户 + */ + SYS_USER("sys_user"), + + /** + * 移动客户端用户 + */ + APP_USER("app_user"); + + /** + * 用户类型标识(用于 token、权限识别等) + */ + private final String userType; + + public static UserType getUserType(String str) { + for (UserType value : values()) { + if (StringUtils.contains(str, value.getUserType())) { + return value; + } + } + throw new RuntimeException("'UserType' not found By " + str); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java new file mode 100644 index 0000000..19266a7 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/BadRequestException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * @author Zheng Jie + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg){ + super(msg); + } + + public BadRequestException(HttpStatus status,String msg){ + super(msg); + this.status = status.value(); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java new file mode 100644 index 0000000..648c078 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityExistException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage1(String entity, String field, String val) { + return StringUtils.capitalize(entity) + " with " + field + " " + val + " existed"; + } + + private static String generateMessage(String entity, String field, String val) { + return field + " :" + val + " 已存在"; + } + +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java new file mode 100644 index 0000000..841dcb8 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/EntityNotFoundException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception; + +import org.springframework.util.StringUtils; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " does not exist"; + } +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/ServiceException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/ServiceException.java new file mode 100644 index 0000000..2219a93 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/ServiceException.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.common.core.exception; + +import lombok.*; + +import java.io.Serial; + +/** + * 业务异常 + * + * @author fys + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public final class ServiceException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + private String detailMessage; + + public ServiceException(String message) { + this.message = message; + } + + public ServiceException(String message, Integer code) { + this.message = message; + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/SseException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/SseException.java new file mode 100644 index 0000000..0e42bab --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/SseException.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.common.core.exception; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * sse 特制异常 + * + * @author LionLi + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public final class SseException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + private String detailMessage; + + public SseException(String message) { + this.message = message; + } + + public SseException(String message, Integer code) { + this.message = message; + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public SseException setMessage(String message) { + this.message = message; + return this; + } + + public SseException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/base/BaseException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..a5116b0 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/base/BaseException.java @@ -0,0 +1,74 @@ +package com.fuyuanshen.common.core.exception.base; + +import lombok.AllArgsConstructor; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 基础异常 + * + * @author fys + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class BaseException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args) { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() { + String message = null; + if (!StringUtils.isEmpty(code)) { + message = MessageUtils.message(code, args); + } + if (message == null) { + message = defaultMessage; + } + return message; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileException.java new file mode 100644 index 0000000..ff1afc3 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileException.java @@ -0,0 +1,21 @@ +package com.fuyuanshen.common.core.exception.file; + +import com.fuyuanshen.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 文件信息异常类 + * + * @author fys + */ +public class FileException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) { + super("file", code, args, null); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileNameLengthLimitExceededException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..d851eda --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名称超长限制异常类 + * + * @author fys + */ +public class FileNameLengthLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileSizeLimitExceededException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..360b642 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名大小限制异常类 + * + * @author fys + */ +public class FileSizeLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java new file mode 100644 index 0000000..9243976 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/handler/ApiError.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.exception.handler; + +import lombok.Data; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +public class ApiError { + + private Integer status = 400; + private Long timestamp; + private String message; + + private ApiError() { + timestamp = System.currentTimeMillis(); + } + + public static ApiError error(String message){ + ApiError apiError = new ApiError(); + apiError.setMessage(message); + return apiError; + } + + public static ApiError error(Integer status, String message){ + ApiError apiError = new ApiError(); + apiError.setStatus(status); + apiError.setMessage(message); + return apiError; + } +} + + diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaException.java new file mode 100644 index 0000000..653dc48 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaException.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码错误异常类 + * + * @author fys + */ +public class CaptchaException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaException() { + super("user.jcaptcha.error"); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaExpireException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..8bc0fda --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码失效异常类 + * + * @author fys + */ +public class CaptchaExpireException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() { + super("user.jcaptcha.expire"); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/UserException.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/UserException.java new file mode 100644 index 0000000..b94a076 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/exception/user/UserException.java @@ -0,0 +1,20 @@ +package com.fuyuanshen.common.core.exception.user; + +import com.fuyuanshen.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 用户信息异常类 + * + * @author fys + */ +public class UserException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public UserException(String code, Object... args) { + super("user", code, args, null); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/RegexPatternPoolFactory.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 0000000..98efcea --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import com.fuyuanshen.common.core.constant.RegexConstants; + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + *

初始化的时候将正则表达式加入缓存池当中

+ *

提高正则表达式的性能,避免重复编译相同的正则表达式

+ * + * @author 21001 + */ +public class RegexPatternPoolFactory extends PatternPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE); + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6); + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER); + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE); + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT); + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = get(RegexConstants.PASSWORD); + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = get(RegexConstants.STATUS); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/YmlPropertySourceFactory.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/YmlPropertySourceFactory.java new file mode 100644 index 0000000..83c1f10 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/factory/YmlPropertySourceFactory.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.common.core.factory; + +import com.fuyuanshen.common.core.utils.StringUtils; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.DefaultPropertySourceFactory; +import org.springframework.core.io.support.EncodedResource; + +import java.io.IOException; + +/** + * yml 配置源工厂 + * + * @author Lion Li + */ +public class YmlPropertySourceFactory extends DefaultPropertySourceFactory { + + @Override + public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + String sourceName = resource.getResource().getFilename(); + if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(resource.getResource()); + factory.afterPropertiesSet(); + return new PropertiesPropertySource(sourceName, factory.getObject()); + } + return super.createPropertySource(name, resource); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/AppPermissionService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/AppPermissionService.java new file mode 100644 index 0000000..ef4b6e9 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/AppPermissionService.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.common.core.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author Lion Li + */ +public interface AppPermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/ConfigService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/ConfigService.java new file mode 100644 index 0000000..8aaf7f5 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/ConfigService.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.core.service; + +/** + * 通用 参数配置服务 + * + * @author Lion Li + */ +public interface ConfigService { + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + String getConfigValue(String configKey); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DeptService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DeptService.java new file mode 100644 index 0000000..b1b6b41 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DeptService.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.DeptDTO; + +import java.util.List; + +/** + * 通用 部门服务 + * + * @author Lion Li + */ +public interface DeptService { + + /** + * 通过部门ID查询部门名称 + * + * @param deptIds 部门ID串逗号分隔 + * @return 部门名称串逗号分隔 + */ + String selectDeptNameByIds(String deptIds); + + /** + * 根据部门ID查询部门负责人 + * + * @param deptId 部门ID,用于指定需要查询的部门 + * @return 返回该部门的负责人ID + */ + Long selectDeptLeaderById(Long deptId); + + /** + * 查询部门 + * + * @return 部门列表 + */ + List selectDeptsByList(); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DictService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DictService.java new file mode 100644 index 0000000..cfc2f53 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/DictService.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.DictDataDTO; +import com.fuyuanshen.common.core.domain.dto.DictTypeDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 字典服务 + * + * @author Lion Li + */ +public interface DictService { + + /** + * 分隔符 + */ + String SEPARATOR = ","; + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + default String getDictLabel(String dictType, String dictValue) { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + default String getDictValue(String dictType, String dictLabel) { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + String getDictLabel(String dictType, String dictValue, String separator); + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + String getDictValue(String dictType, String dictLabel, String separator); + + /** + * 获取字典下所有的字典值与标签 + * + * @param dictType 字典类型 + * @return dictValue为key,dictLabel为值组成的Map + */ + Map getAllDictByDictType(String dictType); + + /** + * 根据字典类型查询详细信息 + * + * @param dictType 字典类型 + * @return 字典类型详细信息 + */ + DictTypeDTO getDictType(String dictType); + + /** + * 根据字典类型查询字典数据列表 + * + * @param dictType 字典类型 + * @return 字典数据列表 + */ + List getDictData(String dictType); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/OssService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/OssService.java new file mode 100644 index 0000000..6170629 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/OssService.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.OssDTO; + +import java.util.List; + +/** + * 通用 OSS服务 + * + * @author Lion Li + */ +public interface OssService { + + /** + * 通过ossId查询对应的url + * + * @param ossIds ossId串逗号分隔 + * @return url串逗号分隔 + */ + String selectUrlByIds(String ossIds); + + /** + * 通过ossId查询列表 + * + * @param ossIds ossId串逗号分隔 + * @return 列表 + */ + List selectByIds(String ossIds); +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PermissionService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PermissionService.java new file mode 100644 index 0000000..85fe060 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PermissionService.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.common.core.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author Lion Li + */ +public interface PermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PostService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PostService.java new file mode 100644 index 0000000..df7a947 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/PostService.java @@ -0,0 +1,10 @@ +package com.fuyuanshen.common.core.service; + +/** + * 通用 岗位服务 + * + * @author AprilWind + */ +public interface PostService { + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/RoleService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/RoleService.java new file mode 100644 index 0000000..2142179 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/RoleService.java @@ -0,0 +1,10 @@ +package com.fuyuanshen.common.core.service; + +/** + * 通用 角色服务 + * + * @author AprilWind + */ +public interface RoleService { + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/TaskAssigneeService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/TaskAssigneeService.java new file mode 100644 index 0000000..55ddb02 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/TaskAssigneeService.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.TaskAssigneeDTO; +import com.fuyuanshen.common.core.domain.model.TaskAssigneeBody; + +/** + * 工作流设计器获取任务执行人 + * + * @author Lion Li + */ +public interface TaskAssigneeService { + + /** + * 查询角色并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询岗位并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询部门并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询用户并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/UserService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/UserService.java new file mode 100644 index 0000000..7f1bf48 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/UserService.java @@ -0,0 +1,127 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.UserDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 用户服务 + * + * @author Lion Li + */ +public interface UserService { + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ + String selectUserNameById(Long userId); + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户名称 + */ + String selectNicknameById(Long userId); + + /** + * 通过用户ID查询用户账户 + * + * @param userIds 用户ID 多个用逗号隔开 + * @return 用户名称 + */ + String selectNicknameByIds(String userIds); + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + String selectPhonenumberById(Long userId); + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + String selectEmailById(Long userId); + + /** + * 通过用户ID查询用户列表 + * + * @param userIds 用户ids + * @return 用户列表 + */ + List selectListByIds(List userIds); + + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + List selectUserIdsByRoleIds(List roleIds); + + /** + * 通过角色ID查询用户 + * + * @param roleIds 角色ids + * @return 用户 + */ + List selectUsersByRoleIds(List roleIds); + + /** + * 通过部门ID查询用户 + * + * @param deptIds 部门ids + * @return 用户 + */ + List selectUsersByDeptIds(List deptIds); + + /** + * 通过岗位ID查询用户 + * + * @param postIds 岗位ids + * @return 用户 + */ + List selectUsersByPostIds(List postIds); + + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + Map selectUserNamesByIds(List userIds); + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + Map selectRoleNamesByIds(List roleIds); + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + Map selectDeptNamesByIds(List deptIds); + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + Map selectPostNamesByIds(List postIds); + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/WorkflowService.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/WorkflowService.java new file mode 100644 index 0000000..37705ff --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/service/WorkflowService.java @@ -0,0 +1,95 @@ +package com.fuyuanshen.common.core.service; + +import com.fuyuanshen.common.core.domain.dto.CompleteTaskDTO; +import com.fuyuanshen.common.core.domain.dto.StartProcessDTO; +import com.fuyuanshen.common.core.domain.dto.StartProcessReturnDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务 + * + * @author may + */ +public interface WorkflowService { + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessIds 业务id + * @return 结果 + */ + boolean deleteInstance(List businessIds); + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + * @return 状态 + */ + String getBusinessStatusByTaskId(Long taskId); + + /** + * 获取当前流程状态 + * + * @param businessId 业务id + * @return 状态 + */ + String getBusinessStatus(String businessId); + + /** + * 设置流程变量 + * + * @param instanceId 流程实例id + * @param variable 流程变量 + */ + void setVariable(Long instanceId, Map variable); + + /** + * 获取流程变量 + * + * @param instanceId 流程实例id + */ + Map instanceVariable(Long instanceId); + + /** + * 按照业务id查询流程实例id + * + * @param businessId 业务id + * @return 结果 + */ + Long getInstanceIdByBusinessId(String businessId); + + /** + * 新增租户流程定义 + * + * @param tenantId 租户id + */ + void syncDef(String tenantId); + + /** + * 启动流程 + * + * @param startProcess 参数 + * @return 结果 + */ + StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess); + + /** + * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); + * + * @param completeTask 参数 + */ + boolean completeTask(CompleteTaskDTO completeTask); + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + boolean completeTask(Long taskId, String message); +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java new file mode 100644 index 0000000..809d77c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/CloseUtil.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.utils; + +import java.io.Closeable; + +/** + * @author Zheng Jie + * @description 用于关闭各种连接,缺啥补啥 + * @date 2021-03-05 + **/ +public class CloseUtil { + + public static void close(Closeable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } + + public static void close(AutoCloseable closeable) { + if (null != closeable) { + try { + closeable.close(); + } catch (Exception e) { + // 静默关闭 + } + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/DateUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/DateUtils.java new file mode 100644 index 0000000..91aacb6 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/DateUtils.java @@ -0,0 +1,300 @@ +package com.fuyuanshen.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; +import com.fuyuanshen.common.core.enums.FormatsType; +import com.fuyuanshen.common.core.exception.ServiceException; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * 时间工具类 + * + * @author fys + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + private static final String[] PARSE_PATTERNS = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + @Deprecated + private DateUtils() { + } + + /** + * 获取当前日期和时间 + * + * @return 当前日期和时间的 Date 对象表示 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期的字符串表示,格式为YYYY-MM-DD + * + * @return 当前日期的字符串表示 + */ + public static String getDate() { + return dateTimeNow(FormatsType.YYYY_MM_DD); + } + + /** + * 获取当前日期的字符串表示,格式为yyyyMMdd + * + * @return 当前日期的字符串表示 + */ + public static String getCurrentDate() { + return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat()); + } + + /** + * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd" + * + * @return 当前日期的路径格式字符串 + */ + public static String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat()); + } + + /** + * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS + * + * @return 当前时间的字符串表示 + */ + public static String getTime() { + return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS); + } + + /** + * 获取当前时间的字符串表示,格式为 "HH:MM:SS" + * + * @return 当前时间的字符串表示,格式为 "HH:MM:SS" + */ + public static String getTimeWithHourMinuteSecond() { + return dateTimeNow(FormatsType.HH_MM_SS); + } + + /** + * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS + * + * @return 当前日期和时间的字符串表示 + */ + public static String dateTimeNow() { + return dateTimeNow(FormatsType.YYYYMMDDHHMMSS); + } + + /** + * 获取当前日期和时间的指定格式的字符串表示 + * + * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @return 当前日期和时间的字符串表示 + */ + public static String dateTimeNow(final FormatsType format) { + return parseDateToStr(format, new Date()); + } + + /** + * 将指定日期格式化为 YYYY-MM-DD 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期字符串 + */ + public static String formatDate(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD, date); + } + + /** + * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String formatDateTime(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date); + } + + /** + * 将指定日期按照指定格式进行格式化 + * + * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String parseDateToStr(final FormatsType format, final Date date) { + return new SimpleDateFormat(format.getTimeFormat()).format(date); + } + + /** + * 将指定格式的日期时间字符串转换为 Date 对象 + * + * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param ts 要解析的日期时间字符串 + * @return 解析后的 Date 对象 + * @throws RuntimeException 如果解析过程中发生异常 + */ + public static Date parseDateTime(final FormatsType format, final String ts) { + try { + return new SimpleDateFormat(format.getTimeFormat()).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 将对象转换为日期对象 + * + * @param str 要转换的对象,通常是字符串 + * @return 转换后的日期对象,如果转换失败或输入为null,则返回null + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), PARSE_PATTERNS); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + * + * @return 服务器启动时间的 Date 对象表示 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间之间的时间差,并以指定单位返回(绝对值) + * + * @param start 起始时间 + * @param end 结束时间 + * @param unit 所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS) + * @return 时间差的绝对值,以指定单位表示 + */ + public static long difference(Date start, Date end, TimeUnit unit) { + // 计算时间差,单位为毫秒,取绝对值避免负数 + long diffInMillis = Math.abs(end.getTime() - start.getTime()); + + // 根据目标单位转换时间差 + return switch (unit) { + case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1); + case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1); + case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1); + case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1); + case MILLISECONDS -> diffInMillis; + case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis); + case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis); + }; + } + + /** + * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回 + * + * @param endDate 结束日期 + * @param nowDate 当前日期 + * @return 表示时间差的字符串,格式为"天 小时 分钟" + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + return String.format("%d天 %d小时 %d分钟", day, hour, min); + } + + /** + * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位 + * + * @param endDate 结束时间 + * @param nowDate 当前时间 + * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示 + */ + public static String getTimeDifference(Date endDate, Date nowDate) { + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60; + // 构建时间差字符串,条件是值不为0才显示 + StringBuilder result = new StringBuilder(); + if (day > 0) { + result.append(String.format("%d天 ", day)); + } + if (hour > 0) { + result.append(String.format("%d小时 ", hour)); + } + if (min > 0) { + result.append(String.format("%d分钟 ", min)); + } + if (sec > 0) { + result.append(String.format("%d秒", sec)); + } + return result.length() > 0 ? result.toString().trim() : "0秒"; + } + + /** + * 将 LocalDateTime 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDateTime 对象 + * @return 转换后的 Date 对象 + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 将 LocalDate 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDate 对象 + * @return 转换后的 Date 对象 + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 校验日期范围 + * + * @param startDate 开始日期 + * @param endDate 结束日期 + * @param maxValue 最大时间跨度的限制值 + * @param unit 时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES" + */ + public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) { + // 校验结束日期不能早于开始日期 + if (endDate.before(startDate)) { + throw new ServiceException("结束日期不能早于开始日期"); + } + + // 计算时间跨度 + long diffInMillis = endDate.getTime() - startDate.getTime(); + + // 根据单位转换时间跨度 + long diff = switch (unit) { + case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis); + case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis); + case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis); + default -> throw new IllegalArgumentException("不支持的时间单位"); + }; + + // 校验时间跨度不超过最大限制 + if (diff > maxValue) { + throw new ServiceException("最大时间跨度为 " + maxValue + " " + unit.toString().toLowerCase()); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java new file mode 100644 index 0000000..598db1e --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ImageToCArrayConverterUtils.java @@ -0,0 +1,90 @@ +package com.fuyuanshen.common.core.utils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public class ImageToCArrayConverterUtils { + + public static void main(String[] args) { + try { + convertImageToCArray("E:\\workspace\\6170_强光_160_80_2.jpg", "E:\\workspace\\output.c", 160, 80); + System.out.println("转换成功!"); + } catch (IOException e) { + System.err.println("转换失败: " + e.getMessage()); + } + } + + public static void convertImageToCArray(String inputPath, String outputPath, + int width, int height) throws IOException { + // 读取原始图片 + BufferedImage originalImage = ImageIO.read(new File(inputPath)); + + // 调整图片尺寸 + BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + resizedImage.getGraphics().drawImage( + originalImage, 0, 0, width, height, null); + + // 转换像素数据为RGB565格式(高位在前) + ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = resizedImage.getRGB(x, y); + + // 提取RGB分量 + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + // 转换为RGB565(5位红,6位绿,5位蓝) + int r5 = (r >> 3) & 0x1F; + int g6 = (g >> 2) & 0x3F; + int b5 = (b >> 3) & 0x1F; + + // 组合为16位值 + int rgb565 = (r5 << 11) | (g6 << 5) | b5; + + // 高位在前(大端序)写入字节 + byteStream.write((rgb565 >> 8) & 0xFF); // 高字节 + byteStream.write(rgb565 & 0xFF); // 低字节 + } + } + + byte[] imageData = byteStream.toByteArray(); + + // 生成C语言数组文件 + try (FileOutputStream fos = new FileOutputStream(outputPath)) { + // 写入注释行(包含尺寸信息) + String header = String.format("/* 0X10,0X10,0X00,0X%02X,0X00,0X%02X,0X01,0X1B, */\n", + width, height); + fos.write(header.getBytes()); + + // 写入数组声明 + fos.write("const unsigned char gImage_data[] = {\n".getBytes()); + + // 写入数据(每行16个字节) + for (int i = 0; i < imageData.length; i++) { + // 写入0X前缀 + fos.write(("0X" + String.format("%02X", imageData[i] & 0xFF)).getBytes()); + + // 添加逗号(最后一个除外) + if (i < imageData.length - 1) { + fos.write(','); + } + + // 换行和缩进 + if ((i + 1) % 16 == 0) { + fos.write('\n'); + } else { + fos.write(' '); + } + } + + // 写入数组结尾 + fos.write("\n};\n".getBytes()); + } + } +} \ No newline at end of file diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MapstructUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MapstructUtils.java new file mode 100644 index 0000000..2adbe0f --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MapstructUtils.java @@ -0,0 +1,93 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import io.github.linpeilie.Converter; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * Mapstruct 工具类 + *

参考文档:mapstruct-plus

+ * + * + * @author Michelle.Chung + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MapstructUtils { + + private final static Converter CONVERTER = SpringUtils.getBean(Converter.class); + + /** + * 将 T 类型对象,转换为 desc 类型的对象并返回 + * + * @param source 数据来源实体 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static V convert(T source, Class desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象 + * + * @param source 数据来源实体 + * @param desc 转换后的对象 + * @return desc + */ + public static V convert(T source, V desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型的集合,转换为 desc 类型的集合并返回 + * + * @param sourceList 数据来源实体列表 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static List convert(List sourceList, Class desc) { + if (ObjectUtil.isNull(sourceList)) { + return null; + } + if (CollUtil.isEmpty(sourceList)) { + return CollUtil.newArrayList(); + } + return CONVERTER.convert(sourceList, desc); + } + + /** + * 将 Map 转换为 beanClass 类型的集合并返回 + * + * @param map 数据来源 + * @param beanClass bean类 + * @return bean对象 + */ + public static T convert(Map map, Class beanClass) { + if (MapUtil.isEmpty(map)) { + return null; + } + if (ObjectUtil.isNull(beanClass)) { + return null; + } + return CONVERTER.convert(map, beanClass); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MessageUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MessageUtils.java new file mode 100644 index 0000000..0b3b98c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/MessageUtils.java @@ -0,0 +1,33 @@ +package com.fuyuanshen.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.context.NoSuchMessageException; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MessageUtils { + + private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class); + + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) { + try { + return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale()); + } catch (NoSuchMessageException e) { + return code; + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/NetUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/NetUtils.java new file mode 100644 index 0000000..a6fd5e1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/NetUtils.java @@ -0,0 +1,84 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.net.NetUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.utils.regex.RegexUtils; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 增强网络相关工具类 + * + * @author 秋辞未寒 + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NetUtils extends NetUtil { + + /** + * 判断是否为IPv6地址 + * + * @param ip IP地址 + * @return 是否为IPv6地址 + */ + public static boolean isIPv6(String ip) { + try { + // 判断是否为IPv6地址 + return InetAddress.getByName(ip) instanceof Inet6Address; + } catch (UnknownHostException e) { + return false; + } + } + + /** + * 判断IPv6地址是否为内网地址 + *

+ * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理: + *
+     * 通配符地址 0:0:0:0:0:0:0:0
+     * 链路本地地址 fe80::/10
+     * 唯一本地地址 fec0::/10
+     * 环回地址 ::1
+     * 
+ * + * @param ip IP地址 + * @return 是否为内网地址 + */ + public static boolean isInnerIPv6(String ip) { + try { + // 判断是否为IPv6地址 + if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) { + // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断 + // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断 + // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机 + // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA) + if (inet6Address.isAnyLocalAddress() + || inet6Address.isLinkLocalAddress() + || inet6Address.isLoopbackAddress() + || inet6Address.isSiteLocalAddress()) { + return true; + } + } + } catch (UnknownHostException e) { + // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。 + throw new IllegalArgumentException("Invalid IPv6 address!", e); + } + return false; + } + + /** + * 判断是否为IPv4地址 + * + * @param ip IP地址 + * @return 是否为IPv4地址 + */ + public static boolean isIPv4(String ip) { + return RegexUtils.isMatch(PatternPool.IPV4, ip); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ObjectUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ObjectUtils.java new file mode 100644 index 0000000..36c4e4f --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ObjectUtils.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.function.Function; + +/** + * 对象工具类 + * + * @author 秋辞未寒 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ObjectUtils extends ObjectUtil { + + /** + * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName); + * + * @param obj 对象 + * @param func 获取方法 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return null; + } + + /** + * 如果对象不为空,则获取对象中的某个字段,否则返回默认值 + * + * @param obj 对象 + * @param func 获取方法 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func, E defaultValue) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return defaultValue; + } + + /** + * 如果值不为空,则返回值,否则返回默认值 + * + * @param obj 对象 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static T notNull(T obj, T defaultValue) { + if (isNotNull(obj)) { + return obj; + } + return defaultValue; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/PageUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/PageUtil.java new file mode 100644 index 0000000..b10bb35 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/PageUtil.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.utils; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.fuyuanshen.common.core.domain.PageResult; + +import java.util.Collections; +import java.util.List; + +/** + * 分页工具 + * @author Zheng Jie + * @date 2018-12-10 + */ +public class PageUtil extends cn.hutool.core.util.PageUtil { + + /** + * List 分页 + */ + public static List paging(int page, int size , List list) { + int fromIndex = page * size; + int toIndex = page * size + size; + if(fromIndex > list.size()){ + return Collections.emptyList(); + } else if(toIndex >= list.size()) { + return list.subList(fromIndex,list.size()); + } else { + return list.subList(fromIndex,toIndex); + } + } + + /** + * Page 数据处理 + */ + public static PageResult toPage(IPage page) { + return new PageResult<>(page.getRecords(), page.getTotal()); + } + + /** + * 自定义分页 + */ + public static PageResult toPage(List list) { + return new PageResult<>(list, list.size()); + } + + /** + * 返回空数据 + */ + public static PageResult noData () { + return new PageResult<>(null, 0); + } + + /** + * 自定义分页 + */ + public static PageResult toPage(List list, long totalElements) { + return new PageResult<>(list, totalElements); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ServletUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..d6d890d --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ServletUtils.java @@ -0,0 +1,289 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.convert.Convert; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.http.HttpStatus; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作 + * + * @author fys + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ServletUtils extends JakartaServletUtil { + + /** + * 获取指定名称的 String 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取指定名称的 Integer 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取指定名称的 Boolean 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取所有请求参数(以 Map 的形式返回) + * + * @param request 请求对象{@link ServletRequest} + * @return 请求参数的 Map,键为参数名,值为参数值数组 + */ + public static Map getParams(ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接) + * + * @param request 请求对象{@link ServletRequest} + * @return 请求参数的 Map,键为参数名,值为拼接后的字符串 + */ + public static Map getParamMap(ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR)); + } + return params; + } + + /** + * 获取当前 HTTP 请求对象 + * + * @return 当前 HTTP 请求对象 + */ + public static HttpServletRequest getRequest() { + try { + return getRequestAttributes().getRequest(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取当前 HTTP 响应对象 + * + * @return 当前 HTTP 响应对象 + */ + public static HttpServletResponse getResponse() { + try { + return getRequestAttributes().getResponse(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取当前请求的 HttpSession 对象 + *

+ * 如果当前请求已经关联了一个会话(即已经存在有效的 session ID), + * 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。 + *

+ * HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等, + * 可以在多个请求之间共享会话数据 + * + * @return 当前请求的 HttpSession 对象 + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + /** + * 获取当前请求的请求属性 + * + * @return {@link ServletRequestAttributes} 请求属性对象 + */ + public static ServletRequestAttributes getRequestAttributes() { + try { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } catch (Exception e) { + return null; + } + } + + /** + * 获取指定请求头的值,如果头部为空则返回空字符串 + * + * @param request 请求对象 + * @param name 头部名称 + * @return 头部值 + */ + public static String getHeader(HttpServletRequest request, String name) { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + /** + * 获取所有请求头的 Map,键为头部名称,值为头部值 + * + * @param request 请求对象 + * @return 请求头的 Map + */ + public static Map getHeaders(HttpServletRequest request) { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端(以 JSON 格式返回) + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 判断当前请求是否为 Ajax 异步请求 + * + * @param request 请求对象 + * @return 是否为 Ajax 请求 + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + + // 判断 Accept 头部是否包含 application/json + String accept = request.getHeader("accept"); + if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + return true; + } + + // 判断 X-Requested-With 头部是否包含 XMLHttpRequest + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + // 判断 URI 后缀是否为 .json 或 .xml + String uri = request.getRequestURI(); + if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + // 判断请求参数 __ajax 是否为 json 或 xml + String ajax = request.getParameter("__ajax"); + return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); + } + + /** + * 获取客户端 IP 地址 + * + * @return 客户端 IP 地址 + */ + public static String getClientIP() { + return getClientIP(getRequest()); + } + + /** + * 对内容进行 URL 编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) { + return URLEncoder.encode(str, StandardCharsets.UTF_8); + } + + /** + * 对内容进行 URL 解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/SpringUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..ef1f73a --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/SpringUtils.java @@ -0,0 +1,67 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.thread.Threading; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +/** + * spring工具类 + * + * @author Lion Li + */ +@Component +public final class SpringUtils extends SpringUtil { + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } + + /** + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } + + /** + * 获取aop代理对象 + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) getBean(invoker.getClass()); + } + + + /** + * 获取spring上下文 + */ + public static ApplicationContext context() { + return getApplicationContext(); + } + + public static boolean isVirtual() { + return Threading.VIRTUAL.isActive(getBean(Environment.class)); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StreamUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StreamUtils.java new file mode 100644 index 0000000..94f5eda --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StreamUtils.java @@ -0,0 +1,282 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * stream 流工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StreamUtils { + + /** + * 将collection过滤 + * + * @param collection 需要转化的集合 + * @param function 过滤方法 + * @return 过滤后的list + */ + public static List filter(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + return collection.stream().filter(function).collect(Collectors.toList()); + } + + /** + * 找到流中满足条件的第一个元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的第一个元素,没有则返回null + */ + public static E findFirst(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return null; + } + return collection.stream().filter(function).findFirst().orElse(null); + } + + /** + * 找到流中任意一个满足条件的元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的任意一个元素,没有则返回null + */ + public static Optional findAny(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return Optional.empty(); + } + return collection.stream().filter(function).findAny(); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function) { + return join(collection, function, StringUtils.SEPARATOR); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @param delimiter 拼接符 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function, CharSequence delimiter) { + if (CollUtil.isEmpty(collection)) { + return StringUtils.EMPTY; + } + return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter)); + } + + /** + * 将collection排序 + * + * @param collection 需要转化的集合 + * @param comparing 排序方法 + * @return 排序后的list + */ + public static List sorted(Collection collection, Comparator comparing) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList()); + } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + */ + public static Map toIdentityMap(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + + /** + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + * + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + */ + public static Map toMap(Collection collection, Function key, Function value) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + */ + public static Map> groupByKey(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream().filter(Objects::nonNull) + .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + */ + public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection + .stream().filter(Objects::nonNull) + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + */ + public static Map> group2Map(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) { + return MapUtil.newHashMap(); + } + return collection + .stream().filter(Objects::nonNull) + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + */ + public static List toList(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); + } + + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + */ + public static Set toSet(Collection collection, Function function) { + if (CollUtil.isEmpty(collection) || function == null) { + return CollUtil.newHashSet(); + } + return collection + .stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + + /** + * 合并两个相同key类型的map + * + * @param map1 第一个需要合并的 map + * @param map2 第二个需要合并的 map + * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况 + * @param map中的key类型 + * @param 第一个 map的value类型 + * @param 第二个 map的value类型 + * @param 最终map的value类型 + * @return 合并后的map + */ + public static Map merge(Map map1, Map map2, BiFunction merge) { + if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) { + return MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map1)) { + map1 = MapUtil.newHashMap(); + } else if (MapUtil.isEmpty(map2)) { + map2 = MapUtil.newHashMap(); + } + Set key = new HashSet<>(); + key.addAll(map1.keySet()); + key.addAll(map2.keySet()); + Map map = new HashMap<>(); + for (K t : key) { + X x = map1.get(t); + Y y = map2.get(t); + V z = merge.apply(x, y); + if (z != null) { + map.put(t, z); + } + } + return map; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StringUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StringUtils.java new file mode 100644 index 0000000..1ecc122 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/StringUtils.java @@ -0,0 +1,365 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import org.springframework.util.AntPathMatcher; + +import java.nio.charset.Charset; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 字符串工具类 + * + * @author Lion Li + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + public static final String SEPARATOR = ","; + + public static final String SLASH = "/"; + + @Deprecated + private StringUtils() { + } + + /** + * 获取参数不为空值 + * + * @param str defaultValue 要判断的value + * @return value 返回值 + */ + public static String blankToDefault(String str, String defaultValue) { + return StrUtil.blankToDefault(str, defaultValue); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) { + return StrUtil.isEmpty(str); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * 去空格 + */ + public static String trim(String str) { + return StrUtil.trim(str); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) { + return substring(str, start, str.length()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) { + return StrUtil.sub(str, start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is {} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) { + return StrUtil.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) { + return Validator.isUrl(link); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static Set str2Set(String str, String sep) { + return new HashSet<>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList<>(); + if (isEmpty(str)) { + return list; + } + + // 过滤空白字符串 + if (filterBlank && isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && isBlank(string)) { + continue; + } + if (trim) { + string = trim(string); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { + return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences); + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) { + return StrUtil.toUnderlineCase(str); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + return StrUtil.equalsAnyIgnoreCase(str, strs); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) { + return StrUtil.upperFirst(StrUtil.toCamelCase(name)); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) { + return StrUtil.toCamelCase(s); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || CollUtil.isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static String padl(final Number num, final int size) { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static String padl(final String s, final int size, final char c) { + final StringBuilder sb = new StringBuilder(size); + if (s != null) { + final int len = s.length(); + if (s.length() <= size) { + sb.append(String.valueOf(c).repeat(size - len)); + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + sb.append(String.valueOf(c).repeat(Math.max(0, size))); + } + return sb.toString(); + } + + /** + * 切分字符串(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @return 分割后的数据列表 + */ + public static List splitList(String str) { + return splitTo(str, Convert::toStr); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @return 分割后的数据列表 + */ + public static List splitList(String str, String separator) { + return splitTo(str, separator, Convert::toStr); + } + + /** + * 切分字符串自定义转换(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, Function mapper) { + return splitTo(str, SEPARATOR, mapper); + } + + /** + * 切分字符串自定义转换 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, String separator, Function mapper) { + if (isBlank(str)) { + return new ArrayList<>(0); + } + return StrUtil.split(str, separator) + .stream() + .filter(Objects::nonNull) + .map(mapper) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + /** + * 不区分大小写检查 CharSequence 是否以指定的前缀开头。 + * + * @param str 要检查的 CharSequence 可能为 null + * @param prefixs 要查找的前缀可能为 null + * @return 是否包含 + */ + public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) { + // 判断是否是以指定字符串开头 + for (CharSequence prefix : prefixs) { + if (StringUtils.startsWithIgnoreCase(str, prefix)) { + return true; + } + } + return false; + } + + /** + * 将字符串从源字符集转换为目标字符集 + * + * @param input 原始字符串 + * @param fromCharset 源字符集 + * @param toCharset 目标字符集 + * @return 转换后的字符串 + */ + public static String convert(String input, Charset fromCharset, Charset toCharset) { + if (isBlank(input)) { + return input; + } + try { + // 从源字符集获取字节 + byte[] bytes = input.getBytes(fromCharset); + // 使用目标字符集解码 + return new String(bytes, toCharset); + } catch (Exception e) { + return input; + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Threads.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Threads.java new file mode 100644 index 0000000..db2952e --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/Threads.java @@ -0,0 +1,63 @@ +package com.fuyuanshen.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.*; + +/** + * 线程相关工具类. + * + * @author fys + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Threads { + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/TreeBuildUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/TreeBuildUtils.java new file mode 100644 index 0000000..462c849 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/TreeBuildUtils.java @@ -0,0 +1,96 @@ +package com.fuyuanshen.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import cn.hutool.core.lang.tree.parser.NodeParser; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 扩展 hutool TreeUtil 封装系统树构建 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TreeBuildUtils extends TreeUtil { + + /** + * 根据前端定制差异化字段 + */ + public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); + + /** + * 构建树形结构 + * + * @param 输入节点的类型 + * @param 节点ID的类型 + * @param list 节点列表,其中包含了要构建树形结构的所有节点 + * @param nodeParser 解析器,用于将输入节点转换为树节点 + * @return 构建好的树形结构列表 + */ + public static List> build(List list, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); + return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); + } + + /** + * 构建树形结构 + * + * @param 输入节点的类型 + * @param 节点ID的类型 + * @param parentId 顶级节点 + * @param list 节点列表,其中包含了要构建树形结构的所有节点 + * @param nodeParser 解析器,用于将输入节点转换为树节点 + * @return 构建好的树形结构列表 + */ + public static List> build(List list, K parentId, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser); + } + + /** + * 获取节点列表中所有节点的叶子节点 + * + * @param 节点ID的类型 + * @param nodes 节点列表 + * @return 包含所有叶子节点的列表 + */ + public static List> getLeafNodes(List> nodes) { + if (CollUtil.isEmpty(nodes)) { + return CollUtil.newArrayList(); + } + return nodes.stream() + .flatMap(TreeBuildUtils::extractLeafNodes) + .collect(Collectors.toList()); + } + + /** + * 获取指定节点下的所有叶子节点 + * + * @param 节点ID的类型 + * @param node 要查找叶子节点的根节点 + * @return 包含所有叶子节点的列表 + */ + private static Stream> extractLeafNodes(Tree node) { + if (!node.hasChild()) { + return Stream.of(node); + } else { + // 递归调用,获取所有子节点的叶子节点 + return node.getChildren().stream() + .flatMap(TreeBuildUtils::extractLeafNodes); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ValidatorUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ValidatorUtils.java new file mode 100644 index 0000000..37cdc70 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ValidatorUtils.java @@ -0,0 +1,35 @@ +package com.fuyuanshen.common.core.utils; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * Validator 校验框架工具 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ValidatorUtils { + + private static final Validator VALID = SpringUtils.getBean(Validator.class); + + /** + * 对给定对象进行参数校验,并根据指定的校验组进行校验 + * + * @param object 要进行校验的对象 + * @param groups 校验组 + * @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常 + */ + public static void validate(T object, Class... groups) { + Set> validate = VALID.validate(object, groups); + if (!validate.isEmpty()) { + throw new ConstraintViolationException("参数校验异常", validate); + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java new file mode 100644 index 0000000..cebd84c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtil.java @@ -0,0 +1,413 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.common.core.utils.file; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import com.fuyuanshen.common.core.exception.BadRequestException; +import com.fuyuanshen.common.core.utils.CloseUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.springframework.web.multipart.MultipartFile; +import java.io.*; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * File工具类,扩展 hutool 工具包 + * + * @author Zheng Jie + * @date 2018-12-27 + */ +@Slf4j +public class FileUtil extends cn.hutool.core.io.FileUtil { + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *

+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + public static final String IMAGE = "图片"; + public static final String TXT = "文档"; + public static final String MUSIC = "音乐"; + public static final String VIDEO = "视频"; + public static final String OTHER = "其他"; + + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = new File(SYS_TEM_DIR + IdUtil.simpleUUID() + prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (!filename.isEmpty())) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (!filename.isEmpty())) { + int dot = filename.lastIndexOf('.'); + if (dot > -1) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + String resultSize; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = DF.format(size / (float) GB) + "GB "; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = DF.format(size / (float) MB) + "MB "; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = DF.format(size / (float) KB) + "KB "; + } else { + resultSize = size + "B "; + } + return resultSize; + } + + /** + * inputStream 转 File + */ + static File inputStreamToFile(InputStream ins, String name){ + File file = new File(SYS_TEM_DIR + name); + if (file.exists()) { + return file; + } + OutputStream os = null; + try { + os = Files.newOutputStream(file.toPath()); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + CloseUtil.close(os); + CloseUtil.close(ins); + } + return file; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + // 过滤非法文件名 + String name = getFileNameNoEx(verifyFilename(file.getOriginalFilename())); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + nowStr + "." + suffix; + String path = filePath + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + System.out.println("was not successful."); + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 导出excel + */ + public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 处理数据以防止CSV注入 + List> sanitizedList = list.parallelStream().map(map -> { + Map sanitizedMap = new LinkedHashMap<>(); + map.forEach((key, value) -> { + if (value instanceof String) { + String strValue = (String) value; + // 检查并处理以特殊字符开头的值 + if (strValue.startsWith("=") || strValue.startsWith("+") || strValue.startsWith("-") || strValue.startsWith("@")) { + strValue = "'" + strValue; // 添加单引号前缀 + } + sanitizedMap.put(key, strValue); + } else { + sanitizedMap.put(key, value); + } + }); + return sanitizedMap; + }).collect(Collectors.toList()); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(sanitizedList, true); + SXSSFSheet sheet = (SXSSFSheet)writer.getSheet(); + //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 + sheet.trackAllColumnsForAutoSizing(); + //列宽自适应 + writer.autoSizeColumnAll(); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=file.xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return IMAGE; + } else if (documents.contains(type)) { + return TXT; + } else if (music.contains(type)) { + return MUSIC; + } else if (video.contains(type)) { + return VIDEO; + } else { + return OTHER; + } + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new BadRequestException("文件超出规定大小:" + maxSize + "MB"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + if(img1Md5 != null){ + return img1Md5.equals(img2Md5); + } + return false; + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + InputStream in = null; + try { + in = Files.newInputStream(file.toPath()); + try { + System.out.println(in.read(b)); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } finally { + CloseUtil.close(in); + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * 验证并过滤非法的文件名 + * @param fileName 文件名 + * @return 文件名 + */ + public static String verifyFilename(String fileName) { + // 过滤掉特殊字符 + fileName = fileName.replaceAll("[\\\\/:*?\"<>|~\\s]", ""); + + // 去掉文件名开头和结尾的空格和点 + fileName = fileName.trim().replaceAll("^[. ]+|[. ]+$", ""); + + // 不允许文件名超过255(在Mac和Linux中)或260(在Windows中)个字符 + int maxFileNameLength = 255; + if (System.getProperty("os.name").startsWith("Windows")) { + maxFileNameLength = 260; + } + if (fileName.length() > maxFileNameLength) { + fileName = fileName.substring(0, maxFileNameLength); + } + + // 过滤掉控制字符 + fileName = fileName.replaceAll("[\\p{Cntrl}]", ""); + + // 过滤掉 ".." 路径 + fileName = fileName.replaceAll("\\.{2,}", ""); + + // 去掉文件名开头的 ".." + fileName = fileName.replaceAll("^\\.+/", ""); + + // 保留文件名中最后一个 "." 字符,过滤掉其他 "." + fileName = fileName.replaceAll("^(.*)(\\.[^.]*)$", "$1").replaceAll("\\.", "") + + fileName.replaceAll("^(.*)(\\.[^.]*)$", "$2"); + + return fileName; + } + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..b75c186 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/FileUtils.java @@ -0,0 +1,43 @@ +package com.fuyuanshen.common.core.utils.file; + +import cn.hutool.core.io.FileUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileUtils extends FileUtil { + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) { + String percentEncodedFileName = percentEncode(realFileName); + String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/MimeTypeUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..7824bef --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author fys + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf"}; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/AddressUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/AddressUtils.java new file mode 100644 index 0000000..bf2aed1 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/AddressUtils.java @@ -0,0 +1,43 @@ +package com.fuyuanshen.common.core.utils.ip; + +import cn.hutool.http.HtmlUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.utils.NetUtils; +import com.fuyuanshen.common.core.utils.StringUtils; + +/** + * 获取地址类 + * + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AddressUtils { + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) { + // 处理空串并过滤HTML标签 + ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); + boolean isIPv6 = NetUtils.isIPv6(ip); + // 判断是否为IPv4或IPv6,如果不是则返回未知地址 + if (!NetUtils.isIPv4(ip) && !isIPv6) { + return UNKNOWN; + } + // 内网不查询 + if (NetUtils.isInnerIPv6(ip) || NetUtils.isInnerIP(ip)) { + return "内网IP"; + } + // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回 + if (isIPv6) { + log.warn("ip2region不支持IPV6地址解析:{}", ip); + // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回 + return "未知"; + } + return RegionUtils.getCityInfo(ip); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/RegionUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/RegionUtils.java new file mode 100644 index 0000000..55f6025 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/ip/RegionUtils.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.common.core.utils.ip; + +import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.io.resource.ResourceUtil; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.lionsoul.ip2region.xdb.Searcher; + +/** + * 根据ip地址定位工具类,离线方式 + * 参考地址:集成 ip2region 实现离线IP地址定位库 + * + * @author lishuyan + */ +@Slf4j +public class RegionUtils { + + // IP地址库文件名称 + public static final String IP_XDB_FILENAME = "ip2region.xdb"; + + private static final Searcher SEARCHER; + + static { + try { + // 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。 + // 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象。 + SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME)); + log.info("RegionUtils初始化成功,加载IP地址库数据成功!"); + } catch (NoResourceException e) { + throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); + } catch (Exception e) { + throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); + } + } + + /** + * 根据IP地址离线获取城市 + */ + public static String getCityInfo(String ip) { + try { + // 3、执行查询 + String region = SEARCHER.search(StringUtils.trim(ip)); + return region.replace("0|", "").replace("|0", ""); + } catch (Exception e) { + log.error("IP地址离线获取城市异常 {}", ip); + return "未知"; + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/reflect/ReflectUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..3eba416 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.common.core.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import com.fuyuanshen.common.core.utils.StringUtils; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.lang.reflect.Method; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author Lion Li + */ +@SuppressWarnings("rawtypes") +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invoke(object, getterMethodName); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E 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 = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexUtils.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexUtils.java new file mode 100644 index 0000000..3bbfb20 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexUtils.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.common.core.utils.regex; + + +import cn.hutool.core.util.ReUtil; +import com.fuyuanshen.common.core.constant.RegexConstants; + +/** + * 正则相关工具类 + * + * @author Feng + */ +public final class RegexUtils extends ReUtil { + + /** + * 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值 + * + * @param input 要提取的输入字符串 + * @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量 + * @param defaultInput 如果没有匹配时返回的默认值 + * @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值 + */ + public static String extractFromString(String input, String regex, String defaultInput) { + try { + String str = ReUtil.get(regex, input, 1); + return str == null ? defaultInput : str; + } catch (Exception e) { + return defaultInput; + } + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexValidator.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexValidator.java new file mode 100644 index 0000000..ed353c0 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/regex/RegexValidator.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.common.core.utils.regex; + +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.core.lang.Validator; +import com.fuyuanshen.common.core.factory.RegexPatternPoolFactory; + +import java.util.regex.Pattern; + +/** + * 正则字段校验器 + * 主要验证字段非空、是否为满足指定格式等 + * + * @author Feng + */ +public class RegexValidator extends Validator { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE; + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6; + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER; + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE; + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD; + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = RegexPatternPoolFactory.STATUS; + + + /** + * 检查输入的账号是否匹配预定义的规则 + * + * @param value 要验证的账号 + * @return 如果账号符合规则,返回 true;否则,返回 false。 + */ + public static boolean isAccount(CharSequence value) { + return isMatchRegex(ACCOUNT, value); + } + + /** + * 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的账号 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的账号 + * @throws ValidateException 如果验证失败 + */ + public static T validateAccount(T value, String errorMsg) throws ValidateException { + if (!isAccount(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + + /** + * 检查输入的状态是否匹配预定义的规则 + * + * @param value 要验证的状态 + * @return 如果状态符合规则,返回 true;否则,返回 false。 + */ + public static boolean isStatus(CharSequence value) { + return isMatchRegex(STATUS, value); + } + + /** + * 验证输入的状态是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的状态 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的状态 + * @throws ValidateException 如果验证失败 + */ + public static T validateStatus(T value, String errorMsg) throws ValidateException { + if (!isStatus(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/sql/SqlUtil.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..17a5d27 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.common.core.utils.sql; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author fys + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SqlUtil { + + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new IllegalArgumentException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) { + if (StringUtils.isEmpty(value)) { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { + throw new IllegalArgumentException("参数存在SQL注入风险"); + } + } + } +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/AddGroup.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/AddGroup.java new file mode 100644 index 0000000..fd19bf6 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/AddGroup.java @@ -0,0 +1,9 @@ +package com.fuyuanshen.common.core.validate; + +/** + * 校验分组 add + * + * @author Lion Li + */ +public interface AddGroup { +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/EditGroup.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/EditGroup.java new file mode 100644 index 0000000..a90f19b --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/EditGroup.java @@ -0,0 +1,9 @@ +package com.fuyuanshen.common.core.validate; + +/** + * 校验分组 edit + * + * @author Lion Li + */ +public interface EditGroup { +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/QueryGroup.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/QueryGroup.java new file mode 100644 index 0000000..f6ade55 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/QueryGroup.java @@ -0,0 +1,9 @@ +package com.fuyuanshen.common.core.validate; + +/** + * 校验分组 query + * + * @author Lion Li + */ +public interface QueryGroup { +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPattern.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPattern.java new file mode 100644 index 0000000..581f63c --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPattern.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.common.core.validate.dicts; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 字典项校验注解 + * + * @author AprilWind + */ +@Constraint(validatedBy = DictPatternValidator.class) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DictPattern { + + /** + * 字典类型,如 "sys_user_sex" + */ + String dictType(); + + /** + * 分隔符 + */ + String separator(); + + /** + * 默认校验失败提示信息 + */ + String message() default "字典值无效"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPatternValidator.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPatternValidator.java new file mode 100644 index 0000000..c9ca6c9 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/dicts/DictPatternValidator.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.common.core.validate.dicts; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; + +/** + * 自定义字典值校验器 + * + * @author AprilWind + */ +public class DictPatternValidator implements ConstraintValidator { + + /** + * 字典类型 + */ + private String dictType; + + /** + * 分隔符 + */ + private String separator = ","; + + /** + * 初始化校验器,提取注解上的字典类型 + * + * @param annotation 注解实例 + */ + @Override + public void initialize(DictPattern annotation) { + this.dictType = annotation.dictType(); + if (StringUtils.isNotBlank(annotation.separator())) { + this.separator = annotation.separator(); + } + } + + /** + * 校验字段值是否为指定字典类型中的合法值 + * + * @param value 被校验的字段值 + * @param context 校验上下文(可用于构建错误信息) + * @return true 表示校验通过(合法字典值),false 表示不通过 + */ + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isBlank(dictType) || StringUtils.isBlank(value)) { + return false; + } + String dictLabel = SpringUtils.getBean(DictService.class).getDictLabel(dictType, value, separator); + return StringUtils.isNotBlank(dictLabel); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPattern.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPattern.java new file mode 100644 index 0000000..12321cc --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPattern.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.core.validate.enumd; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 自定义枚举校验 + * + * @author 秋辞未寒 + * @date 2024-12-09 + */ +@Documented +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Repeatable(EnumPattern.List.class) // 允许在同一元素上多次使用该注解 +@Constraint(validatedBy = {EnumPatternValidator.class}) +public @interface EnumPattern { + + /** + * 需要校验的枚举类型 + */ + Class> type(); + + /** + * 枚举类型校验值字段名称 + * 需确保该字段实现了 getter 方法 + */ + String fieldName(); + + String message() default "输入值不在枚举范围内"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + @Documented + @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) + @Retention(RUNTIME) + @interface List { + EnumPattern[] value(); + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPatternValidator.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPatternValidator.java new file mode 100644 index 0000000..579ae8d --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/validate/enumd/EnumPatternValidator.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.common.core.validate.enumd; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; + +/** + * 自定义枚举校验注解实现 + * + * @author 秋辞未寒 + * @date 2024-12-09 + */ +public class EnumPatternValidator implements ConstraintValidator { + + private EnumPattern annotation; + + @Override + public void initialize(EnumPattern annotation) { + ConstraintValidator.super.initialize(annotation); + this.annotation = annotation; + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isNotBlank(value)) { + String fieldName = annotation.fieldName(); + for (Object e : annotation.type().getEnumConstants()) { + if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { + return true; + } + } + } + return false; + } + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/Xss.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/Xss.java new file mode 100644 index 0000000..431a3c0 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/Xss.java @@ -0,0 +1,26 @@ +package com.fuyuanshen.common.core.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author Lion Li + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Constraint(validatedBy = {XssValidator.class}) +public @interface Xss { + + String message() default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/XssValidator.java b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/XssValidator.java new file mode 100644 index 0000000..e397234 --- /dev/null +++ b/fys-common/fys-common-core/src/main/java/com/fuyuanshen/common/core/xss/XssValidator.java @@ -0,0 +1,21 @@ +package com.fuyuanshen.common.core.xss; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.http.HtmlUtil; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * 自定义xss校验注解实现 + * + * @author Lion Li + */ +public class XssValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); + } + +} diff --git a/fys-common/fys-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..7949877 --- /dev/null +++ b/fys-common/fys-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.fuyuanshen.common.core.config.ApplicationConfig +com.fuyuanshen.common.core.config.AsyncConfig +com.fuyuanshen.common.core.config.ThreadPoolConfig +com.fuyuanshen.common.core.config.ValidatorConfig +com.fuyuanshen.common.core.utils.SpringUtils diff --git a/fys-common/fys-common-doc/pom.xml b/fys-common/fys-common-doc/pom.xml new file mode 100644 index 0000000..8f924d7 --- /dev/null +++ b/fys-common/fys-common-doc/pom.xml @@ -0,0 +1,46 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-doc + + + fys-common-doc 系统接口 + + + + + com.fuyuanshen + fys-common-core + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + + com.github.therapi + therapi-runtime-javadoc + + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + + + diff --git a/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/SpringDocConfig.java b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/SpringDocConfig.java new file mode 100644 index 0000000..8aa3f1e --- /dev/null +++ b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/SpringDocConfig.java @@ -0,0 +1,126 @@ +package com.fuyuanshen.common.doc.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.doc.config.properties.SpringDocProperties; +import com.fuyuanshen.common.doc.handler.OpenApiHandler; +import org.springdoc.core.configuration.SpringDocConfiguration; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.service.SecurityService; +import org.springdoc.core.utils.PropertyResolverUtils; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * 接口文档配置 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@AutoConfiguration(before = SpringDocConfiguration.class) +@EnableConfigurationProperties(SpringDocProperties.class) +@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true) +public class SpringDocConfig { + + private final ServerProperties serverProperties; + + @Bean + @ConditionalOnMissingBean(OpenAPI.class) + public OpenAPI openApi(SpringDocProperties properties) { + OpenAPI openApi = new OpenAPI(); + // 文档基本信息 + SpringDocProperties.InfoProperties infoProperties = properties.getInfo(); + Info info = convertInfo(infoProperties); + openApi.info(info); + // 扩展文档信息 + openApi.externalDocs(properties.getExternalDocs()); + openApi.tags(properties.getTags()); + openApi.paths(properties.getPaths()); + openApi.components(properties.getComponents()); + Set keySet = properties.getComponents().getSecuritySchemes().keySet(); + List list = new ArrayList<>(); + SecurityRequirement securityRequirement = new SecurityRequirement(); + keySet.forEach(securityRequirement::addList); + list.add(securityRequirement); + openApi.security(list); + + return openApi; + } + + private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) { + Info info = new Info(); + info.setTitle(infoProperties.getTitle()); + info.setDescription(infoProperties.getDescription()); + info.setContact(infoProperties.getContact()); + info.setLicense(infoProperties.getLicense()); + info.setVersion(infoProperties.getVersion()); + return info; + } + + /** + * 自定义 openapi 处理器 + */ + @Bean + public OpenAPIService openApiBuilder(Optional openAPI, + SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomisers, + Optional> serverBaseUrlCustomisers, Optional javadocProvider) { + return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); + } + + /** + * 对已经生成好的 OpenApi 进行自定义操作 + */ + @Bean + public OpenApiCustomizer openApiCustomizer() { + String contextPath = serverProperties.getServlet().getContextPath(); + String finalContextPath; + if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) { + finalContextPath = ""; + } else { + finalContextPath = contextPath; + } + // 对所有路径增加前置上下文路径 + return openApi -> { + Paths oldPaths = openApi.getPaths(); + if (oldPaths instanceof PlusPaths) { + return; + } + PlusPaths newPaths = new PlusPaths(); + oldPaths.forEach((k, v) -> newPaths.addPathItem(finalContextPath + k, v)); + openApi.setPaths(newPaths); + }; + } + + /** + * 单独使用一个类便于判断 解决springdoc路径拼接重复问题 + * + * @author Lion Li + */ + static class PlusPaths extends Paths { + + public PlusPaths() { + super(); + } + } + +} diff --git a/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/properties/SpringDocProperties.java b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/properties/SpringDocProperties.java new file mode 100644 index 0000000..b0bc09c --- /dev/null +++ b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/config/properties/SpringDocProperties.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.common.doc.config.properties; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +import java.util.List; + +/** + * swagger 配置属性 + * + * @author Lion Li + */ +@Data +@ConfigurationProperties(prefix = "springdoc") +public class SpringDocProperties { + + /** + * 文档基本信息 + */ + @NestedConfigurationProperty + private InfoProperties info = new InfoProperties(); + + /** + * 扩展文档地址 + */ + @NestedConfigurationProperty + private ExternalDocumentation externalDocs; + + /** + * 标签 + */ + private List tags = null; + + /** + * 路径 + */ + @NestedConfigurationProperty + private Paths paths = null; + + /** + * 组件 + */ + @NestedConfigurationProperty + private Components components = null; + + /** + *

+ * 文档的基础属性信息 + *

+ * + * @see io.swagger.v3.oas.models.info.Info + * + * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 + */ + @Data + public static class InfoProperties { + + /** + * 标题 + */ + private String title = null; + + /** + * 描述 + */ + private String description = null; + + /** + * 联系人信息 + */ + @NestedConfigurationProperty + private Contact contact = null; + + /** + * 许可证 + */ + @NestedConfigurationProperty + private License license = null; + + /** + * 版本 + */ + private String version = null; + + } + +} diff --git a/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/handler/OpenApiHandler.java b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/handler/OpenApiHandler.java new file mode 100644 index 0000000..6b37cda --- /dev/null +++ b/fys-common/fys-common-doc/src/main/java/com/fuyuanshen/common/doc/handler/OpenApiHandler.java @@ -0,0 +1,253 @@ +package com.fuyuanshen.common.doc.handler; + +import cn.hutool.core.io.IoUtil; +import io.swagger.v3.core.jackson.TypeNameResolver; +import io.swagger.v3.core.util.AnnotationsUtils; +import io.swagger.v3.oas.annotations.tags.Tags; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.ServerBaseUrlCustomizer; +import org.springdoc.core.properties.SpringDocConfigProperties; +import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.service.SecurityService; +import org.springdoc.core.utils.PropertyResolverUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.method.HandlerMethod; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 自定义 openapi 处理器 + * 对源码功能进行修改 增强使用 + */ +@Slf4j +@SuppressWarnings("all") +public class OpenApiHandler extends OpenAPIService { + + /** + * The Basic error controller. + */ + private static Class basicErrorController; + + /** + * The Security parser. + */ + private final SecurityService securityParser; + + /** + * The Mappings map. + */ + private final Map mappingsMap = new HashMap<>(); + + /** + * The Springdoc tags. + */ + private final Map springdocTags = new HashMap<>(); + + /** + * The Open api builder customisers. + */ + private final Optional> openApiBuilderCustomisers; + + /** + * The server base URL customisers. + */ + private final Optional> serverBaseUrlCustomizers; + + /** + * The Spring doc config properties. + */ + private final SpringDocConfigProperties springDocConfigProperties; + + /** + * The Cached open api map. + */ + private final Map cachedOpenAPI = new HashMap<>(); + + /** + * The Property resolver utils. + */ + private final PropertyResolverUtils propertyResolverUtils; + + /** + * The javadoc provider. + */ + private final Optional javadocProvider; + + /** + * The Context. + */ + private ApplicationContext context; + + /** + * The Open api. + */ + private OpenAPI openAPI; + + /** + * The Is servers present. + */ + private boolean isServersPresent; + + /** + * The Server base url. + */ + private String serverBaseUrl; + + /** + * Instantiates a new Open api builder. + * + * @param openAPI the open api + * @param securityParser the security parser + * @param springDocConfigProperties the spring doc config properties + * @param propertyResolverUtils the property resolver utils + * @param openApiBuilderCustomizers the open api builder customisers + * @param serverBaseUrlCustomizers the server base url customizers + * @param javadocProvider the javadoc provider + */ + public OpenApiHandler(Optional openAPI, SecurityService securityParser, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, + Optional> openApiBuilderCustomizers, + Optional> serverBaseUrlCustomizers, + Optional javadocProvider) { + super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); + if (openAPI.isPresent()) { + this.openAPI = openAPI.get(); + if (this.openAPI.getComponents() == null) + this.openAPI.setComponents(new Components()); + if (this.openAPI.getPaths() == null) + this.openAPI.setPaths(new Paths()); + if (!CollectionUtils.isEmpty(this.openAPI.getServers())) + this.isServersPresent = true; + } + this.propertyResolverUtils = propertyResolverUtils; + this.securityParser = securityParser; + this.springDocConfigProperties = springDocConfigProperties; + this.openApiBuilderCustomisers = openApiBuilderCustomizers; + this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; + this.javadocProvider = javadocProvider; + if (springDocConfigProperties.isUseFqn()) + TypeNameResolver.std.setUseFqn(true); + } + + @Override + public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { + + Set tags = new HashSet<>(); + Set tagsStr = new HashSet<>(); + + buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); + buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); + + if (!CollectionUtils.isEmpty(tagsStr)) + tagsStr = tagsStr.stream() + .map(str -> propertyResolverUtils.resolve(str, locale)) + .collect(Collectors.toSet()); + + if (springdocTags.containsKey(handlerMethod)) { + io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); + tagsStr.add(tag.getName()); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + + if (!CollectionUtils.isEmpty(tagsStr)) { + if (CollectionUtils.isEmpty(operation.getTags())) + operation.setTags(new ArrayList<>(tagsStr)); + else { + Set operationTagsSet = new HashSet<>(operation.getTags()); + operationTagsSet.addAll(tagsStr); + operation.getTags().clear(); + operation.getTags().addAll(operationTagsSet); + } + } + + if (isAutoTagClasses(operation)) { + + + if (javadocProvider.isPresent()) { + String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); + if (StringUtils.isNotBlank(description)) { + io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); + + // 自定义部分 修改使用java注释当tag名 + List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); + // tag.setName(tagAutoName); + tag.setName(list.get(0)); + operation.addTagsItem(list.get(0)); + + tag.setDescription(description); + if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { + openAPI.addTagsItem(tag); + } + } + } else { + String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); + operation.addTagsItem(tagAutoName); + } + } + + if (!CollectionUtils.isEmpty(tags)) { + // Existing tags + List openApiTags = openAPI.getTags(); + if (!CollectionUtils.isEmpty(openApiTags)) + tags.addAll(openApiTags); + openAPI.setTags(new ArrayList<>(tags)); + } + + // Handle SecurityRequirement at operation level + io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser + .getSecurityRequirements(handlerMethod); + if (securityRequirements != null) { + if (securityRequirements.length == 0) + operation.setSecurity(Collections.emptyList()); + else + securityParser.buildSecurityRequirement(securityRequirements, operation); + } + + return operation; + } + + private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { + // method tags + Set tagsSet = AnnotatedElementUtils + .findAllMergedAnnotations(method, Tags.class); + Set methodTags = tagsSet.stream() + .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); + methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); + if (!CollectionUtils.isEmpty(methodTags)) { + tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale))); + List allTags = new ArrayList<>(methodTags); + addTags(allTags, tags, locale); + } + } + + private void addTags(List sourceTags, Set tags, Locale locale) { + Optional> optionalTagSet = AnnotationsUtils + .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); + optionalTagSet.ifPresent(tagsSet -> { + tagsSet.forEach(tag -> { + tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); + tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); + if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) + tags.add(tag); + }); + }); + } + +} diff --git a/fys-common/fys-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..ef76629 --- /dev/null +++ b/fys-common/fys-common-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.doc.config.SpringDocConfig diff --git a/fys-common/fys-common-encrypt/pom.xml b/fys-common/fys-common-encrypt/pom.xml new file mode 100644 index 0000000..2797815 --- /dev/null +++ b/fys-common/fys-common-encrypt/pom.xml @@ -0,0 +1,54 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-encrypt + + + fys-common-encrypt 数据加解密模块 + + + + + + com.fuyuanshen + fys-common-core + + + + org.bouncycastle + bcprov-jdk15to18 + + + + cn.hutool + hutool-crypto + + + + org.springframework + spring-webmvc + + + + com.baomidou + mybatis-plus-spring-boot3-starter + true + + + org.mybatis + mybatis-spring + + + + + + + diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/ApiEncrypt.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/ApiEncrypt.java new file mode 100644 index 0000000..f743e65 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/ApiEncrypt.java @@ -0,0 +1,20 @@ +package com.fuyuanshen.common.encrypt.annotation; + +import java.lang.annotation.*; + +/** + * 强制加密注解 + * + * @author Michelle.Chung + */ +@Documented +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiEncrypt { + + /** + * 响应加密忽略,默认不加密,为 true 时加密 + */ + boolean response() default false; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/EncryptField.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/EncryptField.java new file mode 100644 index 0000000..43d15cb --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/annotation/EncryptField.java @@ -0,0 +1,44 @@ +package com.fuyuanshen.common.encrypt.annotation; + +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; + +import java.lang.annotation.*; + +/** + * 字段加密注解 + * + * @author 老马 + */ +@Documented +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EncryptField { + + /** + * 加密算法 + */ + AlgorithmType algorithm() default AlgorithmType.DEFAULT; + + /** + * 秘钥。AES、SM4需要 + */ + String password() default ""; + + /** + * 公钥。RSA、SM2需要 + */ + String publicKey() default ""; + + /** + * 私钥。RSA、SM2需要 + */ + String privateKey() default ""; + + /** + * 编码方式。对加密算法为BASE64的不起作用 + */ + EncodeType encode() default EncodeType.DEFAULT; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/ApiDecryptAutoConfiguration.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/ApiDecryptAutoConfiguration.java new file mode 100644 index 0000000..08238ce --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/ApiDecryptAutoConfiguration.java @@ -0,0 +1,32 @@ +package com.fuyuanshen.common.encrypt.config; + +import jakarta.servlet.DispatcherType; +import com.fuyuanshen.common.encrypt.filter.CryptoFilter; +import com.fuyuanshen.common.encrypt.properties.ApiDecryptProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; + +/** + * api 解密自动配置 + * + * @author wdhcr + */ +@AutoConfiguration +@EnableConfigurationProperties(ApiDecryptProperties.class) +@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true") +public class ApiDecryptAutoConfiguration { + + @Bean + public FilterRegistrationBean cryptoFilterRegistration(ApiDecryptProperties properties) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new CryptoFilter(properties)); + registration.addUrlPatterns("/*"); + registration.setName("cryptoFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + return registration; + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/EncryptorAutoConfiguration.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/EncryptorAutoConfiguration.java new file mode 100644 index 0000000..ae79a3e --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/config/EncryptorAutoConfiguration.java @@ -0,0 +1,49 @@ +package com.fuyuanshen.common.encrypt.config; + +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.encrypt.core.EncryptorManager; +import com.fuyuanshen.common.encrypt.interceptor.MybatisDecryptInterceptor; +import com.fuyuanshen.common.encrypt.interceptor.MybatisEncryptInterceptor; +import com.fuyuanshen.common.encrypt.properties.EncryptorProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * 加解密配置 + * + * @author 老马 + * @version 4.6.0 + */ +@AutoConfiguration(after = MybatisPlusAutoConfiguration.class) +@EnableConfigurationProperties(EncryptorProperties.class) +@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") +@Slf4j +public class EncryptorAutoConfiguration { + + @Autowired + private EncryptorProperties properties; + + @Bean + public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { + return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); + } + + @Bean + public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisEncryptInterceptor(encryptorManager, properties); + } + + @Bean + public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisDecryptInterceptor(encryptorManager, properties); + } + +} + + + diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptContext.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptContext.java new file mode 100644 index 0000000..669a215 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptContext.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.common.encrypt.core; + +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import lombok.Data; + +/** + * 加密上下文 用于encryptor传递必要的参数。 + * + * @author 老马 + * @version 4.6.0 + */ +@Data +public class EncryptContext { + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptorManager.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptorManager.java new file mode 100644 index 0000000..e48c80d --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/EncryptorManager.java @@ -0,0 +1,159 @@ +package com.fuyuanshen.common.encrypt.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.io.Resources; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.annotation.EncryptField; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 加密管理类 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +@NoArgsConstructor +public class EncryptorManager { + + /** + * 缓存加密器 + */ + Map encryptorMap = new ConcurrentHashMap<>(); + + /** + * 类加密字段缓存 + */ + Map, Set> fieldCache = new ConcurrentHashMap<>(); + + /** + * 构造方法传入类加密字段缓存 + * + * @param typeAliasesPackage 实体类包 + */ + public EncryptorManager(String typeAliasesPackage) { + scanEncryptClasses(typeAliasesPackage); + } + + + /** + * 获取类加密字段缓存 + */ + public Set getFieldCache(Class sourceClazz) { + return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz)); + } + + /** + * 注册加密执行者到缓存 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { + int key = encryptContext.hashCode(); + if (encryptorMap.containsKey(key)) { + return encryptorMap.get(key); + } + IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); + encryptorMap.put(key, encryptor); + return encryptor; + } + + /** + * 移除缓存中的加密执行者 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public void removeEncryptor(EncryptContext encryptContext) { + this.encryptorMap.remove(encryptContext.hashCode()); + } + + /** + * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。 + * + * @param value 待加密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String encrypt(String value, EncryptContext encryptContext) { + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + return encryptor.encrypt(value, encryptContext.getEncode()); + } + + /** + * 根据配置进行解密 + * + * @param value 待解密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String decrypt(String value, EncryptContext encryptContext) { + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + return encryptor.decrypt(value); + } + + /** + * 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体 + */ + private void scanEncryptClasses(String typeAliasesPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class clazz = Resources.classForName(classMetadata.getClassName()); + Set encryptFieldSet = getEncryptFieldSetFromClazz(clazz); + if (CollUtil.isNotEmpty(encryptFieldSet)) { + fieldCache.put(clazz, encryptFieldSet); + } + } + } + } catch (Exception e) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 获得一个类的加密字段集合 + */ + private Set getEncryptFieldSetFromClazz(Class clazz) { + Set fieldSet = new HashSet<>(); + // 判断clazz如果是接口,内部类,匿名类就直接返回 + if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) { + return fieldSet; + } + while (clazz != null) { + Field[] fields = clazz.getDeclaredFields(); + fieldSet.addAll(Arrays.asList(fields)); + clazz = clazz.getSuperclass(); + } + fieldSet = fieldSet.stream().filter(field -> + field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) + .collect(Collectors.toSet()); + for (Field field : fieldSet) { + field.setAccessible(true); + } + return fieldSet; + } + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/IEncryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/IEncryptor.java new file mode 100644 index 0000000..f772ff4 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/IEncryptor.java @@ -0,0 +1,35 @@ +package com.fuyuanshen.common.encrypt.core; + +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; + +/** + * 加解者 + * + * @author 老马 + * @version 4.6.0 + */ +public interface IEncryptor { + + /** + * 获得当前算法 + */ + AlgorithmType algorithm(); + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + * @return 加密后的字符串 + */ + String encrypt(String value, EncodeType encodeType); + + /** + * 解密 + * + * @param value 待加密字符串 + * @return 解密后的字符串 + */ + String decrypt(String value); +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AbstractEncryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AbstractEncryptor.java new file mode 100644 index 0000000..673d51b --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AbstractEncryptor.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.core.IEncryptor; + +/** + * 所有加密执行者的基类 + * + * @author 老马 + * @version 4.6.0 + */ +public abstract class AbstractEncryptor implements IEncryptor { + + public AbstractEncryptor(EncryptContext context) { + // 用户配置校验与配置注入 + } + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AesEncryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AesEncryptor.java new file mode 100644 index 0000000..659f3af --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/AesEncryptor.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + +/** + * AES算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class AesEncryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public AesEncryptor(EncryptContext context) { + super(context); + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.AES; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptByAesHex(value, context.getPassword()); + } else { + return EncryptUtils.encryptByAes(value, context.getPassword()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByAes(value, context.getPassword()); + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Base64Encryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Base64Encryptor.java new file mode 100644 index 0000000..c5dca59 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Base64Encryptor.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + +/** + * Base64算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Base64Encryptor extends AbstractEncryptor { + + public Base64Encryptor(EncryptContext context) { + super(context); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.BASE64; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + return EncryptUtils.encryptByBase64(value); + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByBase64(value); + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/RsaEncryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/RsaEncryptor.java new file mode 100644 index 0000000..84f82a5 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/RsaEncryptor.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + + +/** + * RSA算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class RsaEncryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public RsaEncryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。"); + } + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.RSA; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptByRsaHex(value, context.getPublicKey()); + } else { + return EncryptUtils.encryptByRsa(value, context.getPublicKey()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByRsa(value, context.getPrivateKey()); + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm2Encryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm2Encryptor.java new file mode 100644 index 0000000..cad8af9 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm2Encryptor.java @@ -0,0 +1,61 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + +/** + * sm2算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Sm2Encryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public Sm2Encryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。"); + } + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM2; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey()); + } else { + return EncryptUtils.encryptBySm2(value, context.getPublicKey()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptBySm2(value, context.getPrivateKey()); + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm4Encryptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm4Encryptor.java new file mode 100644 index 0000000..809de0a --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/core/encryptor/Sm4Encryptor.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.common.encrypt.core.encryptor; + +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + +/** + * sm4算法实现 + * + * @author 老马 + * @version 4.6.0 + */ +public class Sm4Encryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public Sm4Encryptor(EncryptContext context) { + super(context); + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM4; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptBySm4Hex(value, context.getPassword()); + } else { + return EncryptUtils.encryptBySm4(value, context.getPassword()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptBySm4(value, context.getPassword()); + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/AlgorithmType.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/AlgorithmType.java new file mode 100644 index 0000000..cfa78d4 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/AlgorithmType.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.encrypt.enumd; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.encrypt.core.encryptor.*; + +/** + * 算法名称 + * + * @author 老马 + * @version 4.6.0 + */ +@Getter +@AllArgsConstructor +public enum AlgorithmType { + + /** + * 默认走yml配置 + */ + DEFAULT(null), + + /** + * base64 + */ + BASE64(Base64Encryptor.class), + + /** + * aes + */ + AES(AesEncryptor.class), + + /** + * rsa + */ + RSA(RsaEncryptor.class), + + /** + * sm2 + */ + SM2(Sm2Encryptor.class), + + /** + * sm4 + */ + SM4(Sm4Encryptor.class); + + private final Class clazz; +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/EncodeType.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/EncodeType.java new file mode 100644 index 0000000..af31208 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/enumd/EncodeType.java @@ -0,0 +1,26 @@ +package com.fuyuanshen.common.encrypt.enumd; + +/** + * 编码类型 + * + * @author 老马 + * @version 4.6.0 + */ +public enum EncodeType { + + /** + * 默认使用yml配置 + */ + DEFAULT, + + /** + * base64编码 + */ + BASE64, + + /** + * 16进制编码 + */ + HEX; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/CryptoFilter.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/CryptoFilter.java new file mode 100644 index 0000000..43dc423 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/CryptoFilter.java @@ -0,0 +1,110 @@ +package com.fuyuanshen.common.encrypt.filter; + +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import com.fuyuanshen.common.core.constant.HttpStatus; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.encrypt.properties.ApiDecryptProperties; +import org.springframework.http.HttpMethod; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.io.IOException; + + +/** + * Crypto 过滤器 + * + * @author wdhcr + */ +public class CryptoFilter implements Filter { + private final ApiDecryptProperties properties; + + public CryptoFilter(ApiDecryptProperties properties) { + this.properties = properties; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletRequest = (HttpServletRequest) request; + HttpServletResponse servletResponse = (HttpServletResponse) response; + // 获取加密注解 + ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); + boolean responseFlag = apiEncrypt != null && apiEncrypt.response(); + ServletRequest requestWrapper = null; + ServletResponse responseWrapper = null; + EncryptResponseBodyWrapper responseBodyWrapper = null; + + // 是否为 put 或者 post 请求 + if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { + // 是否存在加密标头 + String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); + if (StringUtils.isNotBlank(headerValue)) { + // 请求解密 + requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); + } else { + // 是否有注解,有就报错,没有放行 + if (ObjectUtil.isNotNull(apiEncrypt)) { + HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); + exceptionResolver.resolveException( + servletRequest, servletResponse, null, + new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); + return; + } + } + } + + // 判断是否响应加密 + if (responseFlag) { + responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); + responseWrapper = responseBodyWrapper; + } + + chain.doFilter( + ObjectUtil.defaultIfNull(requestWrapper, request), + ObjectUtil.defaultIfNull(responseWrapper, response)); + + if (responseFlag) { + servletResponse.reset(); + // 对原始内容加密 + String encryptContent = responseBodyWrapper.getEncryptContent( + servletResponse, properties.getPublicKey(), properties.getHeaderFlag()); + // 对加密后的内容写出 + servletResponse.getWriter().write(encryptContent); + } + } + + /** + * 获取 ApiEncrypt 注解 + */ + private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) { + RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); + // 获取注解 + try { + HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest); + if (ObjectUtil.isNotNull(mappingHandler)) { + Object handler = mappingHandler.getHandler(); + if (ObjectUtil.isNotNull(handler)) { + // 从handler获取注解 + if (handler instanceof HandlerMethod handlerMethod) { + return handlerMethod.getMethodAnnotation(ApiEncrypt.class); + } + } + } + } catch (Exception e) { + return null; + } + return null; + } + + @Override + public void destroy() { + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/DecryptRequestBodyWrapper.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/DecryptRequestBodyWrapper.java new file mode 100644 index 0000000..78ab242 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/DecryptRequestBodyWrapper.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.common.encrypt.filter; + +import cn.hutool.core.io.IoUtil; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; +import org.springframework.http.MediaType; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 解密请求参数工具类 + * + * @author wdhcr + */ +public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { + + private final byte[] body; + + public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException { + super(request); + // 获取 AES 密码 采用 RSA 加密 + String headerRsa = request.getHeader(headerFlag); + String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey); + // 解密 AES 密码 + String aesPassword = EncryptUtils.decryptByBase64(decryptAes); + request.setCharacterEncoding(Constants.UTF8); + byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false); + String requestBody = new String(readBytes, StandardCharsets.UTF_8); + // 解密 body 采用 AES 加密 + String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword); + body = decryptBody.getBytes(StandardCharsets.UTF_8); + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + + @Override + public int getContentLength() { + return body.length; + } + + @Override + public long getContentLengthLong() { + return body.length; + } + + @Override + public String getContentType() { + return MediaType.APPLICATION_JSON_VALUE; + } + + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() { + return bais.read(); + } + + @Override + public int available() { + return body.length; + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/EncryptResponseBodyWrapper.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/EncryptResponseBodyWrapper.java new file mode 100644 index 0000000..a46b04e --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -0,0 +1,123 @@ +package com.fuyuanshen.common.encrypt.filter; + +import cn.hutool.core.util.RandomUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import com.fuyuanshen.common.encrypt.utils.EncryptUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +/** + * 加密响应参数包装类 + * + * @author Michelle.Chung + */ +public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { + + private final ByteArrayOutputStream byteArrayOutputStream; + private final ServletOutputStream servletOutputStream; + private final PrintWriter printWriter; + + public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException { + super(response); + this.byteArrayOutputStream = new ByteArrayOutputStream(); + this.servletOutputStream = this.getOutputStream(); + this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream)); + } + + @Override + public PrintWriter getWriter() { + return printWriter; + } + + @Override + public void flushBuffer() throws IOException { + if (servletOutputStream != null) { + servletOutputStream.flush(); + } + if (printWriter != null) { + printWriter.flush(); + } + } + + @Override + public void reset() { + byteArrayOutputStream.reset(); + } + + public byte[] getResponseData() throws IOException { + flushBuffer(); + return byteArrayOutputStream.toByteArray(); + } + + public String getContent() throws IOException { + flushBuffer(); + return byteArrayOutputStream.toString(); + } + + /** + * 获取加密内容 + * + * @param servletResponse response + * @param publicKey RSA公钥 (用于加密 AES 秘钥) + * @param headerFlag 请求头标志 + * @return 加密内容 + * @throws IOException + */ + public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException { + // 生成秘钥 + String aesPassword = RandomUtil.randomString(32); + // 秘钥使用 Base64 编码 + String encryptAes = EncryptUtils.encryptByBase64(aesPassword); + // Rsa 公钥加密 Base64 编码 + String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); + + // 设置响应头 + // vue版本需要设置 + servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); + servletResponse.setHeader("Access-Control-Allow-Origin", "*"); + servletResponse.setHeader("Access-Control-Allow-Methods", "*"); + servletResponse.setHeader(headerFlag, encryptPassword); + servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + + + // 获取原始内容 + String originalBody = this.getContent(); + // 对内容进行加密 + return EncryptUtils.encryptByAes(originalBody, aesPassword); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new ServletOutputStream() { + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + + @Override + public void write(int b) throws IOException { + byteArrayOutputStream.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + byteArrayOutputStream.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + byteArrayOutputStream.write(b, off, len); + } + }; + } + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisDecryptInterceptor.java new file mode 100644 index 0000000..01fbce1 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -0,0 +1,120 @@ +package com.fuyuanshen.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.resultset.ResultSetHandler; +import org.apache.ibatis.plugin.*; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.annotation.EncryptField; +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.core.EncryptorManager; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.Statement; +import java.util.*; + +/** + * 出参解密拦截器 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ResultSetHandler.class, + method = "handleResultSets", + args = {Statement.class}) +}) +@AllArgsConstructor +public class MybatisDecryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + // 获取执行mysql执行结果 + Object result = invocation.proceed(); + if (result == null) { + return null; + } + decryptHandler(result); + return result; + } + + /** + * 解密对象 + * + * @param sourceObject 待加密对象 + */ + private void decryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::decryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::decryptHandler); + return; + } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } + try { + for (Field field : fields) { + field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理解密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String decryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.decrypt(value, encryptContext); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisEncryptInterceptor.java new file mode 100644 index 0000000..e7d3c5a --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -0,0 +1,124 @@ +package com.fuyuanshen.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.annotation.EncryptField; +import com.fuyuanshen.common.encrypt.core.EncryptContext; +import com.fuyuanshen.common.encrypt.core.EncryptorManager; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import com.fuyuanshen.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.PreparedStatement; +import java.util.*; + +/** + * 入参加密拦截器 + * + * @author 老马 + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ParameterHandler.class, + method = "setParameters", + args = {PreparedStatement.class}) +}) +@AllArgsConstructor +public class MybatisEncryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + return invocation; + } + + @Override + public Object plugin(Object target) { + if (target instanceof ParameterHandler parameterHandler) { + // 进行加密操作 + Object parameterObject = parameterHandler.getParameterObject(); + if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { + this.encryptHandler(parameterObject); + } + } + return target; + } + + /** + * 加密对象 + * + * @param sourceObject 待加密对象 + */ + private void encryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::encryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::encryptHandler); + return; + } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } + try { + for (Field field : fields) { + field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理加密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String encryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.encrypt(value, encryptContext); + } + + + @Override + public void setProperties(Properties properties) { + } +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/ApiDecryptProperties.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/ApiDecryptProperties.java new file mode 100644 index 0000000..5d7d852 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/ApiDecryptProperties.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.common.encrypt.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * api解密属性配置类 + * @author wdhcr + */ +@Data +@ConfigurationProperties(prefix = "api-decrypt") +public class ApiDecryptProperties { + + /** + * 加密开关 + */ + private Boolean enabled; + + /** + * 头部标识 + */ + private String headerFlag; + + /** + * 响应加密公钥 + */ + private String publicKey; + + /** + * 请求解密私钥 + */ + private String privateKey; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/EncryptorProperties.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/EncryptorProperties.java new file mode 100644 index 0000000..1651805 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/properties/EncryptorProperties.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.encrypt.properties; + +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import com.fuyuanshen.common.encrypt.enumd.EncodeType; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 加解密属性配置类 + * + * @author 老马 + * @version 4.6.0 + */ +@Data +@ConfigurationProperties(prefix = "mybatis-encryptor") +public class EncryptorProperties { + + /** + * 过滤开关 + */ + private Boolean enable; + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtils.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtils.java new file mode 100644 index 0000000..2611800 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtils.java @@ -0,0 +1,313 @@ +package com.fuyuanshen.common.encrypt.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.asymmetric.SM2; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * 安全相关工具类 + * + * @author 老马 + */ +public class EncryptUtils { + + /** + * 公钥 + */ + public static final String PUBLIC_KEY = "publicKey"; + + /** + * 私钥 + */ + public static final String PRIVATE_KEY = "privateKey"; + + /** + * Base64加密 + * + * @param data 待加密数据 + * @return 加密后字符串 + */ + public static String encryptByBase64(String data) { + return Base64.encode(data, StandardCharsets.UTF_8); + } + + /** + * Base64解密 + * + * @param data 待解密数据 + * @return 解密后字符串 + */ + public static String decryptByBase64(String data) { + return Base64.decodeStr(data, StandardCharsets.UTF_8); + } + + /** + * AES加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * AES加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByAesHex(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8); + } + + /** + * AES解密 + * + * @param data 待解密数据 + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * SM4加密(Base64编码) + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * SM4加密(Hex编码) + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm4Hex(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8); + } + + /** + * sm4解密 + * + * @param data 待解密数据(可以是Base64或Hex编码) + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * 产生sm2加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateSm2Key() { + Map keyMap = new HashMap<>(2); + SM2 sm2 = SmUtil.sm2(); + keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); + return keyMap; + } + + /** + * sm2公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm2(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("SM2需要传入公钥进行加密"); + } + SM2 sm2 = SmUtil.sm2(null, publicKey); + return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * sm2公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm2Hex(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("SM2需要传入公钥进行加密"); + } + SM2 sm2 = SmUtil.sm2(null, publicKey); + return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * sm2私钥解密 + * + * @param data 待解密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptBySm2(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("SM2需要传入私钥进行解密"); + } + SM2 sm2 = SmUtil.sm2(privateKey, null); + return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * 产生RSA加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateRsaKey() { + Map keyMap = new HashMap<>(2); + RSA rsa = SecureUtil.rsa(); + keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64()); + return keyMap; + } + + /** + * rsa公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByRsa(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("RSA需要传入公钥进行加密"); + } + RSA rsa = SecureUtil.rsa(null, publicKey); + return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * rsa公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByRsaHex(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("RSA需要传入公钥进行加密"); + } + RSA rsa = SecureUtil.rsa(null, publicKey); + return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * rsa私钥解密 + * + * @param data 待解密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptByRsa(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("RSA需要传入私钥进行解密"); + } + RSA rsa = SecureUtil.rsa(privateKey, null); + return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * md5加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByMd5(String data) { + return SecureUtil.md5(data); + } + + /** + * sha256加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySha256(String data) { + return SecureUtil.sha256(data); + } + + /** + * sm3加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm3(String data) { + return SmUtil.sm3(data); + } + +} diff --git a/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java new file mode 100644 index 0000000..c81f353 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/java/com/fuyuanshen/common/encrypt/utils/EncryptUtilsTest.java @@ -0,0 +1,78 @@ +package com.fuyuanshen.common.encrypt.utils; + +import com.fuyuanshen.common.core.domain.model.PasswordLoginBody; + +/** + * 登录测试 + * + * @author: 默苍璃 + * @date: 2025-06-3014:29 + */ +public class EncryptUtilsTest { + + public static void main(String[] args) { + // System.out.println(EncryptUtils.encryptByBase64("123456")); + + System.out.println(EncryptUtils.encryptByBase64("1234567890abcdef")); + + String g = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=="; + String s = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y="; + + + String s1 = EncryptUtils.encryptByRsa("MTIzNDU2Nzg5MGFiY2RlZg==", g); + System.out.println(s1); + String s2 = EncryptUtils.decryptByRsa("jJPaW7hgFXD/gjdkrfBOEUdXpPZnQg/LZUASoOJAOLU/XRVXO/5666CzyALjw7neK1ujvRuys4MdKCvr9cRARw==", s); + System.out.println(s2); + + String s3 = EncryptUtils.decryptByBase64(s2); + System.out.println(s3); + + String s4 = EncryptUtils.encryptByAes("123456", s3); + System.out.println(s4); + + String s5 = EncryptUtils.decryptByAes(s4, s3); + System.out.println(s5); + + + // 1. 构造 LoginBody 对象 + PasswordLoginBody loginBody = new PasswordLoginBody(); + loginBody.setClientId("e5cd7e4891bf95d1d19206ce24a7b32e"); + loginBody.setGrantType("password"); + loginBody.setTenantId("894078"); + loginBody.setCode("0"); + loginBody.setUuid("1c285b27f516486f9535face77023aeb"); + // loginBody.setUsername("admin"); + // loginBody.setPassword("admin123"); + loginBody.setUsername("dyf"); + loginBody.setPassword("123456"); + + // 2. 使用更清晰的方式拼接 JSON 字符串 + String jsonLoginBody = String.format( + "{" + + "\"clientId\":\"%s\"," + + "\"grantType\":\"%s\"," + + "\"tenantId\":\"%s\"," + + "\"code\":\"%s\"," + + "\"uuid\":\"%s\"," + + "\"username\":\"%s\"," + + "\"password\":\"%s\"" + + "}", + loginBody.getClientId(), + loginBody.getGrantType(), + loginBody.getTenantId(), + loginBody.getCode(), + loginBody.getUuid(), + loginBody.getUsername(), + loginBody.getPassword() + ); + + + String S5 = EncryptUtils.encryptByAes(jsonLoginBody, s3); + System.out.println(S5); + + String S6 = EncryptUtils.decryptByAes(S5, s3); + System.out.println(S6); + + } + +} diff --git a/fys-common/fys-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..6a4dca5 --- /dev/null +++ b/fys-common/fys-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.fuyuanshen.common.encrypt.config.EncryptorAutoConfiguration +com.fuyuanshen.common.encrypt.config.ApiDecryptAutoConfiguration + diff --git a/fys-common/fys-common-excel/pom.xml b/fys-common/fys-common-excel/pom.xml new file mode 100644 index 0000000..154fd0c --- /dev/null +++ b/fys-common/fys-common-excel/pom.xml @@ -0,0 +1,30 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-excel + + + fys-common-excel + + + + + com.fuyuanshen + fys-common-json + + + + cn.idev.excel + fastexcel + + + + diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/CellMerge.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/CellMerge.java new file mode 100644 index 0000000..709f25a --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/CellMerge.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.common.excel.annotation; + +import com.fuyuanshen.common.excel.core.CellMergeStrategy; + +import java.lang.annotation.*; + +/** + * excel 列单元格合并(合并列相同项) + * + * 需搭配 {@link CellMergeStrategy} 策略使用 + * + * @author Lion Li + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CellMerge { + + /** + * col index + */ + int index() default -1; + + /** + * 合并需要依赖的其他字段名称 + */ + String[] mergeBy() default {}; + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelDictFormat.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelDictFormat.java new file mode 100644 index 0000000..b18f8ba --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelDictFormat.java @@ -0,0 +1,32 @@ +package com.fuyuanshen.common.excel.annotation; + +import com.fuyuanshen.common.core.utils.StringUtils; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * @author Lion Li + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelDictFormat { + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + String separator() default StringUtils.SEPARATOR; + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelEnumFormat.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelEnumFormat.java new file mode 100644 index 0000000..2f09c78 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelEnumFormat.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.excel.annotation; + +import java.lang.annotation.*; + +/** + * 枚举格式化 + * + * @author Liang + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelEnumFormat { + + /** + * 字典枚举类型 + */ + Class> enumClass(); + + /** + * 字典枚举类中对应的code属性名称,默认为code + */ + String codeField() default "code"; + + /** + * 字典枚举类中对应的text属性名称,默认为text + */ + String textField() default "text"; + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelNotation.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelNotation.java new file mode 100644 index 0000000..f362af3 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelNotation.java @@ -0,0 +1,24 @@ +package com.fuyuanshen.common.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 批注 + * @author guzhouyanyu + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelNotation { + + /** + * col index + */ + int index() default -1; + /** + * 批注内容 + */ + String value() default ""; +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelRequired.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelRequired.java new file mode 100644 index 0000000..4d7ee4c --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/annotation/ExcelRequired.java @@ -0,0 +1,26 @@ +package com.fuyuanshen.common.excel.annotation; + +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 是否必填 + * @author guzhouyanyu + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelRequired { + + /** + * col index + */ + int index() default -1; + /** + * 字体颜色 + */ + IndexedColors fontColor() default IndexedColors.RED; +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelBigNumberConvert.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelBigNumberConvert.java new file mode 100644 index 0000000..820dc72 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelBigNumberConvert.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.common.excel.convert; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; + +/** + * 大数值转换 + * Excel 数值长度位15位 大于15位的数值转换位字符串 + * + * @author Lion Li + */ +@Slf4j +public class ExcelBigNumberConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return Convert.toLong(cellData.getData()); + } + + @Override + public WriteCellData convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNotNull(object)) { + String str = Convert.toStr(object); + if (str.length() > 15) { + return new WriteCellData<>(str); + } + } + WriteCellData cellData = new WriteCellData<>(new BigDecimal(object)); + cellData.setType(CellDataTypeEnum.NUMBER); + return cellData; + } + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelDictConvert.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelDictConvert.java new file mode 100644 index 0000000..c43747d --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelDictConvert.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; + +/** + * 字典格式化转换处理 + * + * @author Lion Li + */ +@Slf4j +public class ExcelDictConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String label = cellData.getStringValue(); + String value; + if (StringUtils.isBlank(type)) { + value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); + } else { + value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); + } + return Convert.convert(contentProperty.getField().getType(), value); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String value = Convert.toStr(object); + String label; + if (StringUtils.isBlank(type)) { + label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); + } else { + label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); + } + return new WriteCellData<>(label); + } + + private ExcelDictFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelEnumConvert.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelEnumConvert.java new file mode 100644 index 0000000..df27189 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/convert/ExcelEnumConvert.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; +import com.fuyuanshen.common.excel.annotation.ExcelEnumFormat; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 枚举格式化转换处理 + * + * @author Liang + */ +@Slf4j +public class ExcelEnumConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + cellData.checkEmpty(); + // Excel中填入的是枚举中指定的描述 + Object textValue = switch (cellData.getType()) { + case STRING, DIRECT_STRING, RICH_TEXT_STRING -> cellData.getStringValue(); + case NUMBER -> cellData.getNumberValue(); + case BOOLEAN -> cellData.getBooleanValue(); + default -> throw new IllegalArgumentException("单元格类型异常!"); + }; + // 如果是空值 + if (ObjectUtil.isNull(textValue)) { + return null; + } + Map enumCodeToTextMap = beforeConvert(contentProperty); + // 从Java输出至Excel是code转text + // 因此从Excel转Java应该将text与code对调 + Map enumTextToCodeMap = new HashMap<>(); + enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key)); + // 应该从text -> code中查找 + Object codeValue = enumTextToCodeMap.get(textValue); + return Convert.convert(contentProperty.getField().getType(), codeValue); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + Map enumValueMap = beforeConvert(contentProperty); + String value = Convert.toStr(enumValueMap.get(object), ""); + return new WriteCellData<>(value); + } + + private Map beforeConvert(ExcelContentProperty contentProperty) { + ExcelEnumFormat anno = getAnnotation(contentProperty.getField()); + Map enumValueMap = new HashMap<>(); + Enum[] enumConstants = anno.enumClass().getEnumConstants(); + for (Enum enumConstant : enumConstants) { + Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField()); + String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField()); + enumValueMap.put(codeValue, textValue); + } + return enumValueMap; + } + + private ExcelEnumFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class); + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/CellMergeStrategy.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/CellMergeStrategy.java new file mode 100644 index 0000000..837e051 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/CellMergeStrategy.java @@ -0,0 +1,163 @@ +package com.fuyuanshen.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.metadata.Head; +import cn.idev.excel.write.handler.WorkbookWriteHandler; +import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; +import cn.idev.excel.write.merge.AbstractMergeStrategy; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; +import com.fuyuanshen.common.excel.annotation.CellMerge; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * 列值重复合并策略 + * + * @author Lion Li + */ +@Slf4j +public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { + + private final List cellList; + private final boolean hasTitle; + private int rowIndex; + + public CellMergeStrategy(List list, boolean hasTitle) { + this.hasTitle = hasTitle; + // 行合并开始下标 + this.rowIndex = hasTitle ? 1 : 0; + this.cellList = handle(list, hasTitle); + } + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 + final int rowIndex = cell.getRowIndex(); + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress cellAddresses : cellList) { + final int firstRow = cellAddresses.getFirstRow(); + if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ + cell.setBlank(); + } + } + } + } + + @Override + public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { + //当前表格写完后,统一写入 + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress item : cellList) { + context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); + } + } + } + + @SneakyThrows + private List handle(List list, boolean hasTitle) { + List cellList = new ArrayList<>(); + if (CollUtil.isEmpty(list)) { + return cellList; + } + Field[] fields = ReflectUtils.getFields(list.get(0).getClass(), field -> !"serialVersionUID".equals(field.getName())); + + // 有注解的字段 + List mergeFields = new ArrayList<>(); + List mergeFieldsIndex = new ArrayList<>(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (field.isAnnotationPresent(CellMerge.class)) { + CellMerge cm = field.getAnnotation(CellMerge.class); + mergeFields.add(field); + mergeFieldsIndex.add(cm.index() == -1 ? i : cm.index()); + if (hasTitle) { + ExcelProperty property = field.getAnnotation(ExcelProperty.class); + rowIndex = Math.max(rowIndex, property.value().length); + } + } + } + + Map map = new HashMap<>(); + // 生成两两合并单元格 + for (int i = 0; i < list.size(); i++) { + for (int j = 0; j < mergeFields.size(); j++) { + Field field = mergeFields.get(j); + Object val = ReflectUtils.invokeGetter(list.get(i), field.getName()); + + int colNum = mergeFieldsIndex.get(j); + if (!map.containsKey(field)) { + map.put(field, new RepeatCell(val, i)); + } else { + RepeatCell repeatCell = map.get(field); + Object cellValue = repeatCell.getValue(); + if (cellValue == null || "".equals(cellValue)) { + // 空值跳过不合并 + continue; + } + + if (!cellValue.equals(val)) { + if ((i - repeatCell.getCurrent() > 1)) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); + } else if (i == list.size() - 1) { + if (!isMerge(list, i, field)) { + // 如果最后一行不能合并,检查之前的数据是否需要合并 + if (i - repeatCell.getCurrent() > 1) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + } else if (i > repeatCell.getCurrent()) { + // 如果最后一行可以合并,则直接合并到最后 + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); + } + } else if (!isMerge(list, i, field)) { + if ((i - repeatCell.getCurrent() > 1)) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); + } + map.put(field, new RepeatCell(val, i)); + } + } + } + } + return cellList; + } + + private boolean isMerge(List list, int i, Field field) { + boolean isMerge = true; + CellMerge cm = field.getAnnotation(CellMerge.class); + final String[] mergeBy = cm.mergeBy(); + if (StrUtil.isAllNotBlank(mergeBy)) { + //比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真 + for (String fieldName : mergeBy) { + final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName); + final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName); + if (!Objects.equals(valPre, valCurrent)) { + //依赖字段如有任一不等值,则标记为不可合并 + isMerge = false; + } + } + } + return isMerge; + } + + @Data + @AllArgsConstructor + static class RepeatCell { + + private Object value; + + private int current; + + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelListener.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelListener.java new file mode 100644 index 0000000..7d3440b --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelListener.java @@ -0,0 +1,104 @@ +package com.fuyuanshen.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.exception.ExcelAnalysisException; +import cn.idev.excel.exception.ExcelDataConvertException; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.Set; + +/** + * Excel 导入监听 + * + * @author Yjoioooo + * @author Lion Li + */ +@Slf4j +@NoArgsConstructor +public class DefaultExcelListener extends AnalysisEventListener implements ExcelListener { + + /** + * 是否Validator检验,默认为是 + */ + private Boolean isValidate = Boolean.TRUE; + + /** + * excel 表头数据 + */ + private Map headMap; + + /** + * 导入回执 + */ + private ExcelResult excelResult; + + public DefaultExcelListener(boolean isValidate) { + this.excelResult = new DefaultExcelResult<>(); + this.isValidate = isValidate; + } + + /** + * 处理异常 + * + * @param exception ExcelDataConvertException + * @param context Excel 上下文 + */ + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + String errMsg = null; + if (exception instanceof ExcelDataConvertException excelDataConvertException) { + // 如果是某一个单元格的转换异常 能获取到具体行号 + Integer rowIndex = excelDataConvertException.getRowIndex(); + Integer columnIndex = excelDataConvertException.getColumnIndex(); + errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", + rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + if (exception instanceof ConstraintViolationException constraintViolationException) { + Set> constraintViolations = constraintViolationException.getConstraintViolations(); + String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", "); + errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + excelResult.getErrorList().add(errMsg); + throw new ExcelAnalysisException(errMsg); + } + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.headMap = headMap; + log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap)); + } + + @Override + public void invoke(T data, AnalysisContext context) { + if (isValidate) { + ValidatorUtils.validate(data); + } + excelResult.getList().add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.debug("所有数据解析完成!"); + } + + @Override + public ExcelResult getExcelResult() { + return excelResult; + } + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelResult.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelResult.java new file mode 100644 index 0000000..9e403fe --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DefaultExcelResult.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 默认excel返回对象 + * + * @author Yjoioooo + * @author Lion Li + */ +public class DefaultExcelResult implements ExcelResult { + + /** + * 数据对象list + */ + @Setter + private List list; + + /** + * 错误信息列表 + */ + @Setter + private List errorList; + + public DefaultExcelResult() { + this.list = new ArrayList<>(); + this.errorList = new ArrayList<>(); + } + + public DefaultExcelResult(List list, List errorList) { + this.list = list; + this.errorList = errorList; + } + + public DefaultExcelResult(ExcelResult excelResult) { + this.list = excelResult.getList(); + this.errorList = excelResult.getErrorList(); + } + + @Override + public List getList() { + return list; + } + + @Override + public List getErrorList() { + return errorList; + } + + /** + * 获取导入回执 + * + * @return 导入回执 + */ + @Override + public String getAnalysis() { + int successCount = list.size(); + int errorCount = errorList.size(); + if (successCount == 0) { + return "读取失败,未解析到数据"; + } else { + if (errorCount == 0) { + return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount); + } else { + return ""; + } + } + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DropDownOptions.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DropDownOptions.java new file mode 100644 index 0000000..09545cd --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/DropDownOptions.java @@ -0,0 +1,149 @@ +package com.fuyuanshen.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.exception.ServiceException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + *

Excel下拉可选项

+ * 注意:为确保下拉框解析正确,传值务必使用createOptionValue()做为值的拼接 + * + * @author Emil.Zhang + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuppressWarnings("unused") +public class DropDownOptions { + /** + * 一级下拉所在列index,从0开始算 + */ + private int index = 0; + /** + * 二级下拉所在的index,从0开始算,不能与一级相同 + */ + private int nextIndex = 0; + /** + * 一级下拉所包含的数据 + */ + private List options = new ArrayList<>(); + /** + * 二级下拉所包含的数据Map + *

以每一个一级选项值为Key,每个一级选项对应的二级数据为Value

+ */ + private Map> nextOptions = new HashMap<>(); + /** + * 分隔符 + */ + private static final String DELIMITER = "_"; + + /** + * 创建只有一级的下拉选 + */ + public DropDownOptions(int index, List options) { + this.index = index; + this.options = options; + } + + /** + *

创建每个选项可选值

+ *

注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号

+ * + * @param vars 可选值内包含的参数 + * @return 合规的可选值 + */ + public static String createOptionValue(Object... vars) { + StringBuilder stringBuffer = new StringBuilder(); + String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; + for (int i = 0; i < vars.length; i++) { + String var = StrUtil.trimToEmpty(String.valueOf(vars[i])); + if (!var.matches(regex)) { + throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); + } + stringBuffer.append(var); + if (i < vars.length - 1) { + // 直至最后一个前,都以_作为切割线 + stringBuffer.append(DELIMITER); + } + } + if (stringBuffer.toString().matches("^\\d_*$")) { + throw new ServiceException("禁止以数字开头"); + } + return stringBuffer.toString(); + } + + /** + * 将处理后合理的可选值解析为原始的参数 + * + * @param option 经过处理后的合理的可选项 + * @return 原始的参数 + */ + public static List analyzeOptionValue(String option) { + return StrUtil.split(option, DELIMITER, true, true); + } + + /** + * 创建级联下拉选项 + * + * @param parentList 父实体可选项原始数据 + * @param parentIndex 父下拉选位置 + * @param sonList 子实体可选项原始数据 + * @param sonIndex 子下拉选位置 + * @param parentHowToGetIdFunction 父类如何获取唯一标识 + * @param sonHowToGetParentIdFunction 子类如何获取父类的唯一标识 + * @param howToBuildEveryOption 如何生成下拉选内容 + * @return 级联下拉选项 + */ + public static DropDownOptions buildLinkedOptions(List parentList, + int parentIndex, + List sonList, + int sonIndex, + Function parentHowToGetIdFunction, + Function sonHowToGetParentIdFunction, + Function howToBuildEveryOption) { + DropDownOptions parentLinkSonOptions = new DropDownOptions(); + // 先创建父类的下拉 + parentLinkSonOptions.setIndex(parentIndex); + parentLinkSonOptions.setOptions( + parentList.stream() + .map(howToBuildEveryOption) + .collect(Collectors.toList()) + ); + // 提取父-子级联下拉 + Map> sonOptions = new HashMap<>(); + // 父级依据自己的ID分组 + Map> parentGroupByIdMap = + parentList.stream().collect(Collectors.groupingBy(parentHowToGetIdFunction)); + // 遍历每个子集,提取到Map中 + sonList.forEach(everySon -> { + if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) { + // 找到对应的上级 + T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).get(0); + // 提取名称和ID作为Key + String key = howToBuildEveryOption.apply(parentObj); + // Key对应的Value + List thisParentSonOptionList; + if (sonOptions.containsKey(key)) { + thisParentSonOptionList = sonOptions.get(key); + } else { + thisParentSonOptionList = new ArrayList<>(); + sonOptions.put(key, thisParentSonOptionList); + } + // 往Value中添加当前子集选项 + thisParentSonOptionList.add(howToBuildEveryOption.apply(everySon)); + } + }); + parentLinkSonOptions.setNextIndex(sonIndex); + parentLinkSonOptions.setNextOptions(sonOptions); + return parentLinkSonOptions; + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelDownHandler.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelDownHandler.java new file mode 100644 index 0000000..6791558 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelDownHandler.java @@ -0,0 +1,401 @@ +package com.fuyuanshen.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.metadata.FieldCache; +import cn.idev.excel.metadata.FieldWrapper; +import cn.idev.excel.util.ClassUtils; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.WorkbookUtil; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.annotation.ExcelEnumFormat; + +import java.lang.reflect.Field; +import java.util.*; + +/** + *

Excel表格下拉选操作

+ * 考虑到下拉选过多可能导致Excel打开缓慢的问题,只校验前1000行 + *

+ * 即只有前1000行的数据可以用下拉框,超出的自行通过限制数据量的形式,第二次输出 + * + * @author Emil.Zhang + */ +@Slf4j +public class ExcelDownHandler implements SheetWriteHandler { + + /** + * Excel表格中的列名英文 + * 仅为了解析列英文,禁止修改 + */ + private static final String EXCEL_COLUMN_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /** + * 单选数据Sheet名 + */ + private static final String OPTIONS_SHEET_NAME = "options"; + /** + * 联动选择数据Sheet名的头 + */ + private static final String LINKED_OPTIONS_SHEET_NAME = "linkedOptions"; + /** + * 下拉可选项 + */ + private final List dropDownOptions; + private final DictService dictService; + /** + * 当前单选进度 + */ + private int currentOptionsColumnIndex; + /** + * 当前联动选择进度 + */ + private int currentLinkedOptionsSheetIndex; + + public ExcelDownHandler(List options) { + this.dropDownOptions = options; + this.currentOptionsColumnIndex = 0; + this.currentLinkedOptionsSheetIndex = 0; + this.dictService = SpringUtils.getBean(DictService.class); + } + + /** + *

开始创建下拉数据

+ * 1.通过解析传入的@ExcelProperty同级是否标注有@DropDown选项 + * 如果有且设置了value值,则将其直接置为下拉可选项 + *

+ * 2.或者在调用ExcelUtil时指定了可选项,将依据传入的可选项做下拉 + *

+ * 3.二者并存,注意调用方式 + */ + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Sheet sheet = writeSheetHolder.getSheet(); + // 开始设置下拉框 HSSFWorkbook + DataValidationHelper helper = sheet.getDataValidationHelper(); + Workbook workbook = writeWorkbookHolder.getWorkbook(); + FieldCache fieldCache = ClassUtils.declaredFields(writeWorkbookHolder.getClazz(), writeWorkbookHolder); + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { + Integer index = entry.getKey(); + FieldWrapper wrapper = entry.getValue(); + Field field = wrapper.getField(); + // 循环实体中的每个属性 + // 可选的下拉值 + List options = new ArrayList<>(); + if (field.isAnnotationPresent(ExcelDictFormat.class)) { + // 如果指定了@ExcelDictFormat,则使用字典的逻辑 + ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class); + String dictType = format.dictType(); + String converterExp = format.readConverterExp(); + if (StringUtils.isNotBlank(dictType)) { + // 如果传递了字典名,则依据字典建立下拉 + Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) + .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) + .values(); + options = new ArrayList<>(values); + } else if (StringUtils.isNotBlank(converterExp)) { + // 如果指定了确切的值,则直接解析确切的值 + List strList = StringUtils.splitList(converterExp, format.separator()); + options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]); + } + } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) { + // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 + ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class); + List values = EnumUtil.getFieldValues(format.enumClass(), format.textField()); + options = StreamUtils.toList(values, String::valueOf); + } + if (ObjectUtil.isNotEmpty(options)) { + // 仅当下拉可选项不为空时执行 + if (options.size() > 20) { + // 这里限制如果可选项大于20,则使用额外表形式 + dropDownWithSheet(helper, workbook, sheet, index, options); + } else { + // 否则使用固定值形式 + dropDownWithSimple(helper, sheet, index, options); + } + } + } + if (CollUtil.isEmpty(dropDownOptions)) { + return; + } + dropDownOptions.forEach(everyOptions -> { + // 如果传递了下拉框选择器参数 + if (!everyOptions.getNextOptions().isEmpty()) { + // 当二级选项不为空时,使用额外关联表的形式 + dropDownLinkedOptions(helper, workbook, sheet, everyOptions); + } else if (everyOptions.getOptions().size() > 10) { + // 当一级选项参数个数大于10,使用额外表的形式 + dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions()); + } else { + // 否则使用默认形式 + dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions()); + } + }); + } + + /** + *

简单下拉框

+ * 直接将可选项拼接为指定列的数据校验值 + * + * @param celIndex 列index + * @param value 下拉选可选值 + */ + private void dropDownWithSimple(DataValidationHelper helper, Sheet sheet, Integer celIndex, List value) { + if (ObjectUtil.isEmpty(value)) { + return; + } + this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(ArrayUtil.toArray(value, String.class))); + } + + /** + *

额外表格形式的级联下拉框

+ * + * @param options 额外表格形式存储的下拉可选项 + */ + private void dropDownLinkedOptions(DataValidationHelper helper, Workbook workbook, Sheet sheet, DropDownOptions options) { + String linkedOptionsSheetName = String.format("%s_%d", LINKED_OPTIONS_SHEET_NAME, currentLinkedOptionsSheetIndex); + // 创建联动下拉数据表 + Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName)); + // 将下拉表隐藏 + workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); + // 选项数据 + List firstOptions = options.getOptions(); + Map> secoundOptionsMap = options.getNextOptions(); + + // 采用按行填充数据的方式,避免出现数据无法写入的问题 + // Attempting to write a row in the range that is already written to disk + + // 使用ArrayList记载数据,防止乱序 + List columnNames = new ArrayList<>(); + // 写入第一行,即第一级的数据 + Row firstRow = linkedOptionsDataSheet.createRow(0); + for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) { + String columnName = firstOptions.get(columnIndex); + firstRow.createCell(columnIndex) + .setCellValue(columnName); + columnNames.add(columnName); + } + + // 创建名称管理器 + Name name = workbook.createName(); + // 设置名称管理器的别名 + name.setNameName(linkedOptionsSheetName); + // 以横向第一行创建一级下拉拼接引用位置 + String firstOptionsFunction = String.format("%s!$%s$1:$%s$1", + linkedOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(firstOptions.size()) + ); + // 设置名称管理器的引用位置 + name.setRefersToFormula(firstOptionsFunction); + // 设置数据校验为序列模式,引用的是名称管理器中的别名 + this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName)); + + // 创建二级选项的名称管理器 + for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) { + // 列名 + String firstOptionsColumnName = getExcelColumnName(columIndex); + // 对应的一级值 + String thisFirstOptionsValue = columnNames.get(columIndex); + + // 以该一级选项值创建子名称管理器 + Name sonName = workbook.createName(); + // 设置名称管理器的别名 + sonName.setNameName(thisFirstOptionsValue); + // 以第二行该列数据拼接引用位置 + String sonFunction = String.format("%s!$%s$2:$%s$%d", + linkedOptionsSheetName, + firstOptionsColumnName, + firstOptionsColumnName, + // 二级选项存在则设置为(选项个数+1)行,否则设置为2行 + Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue)) + .orElseGet(ArrayList::new).size(), 1) + 1 + ); + // 设置名称管理器的引用位置 + sonName.setRefersToFormula(sonFunction); + // 数据验证为序列模式,引用到每一个主表中的二级选项位置 + // 创建子项的名称管理器,只是为了使得Excel可以识别到数据 + String mainSheetFirstOptionsColumnName = getExcelColumnName(options.getIndex()); + for (int i = 0; i < 100; i++) { + // 以一级选项对应的主体所在位置创建二级下拉 + String secondOptionsFunction = String.format("=INDIRECT(%s%d)", mainSheetFirstOptionsColumnName, i + 1); + // 二级只能主表每一行的每一列添加二级校验 + markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction)); + } + } + + // 将二级数据处理为按行区分 + Map> columnValueMap = new HashMap<>(); + int currentRow = 1; + while (currentRow >= 0) { + boolean flag = false; + List rowData = new ArrayList<>(); + for (String columnName : columnNames) { + List data = secoundOptionsMap.get(columnName); + if (CollUtil.isEmpty(data)) { + // 添加空字符串填充位置 + rowData.add(" "); + continue; + } + // 取第一个 + String str = data.get(0); + rowData.add(str); + // 通过移除的方式避免重复 + data.remove(0); + // 设置可以继续 + flag = true; + } + columnValueMap.put(currentRow, rowData); + // 可以继续,则增加行数,否则置为负数跳出循环 + if (flag) { + currentRow++; + } else { + currentRow = -1; + } + } + + // 填充第二级选项数据 + columnValueMap.forEach((rowIndex, rowValues) -> { + Row row = linkedOptionsDataSheet.createRow(rowIndex); + for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) { + String rowValue = rowValues.get(columnIndex); + // 填充位置的部分不渲染 + if (StrUtil.isNotBlank(rowValue)) { + row.createCell(columnIndex) + .setCellValue(rowValue); + } + } + }); + + currentLinkedOptionsSheetIndex++; + } + + /** + *

额外表格形式的普通下拉框

+ * 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉 + * + * @param celIndex 下拉选 + * @param value 下拉选可选值 + */ + private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List value) { + //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk + String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex; + // 创建下拉数据表 + Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))) + .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))); + // 将下拉表隐藏 + workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); + // 完善纵向的一级选项数据表 + for (int i = 0; i < value.size(); i++) { + int finalI = i; + // 获取每一选项行,如果没有则创建 + Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) + .orElseGet(() -> simpleDataSheet.createRow(finalI)); + // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列 + Cell cell = Optional.ofNullable(row.getCell(0)) + .orElseGet(() -> row.createCell(0)); + // 设置值 + cell.setCellValue(value.get(i)); + } + + // 创建名称管理器 + Name name = workbook.createName(); + // 设置名称管理器的别名 + String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex); + name.setNameName(nameName); + // 以纵向第一列创建一级下拉拼接引用位置 + String function = String.format("%s!$%s$1:$%s$%d", + tmpOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(0), + value.size()); + // 设置名称管理器的引用位置 + name.setRefersToFormula(function); + // 设置数据校验为序列模式,引用的是名称管理器中的别名 + this.markOptionsToSheet(helper, sheet, celIndex, helper.createFormulaListConstraint(nameName)); + currentOptionsColumnIndex++; + } + + /** + * 挂载下拉的列,仅限一级选项 + */ + private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex, + DataValidationConstraint constraint) { + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex); + markDataValidationToSheet(helper, sheet, constraint, addressList); + } + + /** + * 挂载下拉的列,仅限二级选项 + */ + private void markLinkedOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer rowIndex, + Integer celIndex, DataValidationConstraint constraint) { + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex); + markDataValidationToSheet(helper, sheet, constraint, addressList); + } + + /** + * 应用数据校验 + */ + private void markDataValidationToSheet(DataValidationHelper helper, Sheet sheet, + DataValidationConstraint constraint, CellRangeAddressList addressList) { + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, addressList); + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + //数据校验 + dataValidation.setSuppressDropDownArrow(true); + //错误提示 + dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP); + dataValidation.createErrorBox("提示", "此值与单元格定义数据不一致"); + dataValidation.setShowErrorBox(true); + //选定提示 + dataValidation.createPromptBox("填写说明:", "填写内容只能为下拉中数据,其他数据将导致导入失败"); + dataValidation.setShowPromptBox(true); + sheet.addValidationData(dataValidation); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + *

依据列index获取列名英文

+ * 依据列index转换为Excel中的列名英文 + *

例如第1列,index为0,解析出来为A列

+ * 第27列,index为26,解析为AA列 + *

第28列,index为27,解析为AB列

+ * + * @param columnIndex 列index + * @return 列index所在得英文名 + */ + private String getExcelColumnName(int columnIndex) { + // 26一循环的次数 + int columnCircleCount = columnIndex / 26; + // 26一循环内的位置 + int thisCircleColumnIndex = columnIndex % 26; + // 26一循环的次数大于0,则视为栏名至少两位 + String columnPrefix = columnCircleCount == 0 + ? StrUtil.EMPTY + : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); + // 从26一循环内取对应的栏位名 + String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); + // 将二者拼接即为最终的栏位名 + return columnPrefix + columnNext; + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelListener.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelListener.java new file mode 100644 index 0000000..0222b1f --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelListener.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.common.excel.core; + +import cn.idev.excel.read.listener.ReadListener; + +/** + * Excel 导入监听 + * + * @author Lion Li + */ +public interface ExcelListener extends ReadListener { + + ExcelResult getExcelResult(); + +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelResult.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelResult.java new file mode 100644 index 0000000..fb67765 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/core/ExcelResult.java @@ -0,0 +1,26 @@ +package com.fuyuanshen.common.excel.core; + +import java.util.List; + +/** + * excel返回对象 + * + * @author Lion Li + */ +public interface ExcelResult { + + /** + * 对象列表 + */ + List getList(); + + /** + * 错误列表 + */ + List getErrorList(); + + /** + * 导入回执 + */ + String getAnalysis(); +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/handler/DataWriteHandler.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/handler/DataWriteHandler.java new file mode 100644 index 0000000..cb7b341 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/handler/DataWriteHandler.java @@ -0,0 +1,135 @@ +package com.fuyuanshen.common.excel.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.idev.excel.metadata.data.DataFormatData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.util.StyleUtil; +import cn.idev.excel.write.handler.CellWriteHandler; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.handler.context.CellWriteHandlerContext; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.style.WriteCellStyle; +import cn.idev.excel.write.metadata.style.WriteFont; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; +import com.fuyuanshen.common.excel.annotation.ExcelNotation; +import com.fuyuanshen.common.excel.annotation.ExcelRequired; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 批注、必填 + * + * @author guzhouyanyu + */ +public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { + + /** + * 批注 + */ + private final Map notationMap; + + /** + * 头列字体颜色 + */ + private final Map headColumnMap; + + + public DataWriteHandler(Class clazz) { + notationMap = getNotationMap(clazz); + headColumnMap = getRequiredMap(clazz); + } + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); + + DataFormatData dataFormatData = new DataFormatData(); + // 单元格设置为文本格式 + dataFormatData.setIndex((short) 49); + writeCellStyle.setDataFormatData(dataFormatData); + + if (context.getHead()) { + Cell cell = context.getCell(); + WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder(); + Sheet sheet = writeSheetHolder.getSheet(); + Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + Drawing drawing = sheet.createDrawingPatriarch(); + // 设置标题字体样式 + WriteFont headWriteFont = new WriteFont(); + // 加粗 + headWriteFont.setBold(true); + if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getColumnIndex())) { + // 设置字体颜色 + headWriteFont.setColor(headColumnMap.get(cell.getColumnIndex())); + } + writeCellStyle.setWriteFont(headWriteFont); + CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle); + cell.setCellStyle(cellStyle); + + if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getColumnIndex())) { + // 批注内容 + String notationContext = notationMap.get(cell.getColumnIndex()); + // 创建绘图对象 + Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5)); + comment.setString(new XSSFRichTextString(notationContext)); + cell.setCellComment(comment); + } + } + } + + /** + * 获取必填列 + */ + private static Map getRequiredMap(Class clazz) { + Map requiredMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + // 检查 fields 数组是否为空 + if (fields.length == 0) { + return requiredMap; + } + Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); + + for (int i = 0; i < filteredFields.length; i++) { + Field field = filteredFields[i]; + if (!field.isAnnotationPresent(ExcelRequired.class)) { + continue; + } + ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class); + int columnIndex = excelRequired.index() == -1 ? i : excelRequired.index(); + requiredMap.put(columnIndex, excelRequired.fontColor().getIndex()); + } + return requiredMap; + } + + /** + * 获取批注 + */ + private static Map getNotationMap(Class clazz) { + Map notationMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + // 检查 fields 数组是否为空 + if (fields.length == 0) { + return notationMap; + } + Field[] filteredFields = ReflectUtils.getFields(clazz, field -> !"serialVersionUID".equals(field.getName())); + for (int i = 0; i < filteredFields.length; i++) { + Field field = filteredFields[i]; + if (!field.isAnnotationPresent(ExcelNotation.class)) { + continue; + } + ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class); + int columnIndex = excelNotation.index() == -1 ? i : excelNotation.index(); + notationMap.put(columnIndex, excelNotation.value()); + } + return notationMap; + } +} diff --git a/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/utils/ExcelUtil.java b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/utils/ExcelUtil.java new file mode 100644 index 0000000..5fd28e1 --- /dev/null +++ b/fys-common/fys-common-excel/src/main/java/com/fuyuanshen/common/excel/utils/ExcelUtil.java @@ -0,0 +1,440 @@ +package com.fuyuanshen.common.excel.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.IdUtil; +import cn.idev.excel.FastExcel; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.fill.FillConfig; +import cn.idev.excel.write.metadata.fill.FillWrapper; +import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.file.FileUtils; +import com.fuyuanshen.common.excel.convert.ExcelBigNumberConvert; +import com.fuyuanshen.common.excel.core.*; +import com.fuyuanshen.common.excel.handler.DataWriteHandler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Excel相关处理 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelUtil { + + /** + * 同步导入(适用于小数据量) + * + * @param is 输入流 + * @return 转换后集合 + */ + public static List importExcel(InputStream is, Class clazz) { + return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + } + + + /** + * 使用校验监听器 异步导入 同步返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param isValidate 是否 Validator 检验 默认为是 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { + DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); + FastExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 使用自定义监听器 异步导入 自定义返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param listener 自定义监听器 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { + FastExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os, null); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + * @param options 级联下拉选 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response, List options) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os, options); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os, null); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + * @param options 级联下拉选 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response, List options) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os, options); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { + exportExcel(list, sheetName, clazz, false, os, null); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + * @param options 级联下拉选内容 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os, List options) { + exportExcel(list, sheetName, clazz, false, os, options); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, + OutputStream os, List options) { + ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .registerWriteHandler(new DataWriteHandler(clazz)) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + // 添加下拉框操作 + builder.registerWriteHandler(new ExcelDownHandler(options)); + builder.doWrite(list); + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplate(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplate(List data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) + .build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + // 单表多数据导出 模板格式为 {.属性} + for (T d : data) { + excelWriter.fill(d, fillConfig, writeSheet); + } + excelWriter.finish(); + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiList(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多sheet模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiSheet(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), fillConfig, writeSheet); + } + } + excelWriter.finish(); + } + + /** + * 多sheet模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + for (int i = 0; i < data.size(); i++) { + WriteSheet writeSheet = FastExcel.writerSheet(i).build(); + for (Map.Entry map : data.get(i).entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + } + excelWriter.finish(); + } + + /** + * 重置响应体 + */ + private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { + String filename = encodingFilename(sheetName); + FileUtils.setAttachmentResponseHeader(response, filename); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(StringUtils.SEPARATOR); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(StringUtils.SEPARATOR); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 编码文件名 + */ + public static String encodingFilename(String filename) { + return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; + } + +} diff --git a/fys-common/fys-common-idempotent/pom.xml b/fys-common/fys-common-idempotent/pom.xml new file mode 100644 index 0000000..0ce3dd0 --- /dev/null +++ b/fys-common/fys-common-idempotent/pom.xml @@ -0,0 +1,41 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-idempotent + + + fys-common-idempotent 幂等功能 + + + + + com.fuyuanshen + fys-common-json + + + + com.fuyuanshen + fys-common-redis + + + + cn.hutool + hutool-crypto + + + + cn.dev33 + sa-token-core + + + + + diff --git a/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/annotation/RepeatSubmit.java b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/annotation/RepeatSubmit.java new file mode 100644 index 0000000..f66eca5 --- /dev/null +++ b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/annotation/RepeatSubmit.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.common.idempotent.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * 自定义注解防止表单重复提交 + * + * @author Lion Li + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + int interval() default 5000; + + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * 提示消息 支持国际化 格式为 {code} + */ + String message() default "{repeat.submit.message}"; + +} diff --git a/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/aspectj/RepeatSubmitAspect.java b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/aspectj/RepeatSubmitAspect.java new file mode 100644 index 0000000..852c736 --- /dev/null +++ b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,146 @@ +package com.fuyuanshen.common.idempotent.aspectj; + +import cn.dev33.satoken.SaManager; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import com.fuyuanshen.common.core.constant.GlobalConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.MessageUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import java.util.StringJoiner; + +/** + * 防止重复提交(参考美团GTIS防重系统) + * + * @author Lion Li + */ +@Aspect +public class RepeatSubmitAspect { + + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + + if (interval < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = argsArrayToString(point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); + + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey; + if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) { + KEY_CACHE.set(cacheRepeatKey); + } else { + String message = repeatSubmit.message(); + if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { + message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); + } + throw new ServiceException(message); + } + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { + if (jsonResult instanceof R r) { + try { + // 成功则不删除redis数据 保证在有效时间内无法重复提交 + if (r.getCode() == R.SUCCESS) { + return; + } + RedisUtils.deleteObject(KEY_CACHE.get()); + } finally { + KEY_CACHE.remove(); + } + } + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { + RedisUtils.deleteObject(KEY_CACHE.get()); + KEY_CACHE.remove(); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringJoiner params = new StringJoiner(" "); + if (ArrayUtil.isEmpty(paramsArray)) { + return params.toString(); + } + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + params.add(JsonUtils.toJsonString(o)); + } + } + return params.toString(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.values()) { + return value instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } + +} diff --git a/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/config/IdempotentConfig.java b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/config/IdempotentConfig.java new file mode 100644 index 0000000..995aaf1 --- /dev/null +++ b/fys-common/fys-common-idempotent/src/main/java/com/fuyuanshen/common/idempotent/config/IdempotentConfig.java @@ -0,0 +1,21 @@ +package com.fuyuanshen.common.idempotent.config; + +import com.fuyuanshen.common.idempotent.aspectj.RepeatSubmitAspect; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConfiguration; + +/** + * 幂等功能配置 + * + * @author Lion Li + */ +@AutoConfiguration(after = RedisConfiguration.class) +public class IdempotentConfig { + + @Bean + public RepeatSubmitAspect repeatSubmitAspect() { + return new RepeatSubmitAspect(); + } + +} diff --git a/fys-common/fys-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e1489ff --- /dev/null +++ b/fys-common/fys-common-idempotent/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.idempotent.config.IdempotentConfig diff --git a/fys-common/fys-common-job/pom.xml b/fys-common/fys-common-job/pom.xml new file mode 100644 index 0000000..1df68b6 --- /dev/null +++ b/fys-common/fys-common-job/pom.xml @@ -0,0 +1,46 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-job + + + fys-common-job 定时任务 + + + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + com.aizuda + snail-job-client-starter + + + com.aizuda + snail-job-client-job-core + + + + org.projectlombok + lombok + + + + com.fuyuanshen + fys-common-core + + + + diff --git a/fys-common/fys-common-job/src/main/java/com/fuyuanshen/common/job/config/SnailJobConfig.java b/fys-common/fys-common-job/src/main/java/com/fuyuanshen/common/job/config/SnailJobConfig.java new file mode 100644 index 0000000..718902b --- /dev/null +++ b/fys-common/fys-common-job/src/main/java/com/fuyuanshen/common/job/config/SnailJobConfig.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.common.job.config; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender; +import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent; +import com.aizuda.snailjob.client.starter.EnableSnailJob; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动定时任务 + * + * @author opensnail + * @date 2024-05-17 + */ +@AutoConfiguration +@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true") +@EnableScheduling +@EnableSnailJob +public class SnailJobConfig { + + @EventListener(SnailClientStartingEvent.class) + public void onStarting(SnailClientStartingEvent event) { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + SnailLogbackAppender ca = new SnailLogbackAppender<>(); + ca.setName("snail_log_appender"); + ca.start(); + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(ca); + } + +} diff --git a/fys-common/fys-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..d318fba --- /dev/null +++ b/fys-common/fys-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.job.config.SnailJobConfig diff --git a/fys-common/fys-common-json/pom.xml b/fys-common/fys-common-json/pom.xml new file mode 100644 index 0000000..1c11546 --- /dev/null +++ b/fys-common/fys-common-json/pom.xml @@ -0,0 +1,37 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-json + + + fys-common-json 序列化模块 + + + + + com.fuyuanshen + fys-common-core + + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + diff --git a/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/config/JacksonConfig.java b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/config/JacksonConfig.java new file mode 100644 index 0000000..a97ed1c --- /dev/null +++ b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/config/JacksonConfig.java @@ -0,0 +1,50 @@ +package com.fuyuanshen.common.json.config; + +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.json.handler.BigNumberSerializer; +import com.fuyuanshen.common.json.handler.CustomDateDeserializer; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.context.annotation.Bean; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.TimeZone; + +/** + * jackson 配置 + * + * @author Lion Li + */ +@Slf4j +@AutoConfiguration(before = JacksonAutoConfiguration.class) +public class JacksonConfig { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> { + // 全局配置序列化返回 JSON 处理 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); + javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer()); + builder.modules(javaTimeModule); + builder.timeZone(TimeZone.getDefault()); + log.info("初始化 jackson 配置"); + }; + } + +} diff --git a/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/BigNumberSerializer.java b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/BigNumberSerializer.java new file mode 100644 index 0000000..1e7f35e --- /dev/null +++ b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/BigNumberSerializer.java @@ -0,0 +1,42 @@ +package com.fuyuanshen.common.json.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; + +import java.io.IOException; + +/** + * 超出 JS 最大最小值 处理 + * + * @author Lion Li + */ +@JacksonStdImpl +public class BigNumberSerializer extends NumberSerializer { + + /** + * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 + */ + private static final long MAX_SAFE_INTEGER = 9007199254740991L; + private static final long MIN_SAFE_INTEGER = -9007199254740991L; + + /** + * 提供实例 + */ + public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 超出范围 序列化为字符串 + if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } +} diff --git a/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/CustomDateDeserializer.java b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/CustomDateDeserializer.java new file mode 100644 index 0000000..94e9369 --- /dev/null +++ b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/handler/CustomDateDeserializer.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.common.json.handler; + +import cn.hutool.core.date.DateUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.util.Date; + +/** + * 自定义 Date 类型反序列化处理器(支持多种格式) + * + * @author AprilWind + */ +public class CustomDateDeserializer extends JsonDeserializer { + + /** + * 反序列化逻辑:将字符串转换为 Date 对象 + * + * @param p JSON 解析器,用于获取字符串值 + * @param ctxt 上下文环境(可用于获取更多配置) + * @return 转换后的 Date 对象,若为空字符串返回 null + * @throws IOException 当字符串格式非法或转换失败时抛出 + */ + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return DateUtil.parse(p.getText()); + } + +} diff --git a/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/utils/JsonUtils.java b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/utils/JsonUtils.java new file mode 100644 index 0000000..eb30a88 --- /dev/null +++ b/fys-common/fys-common-json/src/main/java/com/fuyuanshen/common/json/utils/JsonUtils.java @@ -0,0 +1,170 @@ +package com.fuyuanshen.common.json.utils; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author 芋道源码 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class); + + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + /** + * 将对象转换为JSON格式的字符串 + * + * @param object 要转换的对象 + * @return JSON格式的字符串,如果对象为null,则返回null + * @throws RuntimeException 如果转换过程中发生JSON处理异常,则抛出运行时异常 + */ + public static String toJsonString(Object object) { + if (ObjectUtil.isNull(object)) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型的对象 + * + * @param text JSON格式的字符串 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将字节数组转换为指定类型的对象 + * + * @param bytes 字节数组 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字节数组为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型的对象,支持复杂类型 + * + * @param text JSON格式的字符串 + * @param typeReference 指定类型的TypeReference对象 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(String text, TypeReference typeReference) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为Dict对象 + * + * @param text JSON格式的字符串 + * @return 转换后的Dict对象,如果字符串为空或者不是JSON格式则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static Dict parseMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class)); + } catch (MismatchedInputException e) { + // 类型不匹配说明不是json + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为Dict对象的列表 + * + * @param text JSON格式的字符串 + * @return 转换后的Dict对象的列表,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static List parseArrayMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型对象的列表 + * + * @param text JSON格式的字符串 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象的列表,如果字符串为空则返回空列表 + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static List parseArray(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/fys-common/fys-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..b8ba5d6 --- /dev/null +++ b/fys-common/fys-common-json/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.json.config.JacksonConfig diff --git a/fys-common/fys-common-log/pom.xml b/fys-common/fys-common-log/pom.xml new file mode 100644 index 0000000..7afadf6 --- /dev/null +++ b/fys-common/fys-common-log/pom.xml @@ -0,0 +1,32 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-log + + + fys-common-log 日志记录 + + + + + + com.fuyuanshen + fys-common-satoken + + + + com.fuyuanshen + fys-common-json + + + + + diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/annotation/Log.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/annotation/Log.java new file mode 100644 index 0000000..170218c --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/annotation/Log.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.common.log.annotation; + +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.log.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author fys + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; + + + /** + * 排除指定的请求参数 + */ + String[] excludeParamNames() default {}; + +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/aspect/LogAspect.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/aspect/LogAspect.java new file mode 100644 index 0000000..0772e93 --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/aspect/LogAspect.java @@ -0,0 +1,219 @@ +package com.fuyuanshen.common.log.aspect; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessStatus; +import com.fuyuanshen.common.log.event.OperLogEvent; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.Map; +import java.util.StringJoiner; + +/** + * 操作日志记录处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +@AutoConfiguration +public class LogAspect { + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + + /** + * 计时 key + */ + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, Log controllerLog) { + StopWatch stopWatch = new StopWatch(); + KEY_CACHE.set(stopWatch); + stopWatch.start(); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + + // *========数据库日志=========*// + OperLogEvent operLog = new OperLogEvent(); + operLog.setTenantId(LoginHelper.getTenantId()); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = ServletUtils.getClientIP(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + LoginUser loginUser = LoginHelper.getLoginUser(); + operLog.setOperName(loginUser.getUsername()); + operLog.setDeptName(loginUser.getDeptName()); + + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + StopWatch stopWatch = KEY_CACHE.get(); + stopWatch.stop(); + operLog.setCostTime(stopWatch.getDuration().toMillis()); + // 发布事件保存数据库 + SpringUtils.context().publishEvent(operLog); + } catch (Exception exp) { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + } finally { + KEY_CACHE.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 3800)); + } else { + MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); + MapUtil.removeAny(paramsMap, excludeParamNames); + operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { + StringJoiner params = new StringJoiner(" "); + if (ArrayUtil.isEmpty(paramsArray)) { + return params.toString(); + } + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + String str = JsonUtils.toJsonString(o); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, EXCLUDE_PROPERTIES); + MapUtil.removeAny(dict, excludeParamNames); + str = JsonUtils.toJsonString(dict); + } + params.add(str); + } + } + return params.toString(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.values()) { + return value instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessStatus.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessStatus.java new file mode 100644 index 0000000..bbc01f5 --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.common.log.enums; + +/** + * 操作状态 + * + * @author fys + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessType.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessType.java new file mode 100644 index 0000000..bcd0100 --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/BusinessType.java @@ -0,0 +1,58 @@ +package com.fuyuanshen.common.log.enums; + +/** + * 业务操作类型 + * + * @author fys + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/OperatorType.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/OperatorType.java new file mode 100644 index 0000000..459666d --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.fuyuanshen.common.log.enums; + +/** + * 操作人类别 + * + * @author fys + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/LogininforEvent.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/LogininforEvent.java new file mode 100644 index 0000000..b5e96ba --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/LogininforEvent.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.common.log.event; + +import lombok.Data; + +import jakarta.servlet.http.HttpServletRequest; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 登录事件 + * + * @author Lion Li + */ + +@Data +public class LogininforEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户账号 + */ + private String username; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 提示消息 + */ + private String message; + + /** + * 请求体 + */ + private HttpServletRequest request; + + /** + * 其他参数 + */ + private Object[] args; + +} diff --git a/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/OperLogEvent.java b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/OperLogEvent.java new file mode 100644 index 0000000..7280f07 --- /dev/null +++ b/fys-common/fys-common-log/src/main/java/com/fuyuanshen/common/log/event/OperLogEvent.java @@ -0,0 +1,115 @@ +package com.fuyuanshen.common.log.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志事件 + * + * @author Lion Li + */ + +@Data +public class OperLogEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; +} diff --git a/fys-common/fys-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..b4a5ec7 --- /dev/null +++ b/fys-common/fys-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.log.aspect.LogAspect diff --git a/fys-common/fys-common-mail/pom.xml b/fys-common/fys-common-mail/pom.xml new file mode 100644 index 0000000..e18a3e8 --- /dev/null +++ b/fys-common/fys-common-mail/pom.xml @@ -0,0 +1,34 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-mail + + + fys-common-mail 邮件模块 + + + + + com.fuyuanshen + fys-common-core + + + + jakarta.mail + jakarta.mail-api + + + org.eclipse.angus + jakarta.mail + + + + diff --git a/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/MailConfig.java b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/MailConfig.java new file mode 100644 index 0000000..ae34427 --- /dev/null +++ b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/MailConfig.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.common.mail.config; + +import cn.hutool.extra.mail.MailAccount; +import com.fuyuanshen.common.mail.config.properties.MailProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * JavaMail 配置 + * + * @author Michelle.Chung + */ +@AutoConfiguration +@EnableConfigurationProperties(MailProperties.class) +public class MailConfig { + + @Bean + @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") + public MailAccount mailAccount(MailProperties mailProperties) { + MailAccount account = new MailAccount(); + account.setHost(mailProperties.getHost()); + account.setPort(mailProperties.getPort()); + account.setAuth(mailProperties.getAuth()); + account.setFrom(mailProperties.getFrom()); + account.setUser(mailProperties.getUser()); + account.setPass(mailProperties.getPass()); + account.setSocketFactoryPort(mailProperties.getPort()); + account.setStarttlsEnable(mailProperties.getStarttlsEnable()); + account.setSslEnable(mailProperties.getSslEnable()); + account.setTimeout(mailProperties.getTimeout()); + account.setConnectionTimeout(mailProperties.getConnectionTimeout()); + return account; + } + +} diff --git a/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/properties/MailProperties.java b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/properties/MailProperties.java new file mode 100644 index 0000000..c3e560b --- /dev/null +++ b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/config/properties/MailProperties.java @@ -0,0 +1,75 @@ +package com.fuyuanshen.common.mail.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * JavaMail 配置属性 + * + * @author Michelle.Chung + */ +@Data +@ConfigurationProperties(prefix = "mail") +public class MailProperties { + + /** + * 过滤开关 + */ + private Boolean enabled; + + /** + * SMTP服务器域名 + */ + private String host; + + /** + * SMTP服务端口 + */ + private Integer port; + + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + + /** + * 用户名 + */ + private String user; + + /** + * 密码 + */ + private String pass; + + /** + * 发送方,遵循RFC-822标准
+ * 发件人可以是以下形式: + * + *
+     * 1. user@xxx.xx
+     * 2.  name <user@xxx.xx>
+     * 
+ */ + private String from; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private Boolean starttlsEnable; + + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private Long timeout; + + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private Long connectionTimeout; +} diff --git a/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/utils/MailUtils.java b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/utils/MailUtils.java new file mode 100644 index 0000000..090afb9 --- /dev/null +++ b/fys-common/fys-common-mail/src/main/java/com/fuyuanshen/common/mail/utils/MailUtils.java @@ -0,0 +1,469 @@ +package com.fuyuanshen.common.mail.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.JakartaMail; +import cn.hutool.extra.mail.JakartaUserPassAuthenticator; +import cn.hutool.extra.mail.MailAccount; +import jakarta.mail.Authenticator; +import jakarta.mail.Session; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param user 发送人 + * @param pass 授权码 + */ + public static MailAccount getMailAccount(String from, String user, String pass) { + ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); + ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); + return ACCOUNT; + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendText(String to, String subject, String content, File... files) { + return send(to, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, File... files) { + return send(to, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + */ + public static String sendText(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { + return send(to, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { + return send(tos, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, + boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + /** + * 根据配置文件,获取邮件客户端会话 + * + * @param mailAccount 邮件账户配置 + * @param isSingleton 是否单例(全局共享会话) + * @return {@link Session} + * @since 5.5.7 + */ + public static Session getSession(MailAccount mailAccount, boolean isSingleton) { + Authenticator authenticator = null; + if (mailAccount.isAuth()) { + authenticator = new JakartaUserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + } + + return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // + : Session.getInstance(mailAccount.getSmtpProps(), authenticator); + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method start + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param useGlobalSession 是否全局共享Session + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:${cid} + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, + Map imageMap, boolean isHtml, File... files) { + final JakartaMail mail = JakartaMail.create(mailAccount).setUseGlobalSession(useGlobalSession); + + // 可选抄送人 + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + // 可选密送人 + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + + mail.setTos(tos.toArray(new String[0])); + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(isHtml); + mail.setFiles(files); + + // 图片 + if (MapUtil.isNotEmpty(imageMap)) { + for (Entry entry : imageMap.entrySet()) { + mail.addImage(entry.getKey(), entry.getValue()); + // 关闭流 + IoUtil.close(entry.getValue()); + } + } + + return mail.send(); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } + // ------------------------------------------------------------------------------------------------------------------------ Private method end +} diff --git a/fys-common/fys-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/fys-common/fys-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..fa09121 --- /dev/null +++ b/fys-common/fys-common-mail/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.fuyuanshen.common.mail.config.MailConfig diff --git a/fys-common/fys-common-mybatis/pom.xml b/fys-common/fys-common-mybatis/pom.xml new file mode 100644 index 0000000..3a64002 --- /dev/null +++ b/fys-common/fys-common-mybatis/pom.xml @@ -0,0 +1,52 @@ + + + + com.fuyuanshen + fys-common + ${revision} + + 4.0.0 + + fys-common-mybatis + + + fys-common-mybatis 数据库服务 + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-satoken + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + com.baomidou + mybatis-plus-jsqlparser + + + + + p6spy + p6spy + + + + diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataColumn.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataColumn.java new file mode 100644 index 0000000..6c55c1b --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataColumn.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限注解,用于标记数据权限的占位符关键字和替换值 + *

+ * 一个注解只能对应一个模板 + *

+ * + * @author Lion Li + * @version 3.5.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataColumn { + + /** + * 数据权限模板的占位符关键字,默认为 "deptName" + * + * @return 占位符关键字数组 + */ + String[] key() default "deptName"; + + /** + * 数据权限模板的占位符替换值,默认为 "dept_id" + * + * @return 占位符替换值数组 + */ + String[] value() default "dept_id"; + + /** + * 权限标识符 用于通过菜单权限标识符来获取数据权限 + * 拥有此标识符的角色 将不会拼接此角色的数据过滤sql + * + * @return 权限标识符 + */ + String permission() default ""; +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataPermission.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataPermission.java new file mode 100644 index 0000000..60ac5bf --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/annotation/DataPermission.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限组注解,用于标记数据权限配置数组 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + /** + * 数据权限配置数组,用于指定数据权限的占位符关键字和替换值 + * + * @return 数据权限配置数组 + */ + DataColumn[] value(); + + /** + * 权限拼接标识符(用于指定连接语句的sql符号) + * 如不填 默认 select 用 OR 其他语句用 AND + * 内容 OR 或者 AND + */ + String joinStr() default ""; + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/aspect/DataPermissionAspect.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/aspect/DataPermissionAspect.java new file mode 100644 index 0000000..a1667f7 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/aspect/DataPermissionAspect.java @@ -0,0 +1,50 @@ +package com.fuyuanshen.common.mybatis.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.helper.DataPermissionHelper; + +/** + * 数据权限处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +public class DataPermissionAspect { + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(dataPermission)") + public void doBefore(JoinPoint joinPoint, DataPermission dataPermission) { + DataPermissionHelper.setPermission(dataPermission); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(dataPermission)") + public void doAfterReturning(JoinPoint joinPoint, DataPermission dataPermission) { + DataPermissionHelper.removePermission(); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(dataPermission)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, DataPermission dataPermission, Exception e) { + DataPermissionHelper.removePermission(); + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/config/MybatisPlusConfig.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/config/MybatisPlusConfig.java new file mode 100644 index 0000000..dc54c41 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/config/MybatisPlusConfig.java @@ -0,0 +1,138 @@ +package com.fuyuanshen.common.mybatis.config; + +import cn.hutool.core.net.NetUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.fuyuanshen.common.core.factory.YmlPropertySourceFactory; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.mybatis.aspect.DataPermissionAspect; +import com.fuyuanshen.common.mybatis.handler.InjectionMetaObjectHandler; +import com.fuyuanshen.common.mybatis.handler.MybatisExceptionHandler; +import com.fuyuanshen.common.mybatis.handler.PlusPostInitTableInfoHandler; +import com.fuyuanshen.common.mybatis.interceptor.PlusDataPermissionInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.BeansException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.PropertySource; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * mybatis-plus配置类(下方注释有插件介绍) + * + * @author Lion Li + */ +@EnableTransactionManagement(proxyTargetClass = true) +@MapperScan("${mybatis-plus.mapperPackage}") +@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class) +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 多租户插件 必须放到第一位 + try { + TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class); + interceptor.addInnerInterceptor(tenant); + } catch (BeansException ignore) { + } + // 数据权限处理 + interceptor.addInnerInterceptor(dataPermissionInterceptor()); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + return interceptor; + } + + /** + * 数据权限拦截器 + */ + public PlusDataPermissionInterceptor dataPermissionInterceptor() { + return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage")); + } + + /** + * 数据权限切面处理器 + */ + @Bean + public DataPermissionAspect dataPermissionAspect() { + return new DataPermissionAspect(); + } + + /** + * 分页插件,自动识别数据库类型 + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 分页合理化 + paginationInnerInterceptor.setOverflow(true); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 元对象字段填充控制器 + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new InjectionMetaObjectHandler(); + } + + /** + * 使用网卡信息绑定雪花生成器 + * 防止集群雪花ID重复 + */ + @Bean + public IdentifierGenerator idGenerator() { + return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); + } + + /** + * 异常处理器 + */ + @Bean + public MybatisExceptionHandler mybatisExceptionHandler() { + return new MybatisExceptionHandler(); + } + + /** + * 初始化表对象处理器 + */ + @Bean + public PostInitTableInfoHandler postInitTableInfoHandler() { + return new PlusPostInitTableInfoHandler(); + } + + /** + * PaginationInnerInterceptor 分页插件,自动识别数据库类型 + * https://baomidou.com/pages/97710a/ + * OptimisticLockerInnerInterceptor 乐观锁插件 + * https://baomidou.com/pages/0d93c0/ + * MetaObjectHandler 元对象字段填充控制器 + * https://baomidou.com/pages/4c6bcf/ + * ISqlInjector sql注入器 + * https://baomidou.com/pages/42ea4a/ + * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作 + * https://baomidou.com/pages/f9a237/ + * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截) + * IdentifierGenerator 自定义主键策略 + * https://baomidou.com/pages/568eb2/ + * TenantLineInnerInterceptor 多租户插件 + * https://baomidou.com/pages/aef2f2/ + * DynamicTableNameInnerInterceptor 动态表名插件 + * https://baomidou.com/pages/2a45ff/ + */ + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/domain/BaseEntity.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/domain/BaseEntity.java new file mode 100644 index 0000000..ee29d74 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/domain/BaseEntity.java @@ -0,0 +1,70 @@ +package com.fuyuanshen.common.mybatis.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author Lion Li + */ +@Data +public class BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建部门 + */ + @TableField(fill = FieldFill.INSERT) + private Long createDept; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private Long createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/mapper/BaseMapperPlus.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/mapper/BaseMapperPlus.java new file mode 100644 index 0000000..28d3d0c --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/mapper/BaseMapperPlus.java @@ -0,0 +1,334 @@ +package com.fuyuanshen.common.mybatis.core.mapper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @param table 泛型 + * @param vo 泛型 + * @author Lion Li + * @since 2021-05-13 + */ +@SuppressWarnings("unchecked") +public interface BaseMapperPlus extends BaseMapper { + + Log log = LogFactory.getLog(BaseMapperPlus.class); + + /** + * 获取当前实例对象关联的泛型类型 V 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 + */ + default Class currentVoClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; + } + + /** + * 获取当前实例对象关联的泛型类型 T 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 + */ + default Class currentModelClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; + } + + /** + * 使用默认的查询条件查询并返回结果列表 + * + * @return 返回查询结果的列表 + */ + default List selectList() { + return this.selectList(new QueryWrapper<>()); + } + + /** + * 批量插入实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList) { + return Db.saveBatch(entityList); + } + + /** + * 批量根据ID更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList) { + return Db.updateBatchById(entityList); + } + + /** + * 批量插入或更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList) { + return Db.saveOrUpdateBatch(entityList); + } + + /** + * 批量插入实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList, int batchSize) { + return Db.saveBatch(entityList, batchSize); + } + + /** + * 批量根据ID更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList, int batchSize) { + return Db.updateBatchById(entityList, batchSize); + } + + /** + * 批量插入或更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { + return Db.saveOrUpdateBatch(entityList, batchSize); + } + + /** + * 根据ID查询单个VO对象 + * + * @param id 主键ID + * @return 查询到的单个VO对象 + */ + default V selectVoById(Serializable id) { + return selectVoById(id, this.currentVoClass()); + } + + /** + * 根据ID查询单个VO对象并将其转换为指定的VO类 + * + * @param id 主键ID + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoById(Serializable id, Class voClass) { + T obj = this.selectById(id); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 根据ID集合批量查询VO对象列表 + * + * @param idList 主键ID集合 + * @return 查询到的VO对象列表 + */ + default List selectVoByIds(Collection idList) { + return selectVoByIds(idList, this.currentVoClass()); + } + + /** + * 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param idList 主键ID集合 + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByIds(Collection idList, Class voClass) { + List list = this.selectByIds(idList); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据查询条件Map查询VO对象列表 + * + * @param map 查询条件Map + * @return 查询到的VO对象列表 + */ + default List selectVoByMap(Map map) { + return selectVoByMap(map, this.currentVoClass()); + } + + /** + * 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param map 查询条件Map + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByMap(Map map, Class voClass) { + List list = this.selectByMap(map); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件查询单个VO对象 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper) { + return selectVoOne(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询单个VO对象,并根据需要决定是否抛出异常 + * + * @param wrapper 查询条件Wrapper + * @param throwEx 是否抛出异常的标志 + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper, boolean throwEx) { + return selectVoOne(wrapper, this.currentVoClass(), throwEx); + } + + /** + * 根据条件查询单个VO对象,并指定返回的VO对象的类型 + * + * @param wrapper 查询条件Wrapper + * @param voClass 返回的VO对象的Class对象 + * @param 返回的VO对象的类型 + * @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass) { + return selectVoOne(wrapper, voClass, true); + } + + /** + * 根据条件查询单个实体对象,并将其转换为指定的VO对象 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param throwEx 是否抛出异常的标志 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { + T obj = this.selectOne(wrapper, throwEx); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 查询所有VO对象列表 + * + * @return 查询到的VO对象列表 + */ + default List selectVoList() { + return selectVoList(new QueryWrapper<>(), this.currentVoClass()); + } + + /** + * 根据条件查询VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象列表 + */ + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoList(Wrapper wrapper, Class voClass) { + List list = this.selectList(wrapper); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件分页查询VO对象列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象分页列表 + */ + default

> P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + /** + * 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @param

VO对象分页列表的类型 + * @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回 + */ + default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + // 根据条件分页查询实体对象列表 + List list = this.selectList(page, wrapper); + // 创建一个新的VO对象分页列表,并设置分页信息 + IPage voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + if (CollUtil.isEmpty(list)) { + return (P) voPage; + } + voPage.setRecords(MapstructUtils.convert(list, voClass)); + return (P) voPage; + } + + /** + * 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表 + * + * @param wrapper 查询条件Wrapper + * @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象 + * @param 要转换的对象的类型 + * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回 + */ + default List selectObjs(Wrapper wrapper, Function mapper) { + return StreamUtils.toList(this.selectObjs(wrapper), mapper); + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/PageQuery.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/PageQuery.java new file mode 100644 index 0000000..64fba03 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/PageQuery.java @@ -0,0 +1,127 @@ +package com.fuyuanshen.common.mybatis.core.page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.sql.SqlUtil; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 分页查询实体类 + * + * @author Lion Li + */ +@Data +public class PageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 分页大小 + */ + private Integer pageSize; + + /** + * 当前页数 + */ + private Integer pageNum; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc; + + /** + * 当前记录起始索引 默认值 + */ + public static final int DEFAULT_PAGE_NUM = 1; + + /** + * 每页显示记录数 默认值 默认查全部 + */ + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + + /** + * 构建分页对象 + */ + public Page build() { + Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); + Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); + if (pageNum <= 0) { + pageNum = DEFAULT_PAGE_NUM; + } + Page page = new Page<>(pageNum, pageSize); + List orderItems = buildOrderItem(); + if (CollUtil.isNotEmpty(orderItems)) { + page.addOrder(orderItems); + } + return page; + } + + /** + * 构建排序 + * + * 支持的用法如下: + * {isAsc:"asc",orderByColumn:"id"} order by id asc + * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc + * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc + * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc + */ + private List buildOrderItem() { + if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { + return null; + } + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + orderBy = StringUtils.toUnderScoreCase(orderBy); + + // 兼容前端排序类型 + isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); + + String[] orderByArr = orderBy.split(StringUtils.SEPARATOR); + String[] isAscArr = isAsc.split(StringUtils.SEPARATOR); + if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { + throw new ServiceException("排序参数有误"); + } + + List list = new ArrayList<>(); + // 每个字段各自排序 + for (int i = 0; i < orderByArr.length; i++) { + String orderByStr = orderByArr[i]; + String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i]; + if ("asc".equals(isAscStr)) { + list.add(OrderItem.asc(orderByStr)); + } else if ("desc".equals(isAscStr)) { + list.add(OrderItem.desc(orderByStr)); + } else { + throw new ServiceException("排序参数有误"); + } + } + return list; + } + + @JsonIgnore + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + + public PageQuery(Integer pageSize, Integer pageNum) { + this.pageSize = pageSize; + this.pageNum = pageNum; + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/TableDataInfo.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/TableDataInfo.java new file mode 100644 index 0000000..8d58251 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/core/page/TableDataInfo.java @@ -0,0 +1,107 @@ +package com.fuyuanshen.common.mybatis.core.page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class TableDataInfo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) { + this.rows = list; + this.total = total; + this.code = HttpStatus.HTTP_OK; + this.msg = "查询成功"; + } + + /** + * 根据分页对象构建表格分页数据对象 + */ + public static TableDataInfo build(IPage page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * 根据数据列表构建表格分页数据对象 + */ + public static TableDataInfo build(List list) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(list.size()); + return rspData; + } + + /** + * 构建表格分页数据对象 + */ + public static TableDataInfo build() { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + return rspData; + } + + /** + * 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页) + * + * @param list 原始数据列表(全部数据) + * @param page 分页参数对象(包含当前页码、每页大小等) + * @return 构造好的分页结果 TableDataInfo + */ + public static TableDataInfo build(List list, IPage page) { + if (CollUtil.isEmpty(list)) { + return TableDataInfo.build(); + } + List pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list); + return new TableDataInfo<>(pageList, list.size()); + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataBaseType.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataBaseType.java new file mode 100644 index 0000000..84d3cea --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataBaseType.java @@ -0,0 +1,58 @@ +package com.fuyuanshen.common.mybatis.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.utils.StringUtils; + +/** + * 数据库类型 + * + * @author Lion Li + */ +@Getter +@AllArgsConstructor +public enum DataBaseType { + + /** + * MySQL + */ + MY_SQL("MySQL"), + + /** + * Oracle + */ + ORACLE("Oracle"), + + /** + * PostgreSQL + */ + POSTGRE_SQL("PostgreSQL"), + + /** + * SQL Server + */ + SQL_SERVER("Microsoft SQL Server"); + + /** + * 数据库类型 + */ + private final String type; + + /** + * 根据数据库产品名称查找对应的数据库类型 + * + * @param databaseProductName 数据库产品名称 + * @return 对应的数据库类型枚举值,如果未找到则返回 null + */ + public static DataBaseType find(String databaseProductName) { + if (StringUtils.isBlank(databaseProductName)) { + return null; + } + for (DataBaseType type : values()) { + if (type.getType().equals(databaseProductName)) { + return type; + } + } + return null; + } +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataScopeType.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataScopeType.java new file mode 100644 index 0000000..a471d86 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/enums/DataScopeType.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.common.mybatis.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.helper.DataPermissionHelper; + +/** + * 数据权限类型枚举 + *

+ * 支持使用 SpEL 模板表达式定义 SQL 查询条件 + * 内置数据: + * - {@code user}: 当前登录用户信息,参考 {@link LoginUser} + * 内置服务: + * - {@code sdss}: 系统数据权限服务,参考 ISysDataScopeService + * 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作 + * 如需扩展服务,可以通过 ISysDataScopeService 自行编写 + *

+ * + * @author Lion Li + * @version 3.5.0 + */ +@Getter +@AllArgsConstructor +public enum DataScopeType { + + /** + * 全部数据权限 + */ + ALL("1", "", ""), + + /** + * 自定数据权限 + */ + CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "), + + /** + * 部门数据权限 + */ + DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "), + + /** + * 部门及以下数据权限 + */ + DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "), + + /** + * 仅本人数据权限 + */ + SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "), + + /** + * 部门及以下或本人数据权限 + */ + DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 "); + + private final String code; + + /** + * SpEL 模板表达式,用于构建 SQL 查询条件 + */ + private final String sqlTemplate; + + /** + * 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式 + */ + private final String elseSql; + + /** + * 根据枚举代码查找对应的枚举值 + * + * @param code 枚举代码 + * @return 对应的枚举值,如果未找到则返回 null + */ + public static DataScopeType findCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + for (DataScopeType type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/InjectionMetaObjectHandler.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/InjectionMetaObjectHandler.java new file mode 100644 index 0000000..f6cf699 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -0,0 +1,102 @@ +package com.fuyuanshen.common.mybatis.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.common.satoken.utils.LoginHelper; + +import java.util.Date; + +/** + * MP注入处理器 + * + * @author Lion Li + * @date 2021/4/25 + */ +@Slf4j +public class InjectionMetaObjectHandler implements MetaObjectHandler { + + /** + * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void insertFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间 + Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date()); + baseEntity.setCreateTime(current); + baseEntity.setUpdateTime(current); + + // 如果创建人为空,则填充当前登录用户的信息 + if (ObjectUtil.isNull(baseEntity.getCreateBy())) { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + // 填充创建人、更新人和创建部门信息 + baseEntity.setCreateBy(userId); + baseEntity.setUpdateBy(userId); + baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId())); + } + } + } else { + Date date = new Date(); + this.strictInsertFill(metaObject, "createTime", Date.class, date); + this.strictInsertFill(metaObject, "updateTime", Date.class, date); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void updateFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充 + Date current = new Date(); + baseEntity.setUpdateTime(current); + + // 获取当前登录用户的ID,并填充更新人信息 + Long userId = LoginHelper.getUserId(); + if (ObjectUtil.isNotNull(userId)) { + baseEntity.setUpdateBy(userId); + } + } else { + this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取当前登录用户信息 + * + * @return 当前登录用户的信息,如果用户未登录则返回 null + */ + private LoginUser getLoginUser() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + log.warn("自动注入警告 => 用户未登录"); + return null; + } + return loginUser; + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/MybatisExceptionHandler.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/MybatisExceptionHandler.java new file mode 100644 index 0000000..4fcc1a3 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/MybatisExceptionHandler.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.common.mybatis.handler; + +import cn.hutool.http.HttpStatus; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * Mybatis异常处理器 + * + * @author Lion Li + */ +@Slf4j +@RestControllerAdvice +public class MybatisExceptionHandler { + + /** + * 主键或UNIQUE索引,数据重复异常 + */ + @ExceptionHandler(DuplicateKeyException.class) + public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_CONFLICT, "数据库中已存在该记录,请联系管理员确认"); + } + + /** + * Mybatis系统异常 通用处理 + */ + @ExceptionHandler(MyBatisSystemException.class) + public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + String message = e.getMessage(); + if (StringUtils.contains(message, "CannotFindDataSourceException")) { + log.error("请求地址'{}', 未找到数据源", requestURI); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); + } + log.error("请求地址'{}', Mybatis系统异常", requestURI, e); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, message); + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusDataPermissionHandler.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusDataPermissionHandler.java new file mode 100644 index 0000000..8c42de6 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusDataPermissionHandler.java @@ -0,0 +1,357 @@ +package com.fuyuanshen.common.mybatis.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.ibatis.io.Resources; +import com.fuyuanshen.common.core.domain.dto.RoleDTO; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.enums.DataScopeType; +import com.fuyuanshen.common.mybatis.helper.DataPermissionHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.expression.*; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * 数据权限过滤 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionHandler { + + /** + * 类名称与注解的映射关系缓存(由于aop无法拦截mybatis接口类上的注解 只能通过启动预扫描的方式进行) + */ + private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); + + /** + * spel 解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + /** + * bean解析器 用于处理 spel 表达式中对 bean 的调用 + */ + private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + + /** + * 构造方法,扫描指定包下的 Mapper 类并初始化缓存 + * + * @param mapperPackage Mapper 类所在的包路径 + */ + public PlusDataPermissionHandler(String mapperPackage) { + scanMapperClasses(mapperPackage); + } + + /** + * 获取数据过滤条件的 SQL 片段 + * + * @param where 原始的查询条件表达式 + * @param mappedStatementId Mapper 方法的 ID + * @param isSelect 是否为查询语句 + * @return 数据过滤条件的 SQL 片段 + */ + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + try { + // 获取数据权限配置 + DataPermission dataPermission = getDataPermission(mappedStatementId); + // 获取当前登录用户信息 + LoginUser currentUser = DataPermissionHelper.getVariable("user"); + if (ObjectUtil.isNull(currentUser)) { + currentUser = LoginHelper.getLoginUser(); + DataPermissionHelper.setVariable("user", currentUser); + } + // 如果是超级管理员或租户管理员,则不过滤数据 + if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { + return where; + } + // 构造数据过滤条件的 SQL 片段 + String dataFilterSql = buildDataFilter(dataPermission, isSelect); + if (StringUtils.isBlank(dataFilterSql)) { + return where; + } + Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); + // 数据权限使用单独的括号 防止与其他条件冲突 + ParenthesedExpressionList parenthesis = new ParenthesedExpressionList<>(expression); + if (ObjectUtil.isNotNull(where)) { + return new AndExpression(where, parenthesis); + } else { + return parenthesis; + } + } catch (JSQLParserException e) { + throw new ServiceException("数据权限解析异常 => " + e.getMessage()); + } finally { + DataPermissionHelper.removePermission(); + } + } + + /** + * 构建数据过滤条件的 SQL 语句 + * + * @param dataPermission 数据权限注解 + * @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式 + * @return 构建的数据过滤条件的 SQL 语句 + * @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常 + */ + private String buildDataFilter(DataPermission dataPermission, boolean isSelect) { + // 更新或删除需满足所有条件 + String joinStr = isSelect ? " OR " : " AND "; + if (StringUtils.isNotBlank(dataPermission.joinStr())) { + joinStr = " " + dataPermission.joinStr() + " "; + } + LoginUser user = DataPermissionHelper.getVariable("user"); + Object defaultValue = "-1"; + NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue); + context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue)); + context.setBeanResolver(beanResolver); + DataPermissionHelper.getContext().forEach(context::setVariable); + Set conditions = new HashSet<>(); + // 优先设置变量 + List keys = new ArrayList<>(); + Map ignoreMap = new HashMap<>(); + for (DataColumn dataColumn : dataPermission.value()) { + if (dataColumn.key().length != dataColumn.value().length) { + throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); + } + // 包含权限标识符 这直接跳过 + if (StringUtils.isNotBlank(dataColumn.permission()) && + CollUtil.contains(user.getMenuPermission(), dataColumn.permission()) + ) { + ignoreMap.put(dataColumn, Boolean.TRUE); + continue; + } + // 设置注解变量 key 为表达式变量 value 为变量值 + for (int i = 0; i < dataColumn.key().length; i++) { + context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); + } + keys.addAll(Arrays.stream(dataColumn.key()).map(key -> "#" + key).toList()); + } + + for (RoleDTO role : user.getRoles()) { + user.setRoleId(role.getRoleId()); + // 获取角色权限泛型 + DataScopeType type = DataScopeType.findCode(role.getDataScope()); + if (ObjectUtil.isNull(type)) { + throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); + } + // 全部数据权限直接返回 + if (type == DataScopeType.ALL) { + return StringUtils.EMPTY; + } + boolean isSuccess = false; + for (DataColumn dataColumn : dataPermission.value()) { + // 包含权限标识符 这直接跳过 + if (ignoreMap.containsKey(dataColumn)) { + // 修复多角色与权限标识符共用问题 https://gitee.com/dromara/fys-Vue-Plus/issues/IB4CS4 + conditions.add(joinStr + " 1 = 1 "); + isSuccess = true; + continue; + } + // 不包含 key 变量 则不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), keys.toArray(String[]::new))) { + continue; + } + // 当前注解不满足模板 不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), dataColumn.key())) { + continue; + } + // 忽略数据权限 防止spel表达式内有其他sql查询导致死循环调用 + String sql = DataPermissionHelper.ignore(() -> + parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class) + ); + // 解析sql模板并填充 + conditions.add(joinStr + sql); + isSuccess = true; + } + // 未处理成功则填充兜底方案 + if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { + conditions.add(joinStr + type.getElseSql()); + } + } + + if (CollUtil.isNotEmpty(conditions)) { + String sql = StreamUtils.join(conditions, Function.identity(), ""); + return sql.substring(joinStr.length()); + } + return StringUtils.EMPTY; + } + + /** + * 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类 + * + * @param mapperPackage Mapper 类所在的包路径 + */ + private void scanMapperClasses(String mapperPackage) { + // 创建资源解析器和元数据读取工厂 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + // 将 Mapper 包路径按分隔符拆分为数组 + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + // 将包路径转换为资源路径 + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + // 获取指定路径下的所有 .class 文件资源 + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + // 获取资源的类元数据 + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + // 获取资源对应的类对象 + Class clazz = Resources.classForName(classMetadata.getClassName()); + // 查找类中的特定注解 + findAnnotation(clazz); + } + } + } catch (Exception e) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中 + * + * @param clazz 要查找的类 + */ + private void findAnnotation(Class clazz) { + DataPermission dataPermission; + for (Method method : clazz.getMethods()) { + if (method.isDefault() || method.isVarArgs()) { + continue; + } + String mappedStatementId = clazz.getName() + "." + method.getName(); + if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); + dataPermissionCacheMap.put(mappedStatementId, dataPermission); + } + } + if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); + dataPermissionCacheMap.put(clazz.getName(), dataPermission); + } + } + + /** + * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return DataPermission 注解对象,如果不存在则返回 null + */ + public DataPermission getDataPermission(String mapperId) { + // 检查上下文中是否包含映射语句 ID 对应的 DataPermission 注解对象 + if (DataPermissionHelper.getPermission() != null) { + return DataPermissionHelper.getPermission(); + } + // 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象 + if (dataPermissionCacheMap.containsKey(mapperId)) { + return dataPermissionCacheMap.get(mapperId); + } + // 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找 + String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); + if (dataPermissionCacheMap.containsKey(clazzName)) { + return dataPermissionCacheMap.get(clazzName); + } + return null; + } + + /** + * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true + */ + public boolean invalid(String mapperId) { + return getDataPermission(mapperId) == null; + } + + /** + * 对所有null变量找不到的变量返回默认值 + */ + @AllArgsConstructor + private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext { + + private final Object defaultValue; + + @Override + public Object lookupVariable(String name) { + Object obj = super.lookupVariable(name); + // 如果读取到的值是 null,则返回默认值 + if (obj == null) { + return defaultValue; + } + return obj; + } + + } + + /** + * 对所有null变量找不到的变量返回默认值 委托模式 将不需要处理的方法委托给原处理器 + */ + @AllArgsConstructor + private static class NullSafePropertyAccessor implements PropertyAccessor { + + private final PropertyAccessor delegate; + private final Object defaultValue; + + @Override + public Class[] getSpecificTargetClasses() { + return delegate.getSpecificTargetClasses(); + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canRead(context, target, name); + } + + @Override + public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { + TypedValue value = delegate.read(context, target, name); + // 如果读取到的值是 null,则返回默认值 + if (value.getValue() == null) { + return new TypedValue(defaultValue); + } + return value; + } + + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canWrite(context, target, name); + } + + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { + delegate.write(context, target, name, newValue); + } + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusPostInitTableInfoHandler.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusPostInitTableInfoHandler.java new file mode 100644 index 0000000..337dd61 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/handler/PlusPostInitTableInfoHandler.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.common.mybatis.handler; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.session.Configuration; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; + +/** + * 修改表信息初始化方式 + * 目前用于全局修改是否使用逻辑删除 + * + * @author Lion Li + */ +public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler { + + @Override + public void postTableInfo(TableInfo tableInfo, Configuration configuration) { + String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true"); + // 只有关闭时 统一设置false 为true时mp自动判断不处理 + if (!Convert.toBool(flag)) { + ReflectUtils.setFieldValue(tableInfo, "withLogicDelete", false); + } + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataBaseHelper.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataBaseHelper.java new file mode 100644 index 0000000..d6e1a8a --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataBaseHelper.java @@ -0,0 +1,81 @@ +package com.fuyuanshen.common.mybatis.helper; + +import cn.hutool.core.convert.Convert; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.mybatis.enums.DataBaseType; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据库助手 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataBaseHelper { + + private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); + + /** + * 获取当前数据库类型 + */ + public static DataBaseType getDataBaseType() { + DataSource dataSource = DS.determineDataSource(); + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new ServiceException(e.getMessage()); + } + } + + public static boolean isMySql() { + return DataBaseType.MY_SQL == getDataBaseType(); + } + + public static boolean isOracle() { + return DataBaseType.ORACLE == getDataBaseType(); + } + + public static boolean isPostgerSql() { + return DataBaseType.POSTGRE_SQL == getDataBaseType(); + } + + public static boolean isSqlServer() { + return DataBaseType.SQL_SERVER == getDataBaseType(); + } + + public static String findInSet(Object var1, String var2) { + DataBaseType dataBasyType = getDataBaseType(); + String var = Convert.toStr(var1); + if (dataBasyType == DataBaseType.SQL_SERVER) { + // charindex(',100,' , ',0,100,101,') <> 0 + return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2); + } else if (dataBasyType == DataBaseType.POSTGRE_SQL) { + // (select strpos(',0,100,101,' , ',100,')) <> 0 + return "(select strpos(','||%s||',' , ',%s,')) <> 0".formatted(var2, var); + } else if (dataBasyType == DataBaseType.ORACLE) { + // instr(',0,100,101,' , ',100,') <> 0 + return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var); + } + // find_in_set(100 , '0,100,101') + return "find_in_set('%s' , %s) <> 0".formatted(var, var2); + } + + /** + * 获取当前加载的数据库名 + */ + public static List getDataSourceNameList() { + return new ArrayList<>(DS.getDataSources().keySet()); + } +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataPermissionHelper.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataPermissionHelper.java new file mode 100644 index 0000000..23d919c --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/helper/DataPermissionHelper.java @@ -0,0 +1,176 @@ +package com.fuyuanshen.common.mybatis.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.utils.reflect.ReflectUtils; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; +import java.util.function.Supplier; + +/** + * 数据权限助手 + * + * @author Lion Li + * @version 3.5.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked cast") +public class DataPermissionHelper { + + private static final String DATA_PERMISSION_KEY = "data:permission"; + + private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new); + + private static final ThreadLocal PERMISSION_CACHE = new ThreadLocal<>(); + + /** + * 获取当前执行mapper权限注解 + * + * @return 返回当前执行mapper权限注解 + */ + public static DataPermission getPermission() { + return PERMISSION_CACHE.get(); + } + + /** + * 设置当前执行mapper权限注解 + * + * @param dataPermission 数据权限注解 + */ + public static void setPermission(DataPermission dataPermission) { + PERMISSION_CACHE.set(dataPermission); + } + + /** + * 删除当前执行mapper权限注解 + */ + public static void removePermission() { + PERMISSION_CACHE.remove(); + } + + /** + * 从上下文中获取指定键的变量值,并将其转换为指定的类型 + * + * @param key 变量的键 + * @param 变量值的类型 + * @return 指定键的变量值,如果不存在则返回 null + */ + public static T getVariable(String key) { + Map context = getContext(); + return (T) context.get(key); + } + + /** + * 向上下文中设置指定键的变量值 + * + * @param key 要设置的变量的键 + * @param value 要设置的变量值 + */ + public static void setVariable(String key, Object value) { + Map context = getContext(); + context.put(key, value); + } + + /** + * 获取数据权限上下文 + * + * @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息 + * @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException + */ + public static Map getContext() { + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); + if (ObjectUtil.isNull(attribute)) { + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); + } + if (attribute instanceof Map map) { + return map; + } + throw new NullPointerException("data permission context type exception"); + } + + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { + if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + } + return null; + } + + /** + * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) + */ + public static void enableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } else { + ignoreStrategy.setDataPermission(true); + } + Stack reentrantStack = REENTRANT_IGNORE.get(); + reentrantStack.push(reentrantStack.size() + 1); + } + + /** + * 关闭忽略数据权限 + */ + public static void disableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + Stack reentrantStack = REENTRANT_IGNORE.get(); + boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; + if (noOtherIgnoreStrategy && empty) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else if (empty) { + ignoreStrategy.setDataPermission(false); + } + + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + +} diff --git a/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 0000000..1dcf863 --- /dev/null +++ b/fys-common/fys-common-mybatis/src/main/java/com/fuyuanshen/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -0,0 +1,181 @@ +package com.fuyuanshen.common.mybatis.interceptor; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import com.fuyuanshen.common.mybatis.handler.PlusDataPermissionHandler; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { + + private final PlusDataPermissionHandler dataPermissionHandler; + + /** + * 构造函数,初始化 PlusDataPermissionHandler 实例 + * + * @param mapperPackage 扫描的映射器包 + */ + public PlusDataPermissionInterceptor(String mapperPackage) { + this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); + } + + /** + * 在执行查询之前,检查并处理数据权限相关逻辑 + * + * @param executor MyBatis 执行器对象 + * @param ms 映射语句对象 + * @param parameter 方法参数 + * @param rowBounds 分页对象 + * @param resultHandler 结果处理器 + * @param boundSql 绑定的 SQL 对象 + * @throws SQLException 如果发生 SQL 异常 + */ + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 检查是否需要忽略数据权限处理 + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid(ms.getId())) { + return; + } + // 解析 sql 分配对应方法 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + /** + * 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑 + * + * @param sh MyBatis StatementHandler 对象 + * @param connection 数据库连接对象 + * @param transactionTimeout 事务超时时间 + */ + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + // 获取 SQL 命令类型(增、删、改、查) + SqlCommandType sct = ms.getSqlCommandType(); + + // 只处理更新和删除操作的 SQL 语句 + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + /** + * 处理 SELECT 查询语句中的 WHERE 条件 + * + * @param select SELECT 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + if (select instanceof PlainSelect) { + this.setWhere((PlainSelect) select, (String) obj); + } else if (select instanceof SetOperationList setOperationList) { + List + select a.business_id id,a.file_id,b.file_name,b.url fileUrl from app_business_file a left join sys_oss b on a.file_id = b.oss_id + where + + and a.business_id = #{businessId} + + + and a.file_id = #{fileId} + + + a.create_by = #{createBy} + + order by a.create_time desc + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppMenuMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppMenuMapper.xml new file mode 100644 index 0000000..c46dfae --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppMenuMapper.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppOperationVideoMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppOperationVideoMapper.xml new file mode 100644 index 0000000..e303e12 --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppOperationVideoMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoMapper.xml new file mode 100644 index 0000000..cd5d71a --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppPersonnelInfoMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMapper.xml new file mode 100644 index 0000000..31bc8e2 --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMapper.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMenuMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMenuMapper.xml new file mode 100644 index 0000000..145224c --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppRoleMenuMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppUserMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppUserMapper.xml new file mode 100644 index 0000000..d84c337 --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppUserMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/AppUserRoleMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/AppUserRoleMapper.xml new file mode 100644 index 0000000..3a3006e --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/AppUserRoleMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/equipment/APPDeviceMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/equipment/APPDeviceMapper.xml new file mode 100644 index 0000000..a5dd3de --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/equipment/APPDeviceMapper.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fys-modules/fys-app/src/main/resources/mapper/app/equipment/AppDeviceTypeMapper.xml b/fys-modules/fys-app/src/main/resources/mapper/app/equipment/AppDeviceTypeMapper.xml new file mode 100644 index 0000000..5c1fd86 --- /dev/null +++ b/fys-modules/fys-app/src/main/resources/mapper/app/equipment/AppDeviceTypeMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + id + ,type_name,is_support_ble,locate_mode,network_way,create_by, + update_by,create_time,update_time,customer_id,communication_mode + + + + + diff --git a/fys-modules/fys-customer/pom.xml b/fys-modules/fys-customer/pom.xml new file mode 100644 index 0000000..41a5f23 --- /dev/null +++ b/fys-modules/fys-customer/pom.xml @@ -0,0 +1,123 @@ + + + + + com.fuyuanshen + fys-modules + ${revision} + + + 4.0.0 + fys-customer + + + 富源晟客户管理 + + + + + + com.fuyuanshen + fys-system + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-mybatis + + + + com.fuyuanshen + fys-common-translation + + + + + com.fuyuanshen + fys-common-oss + + + + com.fuyuanshen + fys-common-log + + + + + com.fuyuanshen + fys-common-excel + + + + + com.fuyuanshen + fys-common-sms + + + + com.fuyuanshen + fys-common-tenant + + + + com.fuyuanshen + fys-common-security + + + + com.fuyuanshen + fys-common-web + + + + com.fuyuanshen + fys-common-idempotent + + + + com.fuyuanshen + fys-common-sensitive + + + + com.fuyuanshen + fys-common-encrypt + + + + com.fuyuanshen + fys-common-websocket + + + + com.fuyuanshen + fys-common-sse + + + + com.alibaba + easyexcel + 3.3.1 + + + poi-ooxml-schemas + org.apache.poi + + + + + + + diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/constant/ArrayConstants.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/constant/ArrayConstants.java new file mode 100644 index 0000000..8cfbd66 --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/constant/ArrayConstants.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.customer.constant; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 数组相关常量 + * + * @author: 默苍璃 + * @date: 2025-07-0710:10 + */ +public class ArrayConstants { + + /** + * 默认长整型数组 + */ + public static final Long[] DEFAULT_LONG_ARRAY = {666L}; + public static final Long DEFAULT_LONG = 666L; + + /** + * 空字符串数组 + */ + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + + /** + * 获取只读的默认长整型列表 + */ + public static List getDefaultLongList() { + return Collections.unmodifiableList(new ArrayList<>(List.of(DEFAULT_LONG_ARRAY))); + } + + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/controller/CustomerController.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/controller/CustomerController.java new file mode 100644 index 0000000..b38095a --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/controller/CustomerController.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.customer.controller; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.domain.ResponseVO; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.customer.domain.Customer; +import com.fuyuanshen.customer.domain.query.UserQueryCriteria; +import com.fuyuanshen.customer.domain.vo.ConsumerVo; +import com.fuyuanshen.customer.service.CustomerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.undertow.util.BadRequestException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Set; + +/** + * @author: 默苍璃 + * @date: 2025-07-0114:20 + */ +@Slf4j +@Tag(name = "WEB:客户管理", description = "WEB:客户管理") +@RestController +@RequestMapping("/api/customers") +@RequiredArgsConstructor +public class CustomerController { + + private final CustomerService customerService; + + + /** + * 分页查询客户(限制子客户) + * + * @param criteria + * @return + */ + @Operation(summary = "分页查询客户") + @GetMapping(value = "/customer") + public TableDataInfo queryCustomer(UserQueryCriteria criteria) { + Page page = new Page<>(criteria.getPageNum(), criteria.getPageSize()); + return customerService.queryCustomers(criteria, page); + } + + + @GetMapping(value = "/allCustomer") + @Operation(summary = "查询所有客户") + public ResponseVO> queryAllCustomer(UserQueryCriteria criteria) { + List customers = customerService.queryAllCustomers(criteria); + return ResponseVO.success(customers); + } + + + // @Log("新增客户") + @Operation(summary = "新增客户") + @PostMapping(value = "/addCustomer") + public ResponseVO addCustomer(@Validated @RequestBody Customer customer) throws BadRequestException { + if (StringUtils.isBlank(customer.getPassword())) { + throw new BadRequestException("账号密码不能为空"); + } + customer.setPassword(BCrypt.hashpw(customer.getPassword())); + customerService.addCustomer(customer); + return ResponseVO.success("新增客户成功!!!"); + } + + + // @Log("修改客户") + @Operation(summary = "修改客户") + @PutMapping(value = "updateCustomer") + public ResponseVO updateCustomer(@RequestBody Customer resources) throws Exception { + customerService.updateCustomer(resources); + return ResponseVO.success(null); + } + + + // @Log("删除客户") + @Operation(summary = "删除客户") + @DeleteMapping(value = "/deleteCustomer") + public ResponseVO deleteCustomer(@RequestBody Set ids) throws BadRequestException { + if (CollectionUtil.isEmpty(ids)) { + throw new BadRequestException("请选择要删除的客户"); + } + customerService.delete(ids); + return ResponseVO.success(null); + } + + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/Customer.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/Customer.java new file mode 100644 index 0000000..9886065 --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/Customer.java @@ -0,0 +1,131 @@ +package com.fuyuanshen.customer.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * 用户对象 sys_user + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_user") +public class Customer extends TenantEntity { + + /** + * 用户ID + */ + @TableId(value = "user_id") + private Long customerId; + + /** + * 父id + */ + private Long pid; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + @NotNull + private String userName; + + /** + * 用户昵称 + */ + @NotNull + private String nickName; + + @TableField(value = "user_level") + private Byte userLevel; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 用户头像 + */ + private Long avatar; + + /** + * 密码 + */ + @TableField( + insertStrategy = FieldStrategy.NOT_EMPTY, + updateStrategy = FieldStrategy.NOT_EMPTY, + whereStrategy = FieldStrategy.NOT_EMPTY + ) + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + @NotNull + @Schema(name = "是否启用") + private Boolean enabled; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + + public Customer(Long userId) { + this.customerId = userId; + } + + public boolean isSuperAdmin() { + return SystemConstants.SUPER_ADMIN_ID.equals(this.customerId); + } + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/query/UserQueryCriteria.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/query/UserQueryCriteria.java new file mode 100644 index 0000000..c454b4a --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/query/UserQueryCriteria.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-2025 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.fuyuanshen.customer.domain.query; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Zheng Jie + * @date 2018-11-23 + */ +@Data +public class UserQueryCriteria extends BaseEntity { + + @Schema(name = "ID") + private Long id; + + @Schema(name = "ids") + private Set ids; + + @Schema(name = "pid") + private Long pid; + + @Schema(name = "多个ID") + private Set deptIds = new HashSet<>(); + + @Schema(name = "模糊查询") + private String blurry; + + @Schema(name = "是否启用") + private Boolean enabled; + + @Schema(name = "部门ID") + private Long deptId; + + // @Schema(name = "创建时间") + // // private List createTime; + + @Schema(name = "页码", example = "1") + private Integer pageNum = 1; + + @Schema(name = "每页数据量", example = "10") + private Integer pageSize = 10; + + @Schema(name = "偏移量", hidden = true) + private long offset; + + /** + * 用户类型 + * 0 app + * 1 小程序 + */ + @Schema(name = "用户类型 0 app 1 小程序") + private Integer userType; + + /** + * 账号 + */ + @Schema(name = "APP/小程序账号") + private String username; + + /** + * 客户名称 + */ + @Schema(name = "客户名称") + private String customerName; + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/vo/ConsumerVo.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/vo/ConsumerVo.java new file mode 100644 index 0000000..d17971c --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/domain/vo/ConsumerVo.java @@ -0,0 +1,89 @@ +package com.fuyuanshen.customer.domain.vo; + + +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author: 默苍璃 + * @date: 2025-06-1211:34 + */ +@Getter +@Setter +public class ConsumerVo extends TenantEntity { + + @Schema(name = "ID", hidden = true) + private Long customerId; + + @Schema(hidden = true) + private Long deptId; + + @Schema(hidden = true) + private Long pid; + + @Schema(hidden = true) + private Byte userLevel; + + @Schema(name = "账号") + private String userName; + + @Schema(name = "用户昵称") + private String nickName; + + @Schema(name = "邮箱") + private String email; + + @Schema(name = "电话号码") + private String phone; + + @Schema(name = "用户性别") + private String gender; + + @Schema(name = "头像真实名称", hidden = true) + private String avatarName; + + @Schema(name = "头像存储的路径", hidden = true) + private String avatarPath; + + @Schema(name = "密码") + private String password; + + @Schema(name = "是否启用") + private Boolean enabled; + + @Schema(name = "是否为admin账号", hidden = true) + private Boolean isAdmin = false; + + @Schema(name = "最后修改密码的时间", hidden = true) + private Date pwdResetTime; + + private List children; + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConsumerVo consumer = (ConsumerVo) o; + return Objects.equals(customerId, consumer.customerId) && Objects.equals(userName, consumer.userName); + } + + + @Override + public int hashCode() { + return Objects.hash(customerId, userName); + } + + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/mapper/CustomerMapper.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/mapper/CustomerMapper.java new file mode 100644 index 0000000..831c46c --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/mapper/CustomerMapper.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.customer.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fuyuanshen.customer.domain.Customer; +import com.fuyuanshen.customer.domain.query.UserQueryCriteria; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author: 默苍璃 + * @date: 2025-07-0114:37 + */ +@Mapper +public interface CustomerMapper extends BaseMapper { + + /** + * 分页查询客户 + * + * @param criteria + * @return + */ + List findCustomers(@Param("criteria") UserQueryCriteria criteria); + + /** + * 获取分页总数 + * + * @param criteria + * @return + */ + Long countCustomers(@Param("criteria") UserQueryCriteria criteria); + + + /** + * 查询所有客户 + * + * @param criteria + * @return + */ + List queryAllCustomers(@Param("criteria") UserQueryCriteria criteria); + + /** + * 根据条件查询客户 + * + * @param criteria + * @return + */ + List queryCustomers(@Param("criteria") UserQueryCriteria criteria); + + /** + * 根据id查询客户 + * + * @param customerId + * @return + */ + Customer queryCustomerById(@Param("customerId") Long customerId, @Param("pid") Long pid); + + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/CustomerService.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/CustomerService.java new file mode 100644 index 0000000..98ccd3a --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/CustomerService.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.customer.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.customer.domain.Customer; +import com.fuyuanshen.customer.domain.query.UserQueryCriteria; +import com.fuyuanshen.customer.domain.vo.ConsumerVo; +import io.undertow.util.BadRequestException; + +import java.util.List; +import java.util.Set; + +/** + * @author: 默苍璃 + * @date: 2025-07-0114:31 + */ +public interface CustomerService { + + /** + * 分页查询客户 + * + * @param criteria + * @param page + * @return + */ + TableDataInfo queryCustomers(UserQueryCriteria criteria, Page page); + + /** + * 查询所有客户 + * + * @param criteria + * @return + */ + List queryAllCustomers(UserQueryCriteria criteria); + + /** + * 新增客户 + * + * @param customer / + */ + void addCustomer(Customer customer) throws BadRequestException; + + /** + * 修改客户 + * + * @param resources / + * @throws Exception / + */ + void updateCustomer(Customer resources) throws Exception; + + /** + * 删除客户 + * + * @param ids / + */ + void delete(Set ids); + + +} diff --git a/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/impl/CustomerServiceImpl.java b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/impl/CustomerServiceImpl.java new file mode 100644 index 0000000..49baa0b --- /dev/null +++ b/fys-modules/fys-customer/src/main/java/com/fuyuanshen/customer/service/impl/CustomerServiceImpl.java @@ -0,0 +1,131 @@ +package com.fuyuanshen.customer.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.customer.constant.ArrayConstants; +import com.fuyuanshen.customer.domain.Customer; +import com.fuyuanshen.customer.domain.query.UserQueryCriteria; +import com.fuyuanshen.customer.domain.vo.ConsumerVo; +import com.fuyuanshen.customer.mapper.CustomerMapper; +import com.fuyuanshen.customer.service.CustomerService; +import com.fuyuanshen.system.domain.SysUserRole; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.mapper.SysRoleMapper; +import com.fuyuanshen.system.mapper.SysUserRoleMapper; +import com.fuyuanshen.system.service.ISysUserService; +import com.fuyuanshen.system.service.impl.SysUserServiceImpl; +import io.undertow.util.BadRequestException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; + +/** + * @author: 默苍璃 + * @date: 2025-07-0114:31 + */ +@Service +@RequiredArgsConstructor +public class CustomerServiceImpl extends ServiceImpl implements CustomerService { + + private final CustomerMapper customerMapper; + private final SysUserServiceImpl userService; + private final SysRoleMapper roleMapper; + private final SysUserRoleMapper userRoleMapper; + + + /** + * 分页查询客户 + * + * @param criteria + * @param page + * @return + */ + @Override + public TableDataInfo queryCustomers(UserQueryCriteria criteria, Page page) { + criteria.setOffset(page.offset()); + criteria.setPid(LoginHelper.getUserId()); + List users = customerMapper.findCustomers(criteria); + List consumerVoList = BeanUtil.copyToList(users, ConsumerVo.class); + Long total = customerMapper.countCustomers(criteria); + // return PageUtil.toPage(consumerVoList, total); + return new TableDataInfo(consumerVoList, total); + } + + + /** + * 查询所有客户 + * + * @param criteria + * @return + */ + @Override + public List queryAllCustomers(UserQueryCriteria criteria) { + criteria.setPid(LoginHelper.getUserId()); + List users = customerMapper.queryAllCustomers(criteria); + return users; + } + + + /** + * 新增客户 + * + * @param customer / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void addCustomer(Customer customer) throws BadRequestException { + + LoginUser loginUser = LoginHelper.getLoginUser(); + + UserQueryCriteria userQueryCriteria = new UserQueryCriteria(); + userQueryCriteria.setCustomerName(customer.getUserName()); + List customers = customerMapper.queryCustomers(userQueryCriteria); + if (!customers.isEmpty()) { + throw new BadRequestException("用户名已存在!!!"); + } + customer.setUserLevel((byte) (loginUser.getUserLevel() + 1)); + customer.setPid(loginUser.getUserId()); + + save(customer); + + // 新增用户与角色管理 + SysUserRole sysUserRole = new SysUserRole(); + sysUserRole.setUserId(customer.getCustomerId()); + sysUserRole.setRoleId(ArrayConstants.DEFAULT_LONG); + userRoleMapper.insert(sysUserRole); + + } + + + /** + * 修改客户 + * + * @param resources / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCustomer(Customer resources) throws Exception { + saveOrUpdate(resources); + } + + + /** + * 删除客户 + * + * @param ids / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Set ids) { + customerMapper.deleteByIds(ids); + } + + +} diff --git a/fys-modules/fys-customer/src/main/resources/application.properties b/fys-modules/fys-customer/src/main/resources/application.properties new file mode 100644 index 0000000..08e63d2 --- /dev/null +++ b/fys-modules/fys-customer/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=fys-customer diff --git a/fys-modules/fys-customer/src/main/resources/mapper/customer/CustomerMapper.xml b/fys-modules/fys-customer/src/main/resources/mapper/customer/CustomerMapper.xml new file mode 100644 index 0000000..885753d --- /dev/null +++ b/fys-modules/fys-customer/src/main/resources/mapper/customer/CustomerMapper.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + u + . + user_user_id + AS user_user_id, + u.user_name, + u.nick_name AS nickName, + u.email, + u.phone, + u.gender, + u.avatar_name AS avatarName, + u.avatar_path AS avatarPath, + u.enabled, + IF(u.is_admin = 1, true, false) AS isAdmin, + u.pwd_reset_time AS pwdResetTime, + u.dept_id AS deptId, + u.tenant_id AS tenantId + + + + u1 + . + user_id + as user_user_id, u1.dept_id as user_dept_id, u1.user_name as user_user_name, + u1.nick_name as user_nick_name, u1.email as user_email, u1.phone as user_phone, + u1.gender as user_gender, u1.avatar_name as user_avatar_name, u1.avatar_path as user_avatar_path, + u1.enabled as user_enabled, u1.pwd_reset_time as user_pwd_reset_time, u1.create_by as user_create_by, + u1.update_by as user_update_by, u1.create_time as user_create_time, u1.update_time as user_update_time, + u1.user_level, u1.pid as user_pid,u1.is_admin AS admin, + d.dept_id as dept_id, d.name as dept_name + + + + j + . + job_id + as job_id, j.name as job_name + + + + r + . + role_id + as role_id, r.name as role_name, r.level as role_level, r.data_scope as role_data_scope + + + + + + and u1.user_id = #{criteria.id} + + + and u1.enabled = #{criteria.enabled} + + + and u1.dept_id in + + #{deptId} + + + + and ( + u1.user_name like concat('%', #{criteria.blurry}, '%') + or u1.nick_name like concat('%', #{criteria.blurry}, '%') + or u1.email like concat('%', #{criteria.blurry}, '%') + ) + + + and u1.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fys-modules/fys-customer/web2/WEB-INF/web.xml b/fys-modules/fys-customer/web2/WEB-INF/web.xml new file mode 100644 index 0000000..d80081d --- /dev/null +++ b/fys-modules/fys-customer/web2/WEB-INF/web.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/fys-modules/fys-demo/pom.xml b/fys-modules/fys-demo/pom.xml new file mode 100644 index 0000000..d53f11b --- /dev/null +++ b/fys-modules/fys-demo/pom.xml @@ -0,0 +1,108 @@ + + + + com.fuyuanshen + fys-modules + ${revision} + + 4.0.0 + + fys-demo + + + demo模块 + + + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-sms + + + + com.fuyuanshen + fys-common-mail + + + + com.fuyuanshen + fys-common-redis + + + + com.fuyuanshen + fys-common-idempotent + + + + com.fuyuanshen + fys-common-mybatis + + + + com.fuyuanshen + fys-common-log + + + + com.fuyuanshen + fys-common-excel + + + + com.fuyuanshen + fys-common-security + + + + com.fuyuanshen + fys-common-web + + + + com.fuyuanshen + fys-common-ratelimiter + + + + com.fuyuanshen + fys-common-translation + + + + com.fuyuanshen + fys-common-sensitive + + + + com.fuyuanshen + fys-common-encrypt + + + + com.fuyuanshen + fys-common-tenant + + + + com.fuyuanshen + fys-common-websocket + + + + + diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/MailController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/MailController.java new file mode 100644 index 0000000..9d6d587 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/MailController.java @@ -0,0 +1,70 @@ +package com.fuyuanshen.demo.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mail.utils.MailUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.util.Arrays; + + +/** + * 邮件发送案例 + * + * @author Michelle.Chung + */ +@SaIgnore +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/mail") +public class MailController { + + /** + * 发送邮件 + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + */ + @GetMapping("/sendSimpleMessage") + public R sendSimpleMessage(String to, String subject, String text) { + MailUtils.sendText(to, subject, text); + return R.ok(); + } + + /** + * 发送邮件(带附件) + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + * @param filePath 附件路径 + */ + @GetMapping("/sendMessageWithAttachment") + public R sendMessageWithAttachment(String to, String subject, String text, String filePath) { + MailUtils.sendText(to, subject, text, new File(filePath)); + return R.ok(); + } + + /** + * 发送邮件(多附件) + * + * @param to 接收人 + * @param subject 标题 + * @param text 内容 + * @param paths 附件路径 + */ + @GetMapping("/sendMessageWithAttachments") + public R sendMessageWithAttachments(String to, String subject, String text, String[] paths) { + File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new); + MailUtils.sendText(to, subject, text, array); + return R.ok(); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisCacheController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisCacheController.java new file mode 100644 index 0000000..2c8a4ba --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisCacheController.java @@ -0,0 +1,92 @@ +package com.fuyuanshen.demo.controller; + +import cn.hutool.core.thread.ThreadUtil; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Duration; + +/** + * spring-cache 演示案例 + * + * @author Lion Li + */ +// 类级别 缓存统一配置 +//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE) +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/cache") +public class RedisCacheController { + + /** + * 测试 @Cacheable + *

+ * 表示这个方法有了缓存的功能,方法的返回值会被缓存下来 + * 下一次调用该方法前,会去检查是否缓存中已经有值 + * 如果有就直接返回,不调用方法 + * 如果没有,就调用方法,然后把结果缓存起来 + * 这个注解「一般用在查询方法上」 + *

+ * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用 + * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @Cacheable(cacheNames = "demo:cache#60s#10m#20#1", key = "#key", condition = "#key != null") + @GetMapping("/test1") + public R test1(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CachePut + *

+ * 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用 + * 它「通常用在新增或者实时更新方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test2") + public R test2(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试 @CacheEvict + *

+ * 使用了CacheEvict注解的方法,会清空指定缓存 + * 「一般用在删除的方法上」 + *

+ * cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数 + */ + @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null") + @GetMapping("/test3") + public R test3(String key, String value) { + return R.ok("操作成功", value); + } + + /** + * 测试设置过期时间 + * 手动设置过期时间10秒 + * 11秒后获取 判断是否相等 + */ + @GetMapping("/test6") + public R test6(String key, String value) { + RedisUtils.setCacheObject(key, value); + boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10)); + System.out.println("***********" + flag); + ThreadUtil.sleep(11 * 1000); + Object obj = RedisUtils.getCacheObject(key); + return R.ok(value.equals(obj)); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisLockController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisLockController.java new file mode 100644 index 0000000..a22f4df --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisLockController.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.demo.controller; + +import cn.hutool.core.thread.ThreadUtil; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.baomidou.lock.annotation.Lock4j; +import com.baomidou.lock.executor.RedissonLockExecutor; +import com.fuyuanshen.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalTime; + + +/** + * 测试分布式锁的样例 + * + * @author shenxinquan + */ +@Slf4j +@RestController +@RequestMapping("/demo/redisLock") +public class RedisLockController { + + @Autowired + private LockTemplate lockTemplate; + + /** + * 测试lock4j 注解 + */ + @Lock4j(keys = {"#key"}) + @GetMapping("/testLock4j") + public R testLock4j(String key, String value) { + System.out.println("start:" + key + ",time:" + LocalTime.now()); + ThreadUtil.sleep(10000); + System.out.println("end :" + key + ",time:" + LocalTime.now()); + return R.ok("操作成功", value); + } + + /** + * 测试lock4j 工具 + */ + @GetMapping("/testLock4jLockTemplate") + public R testLock4jLockTemplate(String key, String value) { + final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class); + if (null == lockInfo) { + throw new RuntimeException("业务处理中,请稍后再试"); + } + // 获取锁成功,处理业务 + try { + ThreadUtil.sleep(8000); + System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName()); + } finally { + //释放锁 + lockTemplate.releaseLock(lockInfo); + } + //结束 + return R.ok("操作成功", value); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisPubSubController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisPubSubController.java new file mode 100644 index 0000000..5a84575 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisPubSubController.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Redis 发布订阅 演示案例 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/redis/pubsub") +public class RedisPubSubController { + + /** + * 发布消息 + * + * @param key 通道Key + * @param value 发送内容 + */ + @GetMapping("/pub") + public R pub(String key, String value) { + RedisUtils.publish(key, value, consumer -> { + System.out.println("发布通道 => " + key + ", 发送值 => " + value); + }); + return R.ok("操作成功"); + } + + /** + * 订阅消息 + * + * @param key 通道Key + */ + @GetMapping("/sub") + public R sub(String key) { + RedisUtils.subscribe(key, String.class, msg -> { + System.out.println("订阅通道 => " + key + ", 接收值 => " + msg); + }); + return R.ok("操作成功"); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisRateLimiterController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisRateLimiterController.java new file mode 100644 index 0000000..f19f2e6 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/RedisRateLimiterController.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.ratelimiter.annotation.RateLimiter; +import com.fuyuanshen.common.ratelimiter.enums.LimitType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 测试分布式限流样例 + * + * @author Lion Li + */ +@Slf4j +@RestController +@RequestMapping("/demo/rateLimiter") +public class RedisRateLimiterController { + + /** + * 测试全局限流 + * 全局影响 + */ + @RateLimiter(count = 2, time = 10) + @GetMapping("/test") + public R test(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试请求IP限流 + * 同一IP请求受影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP) + @GetMapping("/testip") + public R testip(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试集群实例限流 + * 启动两个后端服务互不影响 + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER) + @GetMapping("/testcluster") + public R testcluster(String value) { + return R.ok("操作成功", value); + } + + /** + * 测试请求IP限流(key基于参数获取) + * 同一IP请求受影响 + * + * 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0} + */ + @RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value") + @GetMapping("/testObj") + public R testObj(String value) { + return R.ok("操作成功", value); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/SmsController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/SmsController.java new file mode 100644 index 0000000..44d3fbe --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/SmsController.java @@ -0,0 +1,82 @@ +package com.fuyuanshen.demo.controller; + +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import org.dromara.sms4j.api.SmsBlend; +import org.dromara.sms4j.api.entity.SmsResponse; +import org.dromara.sms4j.core.factory.SmsFactory; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.LinkedHashMap; + +/** + * 短信演示案例 + * 请先阅读文档 否则无法使用 + * + * @author Lion Li + * @version 4.2.0 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/sms") +public class SmsController { + /** + * 发送短信Aliyun + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendAliyun") + public R sendAliyun(String phones, String templateId) { + LinkedHashMap map = new LinkedHashMap<>(1); + map.put("code", "1234"); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); + return R.ok(smsResponse); + } + + /** + * 发送短信Tencent + * + * @param phones 电话号 + * @param templateId 模板ID + */ + @GetMapping("/sendTencent") + public R sendTencent(String phones, String templateId) { + LinkedHashMap map = new LinkedHashMap<>(1); +// map.put("2", "测试测试"); + map.put("1", "1234"); + SmsBlend smsBlend = SmsFactory.getSmsBlend("config2"); + SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); + return R.ok(smsResponse); + } + + /** + * 添加黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/addBlacklist") + public R addBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.joinInBlacklist(phone); + return R.ok(); + } + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + @GetMapping("/removeBlacklist") + public R removeBlacklist(String phone){ + SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); + smsBlend.removeFromBlacklist(phone); + return R.ok(); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/Swagger3DemoController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/Swagger3DemoController.java new file mode 100644 index 0000000..9d4822a --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/Swagger3DemoController.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * swagger3 用法示例 + * + * @author Lion Li + */ +@RestController +@RequestMapping("/swagger/demo") +public class Swagger3DemoController { + + /** + * 上传请求 + * 必须使用 @RequestPart 注解标注为文件 + * + * @param file 文件 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file) { + return R.ok("操作成功", file.getOriginalFilename()); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestBatchController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestBatchController.java new file mode 100644 index 0000000..78a20f5 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestBatchController.java @@ -0,0 +1,90 @@ +package com.fuyuanshen.demo.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.demo.domain.TestDemo; +import com.fuyuanshen.demo.mapper.TestDemoMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 测试批量方法 + * + * @author Lion Li + * @date 2021-05-30 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/batch") +public class TestBatchController extends BaseController { + + /** + * 为了便于测试 直接引入mapper + */ + private final TestDemoMapper testDemoMapper; + + /** + * 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大) + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/add") +// @DS("slave") + public R add() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + return toAjax(testDemoMapper.insertBatch(list)); + } + + /** + * 新增或更新 可完美替代 saveOrUpdateBatch 高性能 + *

+ * 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度 + */ + @PostMapping("/addOrUpdate") +// @DS("slave") + public R addOrUpdate() { + List list = new ArrayList<>(); + for (int i = 0; i < 1000; i++) { + TestDemo testDemo = new TestDemo(); + testDemo.setOrderNum(-1); + testDemo.setTestKey("批量新增"); + testDemo.setValue("测试新增"); + list.add(testDemo); + } + testDemoMapper.insertBatch(list); + for (int i = 0; i < list.size(); i++) { + TestDemo testDemo = list.get(i); + testDemo.setTestKey("批量新增或修改"); + testDemo.setValue("批量新增或修改"); + if (i % 2 == 0) { + testDemo.setId(null); + } + } + return toAjax(testDemoMapper.insertOrUpdateBatch(list)); + } + + /** + * 删除批量方法 + */ + @DeleteMapping() +// @DS("slave") + public R remove() { + return toAjax(testDemoMapper.delete(new LambdaQueryWrapper() + .eq(TestDemo::getOrderNum, -1L))); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestDemoController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestDemoController.java new file mode 100644 index 0000000..3ab2f76 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestDemoController.java @@ -0,0 +1,147 @@ +package com.fuyuanshen.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.core.validate.QueryGroup; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.excel.core.ExcelResult; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.demo.domain.TestDemo; +import com.fuyuanshen.demo.domain.bo.TestDemoBo; +import com.fuyuanshen.demo.domain.bo.TestDemoImportVo; +import com.fuyuanshen.demo.domain.vo.TestDemoVo; +import com.fuyuanshen.demo.service.ITestDemoService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 测试单表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/demo") +public class TestDemoController extends BaseController { + + private final ITestDemoService testDemoService; + + /** + * 查询测试单表列表 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return testDemoService.queryPageList(bo, pageQuery); + } + + /** + * 自定义分页查询 + */ + @SaCheckPermission("demo:demo:list") + @GetMapping("/page") + public TableDataInfo page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) { + return testDemoService.customPageList(bo, pageQuery); + } + + /** + * 导入数据 + * + * @param file 导入文件 + */ + @Log(title = "测试单表", businessType = BusinessType.IMPORT) + @SaCheckPermission("demo:demo:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file) throws Exception { + ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true); + List list = MapstructUtils.convert(excelResult.getList(), TestDemo.class); + testDemoService.saveBatch(list); + return R.ok(excelResult.getAnalysis()); + } + + /** + * 导出测试单表列表 + */ + @SaCheckPermission("demo:demo:export") + @Log(title = "测试单表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(@Validated TestDemoBo bo, HttpServletResponse response) { + List list = testDemoService.queryList(bo); + // 测试雪花id导出 +// for (TestDemoVo vo : list) { +// vo.setId(1234567891234567893L); +// } + ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response); + } + + /** + * 获取测试单表详细信息 + * + * @param id 测试ID + */ + @SaCheckPermission("demo:demo:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(testDemoService.queryById(id)); + } + + /** + * 新增测试单表 + */ + @SaCheckPermission("demo:demo:add") + @Log(title = "测试单表", businessType = BusinessType.INSERT) + @RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}") + @PostMapping() + public R add(@RequestBody TestDemoBo bo) { + // 使用校验工具对标 @Validated(AddGroup.class) 注解 + // 用于在非 Controller 的地方校验对象 + ValidatorUtils.validate(bo, AddGroup.class); + return toAjax(testDemoService.insertByBo(bo)); + } + + /** + * 修改测试单表 + */ + @SaCheckPermission("demo:demo:edit") + @Log(title = "测试单表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) { + return toAjax(testDemoService.updateByBo(bo)); + } + + /** + * 删除测试单表 + * + * @param ids 测试ID串 + */ + @SaCheckPermission("demo:demo:remove") + @Log(title = "测试单表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestEncryptController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestEncryptController.java new file mode 100644 index 0000000..98586a6 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestEncryptController.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.demo.domain.TestDemoEncrypt; +import com.fuyuanshen.demo.mapper.TestDemoEncryptMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + + +/** + * 测试数据库加解密功能 + * + * @author Lion Li + */ +@Validated +@RestController +@RequestMapping("/demo/encrypt") +public class TestEncryptController { + + @Autowired + private TestDemoEncryptMapper mapper; + @Value("${mybatis-encryptor.enable}") + private Boolean encryptEnable; + + /** + * 测试数据库加解密 + * + * @param key 测试key + * @param value 测试value + */ + @GetMapping() + public R> test(String key, String value) { + if (!encryptEnable) { + throw new RuntimeException("加密功能未开启!"); + } + Map map = new HashMap<>(2); + TestDemoEncrypt demo = new TestDemoEncrypt(); + demo.setTestKey(key); + demo.setValue(value); + mapper.insert(demo); + map.put("加密", demo); + TestDemoEncrypt testDemo = mapper.selectById(demo.getId()); + map.put("解密", testDemo); + return R.ok(map); + } + + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestExcelController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestExcelController.java new file mode 100644 index 0000000..03bfdd0 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestExcelController.java @@ -0,0 +1,160 @@ +package com.fuyuanshen.demo.controller; + +import cn.hutool.core.collection.CollUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.excel.core.ExcelResult; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.demo.domain.vo.ExportDemoVo; +import com.fuyuanshen.demo.listener.ExportDemoListener; +import com.fuyuanshen.demo.service.IExportExcelService; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 测试Excel功能 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/excel") +public class TestExcelController { + + private final IExportExcelService exportExcelService; + + /** + * 单列表多数据 + */ + @GetMapping("/exportTemplateOne") + public void exportTemplateOne(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title", "单列表多数据"); + map.put("test1", "数据测试1"); + map.put("test2", "数据测试2"); + map.put("test3", "数据测试3"); + map.put("test4", "数据测试4"); + map.put("testTest", "666"); + List list = new ArrayList<>(); + list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4")); + list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8")); + list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12")); + ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response); + } + + /** + * 多列表多数据 + */ + @GetMapping("/exportTemplateMuliti") + public void exportTemplateMuliti(HttpServletResponse response) { + Map map = new HashMap<>(); + map.put("title1", "标题1"); + map.put("title2", "标题2"); + map.put("title3", "标题3"); + map.put("title4", "标题4"); + map.put("author", "Lion Li"); + List list1 = new ArrayList<>(); + list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3")); + list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6")); + list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9")); + List list2 = new ArrayList<>(); + list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3")); + list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6")); + List list3 = new ArrayList<>(); + list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3")); + List list4 = new ArrayList<>(); + list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3")); + list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6")); + list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9")); + list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12")); + Map multiListMap = new HashMap<>(); + multiListMap.put("map", map); + multiListMap.put("data1", list1); + multiListMap.put("data2", list2); + multiListMap.put("data3", list3); + multiListMap.put("data4", list4); + ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); + } + + /** + * 导出下拉框 + * + * @param response / + */ + @GetMapping("/exportWithOptions") + public void exportWithOptions(HttpServletResponse response) { + exportExcelService.exportWithOptions(response); + } + + /** + * 多个sheet导出 + */ + @GetMapping("/exportTemplateMultiSheet") + public void exportTemplateMultiSheet(HttpServletResponse response) { + List list1 = new ArrayList<>(); + list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3")); + list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6")); + List list2 = new ArrayList<>(); + list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3")); + list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6")); + List list3 = new ArrayList<>(); + list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3")); + list3.add(new TestObj1("list3测试4", "list3测试5", "list3测试6")); + List list4 = new ArrayList<>(); + list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3")); + list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6")); + + List> list = new ArrayList<>(); + Map sheetMap1 = new HashMap<>(); + sheetMap1.put("data1", list1); + Map sheetMap2 = new HashMap<>(); + sheetMap2.put("data2", list2); + Map sheetMap3 = new HashMap<>(); + sheetMap3.put("data3", list3); + Map sheetMap4 = new HashMap<>(); + sheetMap4.put("data4", list4); + + list.add(sheetMap1); + list.add(sheetMap2); + list.add(sheetMap3); + list.add(sheetMap4); + ExcelUtil.exportTemplateMultiSheet(list, "多sheet列表", "excel/多sheet列表.xlsx", response); + } + + /** + * 导入表格 + */ + @PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public List importWithOptions(@RequestPart("file") MultipartFile file) throws Exception { + // 处理解析结果 + ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), ExportDemoVo.class, new ExportDemoListener()); + return excelResult.getList(); + } + + @Data + @AllArgsConstructor + static class TestObj1 { + private String test1; + private String test2; + private String test3; + } + + @Data + @AllArgsConstructor + static class TestObj { + private String name; + private String list1; + private String list2; + private String list3; + private String list4; + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestI18nController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestI18nController.java new file mode 100644 index 0000000..548c974 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestI18nController.java @@ -0,0 +1,71 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.MessageUtils; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + + +/** + * 测试国际化 + * + * @author Lion Li + */ +@Validated +@RestController +@RequestMapping("/demo/i18n") +public class TestI18nController { + + /** + * 通过code获取国际化内容 + * code为 messages.properties 中的 key + *

+ * 测试使用 user.register.success + * + * @param code 国际化code + */ + @GetMapping() + public R get(String code) { + return R.ok(MessageUtils.message(code)); + } + + /** + * Validator 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test1") + public R test1(@NotBlank(message = "{not.null}") String str) { + return R.ok(str); + } + + /** + * Bean 校验国际化 + * 不传值 分别查看异常返回 + *

+ * 测试使用 not.null + */ + @GetMapping("/test2") + public R test2(@Validated TestI18nBo bo) { + return R.ok(bo); + } + + @Data + public static class TestI18nBo { + + @NotBlank(message = "{not.null}") + private String name; + + @NotNull(message = "{not.null}") + @Range(min = 0, max = 100, message = "{length.not.valid}") + private Integer age; + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestSensitiveController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestSensitiveController.java new file mode 100644 index 0000000..398c6a3 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestSensitiveController.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.sensitive.annotation.Sensitive; +import com.fuyuanshen.common.sensitive.core.SensitiveStrategy; +import lombok.Data; +import com.fuyuanshen.common.sensitive.core.SensitiveService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 测试数据脱敏控制器 + *

+ * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + * @see SensitiveService + */ +@RestController +@RequestMapping("/demo/sensitive") +public class TestSensitiveController extends BaseController { + + /** + * 测试数据脱敏 + */ + @GetMapping("/test") + public R test() { + TestSensitive testSensitive = new TestSensitive(); + testSensitive.setIdCard("210397198608215431"); + testSensitive.setPhone("17640125371"); + testSensitive.setAddress("北京市朝阳区某某四合院1203室"); + testSensitive.setEmail("17640125371@163.com"); + testSensitive.setBankCard("6226456952351452853"); + return R.ok(testSensitive); + } + + @Data + static class TestSensitive { + + /** + * 身份证 + */ + @Sensitive(strategy = SensitiveStrategy.ID_CARD) + private String idCard; + + /** + * 电话 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE, roleKey = "common") + private String phone; + + /** + * 地址 + */ + @Sensitive(strategy = SensitiveStrategy.ADDRESS, perms = "system:user:query") + private String address; + + /** + * 邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL, roleKey = "common", perms = "system:user:query1") + private String email; + + /** + * 银行卡 + */ + @Sensitive(strategy = SensitiveStrategy.BANK_CARD, roleKey = "common1", perms = "system:user:query") + private String bankCard; + + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestTreeController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestTreeController.java new file mode 100644 index 0000000..f9ed78a --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/TestTreeController.java @@ -0,0 +1,107 @@ +package com.fuyuanshen.demo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.core.validate.QueryGroup; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.demo.domain.bo.TestTreeBo; +import com.fuyuanshen.demo.domain.vo.TestTreeVo; +import com.fuyuanshen.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +/** + * 测试树表Controller + * + * @author Lion Li + * @date 2021-07-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/tree") +public class TestTreeController extends BaseController { + + private final ITestTreeService testTreeService; + + /** + * 查询测试树表列表 + */ + @SaCheckPermission("demo:tree:list") + @GetMapping("/list") + public R> list(@Validated(QueryGroup.class) TestTreeBo bo) { + List list = testTreeService.queryList(bo); + return R.ok(list); + } + + /** + * 导出测试树表列表 + */ + @SaCheckPermission("demo:tree:export") + @Log(title = "测试树表", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(@Validated TestTreeBo bo, HttpServletResponse response) { + List list = testTreeService.queryList(bo); + ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response); + } + + /** + * 获取测试树表详细信息 + * + * @param id 测试树ID + */ + @SaCheckPermission("demo:tree:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable("id") Long id) { + return R.ok(testTreeService.queryById(id)); + } + + /** + * 新增测试树表 + */ + @SaCheckPermission("demo:tree:add") + @Log(title = "测试树表", businessType = BusinessType.INSERT) + @RepeatSubmit + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(testTreeService.insertByBo(bo)); + } + + /** + * 修改测试树表 + */ + @SaCheckPermission("demo:tree:edit") + @Log(title = "测试树表", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) { + return toAjax(testTreeService.updateByBo(bo)); + } + + /** + * 删除测试树表 + * + * @param ids 测试树ID串 + */ + @SaCheckPermission("demo:tree:remove") + @Log(title = "测试树表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true)); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/WebSocketController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/WebSocketController.java new file mode 100644 index 0000000..41ee45f --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/WebSocketController.java @@ -0,0 +1,33 @@ +package com.fuyuanshen.demo.controller; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.websocket.dto.WebSocketMessageDto; +import com.fuyuanshen.common.websocket.utils.WebSocketUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * WebSocket 演示案例 + * + * @author zendwang + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/websocket") +@Slf4j +public class WebSocketController { + + /** + * 发布消息 + * + * @param dto 发送内容 + */ + @GetMapping("/send") + public R send(WebSocketMessageDto dto) throws InterruptedException { + WebSocketUtils.publishMessage(dto); + return R.ok("操作成功"); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/package-info.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/package-info.java new file mode 100644 index 0000000..43ddfa6 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.demo.controller; diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/BoundedQueueController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/BoundedQueueController.java new file mode 100644 index 0000000..de73464 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/BoundedQueueController.java @@ -0,0 +1,90 @@ +package com.fuyuanshen.demo.controller.queue; + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.redis.utils.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 有界队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/bounded") +public class BoundedQueueController { + + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param capacity 容量 + */ + @GetMapping("/add") + public R add(String queueName, int capacity) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyBoundedQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + // 初始化设置一次即可 + if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) { + log.info("通道: {} , 设置容量: {}", queueName, capacity); + } else { + log.info("通道: {} , 设置容量失败", queueName); + return R.fail("操作失败"); + } + for (int i = 0; i < 11; i++) { + String data = "data-" + i; + boolean flag = QueueUtils.addBoundedQueueObject(queueName, data); + if (flag == false) { + log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/remove") + public R remove(String queueName) { + String data = "data-" + 5; + if (QueueUtils.removeBoundedQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + String data; + do { + data = QueueUtils.getBoundedQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/DelayedQueueController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/DelayedQueueController.java new file mode 100644 index 0000000..d1e0d0d --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/DelayedQueueController.java @@ -0,0 +1,97 @@ +package com.fuyuanshen.demo.controller.queue; + +import cn.dev33.satoken.annotation.SaIgnore; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.redis.utils.QueueUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * 延迟队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + * 例如: 创建订单30分钟后过期处理 + *

+ * 集群测试通过 同一个数据只会被消费一次 做好事务补偿 + * 集群测试流程 两台集群分别开启订阅 在其中一台发送数据 观察接收消息的规律 + * + * @author Lion Li + * @version 3.6.0 + */ +@SaIgnore +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/delayed") +public class DelayedQueueController { + + /** + * 订阅队列 + * + * @param queueName 队列名 + */ + @GetMapping("/subscribe") + public R subscribe(String queueName) { + log.info("通道: {} 监听中......", queueName); + // 项目初始化设置一次即可 + QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> { + // 观察接收时间 + log.info("通道: {}, 收到数据: {}", queueName, orderNum); + return CompletableFuture.runAsync(() -> { + // 异步处理数据逻辑 不要在上方处理业务逻辑 + log.info("数据处理: {}", orderNum); + }); + }, true); + return R.ok("操作成功"); + } + + /** + * 添加队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + * @param time 延迟时间(秒) + */ + @GetMapping("/add") + public R add(String queueName, String orderNum, Long time) { + QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS); + // 观察发送时间 + log.info("通道: {} , 发送数据: {}", queueName, orderNum); + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param orderNum 订单号 + */ + @GetMapping("/remove") + public R remove(String queueName, String orderNum) { + if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) { + log.info("通道: {} , 删除数据: {}", queueName, orderNum); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 销毁队列 + * + * @param queueName 队列名 + */ + @GetMapping("/destroy") + public R destroy(String queueName) { + // 用完了一定要销毁 否则会一直存在 + QueueUtils.destroyDelayedQueue(queueName); + return R.ok("操作成功"); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityDemo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityDemo.java new file mode 100644 index 0000000..fe54399 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityDemo.java @@ -0,0 +1,22 @@ +package com.fuyuanshen.demo.controller.queue; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 实体类 注意不允许使用内部类 否则会找不到类 + * + * @author Lion Li + * @version 3.6.0 + */ +@Data +@NoArgsConstructor +public class PriorityDemo implements Comparable { + private String name; + private Integer orderNum; + + @Override + public int compareTo(PriorityDemo other) { + return Integer.compare(getOrderNum(), other.getOrderNum()); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityQueueController.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityQueueController.java new file mode 100644 index 0000000..ac36e9c --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/controller/queue/PriorityQueueController.java @@ -0,0 +1,89 @@ +package com.fuyuanshen.demo.controller.queue; + +import cn.hutool.core.util.RandomUtil; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.redis.utils.QueueUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 优先队列 演示案例 + *

+ * 轻量级队列 重量级数据量 请使用 MQ + *

+ * 集群测试通过 同一个消息只会被消费一次 做好事务补偿 + * 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条 + * + * @author Lion Li + * @version 3.6.0 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/queue/priority") +public class PriorityQueueController { + + /** + * 添加队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/add") + public R add(String queueName) { + // 用完了一定要销毁 否则会一直存在 + boolean b = QueueUtils.destroyPriorityQueue(queueName); + log.info("通道: {} , 删除: {}", queueName, b); + + for (int i = 0; i < 10; i++) { + int randomNum = RandomUtil.randomInt(10); + PriorityDemo data = new PriorityDemo(); + data.setName("data-" + i); + data.setOrderNum(randomNum); + if (QueueUtils.addPriorityQueueObject(queueName, data)) { + log.info("通道: {} , 发送数据: {}", queueName, data); + } else { + log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data); + } + } + return R.ok("操作成功"); + } + + /** + * 删除队列数据 + * + * @param queueName 队列名 + * @param name 对象名 + * @param orderNum 排序号 + */ + @GetMapping("/remove") + public R remove(String queueName, String name, Integer orderNum) { + PriorityDemo data = new PriorityDemo(); + data.setName(name); + data.setOrderNum(orderNum); + if (QueueUtils.removePriorityQueueObject(queueName, data)) { + log.info("通道: {} , 删除数据: {}", queueName, data); + } else { + return R.fail("操作失败"); + } + return R.ok("操作成功"); + } + + /** + * 获取队列数据 + * + * @param queueName 队列名 + */ + @GetMapping("/get") + public R get(String queueName) { + PriorityDemo data; + do { + data = QueueUtils.getPriorityQueueObject(queueName); + log.info("通道: {} , 获取数据: {}", queueName, data); + } while (data != null); + return R.ok("操作成功"); + } + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemo.java new file mode 100644 index 0000000..a88187e --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemo.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.demo.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 测试单表对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_demo") +public class TestDemo extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 排序号 + */ + @OrderBy(asc = false, sort = 1) + private Integer orderNum; + + /** + * key键 + */ + private String testKey; + + /** + * 值 + */ + private String value; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemoEncrypt.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemoEncrypt.java new file mode 100644 index 0000000..9c984e1 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestDemoEncrypt.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.demo.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.encrypt.annotation.EncryptField; +import com.fuyuanshen.common.encrypt.enumd.AlgorithmType; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_demo") +public class TestDemoEncrypt extends TestDemo { + + /** + * key键 + */ + // @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==") + @EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB") + private String testKey; + + /** + * 值 + */ + // @EncryptField // 什么也不写走默认yml配置 + // @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5") + @EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5") + private String value; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestTree.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestTree.java new file mode 100644 index 0000000..d08585e --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/TestTree.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.demo.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.Version; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 测试树表对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_tree") +public class TestTree extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 用户id + */ + private Long userId; + + /** + * 树节点名 + */ + private String treeName; + + /** + * 版本 + */ + @Version + private Long version; + + /** + * 删除标志 + */ + @TableLogic + private Long delFlag; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoBo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoBo.java new file mode 100644 index 0000000..4f91d81 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoBo.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.demo.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.demo.domain.TestDemo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestDemo.class, reverseConvertGenerate = false) +public class TestDemoBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Integer orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class}) + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class}) + private String value; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoImportVo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoImportVo.java new file mode 100644 index 0000000..7779700 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestDemoImportVo.java @@ -0,0 +1,53 @@ +package com.fuyuanshen.demo.domain.bo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * 测试单表业务对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +public class TestDemoImportVo { + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空") + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空") + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @NotNull(message = "排序号不能为空") + @ExcelProperty(value = "排序号") + private Long orderNum; + + /** + * key键 + */ + @NotBlank(message = "key键不能为空") + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @NotBlank(message = "值不能为空") + @ExcelProperty(value = "值") + private String value; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestTreeBo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestTreeBo.java new file mode 100644 index 0000000..6c9e4bb --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/bo/TestTreeBo.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.demo.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.demo.domain.TestTree; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 测试树表业务对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestTree.class, reverseConvertGenerate = false) +public class TestTreeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 部门id + */ + @NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 用户id + */ + @NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 树节点名 + */ + @NotBlank(message = "树节点名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String treeName; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/package-info.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/package-info.java new file mode 100644 index 0000000..2a95519 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.demo.domain; diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/ExportDemoVo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/ExportDemoVo.java new file mode 100644 index 0000000..89ffcea --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/ExportDemoVo.java @@ -0,0 +1,122 @@ +package com.fuyuanshen.demo.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.enums.UserStatus; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.annotation.ExcelEnumFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.common.excel.convert.ExcelEnumConvert; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 带有下拉选的Excel导出 + * + * @author Emil.Zhang + */ +@Data +@ExcelIgnoreUnannotated +@AllArgsConstructor +@NoArgsConstructor +public class ExportDemoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名", index = 0) + @NotEmpty(message = "用户名不能为空", groups = AddGroup.class) + private String nickName; + + /** + * 用户类型 + *

+ * 使用ExcelEnumFormat注解需要进行下拉选的部分 + */ + @ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class) + @ExcelEnumFormat(enumClass = UserStatus.class, textField = "info") + @NotEmpty(message = "用户类型不能为空", groups = AddGroup.class) + private String userStatus; + + /** + * 性别 + *

+ * 使用ExcelDictFormat注解需要进行下拉选的部分 + */ + @ExcelProperty(value = "性别", index = 2, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + @NotEmpty(message = "性别不能为空", groups = AddGroup.class) + private String gender; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号", index = 3) + @NotEmpty(message = "手机号不能为空", groups = AddGroup.class) + private String phoneNumber; + + /** + * Email + */ + @ExcelProperty(value = "Email", index = 4) + @NotEmpty(message = "Email不能为空", groups = AddGroup.class) + private String email; + + /** + * 省 + *

+ * 级联下拉,仅判断是否选了 + */ + @ExcelProperty(value = "省", index = 5) + @NotNull(message = "省不能为空", groups = AddGroup.class) + private String province; + + /** + * 数据库中的省ID + *

+ * 处理完毕后再判断是否市正确的值 + */ + @NotNull(message = "请勿手动输入", groups = EditGroup.class) + private Integer provinceId; + + /** + * 市 + *

+ * 级联下拉 + */ + @ExcelProperty(value = "市", index = 6) + @NotNull(message = "市不能为空", groups = AddGroup.class) + private String city; + + /** + * 数据库中的市ID + */ + @NotNull(message = "请勿手动输入", groups = EditGroup.class) + private Integer cityId; + + /** + * 县 + *

+ * 级联下拉 + */ + @ExcelProperty(value = "县", index = 7) + @NotNull(message = "县不能为空", groups = AddGroup.class) + private String area; + + /** + * 数据库中的县ID + */ + @NotNull(message = "请勿手动输入", groups = EditGroup.class) + private Integer areaId; +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestDemoVo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestDemoVo.java new file mode 100644 index 0000000..eb2d23e --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestDemoVo.java @@ -0,0 +1,111 @@ +package com.fuyuanshen.demo.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelNotation; +import com.fuyuanshen.common.excel.annotation.ExcelRequired; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.demo.domain.TestDemo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 测试单表视图对象 test_demo + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestDemo.class) +public class TestDemoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 部门id + */ + @ExcelRequired + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelRequired + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 排序号 + */ + @ExcelRequired + @ExcelProperty(value = "排序号") + private Integer orderNum; + + /** + * key键 + */ + @ExcelNotation(value = "测试key") + @ExcelProperty(value = "key键") + private String testKey; + + /** + * 值 + */ + @ExcelNotation(value = "测试value") + @ExcelProperty(value = "值") + private String value; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 创建人 + */ + @ExcelProperty(value = "创建人") + private Long createBy; + + /** + * 创建人账号 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + @ExcelProperty(value = "创建人账号") + private String createByName; + + /** + * 更新时间 + */ + @ExcelProperty(value = "更新时间") + private Date updateTime; + + /** + * 更新人 + */ + @ExcelProperty(value = "更新人") + private Long updateBy; + + /** + * 更新人账号 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "updateBy") + @ExcelProperty(value = "更新人账号") + private String updateByName; + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestTreeVo.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestTreeVo.java new file mode 100644 index 0000000..6f8a65b --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/domain/vo/TestTreeVo.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.demo.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.demo.domain.TestTree; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 测试树表视图对象 test_tree + * + * @author Lion Li + * @date 2021-07-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestTree.class) +public class TestTreeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 父id + */ + @ExcelProperty(value = "父id") + private Long parentId; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 用户id + */ + @ExcelProperty(value = "用户id") + private Long userId; + + /** + * 树节点名 + */ + @ExcelProperty(value = "树节点名") + private String treeName; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/listener/ExportDemoListener.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/listener/ExportDemoListener.java new file mode 100644 index 0000000..593441d --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/listener/ExportDemoListener.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.demo.listener; + +import cn.hutool.core.util.NumberUtil; +import cn.idev.excel.context.AnalysisContext; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.excel.core.DefaultExcelListener; +import com.fuyuanshen.common.excel.core.DropDownOptions; +import com.fuyuanshen.demo.domain.vo.ExportDemoVo; + +import java.util.List; + +/** + * Excel带下拉框的解析处理器 + * + * @author Emil.Zhang + */ +public class ExportDemoListener extends DefaultExcelListener { + + public ExportDemoListener() { + // 显示使用构造函数,否则将导致空指针 + super(true); + } + + @Override + public void invoke(ExportDemoVo data, AnalysisContext context) { + // 先校验必填 + ValidatorUtils.validate(data, AddGroup.class); + + // 处理级联下拉的部分 + String province = data.getProvince(); + String city = data.getCity(); + String area = data.getArea(); + // 本行用户选择的省 + List thisRowSelectedProvinceOption = DropDownOptions.analyzeOptionValue(province); + if (thisRowSelectedProvinceOption.size() == 2) { + String provinceIdStr = thisRowSelectedProvinceOption.get(1); + if (NumberUtil.isNumber(provinceIdStr)) { + // 严格要求数据的话可以在这里做与数据库相关的判断 + // 例如判断省信息是否在数据库中存在等,建议结合RedisCache做缓存10s,减少数据库调用 + data.setProvinceId(Integer.parseInt(provinceIdStr)); + } + } + // 本行用户选择的市 + List thisRowSelectedCityOption = DropDownOptions.analyzeOptionValue(city); + if (thisRowSelectedCityOption.size() == 2) { + String cityIdStr = thisRowSelectedCityOption.get(1); + if (NumberUtil.isNumber(cityIdStr)) { + data.setCityId(Integer.parseInt(cityIdStr)); + } + } + // 本行用户选择的县 + List thisRowSelectedAreaOption = DropDownOptions.analyzeOptionValue(area); + if (thisRowSelectedAreaOption.size() == 2) { + String areaIdStr = thisRowSelectedAreaOption.get(1); + if (NumberUtil.isNumber(areaIdStr)) { + data.setAreaId(Integer.parseInt(areaIdStr)); + } + } + + // 处理完毕以后判断是否符合规则 + ValidatorUtils.validate(data, EditGroup.class); + + // 添加到处理结果中 + getExcelResult().getList().add(data); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoEncryptMapper.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoEncryptMapper.java new file mode 100644 index 0000000..1a52eb0 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoEncryptMapper.java @@ -0,0 +1,13 @@ +package com.fuyuanshen.demo.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.demo.domain.TestDemoEncrypt; + +/** + * 测试加密功能 + * + * @author Lion Li + */ +public interface TestDemoEncryptMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoMapper.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoMapper.java new file mode 100644 index 0000000..bdbed54 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestDemoMapper.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.demo.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.demo.domain.TestDemo; +import com.fuyuanshen.demo.domain.vo.TestDemoVo; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface TestDemoMapper extends BaseMapperPlus { + + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + Page customPageList(@Param("page") Page page, @Param("ew") Wrapper wrapper); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + default

> P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + @Override + @DataPermission(value = { + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }, joinStr = "AND") + List selectByIds(@Param(Constants.COLL) Collection idList); + + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int updateById(@Param(Constants.ENTITY) TestDemo entity); + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestTreeMapper.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestTreeMapper.java new file mode 100644 index 0000000..444c7cc --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/TestTreeMapper.java @@ -0,0 +1,21 @@ +package com.fuyuanshen.demo.mapper; + +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.demo.domain.TestTree; +import com.fuyuanshen.demo.domain.vo.TestTreeVo; + +/** + * 测试树表Mapper接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +@DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") +}) +public interface TestTreeMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/package-info.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/package-info.java new file mode 100644 index 0000000..268c0fc --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/mapper/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.demo.mapper; diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/IExportExcelService.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/IExportExcelService.java new file mode 100644 index 0000000..7254a84 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/IExportExcelService.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.demo.service; + +import jakarta.servlet.http.HttpServletResponse; + +/** + * 导出下拉框Excel示例 + * + * @author Emil.Zhang + */ +public interface IExportExcelService { + + /** + * 导出下拉框 + * + * @param response / + */ + void exportWithOptions(HttpServletResponse response); +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestDemoService.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestDemoService.java new file mode 100644 index 0000000..8b0e71c --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestDemoService.java @@ -0,0 +1,71 @@ +package com.fuyuanshen.demo.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.demo.domain.TestDemo; +import com.fuyuanshen.demo.domain.bo.TestDemoBo; +import com.fuyuanshen.demo.domain.vo.TestDemoVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试单表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestDemoService { + + /** + * 查询单个 + * + * @return + */ + TestDemoVo queryById(Long id); + + /** + * 查询列表 + */ + TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 自定义分页查询 + */ + TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery); + + /** + * 查询列表 + */ + List queryList(TestDemoBo bo); + + /** + * 根据新增业务对象插入测试单表 + * + * @param bo 测试单表新增业务对象 + * @return + */ + Boolean insertByBo(TestDemoBo bo); + + /** + * 根据编辑业务对象修改测试单表 + * + * @param bo 测试单表编辑业务对象 + * @return + */ + Boolean updateByBo(TestDemoBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 批量保存 + */ + Boolean saveBatch(List list); +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestTreeService.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestTreeService.java new file mode 100644 index 0000000..00c02ba --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/ITestTreeService.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.demo.service; + +import com.fuyuanshen.demo.domain.bo.TestTreeBo; +import com.fuyuanshen.demo.domain.vo.TestTreeVo; + +import java.util.Collection; +import java.util.List; + +/** + * 测试树表Service接口 + * + * @author Lion Li + * @date 2021-07-26 + */ +public interface ITestTreeService { + /** + * 查询单个 + * + * @return + */ + TestTreeVo queryById(Long id); + + /** + * 查询列表 + */ + List queryList(TestTreeBo bo); + + /** + * 根据新增业务对象插入测试树表 + * + * @param bo 测试树表新增业务对象 + * @return + */ + Boolean insertByBo(TestTreeBo bo); + + /** + * 根据编辑业务对象修改测试树表 + * + * @param bo 测试树表编辑业务对象 + * @return + */ + Boolean updateByBo(TestTreeBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/ExportExcelServiceImpl.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/ExportExcelServiceImpl.java new file mode 100644 index 0000000..28faa99 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/ExportExcelServiceImpl.java @@ -0,0 +1,236 @@ +package com.fuyuanshen.demo.service.impl; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.excel.core.DropDownOptions; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.demo.domain.vo.ExportDemoVo; +import com.fuyuanshen.demo.service.IExportExcelService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 导出下拉框Excel示例 + * + * @author Emil.Zhang + */ +@Service +@RequiredArgsConstructor +public class ExportExcelServiceImpl implements IExportExcelService { + + @Override + public void exportWithOptions(HttpServletResponse response) { + // 创建表格数据,业务中一般通过数据库查询 + List excelDataList = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + // 模拟数据库中的一条数据 + ExportDemoVo everyRowData = new ExportDemoVo(); + everyRowData.setNickName("用户-" + i); + everyRowData.setUserStatus(SystemConstants.NORMAL); + everyRowData.setGender("1"); + everyRowData.setPhoneNumber(String.format("175%08d", i)); + everyRowData.setEmail(String.format("175%08d", i) + "@163.com"); + everyRowData.setProvinceId(i); + everyRowData.setCityId(i); + everyRowData.setAreaId(i); + excelDataList.add(everyRowData); + } + + // 通过@ExcelIgnoreUnannotated配合@ExcelProperty合理显示需要的列 + // 并通过@DropDown注解指定下拉值,或者通过创建ExcelOptions来指定下拉框 + // 使用ExcelOptions时建议指定列index,防止出现下拉列解析不对齐 + + // 首先从数据库中查询下拉框内的可选项 + // 这里模拟查询结果 + List provinceList = getProvinceList(), + cityList = getCityList(provinceList), + areaList = getAreaList(cityList); + int provinceIndex = 5, cityIndex = 6, areaIndex = 7; + + DropDownOptions provinceToCity = DropDownOptions.buildLinkedOptions( + provinceList, + provinceIndex, + cityList, + cityIndex, + DemoCityData::getId, + DemoCityData::getPid, + everyOptions -> DropDownOptions.createOptionValue( + everyOptions.getName(), + everyOptions.getId() + ) + ); + + DropDownOptions cityToArea = DropDownOptions.buildLinkedOptions( + cityList, + cityIndex, + areaList, + areaIndex, + DemoCityData::getId, + DemoCityData::getPid, + everyOptions -> DropDownOptions.createOptionValue( + everyOptions.getName(), + everyOptions.getId() + ) + ); + + // 把所有的下拉框存储 + List options = new ArrayList<>(); + options.add(provinceToCity); + options.add(cityToArea); + + // 到此为止所有的下拉框可选项已全部配置完毕 + + // 接下来需要将Excel中的展示数据转换为对应的下拉选 + List outList = StreamUtils.toList(excelDataList, everyRowData -> { + // 只需要处理没有使用@ExcelDictFormat注解的下拉框 + // 一般来说,可以直接在数据库查询即查询出省市县信息,这里通过模拟操作赋值 + everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId())); + everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId())); + everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId())); + return everyRowData; + }); + + ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options); + } + + private String buildOptions(List cityDataList, Integer id) { + Map> groupByIdMap = + cityDataList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + if (groupByIdMap.containsKey(id)) { + DemoCityData demoCityData = groupByIdMap.get(id).get(0); + return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId()); + } else { + return StrUtil.EMPTY; + } + } + + /** + * 模拟查询数据库操作 + * + * @return / + */ + private List getProvinceList() { + List provinceList = new ArrayList<>(); + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + provinceList.add(new DemoCityData(0, null, "P100000")); + provinceList.add(new DemoCityData(1, null, "P200000")); + provinceList.add(new DemoCityData(2, null, "P300000")); + + return provinceList; + } + + /** + * 模拟查找数据库操作,需要连带查询出省的数据 + * + * @param provinceList 模拟的父省数据 + * @return / + */ + private List getCityList(List provinceList) { + List cityList = new ArrayList<>(); + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + cityList.add(new DemoCityData(0, 0, "C110000")); + cityList.add(new DemoCityData(1, 0, "C120000")); + cityList.add(new DemoCityData(2, 1, "C210000")); + cityList.add(new DemoCityData(3, 1, "C220000")); + cityList.add(new DemoCityData(4, 1, "C230000")); + + selectParentData(provinceList, cityList); + + return cityList; + } + + /** + * 模拟查找数据库操作,需要连带查询出市的数据 + * + * @param cityList 模拟的父市数据 + * @return / + */ + private List getAreaList(List cityList) { + List areaList = new ArrayList<>(); + + int minCount = 500; + int maxCount = 10000; + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) { + areaList.add(new DemoCityData(areaList.size(), 0, String.format("A11%04d", i))); + } + + for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) { + areaList.add(new DemoCityData(areaList.size(), 1, String.format("A12%04d", i))); + } + + for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) { + areaList.add(new DemoCityData(areaList.size(), 2, String.format("A21%04d", i))); + } + + for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) { + areaList.add(new DemoCityData(areaList.size(), 3, String.format("A22%04d", i))); + } + + for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) { + areaList.add(new DemoCityData(areaList.size(), 4, String.format("A23%04d", i))); + } + + selectParentData(cityList, areaList); + + return areaList; + } + + /** + * 模拟数据库的查询父数据操作 + * + * @param parentList / + * @param sonList / + */ + private void selectParentData(List parentList, List sonList) { + Map> parentGroupByIdMap = + parentList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + + sonList.forEach(everySon -> { + if (parentGroupByIdMap.containsKey(everySon.getPid())) { + everySon.setPData(parentGroupByIdMap.get(everySon.getPid()).get(0)); + } + }); + } + + /** + * 模拟的数据库省市县 + */ + @Data + private static class DemoCityData { + /** + * 数据库id字段 + */ + private Integer id; + /** + * 数据库pid字段 + */ + private Integer pid; + /** + * 数据库name字段 + */ + private String name; + /** + * MyBatisPlus连带查询父数据 + */ + private DemoCityData pData; + + public DemoCityData(Integer id, Integer pid, String name) { + this.id = id; + this.pid = pid; + this.name = name; + } + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestDemoServiceImpl.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestDemoServiceImpl.java new file mode 100644 index 0000000..ad9cf14 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestDemoServiceImpl.java @@ -0,0 +1,116 @@ +package com.fuyuanshen.demo.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.demo.domain.TestDemo; +import com.fuyuanshen.demo.domain.bo.TestDemoBo; +import com.fuyuanshen.demo.domain.vo.TestDemoVo; +import com.fuyuanshen.demo.mapper.TestDemoMapper; +import com.fuyuanshen.demo.service.ITestDemoService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试单表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +@RequiredArgsConstructor +@Service +public class TestDemoServiceImpl implements ITestDemoService { + + private final TestDemoMapper baseMapper; + + @Override + public TestDemoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + public TableDataInfo queryPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 自定义分页查询 + */ + @Override + public TableDataInfo customPageList(TestDemoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.customPageList(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(TestDemoBo bo) { + return baseMapper.selectVoList(buildQueryWrapper(bo)); + } + + private LambdaQueryWrapper buildQueryWrapper(TestDemoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey()); + lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + lqw.orderByAsc(TestDemo::getId); + return lqw; + } + + @Override + public Boolean insertByBo(TestDemoBo bo) { + TestDemo add = MapstructUtils.convert(bo, TestDemo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestDemoBo bo) { + TestDemo update = MapstructUtils.convert(bo, TestDemo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestDemo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + List list = baseMapper.selectByIds(ids); + if (list.size() != ids.size()) { + throw new ServiceException("您没有删除权限!"); + } + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Boolean saveBatch(List list) { + return baseMapper.insertBatch(list); + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestTreeServiceImpl.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestTreeServiceImpl.java new file mode 100644 index 0000000..c8a410d --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/TestTreeServiceImpl.java @@ -0,0 +1,88 @@ +package com.fuyuanshen.demo.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.demo.domain.TestTree; +import com.fuyuanshen.demo.domain.bo.TestTreeBo; +import com.fuyuanshen.demo.domain.vo.TestTreeVo; +import com.fuyuanshen.demo.mapper.TestTreeMapper; +import com.fuyuanshen.demo.service.ITestTreeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 测试树表Service业务层处理 + * + * @author Lion Li + * @date 2021-07-26 + */ +// @DS("slave") // 切换从库查询 +@RequiredArgsConstructor +@Service +public class TestTreeServiceImpl implements ITestTreeService { + + private final TestTreeMapper baseMapper; + + @Override + public TestTreeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + // @DS("slave") // 切换从库查询 + @Override + public List queryList(TestTreeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(TestTreeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + lqw.orderByAsc(TestTree::getId); + return lqw; + } + + @Override + public Boolean insertByBo(TestTreeBo bo) { + TestTree add = MapstructUtils.convert(bo, TestTree.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + public Boolean updateByBo(TestTreeBo bo) { + TestTree update = MapstructUtils.convert(bo, TestTree.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + * + * @param entity 实体类数据 + */ + private void validEntityBeforeSave(TestTree entity) { + //TODO 做一些数据校验,如唯一约束 + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/package-info.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/package-info.java new file mode 100644 index 0000000..434bcd2 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/impl/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.demo.service.impl; diff --git a/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/package-info.java b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/package-info.java new file mode 100644 index 0000000..06d5b65 --- /dev/null +++ b/fys-modules/fys-demo/src/main/java/com/fuyuanshen/demo/service/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.demo.service; diff --git a/fys-modules/fys-demo/src/main/resources/excel/单列表.xlsx b/fys-modules/fys-demo/src/main/resources/excel/单列表.xlsx new file mode 100644 index 0000000..0f7347d Binary files /dev/null and b/fys-modules/fys-demo/src/main/resources/excel/单列表.xlsx differ diff --git a/fys-modules/fys-demo/src/main/resources/excel/多sheet列表.xlsx b/fys-modules/fys-demo/src/main/resources/excel/多sheet列表.xlsx new file mode 100644 index 0000000..5277f2e Binary files /dev/null and b/fys-modules/fys-demo/src/main/resources/excel/多sheet列表.xlsx differ diff --git a/fys-modules/fys-demo/src/main/resources/excel/多列表.xlsx b/fys-modules/fys-demo/src/main/resources/excel/多列表.xlsx new file mode 100644 index 0000000..c7d11dc Binary files /dev/null and b/fys-modules/fys-demo/src/main/resources/excel/多列表.xlsx differ diff --git a/fys-modules/fys-demo/src/main/resources/mapper/demo/TestDemoMapper.xml b/fys-modules/fys-demo/src/main/resources/mapper/demo/TestDemoMapper.xml new file mode 100644 index 0000000..932cd43 --- /dev/null +++ b/fys-modules/fys-demo/src/main/resources/mapper/demo/TestDemoMapper.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/fys-modules/fys-demo/src/main/resources/mapper/demo/TestTreeMapper.xml b/fys-modules/fys-demo/src/main/resources/mapper/demo/TestTreeMapper.xml new file mode 100644 index 0000000..841756d --- /dev/null +++ b/fys-modules/fys-demo/src/main/resources/mapper/demo/TestTreeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-demo/src/main/resources/mapper/package-info.md b/fys-modules/fys-demo/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/fys-modules/fys-demo/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/fys-modules/fys-equipment/pom.xml b/fys-modules/fys-equipment/pom.xml new file mode 100644 index 0000000..ee98be4 --- /dev/null +++ b/fys-modules/fys-equipment/pom.xml @@ -0,0 +1,136 @@ + + + + + com.fuyuanshen + fys-modules + ${revision} + + + 4.0.0 + + fys-equipment + + + 富源晟设备管理 + + + + + + + + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-mybatis + + + + com.fuyuanshen + fys-common-translation + + + + com.fuyuanshen + fys-system + + + + + com.fuyuanshen + fys-common-oss + + + + com.fuyuanshen + fys-common-log + + + + + com.fuyuanshen + fys-common-excel + + + + + com.fuyuanshen + fys-common-sms + + + + com.fuyuanshen + fys-common-tenant + + + + com.fuyuanshen + fys-common-security + + + + com.fuyuanshen + fys-common-web + + + + com.fuyuanshen + fys-common-idempotent + + + + com.fuyuanshen + fys-common-sensitive + + + + com.fuyuanshen + fys-common-encrypt + + + + com.fuyuanshen + fys-common-websocket + + + + com.fuyuanshen + fys-common-sse + + + + + com.fuyuanshen + fys-customer + + + + com.alibaba + easyexcel + 3.3.1 + + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.54 + + + + + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/DeviceConstants.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/DeviceConstants.java new file mode 100644 index 0000000..037c74d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/DeviceConstants.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.equipment.constants; + +/** + * @author: 默苍璃 + * @date: 2025-06-0609:42 + */ +public class DeviceConstants { + + /** + * 文件访问路径前缀 + */ + public static final String FILE_ACCESS_PREFIX = "images"; + + /** + * 文件访问路径前缀 + */ + public static final String FILE_ACCESS_ISOLATION = "device"; + + /** + * 错误报告目录 + */ + public static final String ERROR_REPORT_DIR = "error_reports"; + + /** + * 文件访问IP地址 + */ + // public static final String FILE_ACCESS_IP = "http://fuyuanshen.com:81/"; + public static final String FILE_ACCESS_IP = "https://fuyuanshen.com/"; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/ExceptionMessages.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/ExceptionMessages.java new file mode 100644 index 0000000..9811369 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/ExceptionMessages.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.equipment.constants; + +/** + * @author: 默苍璃 + * @date: 2025-06-1109:26 + */ +public class ExceptionMessages { + + /** + * 禁止管理员操作客户数据的提示 + */ + public static final String ADMIN_OPERATION_NOT_ALLOWED = "警告:请不要随意操作客户数据!"; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RedisConstants.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RedisConstants.java new file mode 100644 index 0000000..38a6e49 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RedisConstants.java @@ -0,0 +1,8 @@ +package com.fuyuanshen.equipment.constants; + +public class RedisConstants { + + public static final String APP_FORGOT_PASSWORD_SMS_TOKEN = "app_sms_forgotPassword:"; + + public static final String APP_REGISTER_SMS_TOKEN = "app_sms_register:"; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RoleConstants.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RoleConstants.java new file mode 100644 index 0000000..a46295f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/constants/RoleConstants.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.equipment.constants; + +/** + * @author: 默苍璃 + * @date: 2025-06-0609:42 + */ +public class RoleConstants { + + /** + * 添加客户默认绑定角色 + */ + public static final String ROLE_CUSTOMER = "普通客户"; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceAPPController.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceAPPController.java new file mode 100644 index 0000000..d28d7f2 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceAPPController.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.equipment.controller; + + +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.equipment.domain.bo.UserAppBo; +import com.fuyuanshen.equipment.service.AppUserService; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Slf4j +@Tag(name = "设备管理", description = "设备:设备管理") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/app/device") +public class DeviceAPPController extends BaseController { + + + + + + // + // + // @GetMapping + // @Operation(summary = "WEB端查看APP客户设备绑定") + // public TableDataInfo queryAPPDevice(APPDeviceQueryCriteria criteria) { + // Page page = new Page<>(criteria.getPage(), criteria.getSize()); + // TableDataInfo devices = null; + // devices = appDeviceService.queryAll(page, criteria); + // return devices; + // } + + +} + + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java new file mode 100644 index 0000000..9633007 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceController.java @@ -0,0 +1,204 @@ +package com.fuyuanshen.equipment.controller; + + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.constant.ResponseMessageConstants; +import com.fuyuanshen.common.core.domain.ResponseVO; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.file.FileUtil; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.customer.mapper.CustomerMapper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO; +import com.fuyuanshen.equipment.domain.dto.ImportResult; +import com.fuyuanshen.equipment.domain.form.DeviceForm; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.CustomerVo; +import com.fuyuanshen.equipment.excel.DeviceImportParams; +import com.fuyuanshen.equipment.excel.UploadDeviceDataListener; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.DeviceService; +import com.fuyuanshen.equipment.service.impl.DeviceExportService; +import com.fuyuanshen.system.service.ISysOssService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.undertow.util.BadRequestException; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Slf4j +@Tag(name = "web:设备管理", description = "web:设备管理") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/device") +public class DeviceController { + + private final ISysOssService ossService; + private final DeviceService deviceService; + private final DeviceMapper deviceMapper; + private final CustomerMapper customerMapper; + private final DeviceTypeMapper deviceTypeMapper; + + private final DeviceExportService exportService; + + + @Operation(summary = "分页查询设备列表", security = {@SecurityRequirement(name = "bearer-key")}) + @GetMapping + public TableDataInfo queryDevice(DeviceQueryCriteria criteria) throws IOException { + Page page = new Page<>(criteria.getPageNum(), criteria.getPageSize()); + return deviceService.queryAll(criteria, page); + } + + + // @Log("新增设备") + @Operation(summary = "新增设备") + @PostMapping(value = "/add") + public ResponseVO addDevice(@Validated @ModelAttribute DeviceForm deviceForm) { + try { + deviceService.addDevice(deviceForm); + } catch (Exception e) { + log.error("addDevice error: " + e.getMessage()); + return ResponseVO.fail(e.getMessage()); + } + return ResponseVO.success(null); + } + + + // @Log("修改设备") + @Operation(summary = "修改设备") + @PutMapping(value = "/update") + public ResponseVO updateDevice(@Validated @ModelAttribute DeviceForm deviceForm) { + try { + deviceService.update(deviceForm); + } catch (Exception e) { + e.printStackTrace(); + log.error("updateDevice error: " + e.getMessage()); + return ResponseVO.fail("出错了"); + } + return ResponseVO.success(null); + } + + + @Operation(summary = "设备详情") + @GetMapping(value = "/detail/{id}") + public ResponseVO getDevice(@PathVariable Long id) { + Device device = deviceService.getById(id); + return ResponseVO.success(device); + } + + + // @Log("删除设备") + @Operation(summary = "删除设备") + @DeleteMapping(value = "/delete") + public ResponseVO deleteDevice(@Parameter(name = "传ID数组[]") @RequestBody List ids) { + deviceService.deleteAll(ids); + return ResponseVO.success(ResponseMessageConstants.DELETE_SUCCESS); + } + + + @Operation(summary = "设备定位") + @GetMapping(value = "/locateDevice") + public ResponseVO locateDevice(DeviceQueryCriteria criteria) throws IOException { + List devices = deviceService.queryAllDevices(criteria); + return ResponseVO.success(devices); + } + + + // @Log("分配客户") + @Operation(summary = "分配客户") + @PutMapping(value = "/assignCustomer") + public ResponseVO assignCustomer(@Validated @RequestBody CustomerVo customerVo) { + deviceService.assignCustomer(customerVo); + return ResponseVO.success(null); + } + + + // @Log("撤回设备") + @Operation(summary = "撤回分配设备") + @PostMapping(value = "/withdraw") + public ResponseVO withdrawDevice(@RequestBody List ids) { + try { + deviceService.withdrawDevice(ids); + } catch (Exception e) { + log.error("updateDevice error: " + e.getMessage()); + return ResponseVO.fail("出错了"); + } + return ResponseVO.success(null); + } + + + /** + * @param deviceForm + * @return + * @ModelAttribute 主要用于将请求参数绑定到 Java 对象上,它会从 HTTP 请求的查询参数(Query Parameters) + * 或表单数据(Form Data)中提取值,并自动填充到指定的对象属性中。 + */ + // @Log("解绑设备") + @Operation(summary = "解绑设备") + @PostMapping(value = "/unbind") + public ResponseVO unbindDevice(@Validated @ModelAttribute DeviceForm deviceForm) { + deviceService.unbindDevice(deviceForm); + return ResponseVO.success("解绑成功!!!"); + } + + + @Operation(summary = "导出数据设备") + @GetMapping(value = "/download") + public void exportDevice(HttpServletResponse response, DeviceQueryCriteria criteria) throws IOException { + List devices = deviceService.queryAll(criteria); + exportService.export(devices, response); + } + + + @Operation(summary = "导入设备数据") + @PostMapping(value = "/import", consumes = "multipart/form-data") + public ResponseVO importData(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) throws BadRequestException { + + String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); + if (!("xlsx".equalsIgnoreCase(suffix))) { + throw new BadRequestException("只能上传Excel——xlsx格式文件"); + } + + ImportResult result = new ImportResult(); + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + DeviceImportParams params = DeviceImportParams.builder().ossService(ossService).deviceService(deviceService).tenantId(loginUser.getTenantId()).file(file).filePath("").deviceMapper(deviceMapper).deviceTypeMapper(deviceTypeMapper).userId(loginUser.getUserId()).customerMapper(customerMapper).build(); + // 创建监听器 + UploadDeviceDataListener listener = new UploadDeviceDataListener(params); + // 读取Excel + EasyExcel.read(file.getInputStream(), DeviceExcelImportDTO.class, listener).sheet().doRead(); + // 获取导入结果 + result = listener.getImportResult(); + // 设置响应消息 + String message = String.format("成功导入 %d 条数据,失败 %d 条", result.getSuccessCount(), result.getFailureCount()); + // 返回带有正确泛型的响应 + return ResponseVO.success(message, result); + } catch (Exception e) { + log.error("导入设备数据出错: {}", e.getMessage(), e); + // 在异常情况下,设置默认结果 + String errorMessage = String.format("导入失败: %s。成功 %d 条,失败 %d 条", e.getMessage(), result.getSuccessCount(), result.getFailureCount()); + // 使用新方法确保类型正确 + return ResponseVO.fail(errorMessage, result); + } + } + +} + + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceTypeController.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceTypeController.java new file mode 100644 index 0000000..3e5d8be --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/controller/DeviceTypeController.java @@ -0,0 +1,82 @@ +package com.fuyuanshen.equipment.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.domain.ResponseVO; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.form.DeviceTypeForm; +import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria; +import com.fuyuanshen.equipment.service.DeviceTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/13 + **/ +@Tag(name = "设备类型管理", description = "设备类型管理") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/deviceType") +public class DeviceTypeController { + + private final DeviceTypeService deviceTypeService; + + + @GetMapping + @Operation(summary = "分页查询设备类型") + public TableDataInfo queryDeviceType(DeviceTypeQueryCriteria criteria) { + Page page = new Page<>(criteria.getPageNum(), criteria.getPageSize()); + return deviceTypeService.queryAll(criteria, page); + } + + + @GetMapping(value = "/all") + @Operation(summary = "查询所有设备类型") + public ResponseVO queryDeviceTypes() { + List deviceTypes = deviceTypeService.queryDeviceTypes(); + return ResponseVO.success(deviceTypes); + } + + + // @Log("新增设备类型") + @Operation(summary = "新增设备类型") + @PostMapping(value = "/add") + public ResponseVO createDeviceType(@Validated @RequestBody DeviceType resources) { + deviceTypeService.create(resources); + return ResponseVO.success(null); + } + + + // @Log("修改设备类型") + @Operation(summary = "修改设备类型") + @PutMapping(value = "/update") + public ResponseVO updateDeviceType(@Validated @RequestBody DeviceTypeForm resources) { + deviceTypeService.update(resources); + return ResponseVO.success(null); + } + + + // @Log("删除设备类型") + @Operation(summary = "删除设备类型") + @DeleteMapping(value = "/delete") + public ResponseVO deleteDeviceType(@Parameter(name = "传ID数组[]") @RequestBody List ids) { + deviceTypeService.deleteAll(ids); + return ResponseVO.success("删除成功!!!"); + } + + + @GetMapping(value = "/communicationMode") + @Operation(summary = "获取设备类型通讯方式") + public ResponseVO getCommunicationMode(@Parameter(name = "设备类型ID", required = true) Long id) { + return ResponseVO.success(deviceTypeService.getCommunicationMode(id)); + } + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/IgnoreFailedImageConverter.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/IgnoreFailedImageConverter.java new file mode 100644 index 0000000..650bec9 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/IgnoreFailedImageConverter.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.equipment.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +/** + * @author: 默苍璃 + * @date: 2025-06-0618:56 + */ + +public class IgnoreFailedImageConverter implements Converter { + private static final Logger logger = LoggerFactory.getLogger(IgnoreFailedImageConverter.class); + + @Override + public Class supportJavaTypeKey() { + return URL.class; + } + + // @Override + // public CellDataTypeEnum supportExcelTypeKey() { + // return CellDataTypeEnum.STRING; + // } + + @Override + public WriteCellData convertToExcelData(URL value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (value == null) { + return null; + } + + try { + URLConnection conn = value.openConnection(); + conn.setConnectTimeout(2000); // 2秒超时 + conn.setReadTimeout(3000); // 3秒超时 + + try (InputStream inputStream = conn.getInputStream()) { + // byte[] bytes = FileUtils.readInputStream(inputStream, value.toString()); + // 替代 FileUtils.readInputStream 的自定义方法 + byte[] bytes = readInputStream(inputStream); + return new WriteCellData<>(bytes); + } + } catch (Exception e) { + // 静默忽略错误,只记录日志 + logger.debug("忽略图片加载失败: {}, 原因: {}", value, e.getMessage()); + // return null; // 返回null表示不写入图片 + return new WriteCellData<>(new byte[0]); // 返回空数组而不是 null + } + } + + /** + * 替代 FileUtils.readInputStream 的自定义方法 + * + * @param inputStream 输入流 + * @return 字节数组 + * @throws Exception 读取异常 + */ + private byte[] readInputStream(InputStream inputStream) throws Exception { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + return outputStream.toByteArray(); + } +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/ImageReadConverter.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/ImageReadConverter.java new file mode 100644 index 0000000..3e6d12f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/converter/ImageReadConverter.java @@ -0,0 +1,44 @@ +package com.fuyuanshen.equipment.converter;// package com.fuyuanshen.modules.system.converter; +// +// import com.alibaba.excel.converters.Converter; +// import com.alibaba.excel.enums.CellDataTypeEnum; +// import com.alibaba.excel.metadata.GlobalConfiguration; +// import com.alibaba.excel.metadata.data.ReadCellData; +// import com.alibaba.excel.metadata.property.ExcelContentProperty; +// +// import java.io.ByteArrayOutputStream; +// import java.io.IOException; +// +// /** +// * @author: 默苍璃 +// * @date: 2025-06-0710:01 +// */ +// +// public class ImageReadConverter implements Converter { +// +// @Override +// public Class supportJavaTypeKey() { +// return byte[].class; +// } +// +// // @Override +// // public CellDataTypeEnum supportExcelTypeKey() { +// // return CellDataTypeEnum.IMAGE; +// // } +// +// @Override +// public byte[] convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws IOException { +// if (cellData.getType() == CellDataTypeEnum.IMAGE) { +// ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +// cellData.getImageValueList().forEach(image -> { +// try { +// outputStream.write(image.getImageBytes()); +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// }); +// return outputStream.toByteArray(); +// } +// return null; +// } +// } \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/APPDevice1.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/APPDevice1.java new file mode 100644 index 0000000..15b9245 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/APPDevice1.java @@ -0,0 +1,106 @@ +package com.fuyuanshen.equipment.domain; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @Description: 设备表 + * @Author: WY + * @Date: 2025/5/16 + **/ +@Data +@TableName("app_device") +public class APPDevice1 extends TenantEntity { + + @TableId(value = "app_device_id", type = IdType.AUTO) + @Schema(name = "ID") + private Long id; + + @Schema(name = "设备类型") + private Long deviceType; + + @Schema(name = "设备类型名称") + private String deviceTypeName; + + @Schema(name = "客户号") + private Long customerId; + + @Schema(name = "所属客户") + private String customerName; + + /*@Schema(name = "设备编号") + private String deviceNo;*/ + + @Schema(name = "设备名称") + private String deviceName; + + @Schema(name = "设备图片") + private String devicePic; + + @Schema(name = "设备MAC") + private String deviceMac; + + @Schema(name = "设备IMEI") + private String deviceImei; + + @Schema(name = "设备SN") + private String deviceSn; + + @Schema(name = "经度") + private String longitude; + + @Schema(name = "纬度") + private String latitude; + + @Schema(name = "备注") + private String remark; + + @TableField(exist = false) + @Schema(name = "设备类型名称") + private String typeName; + + /** + * 租户ID + */ + @TableField(value = "tenant_id") + @Schema(hidden = true) + private String tenantId; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @Schema(name = "设备状态") + private Integer deviceStatus; + + /** + * 绑定状态 + * 0 未绑定 + * 1 已绑定 + */ + @Schema(name = "绑定状态") + private Integer bindingStatus; + + /** + * 绑定类型 + * 0 APP + * 1 小程序 + */ + @Schema(name = "绑定类型") + private Integer bindingType; + + + public void copy(APPDevice1 source) { + BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true)); + } + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/CustomerDevice.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/CustomerDevice.java new file mode 100644 index 0000000..10f2b94 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/CustomerDevice.java @@ -0,0 +1,42 @@ +package com.fuyuanshen.equipment.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.core.conditions.update.Update; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +/** + * @author: 默苍璃 + * @date: 2025-06-1308:57 + */ +@Getter +@Setter +@TableName("customer_device") +public class CustomerDevice extends TenantEntity { + + @NotNull(groups = Update.class) + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @TableField(value = "customer_id") + private Long customerId; + + @TableField(value = "device_id") + private Long deviceId; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @TableField(value = "assign_status") + private Byte assignStatus; + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java new file mode 100644 index 0000000..1b4ec8f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/Device.java @@ -0,0 +1,114 @@ +package com.fuyuanshen.equipment.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @Description: 设备表 + * @Author: WY + * @Date: 2025/5/16 + **/ +@Data +@TableName("device") +@JsonInclude(JsonInclude.Include.ALWAYS) // 关键注解 +public class Device extends TenantEntity { + + @TableId(value = "id", type = IdType.AUTO) + @Schema(name = "ID") + private Long id; + + @Schema(name = "设备记录ID") + @TableField(exist = false) + private Long assignId; + + @Schema(name = "设备类型") + private Long deviceType; + + @Schema(name = "设备类型名称") + private String typeName; + + @Schema(name = "客户号") + private Long customerId; + + @Schema(name = "所属客户") + private String customerName; + + /** + * 当前所有者 + * current_owner_id + */ + @Schema(name = "当前所有者") + private Long currentOwnerId; + + /** + * 原始所有者(创建者) + * original_owner_id + */ + @Schema(name = "原始所有者(创建者)") + private Long originalOwnerId; + + /** + * 原始设备 + */ + @Schema(name = "原始设备") + private Long originalDeviceId; + + @Schema(name = "设备编号") + private String deviceNo; + + @Schema(name = "设备名称") + private String deviceName; + + @Schema(name = "设备图片") + private String devicePic; + + @Schema(name = "设备MAC") + private String deviceMac; + + @Schema(name = "蓝牙名称") + private String bluetoothName; + + @Schema(name = "设备IMEI") + private String deviceImei; + + @Schema(name = "设备SN") + private String deviceSn; + + @Schema(name = "经度") + private String longitude; + + @Schema(name = "纬度") + private String latitude; + + @Schema(name = "备注") + private String remark; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @Schema(name = "设备状态") + private Integer deviceStatus; + + /** + * 绑定状态 + * 0 未绑定 + * 1 已绑定 + */ + @Schema(name = "绑定状态") + private Integer bindingStatus; + + /** + * 创建人名称 + */ + private String createByName; + + private Long bindingUserId; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceAssignments.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceAssignments.java new file mode 100644 index 0000000..071cb5a --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceAssignments.java @@ -0,0 +1,86 @@ +package com.fuyuanshen.equipment.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 设备分配记录表 + * + * @TableName device_assignments + */ +@TableName(value = "device_assignments") +@Data +public class DeviceAssignments extends TenantEntity { + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 设备id + */ + private Long deviceId; + + /** + * 分配方 + */ + private Long fromCustomerId; + + /** + * 接收方 + */ + private Long toCustomerId; + + /** + * 分配者 + * assigner_name + */ + private Long assignerId; + private String assignerName; + + /** + * 接收者 + * assignee_name + */ + private Long assigneeId; + private String assigneeName; + + /** + * 分配时间 + */ + private LocalDateTime assignedAt; + + /** + * 0 未授权 + * 1 已授权 + * 是否同步授权了设备类型 + */ + private Integer deviceTypeGranted; + + /** + * 0 否 + * 1 是 + * 是否直接分配(用于父级显示) + */ + private Integer direct; + + /** + * 0 否 + * 1 是 + * 设备是否有效 + */ + private Integer active; + + /** + * 分配等级(用于失效) + */ + private String lever; + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceLog.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceLog.java new file mode 100644 index 0000000..2ccd0a3 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceLog.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.equipment.domain; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/24 + **/ +@Data +@TableName("device_log") +public class DeviceLog extends TenantEntity { + + @TableId(value = "id", type = IdType.AUTO) + // @Schema(value = "ID") + private Long id; + + // @Schema(value = "设备行为") + private String deviceAction; + + // @Schema(value = "设备名称") + private String deviceName; + + // @Schema(value = "数据来源") + private String dataSource; + + // @Schema(value = "内容") + private String content; + + public void copy(DeviceLog source){ + BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); + } + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java new file mode 100644 index 0000000..76e554e --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceType.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.equipment.domain; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.baomidou.mybatisplus.annotation.*; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @Description: 设备类型 + * @Author: WY + * @Date: 2025/5/14 + **/ +@Data +@TableName("device_type") +public class DeviceType extends TenantEntity { + + @TableId(value = "id", type = IdType.AUTO) + @Schema(name = "ID", hidden = true) + private Long id; + + @Schema(name = "客户号") + private Long customerId; + + @Schema(name = "创建该类型的客户") + private Long ownerCustomerId; + + /** + * 原始所有者(创建者) + * original_owner_id + */ + @Schema(name = "原始所有者(创建者)") + private Long originalOwnerId; + + /** + * 原始设备 + */ + @Schema(name = "原始设备类型") + private Long originalDeviceId; + + @NotBlank(message = "设备类型名称不能为空") + @Schema(name = "类型名称", required = true) + private String typeName; + + @Schema(name = "是否支持蓝牙") + private Boolean isSupportBle; + + @Schema(name = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") + private String locateMode; + + @Schema(name = "联网方式", example = "0:无;1:4G;2:WIFI") + private String networkWay; + + @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + private String communicationMode; + + /** + * 创建人名称 + */ + private String createByName; + + public void copy(DeviceType source) { + BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true)); + } + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceTypeGrants.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceTypeGrants.java new file mode 100644 index 0000000..20cbb15 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/DeviceTypeGrants.java @@ -0,0 +1,95 @@ +package com.fuyuanshen.equipment.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; + +import java.util.Date; + +/** + * 设备分配权限表 (解决跨客户共享) + * + * @TableName device_type_grants + */ +@TableName(value = "device_type_grants") +@Data +public class DeviceTypeGrants extends TenantEntity { + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 设备类型 + */ + private Long deviceTypeId; + + /** + * 被授权的客户 + */ + private Long customerId; + + /** + * 授权方客户 + */ + private Long grantorCustomerId; + + /** + * 生成日期 + */ + private Date grantedAt; + + /** + * 关联分配记录 + */ + private Long assignmentId; + + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null) { + return false; + } + if (getClass() != that.getClass()) { + return false; + } + DeviceTypeGrants other = (DeviceTypeGrants) that; + return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getDeviceTypeId() == null ? other.getDeviceTypeId() == null : this.getDeviceTypeId().equals(other.getDeviceTypeId())) && (this.getCustomerId() == null ? other.getCustomerId() == null : this.getCustomerId().equals(other.getCustomerId())) && (this.getGrantorCustomerId() == null ? other.getGrantorCustomerId() == null : this.getGrantorCustomerId().equals(other.getGrantorCustomerId())) && (this.getGrantedAt() == null ? other.getGrantedAt() == null : this.getGrantedAt().equals(other.getGrantedAt())) && (this.getAssignmentId() == null ? other.getAssignmentId() == null : this.getAssignmentId().equals(other.getAssignmentId())); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); + result = prime * result + ((getDeviceTypeId() == null) ? 0 : getDeviceTypeId().hashCode()); + result = prime * result + ((getCustomerId() == null) ? 0 : getCustomerId().hashCode()); + result = prime * result + ((getGrantorCustomerId() == null) ? 0 : getGrantorCustomerId().hashCode()); + result = prime * result + ((getGrantedAt() == null) ? 0 : getGrantedAt().hashCode()); + result = prime * result + ((getAssignmentId() == null) ? 0 : getAssignmentId().hashCode()); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + sb.append("Hash = ").append(hashCode()); + sb.append(", id=").append(id); + sb.append(", deviceTypeId=").append(deviceTypeId); + sb.append(", customerId=").append(customerId); + sb.append(", grantorCustomerId=").append(grantorCustomerId); + sb.append(", grantedAt=").append(grantedAt); + sb.append(", assignmentId=").append(assignmentId); + sb.append("]"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/UserApp.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/UserApp.java new file mode 100644 index 0000000..64679ac --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/UserApp.java @@ -0,0 +1,99 @@ +package com.fuyuanshen.equipment.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * APP用户信息对象 app_user + * + * @author Lion Li + * @date 2025-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("app_user") +public class UserApp extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @TableId(value = "user_id") + private Long userId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(app_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 头像地址 + */ + private Long avatar; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/UserAppBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/UserAppBo.java new file mode 100644 index 0000000..c1cb78c --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/bo/UserAppBo.java @@ -0,0 +1,95 @@ +package com.fuyuanshen.equipment.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.equipment.domain.UserApp; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * APP用户信息业务对象 app_user + * + * @author Lion Li + * @date 2025-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = UserApp.class, reverseConvertGenerate = false) +public class UserAppBo extends BaseEntity { + + /** + * 用户ID + */ + @NotNull(message = "用户ID不能为空", groups = { EditGroup.class }) + private Long userId; + + /** + * 用户账号 + */ + @NotBlank(message = "用户账号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String userName; + + /** + * 用户昵称 + */ + // @NotBlank(message = "用户昵称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String nickName; + + /** + * 用户类型(app_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 头像地址 + */ + private Long avatar; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceBo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceBo.java new file mode 100644 index 0000000..716d937 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/AppDeviceBo.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.equipment.domain.dto; + +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 绑定设备参数 + */ +@Data +public class AppDeviceBo { + /** + * 设备IMEI + */ + private String deviceImei; + + /** + * 设备MAC + */ + private String deviceMac; + + /** + * 通讯方式 0:4G; 1:蓝牙 + */ + @NotNull(message = "通讯方式不能为空", groups = { EditGroup.class }) + private Integer communicationMode; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelExportDTO.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelExportDTO.java new file mode 100644 index 0000000..28aa32c --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelExportDTO.java @@ -0,0 +1,80 @@ +package com.fuyuanshen.equipment.domain.dto; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.fuyuanshen.equipment.converter.IgnoreFailedImageConverter; +import lombok.Data; + +import java.net.URL; + +/** + * @author: 默苍璃 + * @date: 2025-06-0618:16 + */ +@Data +@HeadRowHeight(20) // 表头行高 +@ContentRowHeight(100) // 内容行高 +public class DeviceExcelExportDTO { + + @ExcelProperty("ID") + private Long id; + + @ExcelProperty("设备类型") + private Long deviceType; + + // @ExcelProperty("客户号") + // private Long customerId; + + @ExcelProperty("所属客户") + private String customerName; + + @ExcelProperty("设备名称") + @ColumnWidth(20) + private String deviceName; + + @ExcelProperty(value = "设备图片", converter = IgnoreFailedImageConverter.class) + @ColumnWidth(15) // 设置图片列宽度 + private URL devicePic; // 使用URL类型 + + @ExcelProperty("设备MAC") + @ColumnWidth(20) + private String deviceMac; + + @ExcelProperty("设备IMEI") + @ColumnWidth(20) + private String deviceImei; + + @ExcelProperty("经度") + private String longitude; + + @ExcelProperty("纬度") + private String latitude; + + @ExcelProperty("备注") + @ColumnWidth(30) + private String remark; + + @ExcelProperty("设备类型名称") + @ColumnWidth(20) + private String typeName; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @ExcelProperty("设备状态") + @ColumnWidth(20) + private String deviceStatus; + + @ExcelProperty("创建时间") + @ColumnWidth(20) + private String createTime; + + @ExcelProperty("创建人") + @ColumnWidth(20) + private String createBy; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelImportDTO.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelImportDTO.java new file mode 100644 index 0000000..f2f9a87 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/DeviceExcelImportDTO.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.equipment.domain.dto; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.alibaba.excel.converters.bytearray.ByteArrayImageConverter; +import lombok.Data; + +/** + * @author: 默苍璃 + * @date: 2025-06-0710:00 + */ +@Data +@HeadRowHeight(20) // 表头行高 +@ContentRowHeight(100) // 内容行高 +public class DeviceExcelImportDTO { + + @ExcelProperty("设备类型") + private Long deviceType; + + @ExcelProperty("客户号") + private Long customerId; + + @ExcelProperty("设备名称") + @ColumnWidth(20) + private String deviceName; + + @ExcelProperty(value = "设备图片", converter = ByteArrayImageConverter.class) + @ColumnWidth(15) + private byte[] devicePic; + + // 添加图片写入方法 + public void setDevicePicFromBytes(byte[] bytes) { + this.devicePic = bytes; + } + + @ExcelProperty("设备MAC") + @ColumnWidth(20) + private String deviceMac; + + @ExcelProperty("设备IMEI") + @ColumnWidth(20) + private String deviceImei; + + @ExcelProperty("设备SN") + @ColumnWidth(20) + private String deviceSn; + + @ExcelProperty("经度") + private String longitude; + + @ExcelProperty("纬度") + private String latitude; + + @ExcelProperty("备注") + @ColumnWidth(30) + private String remark; + + @ExcelProperty("设备类型名称") + @ColumnWidth(20) + private String typeName; + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/ImportResult.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/ImportResult.java new file mode 100644 index 0000000..fd4fa5e --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/dto/ImportResult.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.equipment.domain.dto; + +import lombok.Data; + +import java.util.List; + +/** + * @author: 默苍璃 + * @date: 2025-06-0717:01 + */ +@Data +public class ImportResult { + private int successCount; + private int failureCount; + private List failedRecords; + // private byte[] errorExcelData; // 存储失败数据的Excel字节流 + + private String errorExcelUrl; // 改为存储错误报告的URL + + // 添加成功/失败统计信息 + public String getSummary() { + return String.format("成功导入 %d 条数据,失败 %d 条", successCount, failureCount); + } + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java new file mode 100644 index 0000000..77713ed --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceForm.java @@ -0,0 +1,56 @@ +package com.fuyuanshen.equipment.domain.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + + +/** + * @Description: 设备表单 + * @Author: WY + * @Date: 2025/5/17 + **/ +@Data +public class DeviceForm { + + @Schema(title = "ID", hidden = true) + private Long id; + + @Schema(title = "设备记录ID") + private Long assignId; + + @Schema(title = "设备类型") + private Long deviceType; + + @Schema(title = "客户号") + private Long customerId; + + /*@Schema(value = "设备编号") + private String deviceNo;*/ + + @NotBlank(message = "设备名称不能为空") + @Schema(title = "设备名称", required = true) + private String deviceName; + + @Schema(title = "设备图片存储路径", hidden = true) + private String devicePic; + + // @NotBlank(message = "设备MAC不能为空") + @Schema(title = "设备MAC") + private String deviceMac; + + @Schema(title = "设备IMEI") + private String deviceImei; + + // @NotBlank(message = "设备SN不能为空") + @Schema(title = "设备SN", required = true) + private String deviceSn; + + @Schema(title = "设备图片") + private MultipartFile file; + + @Schema(title = "备注") + private String remark; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java new file mode 100644 index 0000000..d416718 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/form/DeviceTypeForm.java @@ -0,0 +1,39 @@ +package com.fuyuanshen.equipment.domain.form; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @Description: 设备类型 + * @Author: WY + * @Date: 2025/5/14 + **/ +@Data +public class DeviceTypeForm { + + @Schema(name = "ID", hidden = true) + private Long id; + + @Schema(name = "类型名称", required = true) + private String typeName; + + @Schema(name = "是否支持蓝牙") + private Boolean isSupportBle; + + @Schema(name = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗") + private String locateMode; + + @Schema(name = "联网方式", example = "0:无;1:4G;2:WIFI") + private String networkWay; + + @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + private String communicationMode; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/APPDeviceQueryCriteria1.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/APPDeviceQueryCriteria1.java new file mode 100644 index 0000000..a95085b --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/APPDeviceQueryCriteria1.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.equipment.domain.query; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Set; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Data +public class APPDeviceQueryCriteria1 { + + @Schema(name = "设备名称") + private String deviceName; + + @Schema(name = "设备类型") + private Long deviceType; + + @Schema(name = "设备MAC") + private String deviceMac; + + @Schema(name = "设备IMEI") + private String deviceImei; + + @Schema(name = "设备SN") + private String deviceSn; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @Schema(name = "设备状态 0 失效 1 正常 ") + private Integer deviceStatus; + + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + @Schema(name = "创建时间") + private List createTime; + + @Schema(name = "页码", example = "1") + private Integer page = 1; + + @Schema(name = "每页数据量", example = "10") + private Integer size = 10; + + @Schema(name = "客户id") + private Long customerId; + private Set customerIds; + + @Schema(name = "当前所有者") + private Long currentOwnerId; + + @Schema(name = "租户ID") + private Long tenantId; + + @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + private Integer communicationMode; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceAssignmentQuery.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceAssignmentQuery.java new file mode 100644 index 0000000..5597e1f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceAssignmentQuery.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.equipment.domain.query; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author: 默苍璃 + * @date: 2025-07-0910:27 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DeviceAssignmentQuery implements Serializable { + + /** + * 表示这个 serialVersionUID 字段是专门为 Java 序列化机制服务的。 + */ + @Serial + private static final long serialVersionUID = 1L; + + private Long deviceId; + + /** + * 分配者 + */ + private Long assignerId; + + /** + * 接收者 + */ + private Long assigneeId; + + /** + * 0 否 + * 1 是 + * 设备是否有效 + */ + private Integer active; + + /** + * 分配等级(用于失效) + */ + private String lever; + + + // Getters and Setters + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java new file mode 100644 index 0000000..09f9815 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceQueryCriteria.java @@ -0,0 +1,68 @@ +package com.fuyuanshen.equipment.domain.query; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Set; + +/** + * 设备查询参数 + * + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Data +public class DeviceQueryCriteria extends BaseEntity { + + @Schema(name = "设备id") + private Long deviceId; + + @Schema(name = "设备名称") + private String deviceName; + + @Schema(name = "设备类型") + private Long deviceType; + + @Schema(name = "设备MAC") + private String deviceMac; + + @Schema(name = "设备IMEI") + private String deviceImei; + + @Schema(name = "设备SN") + private String deviceSn; + + /** + * 设备状态 + * 0 失效 + * 1 正常 + */ + @Schema(name = "设备状态 0 失效 1 正常 ") + private Integer deviceStatus; + + @Schema(name = "页码", example = "1") + private Integer pageNum = 1; + + @Schema(name = "每页数据量", example = "10") + private Integer pageSize = 10; + + @Schema(name = "客户id") + private Long customerId; + private Set customerIds; + + @Schema(name = "当前所有者") + private Long currentOwnerId; + + @Schema(name = "租户ID") + private String tenantId; + + @Schema(name = "通讯方式", example = "0:4G;1:蓝牙") + private Integer communicationMode; + + /* app绑定用户id */ + private Long bindingUserId; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java new file mode 100644 index 0000000..a2a0ad0 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/query/DeviceTypeQueryCriteria.java @@ -0,0 +1,38 @@ +package com.fuyuanshen.equipment.domain.query; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Set; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/14 + **/ +@Data +public class DeviceTypeQueryCriteria extends BaseEntity implements Serializable { + + @Schema(name = "设备类型id") + private Long deviceTypeId; + + @Schema(name = "型号名称") + private String typeName; + + @Schema(name = "所属客户") + private Set customerIds; + + @Schema(name = "所属客户") + private Long customerId; + + @Schema(name = "com.fuyuanshen") + private Long tenantId; + + @Schema(name = "页码", example = "1") + private Integer pageNum = 1; + + @Schema(name = "每页数据量", example = "10") + private Integer pageSize = 10; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java new file mode 100644 index 0000000..ae4eff8 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/AppDeviceVo.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.equipment.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class AppDeviceVo { + + private Long id; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 设备IMEI + */ + private String deviceImei; + + /** + * 设备MAC + */ + private String deviceMac; + + /** + * 通讯方式 0:4G;1:蓝牙 + */ + private Integer communicationMode; + + /** + * 设备图片 + */ + private String devicePic; + + /** + * 蓝牙名称 + */ + private String bluetoothName; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java new file mode 100644 index 0000000..b2ff048 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/CustomerVo.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.equipment.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * @Description: 分配客户的Vo类 + * @Author: WY + * @Date: 2025/5/28 + **/ +@Data +@Validated +public class CustomerVo { + + @Schema(name = "客户ID") + @NotNull(message = "客户ID不能为空") + private Long customerId; + + @Schema(name = "设备ID") + @NotNull(message = "设备ID不能为空") + private List deviceIds; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/UserAppVo.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/UserAppVo.java new file mode 100644 index 0000000..537c1a7 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/domain/vo/UserAppVo.java @@ -0,0 +1,120 @@ +package com.fuyuanshen.equipment.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.equipment.domain.UserApp; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * APP用户信息视图对象 app_user + * + * @author Lion Li + * @date 2025-06-27 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = UserApp.class) +public class UserAppVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "用户账号") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户昵称") + private String nickName; + + /** + * 用户类型(app_user系统用户) + */ + @ExcelProperty(value = "用户类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "a=pp_user系统用户") + private String userType; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** + * 头像地址 + */ + @ExcelProperty(value = "头像地址") + private Long avatar; + + /** + * 密码 + */ + @ExcelProperty(value = "密码") + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 最后登录IP + */ + @ExcelProperty(value = "最后登录IP") + private String loginIp; + + /** + * 最后登录时间 + */ + @ExcelProperty(value = "最后登录时间") + private Date loginDate; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 部门ID + */ + private Long deptId; +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/BindingStatusEnum.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/BindingStatusEnum.java new file mode 100644 index 0000000..cb50ab2 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/BindingStatusEnum.java @@ -0,0 +1,35 @@ +package com.fuyuanshen.equipment.enums; + +/** + * @author: 默苍璃 + * @date: 2025-06-1818:17 + */ +public enum BindingStatusEnum { + UNBOUND(0, "未绑定"), BOUND(1, "已绑定"); + + private final int code; + private final String description; + + BindingStatusEnum(int code, String description) { + this.code = code; + this.description = description; + } + + public int getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public static BindingStatusEnum fromCode(int code) { + for (BindingStatusEnum status : values()) { + if (status.getCode() == code) { + return status; + } + } + throw new IllegalArgumentException("Invalid binding status code: " + code); + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/CommunicationModeEnum.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/CommunicationModeEnum.java new file mode 100644 index 0000000..064ff68 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/CommunicationModeEnum.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.equipment.enums; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * 通讯方式枚举 + * + * @author: 默苍璃 + * @date: 2025-06-2414:11 + */ +public enum CommunicationModeEnum { + + FOUR_G(0, "4G"), + BLUETOOTH(1, "蓝牙"); + + private final int value; + private final String label; + + CommunicationModeEnum(int value, String label) { + this.value = value; + this.label = label; + } + + @JsonValue + public int getValue() { + return value; + } + + public String getLabel() { + return label; + } + + /** + * 根据值获取标签 + */ + public static String getLabelByValue(int value) { + for (CommunicationModeEnum mode : values()) { + if (mode.getValue() == value) { + return mode.getLabel(); + } + } + return null; + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceActiveStatusEnum.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceActiveStatusEnum.java new file mode 100644 index 0000000..9907300 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceActiveStatusEnum.java @@ -0,0 +1,44 @@ +package com.fuyuanshen.equipment.enums; + +/** + * 设备有效性状态枚举 + * + * @author: 默苍璃 + * @date: 2025-06-2113:26 + */ +public enum DeviceActiveStatusEnum { + + INACTIVE(0, "无效"), ACTIVE(1, "有效"); + + private final Integer code; + private final String description; + + DeviceActiveStatusEnum(Integer code, String description) { + this.code = code; + this.description = description; + } + + public Integer getCode() { + return code; + } + + public String getDescription() { + return description; + } + + /** + * 根据 code 获取描述 + * + * @param code 状态码 + * @return 描述信息 + */ + public static String getDescriptionByCode(Integer code) { + for (DeviceActiveStatusEnum status : values()) { + if (status.getCode().equals(code)) { + return status.getDescription(); + } + } + return null; + } +} + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceAuthorizationStatus.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceAuthorizationStatus.java new file mode 100644 index 0000000..e3c37ee --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceAuthorizationStatus.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.equipment.enums; + +/** + * @author: 默苍璃 + * @date: 2025-06-1918:50 + */ +public enum DeviceAuthorizationStatus { + /** + * 未授权 + */ + UNAUTHORIZED(0), + + /** + * 已授权 + */ + AUTHORIZED(1); + + private final int value; + + DeviceAuthorizationStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + /** + * 根据整数值获取对应的枚举值 + * + * @param value 整数值(0 或 1) + * @return 对应的 DeviceAuthorizationStatus 枚举 + * @throws IllegalArgumentException 如果值不是 0 或 1 + */ + public static DeviceAuthorizationStatus fromValue(int value) { + for (DeviceAuthorizationStatus status : values()) { + if (status.getValue() == value) { + return status; + } + } + throw new IllegalArgumentException("Invalid device authorization status value: " + value); + } + +} + diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceStatusEnum.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceStatusEnum.java new file mode 100644 index 0000000..dac2ec2 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/enums/DeviceStatusEnum.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.equipment.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备状态枚举 + * + * @author: 默苍璃 + * @date: 2025-06-1916:02 + */ +@Getter +@AllArgsConstructor +public enum DeviceStatusEnum { + + /** + * 失效 + */ + INVALID(0, "失效"), + + /** + * 正常 + */ + NORMAL(1, "正常"); + + /** + * 状态码 + */ + private final Integer code; + + /** + * 描述 + */ + private final String description; + + /** + * 根据状态码获取描述 + * + * @param code 状态码 + * @return 描述 + */ + public static String getDescriptionByCode(Integer code) { + for (DeviceStatusEnum status : DeviceStatusEnum.values()) { + if (status.getCode().equals(code)) { + return status.getDescription(); + } + } + return null; + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java new file mode 100644 index 0000000..97d333d --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/DeviceImportParams.java @@ -0,0 +1,34 @@ +package com.fuyuanshen.equipment.excel; + +import com.fuyuanshen.customer.mapper.CustomerMapper; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.DeviceService; +import com.fuyuanshen.system.service.ISysOssService; +import lombok.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author: 默苍璃 + * @date: 2025-06-1414:56 + */ +@AllArgsConstructor +@NoArgsConstructor(force = true) +@Getter +@Setter +@Builder +public class DeviceImportParams { + + private DeviceService deviceService; + private DeviceMapper deviceMapper; + private CustomerMapper customerMapper; + private DeviceTypeMapper deviceTypeMapper; + private ISysOssService ossService; + private MultipartFile file; + private String filePath; + private String ip; + private String username; + private Long userId; + private String tenantId; + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java new file mode 100644 index 0000000..7647eaa --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/excel/UploadDeviceDataListener.java @@ -0,0 +1,263 @@ +package com.fuyuanshen.equipment.excel; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.equipment.constants.DeviceConstants; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO; +import com.fuyuanshen.equipment.domain.dto.ImportResult; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.handler.ImageWriteHandler; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.util.ZipSecureFile; +import org.apache.poi.xssf.usermodel.*; + +import java.io.File; +import java.util.*; + + +@Slf4j +public class UploadDeviceDataListener implements ReadListener { + + // 存储图片数据的映射 + private final Map rowImageMap = new HashMap<>(); + + private final DeviceImportParams params; + + private final Map rowDeviceMap = new HashMap<>(); + private final Map rowDtoMap = new HashMap<>(); + private final List rowIndexList = new ArrayList<>(); + + private int successCount = 0; + private int failureCount = 0; + private final List failedRecords = new ArrayList<>(); + + + public UploadDeviceDataListener(DeviceImportParams params) { + this.params = params; + } + + + public ImportResult getImportResult() { + ImportResult result = new ImportResult(); + result.setSuccessCount(successCount); + result.setFailureCount(failureCount); + + // 准备失败记录(包含图片数据) + List failedRecordsWithImages = new ArrayList<>(); + + for (DeviceExcelImportDTO failedRecord : failedRecords) { + // 获取原始行号 + Integer rowIndex = null; + for (Map.Entry entry : rowDtoMap.entrySet()) { + if (entry.getValue() == failedRecord) { + rowIndex = entry.getKey(); + break; + } + } + + if (rowIndex != null) { + // 创建副本,避免修改原始数据 + DeviceExcelImportDTO recordWithImage = new DeviceExcelImportDTO(); + BeanUtil.copyProperties(failedRecord, recordWithImage); + + // 设置图片数据 + byte[] imageData = rowImageMap.get(rowIndex); + if (imageData != null) { + recordWithImage.setDevicePicFromBytes(imageData); + } + + failedRecordsWithImages.add(recordWithImage); + } else { + failedRecordsWithImages.add(failedRecord); + } + } + + result.setFailedRecords(failedRecordsWithImages); + + // 生成错误报告 + if (!failedRecordsWithImages.isEmpty()) { + try { + // 生成唯一的文件名 + String fileName = "import_errors_" + System.currentTimeMillis() + ".xlsx"; + + // 创建错误报告目录 + String errorDirPath = params.getFilePath() + DeviceConstants.ERROR_REPORT_DIR; + File errorDir = new File(errorDirPath); + if (!errorDir.exists() && !errorDir.mkdirs()) { + log.error("无法创建错误报告目录: {}", errorDirPath); + return result; + } + + // 保存错误报告到文件(包含图片) + File errorFile = new File(errorDir, fileName); + EasyExcel.write(errorFile, DeviceExcelImportDTO.class).registerWriteHandler(new ImageWriteHandler()) // 添加图片处理 + .sheet("失败数据").doWrite(failedRecordsWithImages); + + // 生成访问URL + // String errorExcelUrl = params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.ERROR_REPORT_DIR + "/" + fileName; + SysOssVo upload = params.getOssService().upload(errorFile); + result.setErrorExcelUrl(upload.getUrl()); + log.info("错误报告已保存: {}", errorFile.getAbsolutePath()); + } catch (Exception e) { + log.error("生成错误报告失败", e); + } + } + + return result; + } + + + @Override + public void invoke(DeviceExcelImportDTO data, AnalysisContext context) { + log.info("解析到一条数据: {}", JSON.toJSONString(data)); + int rowIndex = context.readRowHolder().getRowIndex(); + + Device device = new Device(); + BeanUtil.copyProperties(data, device, true); + rowDeviceMap.put(rowIndex, device); + rowDtoMap.put(rowIndex, data); + rowIndexList.add(rowIndex); + + } + + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + try { + processImages(); + processDataRowByRow(); + log.info("数据处理完成!成功:{}条,失败:{}条", successCount, failureCount); + } catch (Exception e) { + log.error("数据处理失败:{}", e.getMessage(), e); + throw new RuntimeException("导入失败", e); + } + } + + + private void processDataRowByRow() { + LoginUser loginUser = LoginHelper.getLoginUser(); + for (Integer rowIndex : rowIndexList) { + Device device = rowDeviceMap.get(rowIndex); + DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex); + try { + // DeviceQueryCriteria criteria = new DeviceQueryCriteria(); + // criteria.setDeviceMac(device.getDeviceMac()); + // criteria.setTenantId(params.getTenantId()); + // List deviceList = params.getDeviceMapper().findAll(criteria); + // if (!deviceList.isEmpty()) { + // throw new RuntimeException("设备MAC重复"); + // } + // device.setTenantId(params.getTenantId()); + + // 设备类型 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("type_name", device.getTypeName()); + // wrapper.eq("customer_id", params.getUserId()); + List deviceTypes = params.getDeviceTypeMapper().selectList(wrapper); + if (CollectionUtil.isNotEmpty(deviceTypes)) { + device.setDeviceType(deviceTypes.get(0).getId()); + } + device.setCurrentOwnerId(loginUser.getUserId()); + device.setOriginalOwnerId(loginUser.getUserId()); + device.setCreateByName(loginUser.getNickname()); + params.getDeviceService().save(device); + successCount++; + log.info("行 {} 数据插入成功", rowIndex); + } catch (Exception e) { + failureCount++; + failedRecords.add(originalDto); + log.error("行 {} 数据插入失败: {}", rowIndex, e.getMessage()); + } + } + } + + private void processImages() { + try (OPCPackage opcPackage = OPCPackage.open(params.getFile().getInputStream())) { + ZipSecureFile.setMinInflateRatio(-1.0d); + XSSFWorkbook workbook = new XSSFWorkbook(opcPackage); + XSSFSheet sheet = workbook.getSheetAt(0); + + XSSFDrawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) return; + + for (XSSFShape shape : drawing.getShapes()) { + if (shape instanceof XSSFPicture) { + XSSFPicture picture = (XSSFPicture) shape; + XSSFClientAnchor anchor = picture.getPreferredSize(); + int rowIndex = anchor.getRow1(); + int colIndex = anchor.getCol1(); + + if (colIndex == 2) { + Device device = rowDeviceMap.get(rowIndex); + if (device != null) { + try { + byte[] imageData = picture.getPictureData().getData(); + String extraValue = getCellValue(sheet, rowIndex, 4); + String imageUrl = uploadAndGenerateUrl(imageData, extraValue); + device.setDevicePic(imageUrl); + + // 2. 保存图片数据到DTO,用于错误报告 + rowImageMap.put(rowIndex, imageData); + } catch (Exception e) { + log.error("行 {} 图片处理失败: {}", rowIndex, e.getMessage()); + device.setDevicePic(null); // 设置为空,让插入继续 + } + } + } + } + } + } catch (Exception e) { + log.error("图片处理失败:{}", e.getMessage(), e); + } + } + + private String getCellValue(XSSFSheet sheet, int rowIndex, int colIndex) { + XSSFRow row = sheet.getRow(rowIndex); + if (row == null) return null; + + XSSFCell cell = row.getCell(colIndex); + if (cell == null) return null; + + return cell.toString(); + } + + private String uploadAndGenerateUrl(byte[] imageData, String deviceMac) { + if (imageData == null || imageData.length == 0) { + log.warn("图片数据为空"); + return null; + } + try { + String fileExtension = "jpg"; + String newFileName = "PS_" + new Random(8) + "." + fileExtension; + // String targetDirPath = params.getFilePath() + DeviceConstants.FILE_ACCESS_ISOLATION; + // File targetDir = new File(targetDirPath); + // + // if (!targetDir.exists() && !targetDir.mkdirs()) { + // log.error("无法创建目录: {}", targetDirPath); + // return null; + // } + // + // File newFile = new File(targetDir, newFileName); + // Files.write(newFile.toPath(), imageData); + // + // return params.getIp() + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.FILE_ACCESS_ISOLATION + "/" + newFileName; + SysOssVo upload = params.getOssService().upload(imageData, newFileName); + return upload.getUrl(); + } catch (Exception e) { + log.error("保存图片失败", e); + return null; + } + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java new file mode 100644 index 0000000..f0fbc57 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/handler/ImageWriteHandler.java @@ -0,0 +1,69 @@ +package com.fuyuanshen.equipment.handler; + +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFSheet; + +/** + * @author: 默苍璃 + * @date: 2025-06-0718:05 + */ +public class ImageWriteHandler implements SheetWriteHandler { + + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + // 不需要实现 + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Workbook workbook = writeWorkbookHolder.getWorkbook(); + Sheet sheet = writeSheetHolder.getSheet(); + + // 获取设备图片列索引(假设是第4列,索引3) + int imageColIndex = 3; + + // 遍历所有行 + for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) { // 从第2行开始(跳过标题) + Row row = sheet.getRow(rowIndex); + if (row == null) continue; + + Cell imageCell = row.getCell(imageColIndex); + if (imageCell == null) continue; + + // 获取图片数据 + byte[] imageData = null; + if (imageCell.getCellType() == CellType.STRING) { + // 处理Base64编码的图片(如果需要) + } + + if (imageData != null && imageData.length > 0) { + try { + // 添加图片到工作表 + int pictureIdx = workbook.addPicture(imageData, Workbook.PICTURE_TYPE_JPEG); + + // 创建绘图对象 + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet) sheet; + XSSFDrawing drawing = xssfSheet.createDrawingPatriarch(); + + // 设置图片位置 + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, imageColIndex, rowIndex, imageColIndex + 1, rowIndex + 1); + + // 创建图片 + drawing.createPicture(anchor, pictureIdx); + } + + // 清除单元格内容 + imageCell.setBlank(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceAssignmentsMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceAssignmentsMapper.java new file mode 100644 index 0000000..e411c23 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceAssignmentsMapper.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.equipment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fuyuanshen.equipment.domain.DeviceAssignments; +import com.fuyuanshen.equipment.domain.query.DeviceAssignmentQuery; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author 97433 + * @description 针对表【device_assignments】的数据库操作Mapper + * @createDate 2025-06-19 18:19:13 + * @Entity system.domain.DeviceAssignments + */ +@Mapper +public interface DeviceAssignmentsMapper extends BaseMapper { + + /** + * 查询设备分配信息 + * + * @param deviceAssignmentQuery + * @return + */ + List deviceAssignmentsMapper(@Param("query") DeviceAssignmentQuery deviceAssignmentQuery); +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java new file mode 100644 index 0000000..74e586f --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceMapper.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.equipment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Mapper +public interface DeviceMapper extends BaseMapper { + + /** + * 分页查询设备 + * + * @param criteria + * @param page + * @return + */ + IPage findAll(@Param("criteria") DeviceQueryCriteria criteria, Page page); + + List findAll(@Param("criteria") DeviceQueryCriteria criteria); + + List findAllDevices(@Param("criteria") DeviceQueryCriteria criteria); + + /** + * 根据条件查询 + * + * @param criteria + * @return + */ + List findDevices(@Param("criteria") DeviceQueryCriteria criteria); + + Page queryAppDeviceList(Page page, @Param("criteria") DeviceQueryCriteria criteria); + + /** + * 获取分配设备的客户 + * + * @param customerId + * @return + */ + Device getAssignCustomer(Long customerId); + + /** + * 获取设备链 + * + * @param originalDeviceId + * @return + */ + List findByOriginalDeviceId(Long originalDeviceId); +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeGrantsMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeGrantsMapper.java new file mode 100644 index 0000000..543e3ef --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeGrantsMapper.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.equipment.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author 97433 +* @description 针对表【device_type_grants】的数据库操作Mapper +* @createDate 2025-06-19 13:49:33 +* @Entity generator.domain.DeviceTypeGrants +*/ +@Mapper +public interface DeviceTypeGrantsMapper extends BaseMapper { + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeMapper.java new file mode 100644 index 0000000..90799fe --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/DeviceTypeMapper.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.equipment.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/14 + **/ +@Mapper +public interface DeviceTypeMapper extends BaseMapper { + + /** + * 分页查询设备类型 + * + * @param criteria + * @param page + * @return + */ + IPage findAll(@Param("criteria") DeviceTypeQueryCriteria criteria, Page page); + + + /** + * 查询所有设备类型 + * + * @param criteria + * @return + */ + List findAll(@Param("criteria") DeviceTypeQueryCriteria criteria); + + /** + * 获取已经分配的设备类型 + * + * @param customerId + * @return + */ + DeviceType getAssignType(@Param("deviceType") Long deviceType, @Param("customerId") Long customerId); + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/UserAppMapper.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/UserAppMapper.java new file mode 100644 index 0000000..d5399e2 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/mapper/UserAppMapper.java @@ -0,0 +1,17 @@ +package com.fuyuanshen.equipment.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.equipment.domain.UserApp; +import com.fuyuanshen.equipment.domain.vo.UserAppVo; +import org.apache.ibatis.annotations.Mapper; + +/** + * APP用户信息Mapper接口 + * + * @author Lion Li + * @date 2025-06-27 + */ +@Mapper +public interface UserAppMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/AppUserService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/AppUserService.java new file mode 100644 index 0000000..c5efd02 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/AppUserService.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.equipment.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.equipment.domain.bo.UserAppBo; + +import java.util.Collection; +import java.util.List; + +/** + * APP用户信息Service接口 + * + * @author Lion Li + * @date 2025-06-27 + */ +public interface AppUserService { + + /** + * 修改APP用户信息 + * + * @param bo APP用户信息 + * @return 是否修改成功 + */ + Boolean updateByBo(UserAppBo bo); + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceAssignmentsService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceAssignmentsService.java new file mode 100644 index 0000000..c8c30de --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceAssignmentsService.java @@ -0,0 +1,13 @@ +package com.fuyuanshen.equipment.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fuyuanshen.equipment.domain.DeviceAssignments; + +/** +* @author 97433 +* @description 针对表【device_assignments】的数据库操作Service +* @createDate 2025-06-19 18:19:13 +*/ +public interface DeviceAssignmentsService extends IService { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java new file mode 100644 index 0000000..dadf2fb --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceService.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.equipment.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.fuyuanshen.common.core.domain.PageResult; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.form.DeviceForm; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.CustomerVo; + +import java.io.IOException; +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +public interface DeviceService extends IService { + + /** + * 查询设备数据分页 + * + * @param criteria 条件 + * @param page 分页参数 + * @return PageResult + */ + TableDataInfo queryAll(DeviceQueryCriteria criteria, Page page) throws IOException; + + /** + * 查询所有数据不分页 + * + * @param criteria 条件参数 + * @return List + */ + List queryAll(DeviceQueryCriteria criteria); + + /** + * 查询所有设备信息 + * + * @param criteria + * @return + */ + List queryAllDevices(DeviceQueryCriteria criteria); + + /** + * 新增设备 + * + * @param resources + */ + void addDevice(DeviceForm resources) throws Exception; + + /** + * 修改设备 + * + * @param resources / + */ + void update(DeviceForm resources) throws Exception; + + /** + * 批量删除 + * + * @param ids / + */ + void deleteAll(List ids); + + /** + * 分配客户 + */ + void assignCustomer(CustomerVo customerVo); + + /** + * 撤回设备 + */ + void withdrawDevice(List ids); + + /** + * 解绑设备 + * + * @param deviceForm + */ + void unbindDevice(DeviceForm deviceForm); + + TableDataInfo queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery); + + int bindDevice(AppDeviceBo bo); + + int unBindDevice(Long id); + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeGrantsService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeGrantsService.java new file mode 100644 index 0000000..dfe2f20 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeGrantsService.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.equipment.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; + +/** +* @author 97433 +* @description 针对表【device_type_grants】的数据库操作Service +* @createDate 2025-06-19 13:49:33 +*/ +public interface DeviceTypeGrantsService extends IService { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeService.java new file mode 100644 index 0000000..aa2b622 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/DeviceTypeService.java @@ -0,0 +1,72 @@ +package com.fuyuanshen.equipment.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.fuyuanshen.common.core.domain.PageResult; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.form.DeviceTypeForm; +import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria; + +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/14 + **/ +public interface DeviceTypeService extends IService { + + /** + * 查询数据分页 + * + * @param criteria 条件 + * @param page 分页参数 + * @return PageResult + */ + TableDataInfo queryAll(DeviceTypeQueryCriteria criteria, Page page); + + /** + * 查询所有数据不分页 + * + * @param criteria 条件参数 + * @return List + */ + List queryAll(DeviceTypeQueryCriteria criteria); + + /** + * 查询所有设备类型 + * + * @return + */ + List queryDeviceTypes(); + + /** + * 新增设备类型 + * + * @param resources / + */ + void create(DeviceType resources); + + /** + * 修改设备类型 + * + * @param resources / + */ + void update(DeviceTypeForm resources); + + /** + * 多选删除 + * + * @param ids / + */ + void deleteAll(List ids); + + /** + * 获取设备类型通讯方式 + * + * @param id / + * @return DeviceTypeDto + */ + DeviceType getCommunicationMode(Long id); +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceAssignmentsServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceAssignmentsServiceImpl.java new file mode 100644 index 0000000..dd9bda9 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceAssignmentsServiceImpl.java @@ -0,0 +1,17 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fuyuanshen.equipment.domain.DeviceAssignments; +import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper; +import com.fuyuanshen.equipment.service.DeviceAssignmentsService; +import org.springframework.stereotype.Service; + +/** +* @author 97433 +* @description 针对表【device_assignments】的数据库操作Service实现 +* @createDate 2025-06-19 18:19:13 +*/ +@Service +public class DeviceAssignmentsServiceImpl extends ServiceImpl implements DeviceAssignmentsService { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceExportService.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceExportService.java new file mode 100644 index 0000000..22650e3 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceExportService.java @@ -0,0 +1,86 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.DateUtils; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.dto.DeviceExcelExportDTO; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author: 默苍璃 + * @date: 2025-06-0618:22 + */ +@Service +public class DeviceExportService { + + public void export(List devices, HttpServletResponse response) { + try { + String fileName = "设备列表_" + System.currentTimeMillis() + ".xlsx"; + // 使用URLEncoder进行RFC 5987编码 + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20"); + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + // 使用RFC 5987标准编码文件名 + response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName); + + // 转换为DTO列表 + List dtoList = devices.stream().map(device -> { + DeviceExcelExportDTO dto = new DeviceExcelExportDTO(); + dto.setId(device.getId()); + dto.setDeviceType(device.getDeviceType()); + dto.setCustomerName(device.getCustomerName()); + dto.setDeviceName(device.getDeviceName()); + dto.setDeviceMac(device.getDeviceMac()); + // 设备IMEI + dto.setDeviceImei(device.getDeviceImei()); + dto.setLongitude(device.getLongitude()); + dto.setLatitude(device.getLatitude()); + dto.setRemark(device.getRemark()); + dto.setTypeName(device.getTypeName()); + dto.setCreateBy(device.getCreateByName()); + Integer deviceStatus = device.getDeviceStatus(); + dto.setDeviceStatus(deviceStatus == 1 ? "正常" : "失效"); + // 时间戳转换 + dto.setCreateTime(DateUtils.format(device.getCreateTime(), "yyyy-MM-dd HH:mm:ss")); + + // 处理图片URL转换 + handleDevicePic(device, dto); + + return dto; + }).collect(Collectors.toList()); + + // 写入Excel + EasyExcel.write(response.getOutputStream(), DeviceExcelExportDTO.class).sheet("设备数据").doWrite(dtoList); + + } catch (IOException e) { + throw new RuntimeException("导出Excel失败", e); + } + } + + + private void handleDevicePic(Device device, DeviceExcelExportDTO dto) { + String picUrl = device.getDevicePic(); + if (picUrl != null && !picUrl.trim().isEmpty()) { + try { + // 尝试创建URL对象(会自动验证格式) + dto.setDevicePic(new URL(picUrl)); + } catch (MalformedURLException e) { + // 不是有效URL时设置为null + dto.setDevicePic(null); + } + } else { + dto.setDevicePic(null); + } + } + +} \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java new file mode 100644 index 0000000..c4d4310 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceServiceImpl.java @@ -0,0 +1,565 @@ +package com.fuyuanshen.equipment.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.UUID; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.AppLoginHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.customer.domain.Customer; +import com.fuyuanshen.customer.mapper.CustomerMapper; +import com.fuyuanshen.equipment.constants.DeviceConstants; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceAssignments; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; +import com.fuyuanshen.equipment.domain.dto.AppDeviceBo; +import com.fuyuanshen.equipment.domain.form.DeviceForm; +import com.fuyuanshen.equipment.domain.query.DeviceAssignmentQuery; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria; +import com.fuyuanshen.equipment.domain.vo.AppDeviceVo; +import com.fuyuanshen.equipment.domain.vo.CustomerVo; +import com.fuyuanshen.equipment.enums.BindingStatusEnum; +import com.fuyuanshen.equipment.enums.CommunicationModeEnum; +import com.fuyuanshen.equipment.enums.DeviceActiveStatusEnum; +import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.DeviceAssignmentsService; +import com.fuyuanshen.equipment.service.DeviceService; +import com.fuyuanshen.equipment.service.DeviceTypeGrantsService; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import com.fuyuanshen.system.service.ISysOssService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/16 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class DeviceServiceImpl extends ServiceImpl implements DeviceService { + + public static final String USER_ID_SEPARATOR = ":"; + + @Value("${file.device.pic}") + private String filePath; + @Value("${file.device.ip}") + private String ip; + + private final DeviceMapper deviceMapper; + private final DeviceTypeMapper deviceTypeMapper; + private final CustomerMapper customerMapper; + + private final ISysOssService ossService; + + private final DeviceAssignmentsService deviceAssignmentsService; + private final DeviceAssignmentsMapper deviceAssignmentsMapper; + + private final DeviceTypeGrantsService deviceTypeGrantsService; + private final DeviceTypeGrantsMapper deviceTypeGrantsMapper; + + + /** + * 分页查询设备 + * + * @param criteria 条件 + * @param page 分页参数 + * @return + * @throws IOException + */ + @Override + public TableDataInfo queryAll(DeviceQueryCriteria criteria, Page page) throws IOException { + + criteria.setCurrentOwnerId(LoginHelper.getUserId()); + IPage devices = deviceMapper.findAll(criteria, page); + + List records = devices.getRecords(); + for (Device record : records) { + if (record.getDevicePic() == null) { + record.setDevicePic(""); + } + } + + return new TableDataInfo(records, devices.getTotal()); + } + + + @Override + public List queryAll(DeviceQueryCriteria criteria) { + return deviceMapper.findAll(criteria); + } + + + /** + * 查询所有设备 + * + * @param criteria 筛选条件 + * @return + */ + @Override + public List queryAllDevices(DeviceQueryCriteria criteria) { + return deviceMapper.findAllDevices(criteria); + } + + + /** + * 新增设备 + * + * @param deviceForm + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void addDevice(DeviceForm deviceForm) throws Exception { + DeviceTypeQueryCriteria queryCriteria = new DeviceTypeQueryCriteria(); + queryCriteria.setDeviceTypeId(deviceForm.getDeviceType()); + queryCriteria.setCustomerId(LoginHelper.getUserId()); + DeviceTypeGrants typeGrants = deviceTypeGrantsMapper.selectById(queryCriteria.getDeviceTypeId()); + DeviceType deviceTypes = deviceTypeMapper.selectById(typeGrants.getDeviceTypeId()); + if (deviceTypes == null) { + throw new Exception("设备类型不存在!!!"); + } + + // 保存图片并获取URL + if (deviceForm.getFile() != null) { + SysOssVo upload = ossService.upload(deviceForm.getFile()); + // 设置图片路径 + deviceForm.setDevicePic(upload.getUrl()); + } + + // 转换对象并插入数据库 + Device device = new Device(); + BeanUtil.copyProperties(deviceForm, device, true); + device.setDeviceNo(createDeviceNo()); + LoginUser loginUser = LoginHelper.getLoginUser(); + device.setCurrentOwnerId(loginUser.getUserId()); + device.setOriginalOwnerId(loginUser.getUserId()); + device.setCreateByName(loginUser.getNickname()); + device.setTypeName(deviceTypes.getTypeName()); + device.setDeviceType(deviceTypes.getId()); + + deviceMapper.insert(device); + + // 新增设备类型记录 + DeviceAssignments assignments = new DeviceAssignments(); + assignments.setDeviceId(device.getId()); + assignments.setAssignedAt(LocalDateTime.now()); + // 分配者 + assignments.setAssignerId(loginUser.getUserId()); + assignments.setAssignerName(loginUser.getUsername()); + // 接收者 + assignments.setAssigneeId(loginUser.getUserId()); + assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode()); + String lever = USER_ID_SEPARATOR + loginUser.getUserId(); + assignments.setLever(lever); + deviceAssignmentsService.save(assignments); + + } + + private String createDeviceNo() { + String uuidStr = UUID.fastUUID().toString(); // 获取带 - 的标准格式字符串 + return uuidStr.replaceAll("-", ""); + } + + + /** + * 更新设备信息 + * + * @param deviceForm / + * @throws Exception + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void update(DeviceForm deviceForm) throws Exception { + DeviceAssignments deviceAssignments = deviceAssignmentsMapper.selectById(deviceForm.getId()); + + DeviceTypeQueryCriteria deviceTypeQueryCriteria = new DeviceTypeQueryCriteria(); + deviceTypeQueryCriteria.setDeviceTypeId(deviceAssignments.getDeviceId()); + deviceTypeQueryCriteria.setCustomerId(LoginHelper.getUserId()); + List deviceTypes = deviceTypeMapper.findAll(deviceTypeQueryCriteria); + if (deviceTypes.isEmpty()) { + throw new Exception("设备类型不存在!!!"); + } + Device device = deviceMapper.selectById(deviceAssignments.getDeviceId()); + if (device == null) { + throw new Exception("设备不存在!!!"); + } + // 设备类型 + Long deviceType = device.getDeviceType(); + + // 处理上传的图片 + if (deviceForm.getFile() != null) { + SysOssVo upload = ossService.upload(deviceForm.getFile()); + // 设置图片路径 + deviceForm.setDevicePic(upload.getUrl()); + } + + // 更新字段 + BeanUtil.copyProperties(deviceForm, device, true); + device.setId(deviceAssignments.getDeviceId()); + device.setDeviceType(deviceType); + device.setUpdateTime(new Timestamp(System.currentTimeMillis())); + deviceMapper.updateById(device); + } + + + /** + * 保存设备图片并返回访问路径 + * + * @param file MultipartFile + * @param deviceMac 设备MAC用于生成唯一文件名 + * @return 文件存储路径 URL 形式 + */ + private String saveDeviceImage(MultipartFile file, String deviceMac) throws IOException { + if (file == null || file.isEmpty()) { + return null; + } + + String originalFileName = file.getOriginalFilename(); + String fileExtension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1); + String newFileName = "PS_" + deviceMac + "." + fileExtension; + + File newFile = new File(filePath + DeviceConstants.FILE_ACCESS_ISOLATION + File.separator + newFileName); + + if (!newFile.getParentFile().exists()) { + newFile.getParentFile().mkdirs(); + } + + log.info("图片保存路径: {}", newFile.getAbsolutePath()); + file.transferTo(newFile); + + return ip + DeviceConstants.FILE_ACCESS_PREFIX + "/" + DeviceConstants.FILE_ACCESS_ISOLATION + "/" + newFileName; + } + + + /** + * 删除设备 + * + * @param ids / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAll(List ids) { + List invalidIds = new ArrayList<>(); + + deviceAssignmentsMapper.deleteByIds(ids); + // + // for (Long id : ids) { + // + // Device deviceType = deviceMapper.selectById(id); + // if (deviceType == null || !Objects.equals(deviceType.getCurrentOwnerId(), LoginHelper.getUserId())) { + // invalidIds.add(id); + // } + // } + // if (!invalidIds.isEmpty()) { + // throw new RuntimeException("以下设备无法删除(ID 不存在或无权限): " + invalidIds); + // } + // + // deviceMapper.deleteByIds(ids); + } + + + /** + * 分配客户 + * + * @param customerVo + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignCustomer(CustomerVo customerVo) { + + if (customerVo.getDeviceIds().isEmpty() || customerVo.getCustomerId() == null) { + throw new RuntimeException("请选择设备或客户"); + } + + // 获取当前登录用户 + LoginUser loginUser = LoginHelper.getLoginUser(); + // 获取分配用户信息 + Customer assignUser = customerMapper.selectById(customerVo.getCustomerId()); + // 获取分配设备信息 + List assignments = deviceAssignmentsMapper.selectByIds(customerVo.getDeviceIds()); + + // 批量更新设备状态 + List deviceTypeGrants = new ArrayList<>(); + for (DeviceAssignments assignment : assignments) { + + if (assignment.getToCustomerId() != null) { + log.info("设备已经分配客户!!!"); + continue; + } + + Device device = deviceMapper.selectById(assignment.getDeviceId()); + + // 如果设备已分配给需要分配的客户,则跳过 + DeviceAssignmentQuery dq = DeviceAssignmentQuery.builder().deviceId(device.getId()).assigneeId(customerVo.getCustomerId()) + .active(DeviceActiveStatusEnum.ACTIVE.getCode()).lever(assignment.getLever() + USER_ID_SEPARATOR).build(); + // 查询分配 + List assignmentsList = deviceAssignmentsMapper.deviceAssignmentsMapper(dq); + if (CollectionUtil.isNotEmpty(assignmentsList)) { + log.info("设备 {} 已分配给客户 {}", device.getDeviceName(), device.getCustomerName()); + continue; + } + + // 更改分配客户 + assignment.setAssigneeName(assignUser.getNickName()); + assignment.setToCustomerId(customerVo.getCustomerId()); + deviceAssignmentsMapper.updateById(assignment); + + // 设备失效 + // 获取用户的设备记录 + DeviceAssignmentQuery dq1 = DeviceAssignmentQuery.builder().deviceId(device.getId()).assignerId(loginUser.getUserId()) + .active(DeviceActiveStatusEnum.ACTIVE.getCode()).lever(assignment.getLever() + USER_ID_SEPARATOR).build(); + List ag = deviceAssignmentsMapper.deviceAssignmentsMapper(dq1); + if (CollectionUtil.isNotEmpty(ag)) { + for (DeviceAssignments d : ag) { + d.setActive(DeviceActiveStatusEnum.INACTIVE.getCode()); + deviceAssignmentsMapper.updateById(d); + } + } + + // 新增设备类型记录 + DeviceAssignments dam = new DeviceAssignments(); + dam.setDeviceId(device.getId()); + dam.setAssignedAt(LocalDateTime.now()); + // 分配者 + dam.setAssignerId(loginUser.getUserId()); + dam.setAssignerName(loginUser.getUsername()); + // 接收者 + dam.setAssigneeId(assignUser.getCustomerId()); + // assignments.setAssigneeName(assignUser.getUsername()); + dam.setActive(DeviceActiveStatusEnum.ACTIVE.getCode()); + String lever = assignment.getLever() + USER_ID_SEPARATOR + assignUser.getCustomerId(); + dam.setLever(lever); + deviceAssignmentsService.save(dam); + + // 判断设备类型是否存在 + DeviceTypeGrants dtg = deviceTypeGrantsMapper.selectOne(new LambdaQueryWrapper() + .eq(DeviceTypeGrants::getDeviceTypeId, device.getDeviceType()) + .eq(DeviceTypeGrants::getCustomerId, assignUser.getCustomerId())); + if (dtg != null) { + continue; + } + // 创建并保存设备类型授权记录 + DeviceTypeGrants deviceTypeGrant = new DeviceTypeGrants(); + deviceTypeGrant.setGrantedAt(new Date()); + deviceTypeGrant.setDeviceTypeId(device.getDeviceType()); + // 关联分配记录 + deviceTypeGrant.setAssignmentId(dam.getId()); + // 被授权的客户 + deviceTypeGrant.setCustomerId(customerVo.getCustomerId()); + // 授权方客户 + deviceTypeGrant.setGrantorCustomerId(loginUser.getUserId()); + deviceTypeGrants.add(deviceTypeGrant); + } + + deviceTypeGrantsService.saveBatch(deviceTypeGrants); + + } + + /** + * 创建并保存设备类型授权记录 + * + * @param device 当前设备对象 + * @param currentUser 当前登录用户 + * @param customerVo 客户信息 + * @param deviceTypeGrants 授权记录集合 + */ + private void createAndSaveDeviceTypeGrants(Device device, LoginUser currentUser, CustomerVo customerVo, List deviceTypeGrants) { + // Long generatedId = NanoId.generate(NanoIdLengthEnum.HIGH_CONCURRENCY.getLength()); + DeviceTypeGrants deviceTypeGrant = new DeviceTypeGrants(); + deviceTypeGrant.setGrantedAt(new Date()); + deviceTypeGrant.setDeviceTypeId(device.getDeviceType()); + // deviceTypeGrant.setAssignmentId(generatedId); + deviceTypeGrant.setCustomerId(customerVo.getCustomerId()); + deviceTypeGrant.setGrantorCustomerId(currentUser.getUserId()); + deviceTypeGrants.add(deviceTypeGrant); + } + + + /** + * 撤回设备 + * + * @param ids + */ + // @Override + // public void withdrawDevice(List ids) { + // ids.forEach((id) -> { + // List deviceChain = getDeviceChain(id); + // deviceChain.forEach((device) -> { + // device.setDeviceStatus(DeviceStatusEnum.INVALID.getCode()); + // deviceMapper.updateById(device); + // }); + // }); + // + // ids.forEach((id) -> { + // Device device = new Device(); + // device.setId(id); + // device.setCustomerId(null); + // device.setCustomerName(""); + // deviceMapper.updateById(device); + // }); + // + // } + // + // + // public List getDeviceChain(Long originalDeviceId) { + // List chain = new ArrayList<>(); + // Set visited = new HashSet<>(); // 防止循环引用 + // findNext(chain, visited, originalDeviceId); + // return chain; + // } + // + // private void findNext(List chain, Set visited, Long currentOriginalDeviceId) { + // if (visited.contains(currentOriginalDeviceId)) { + // log.info("检测到循环引用,终止递归"); + // return; + // } + // visited.add(currentOriginalDeviceId); + // + // List devices = deviceMapper.findByOriginalDeviceId(currentOriginalDeviceId); + // for (Device device : devices) { + // chain.add(device); + // findNext(chain, visited, device.getId()); + // } + // } + + + /** + * 撤回设备 + * + * @param ids + */ + @Override + @Transactional + public void withdrawDevice(List ids) { + + for (Long id : ids) { + DeviceAssignments assignment = deviceAssignmentsMapper.selectById(id); + Device device = deviceMapper.selectById(assignment.getDeviceId()); + // 接收者 + assignment.setAssigneeName(""); + assignment.setToCustomerId(null); + deviceAssignmentsMapper.updateById(assignment); + + // 获取所有已分配的设备 + DeviceAssignmentQuery dq = DeviceAssignmentQuery.builder().deviceId(device.getId()) + .active(DeviceActiveStatusEnum.ACTIVE.getCode()).lever(assignment.getLever() + USER_ID_SEPARATOR).build(); + // 查询分配 + List assignmentsList = deviceAssignmentsMapper.deviceAssignmentsMapper(dq); + for (DeviceAssignments assignments : assignmentsList) { + assignments.setActive(DeviceActiveStatusEnum.INACTIVE.getCode()); + deviceAssignmentsMapper.updateById(assignments); + } + } + + } + + + /** + * 解绑设备 + * + * @param deviceForm + */ + @Override + @Transactional + public void unbindDevice(DeviceForm deviceForm) { + + } + + + @Override + public TableDataInfo queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) { + Long userId = AppLoginHelper.getUserId(); + bo.setBindingUserId(userId); + Page result = baseMapper.queryAppDeviceList(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + + @Override + public int bindDevice(AppDeviceBo bo) { + Integer mode = bo.getCommunicationMode(); + Long userId = AppLoginHelper.getUserId(); + if (mode == CommunicationModeEnum.FOUR_G.getValue()) { + + String deviceImei = bo.getDeviceImei(); + QueryWrapper qw = new QueryWrapper() + .eq("device_imei", deviceImei); + List devices = baseMapper.selectList(qw); + if (devices.isEmpty()) { + throw new RuntimeException("请先将设备入库!!!"); + } + Device device = devices.get(0); + if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { + throw new RuntimeException("设备已绑定"); + } + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId); + ; + + return baseMapper.update(null, deviceUpdateWrapper); + } else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) { + String deviceMac = bo.getDeviceMac(); + QueryWrapper qw = new QueryWrapper() + .eq("device_mac", deviceMac); + List devices = baseMapper.selectList(qw); + if (devices.isEmpty()) { + throw new RuntimeException("请先将设备入库!!!"); + } + Device device = devices.get(0); + if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) { + throw new RuntimeException("设备已绑定"); + } + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_status", BindingStatusEnum.BOUND.getCode()) + .set("binding_user_id", userId); + return baseMapper.update(null, qw); + } else { + throw new RuntimeException("通讯方式错误"); + } + + } + + @Override + public int unBindDevice(Long id) { + Device device = baseMapper.selectById(id); + if (device == null) { + throw new RuntimeException("请先将设备入库!!!"); + } +// DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType()); +// String mode = deviceType.getCommunicationMode(); + UpdateWrapper deviceUpdateWrapper = new UpdateWrapper<>(); + deviceUpdateWrapper.eq("id", device.getId()) + .set("binding_status", BindingStatusEnum.UNBOUND.getCode()); + return baseMapper.update(null, deviceUpdateWrapper); + } + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeGrantsServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeGrantsServiceImpl.java new file mode 100644 index 0000000..650c3a3 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeGrantsServiceImpl.java @@ -0,0 +1,18 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; +import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper; +import com.fuyuanshen.equipment.service.DeviceTypeGrantsService; +import org.springframework.stereotype.Service; + +/** +* @author 97433 +* @description 针对表【device_type_grants】的数据库操作Service实现 +* @createDate 2025-06-19 13:49:33 +*/ +@Service +public class DeviceTypeGrantsServiceImpl extends ServiceImpl +implements DeviceTypeGrantsService { + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java new file mode 100644 index 0000000..28bbced --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/DeviceTypeServiceImpl.java @@ -0,0 +1,196 @@ +package com.fuyuanshen.equipment.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fuyuanshen.common.core.domain.PageResult; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.PageUtil; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.equipment.domain.Device; +import com.fuyuanshen.equipment.domain.DeviceType; +import com.fuyuanshen.equipment.domain.DeviceTypeGrants; +import com.fuyuanshen.equipment.domain.form.DeviceTypeForm; +import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria; +import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria; +import com.fuyuanshen.equipment.mapper.DeviceMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper; +import com.fuyuanshen.equipment.mapper.DeviceTypeMapper; +import com.fuyuanshen.equipment.service.DeviceTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @Description: + * @Author: WY + * @Date: 2025/5/14 + **/ +@Service +@RequiredArgsConstructor +public class DeviceTypeServiceImpl extends ServiceImpl implements DeviceTypeService { + + private final DeviceTypeMapper deviceTypeMapper; + private final DeviceMapper deviceMapper; + + private final DeviceTypeGrantsMapper deviceTypeGrantsMapper; + + + /** + * 分页查询设备类型 + * + * @param criteria 条件 + * @param page 分页参数 + * @return + */ + @Override + public TableDataInfo queryAll(DeviceTypeQueryCriteria criteria, Page page) { + criteria.setCustomerId(LoginHelper.getUserId()); + // return + IPage deviceTypeIPage = deviceTypeMapper.findAll(criteria, page); + return new TableDataInfo(deviceTypeIPage.getRecords(), deviceTypeIPage.getTotal()); + } + + + @Override + public List queryAll(DeviceTypeQueryCriteria criteria) { + return deviceTypeMapper.findAll(criteria); + } + + + /** + * 查询所有设备类型 + * + * @return + */ + @Override + public List queryDeviceTypes() { + DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria(); + Long userId = LoginHelper.getUserId(); + criteria.setCustomerId(userId); + return deviceTypeMapper.findAll(criteria); + } + + + /** + * 新增设备类型 + * + * @param resources / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void create(DeviceType resources) { + LoginUser loginUser = LoginHelper.getLoginUser(); + resources.setCustomerId(loginUser.getUserId()); + resources.setOwnerCustomerId(loginUser.getUserId()); + resources.setOriginalOwnerId(loginUser.getUserId()); + resources.setCreateByName(loginUser.getNickname()); + deviceTypeMapper.insert(resources); + + // 自动授权给自己 + DeviceTypeGrants deviceTypeGrants = new DeviceTypeGrants(); + deviceTypeGrants.setDeviceTypeId(resources.getId()); + deviceTypeGrants.setCustomerId(loginUser.getUserId()); + deviceTypeGrants.setGrantorCustomerId(loginUser.getUserId()); + deviceTypeGrants.setGrantedAt(new Date()); + deviceTypeGrantsMapper.insert(deviceTypeGrants); + } + + + /** + * 修改设备类型 + * + * @param resources / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void update(DeviceTypeForm resources) { + DeviceTypeGrants deviceTypeGrants = deviceTypeGrantsMapper.selectById(resources.getId()); + if (deviceTypeGrants == null) { + throw new RuntimeException("设备类型不存在"); + } + resources.setId(deviceTypeGrants.getDeviceTypeId()); + DeviceType deviceType = deviceTypeMapper.selectById(resources.getId()); + if (deviceType == null) { + throw new RuntimeException("设备类型不存在"); + } + if (!Objects.equals(deviceType.getCustomerId(), LoginHelper.getUserId())) { + throw new RuntimeException("无权修改该设备类型"); + } + // if (deviceMapper.countByTypeId(resources.getId()) > 0) + // throw new RuntimeException("该设备类型已被使用,无法删除"); + BeanUtil.copyProperties(resources, deviceType); + deviceTypeMapper.updateById(deviceType); + } + + + /** + * 删除设备类型 + * + * @param ids / + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAll(List ids) { + + deviceTypeGrantsMapper.deleteByIds(ids); + // + // List invalidIds = new ArrayList<>(); + // List invalidId2 = new ArrayList<>(); + // for (Long id : ids) { + // DeviceType deviceType = deviceTypeMapper.selectById(id); + // if (deviceType == null || !Objects.equals(deviceType.getOwnerCustomerId(), LoginHelper.getUserId())) { + // invalidIds.add(id); + // } + // DeviceQueryCriteria deviceQueryCriteria = new DeviceQueryCriteria(); + // deviceQueryCriteria.setDeviceType(id); + // List devices = deviceMapper.findAll(deviceQueryCriteria); + // if (!devices.isEmpty()) { + // invalidId2.add(id); + // } + // } + // if (!invalidIds.isEmpty()) { + // throw new RuntimeException("以下设备类型无法删除(ID 不存在或无权限): " + invalidIds); + // } + // if (!invalidId2.isEmpty()) { + // throw new RuntimeException("以下设备类型无法删除(已绑定设备): " + invalidId2); + // } + // + // deviceTypeMapper.deleteByIds(ids); + } + + + /** + * 获取设备类型通讯方式 + * + * @param id / + * @return DeviceTypeDto + */ + @Override + public DeviceType getCommunicationMode(Long id) { + + DeviceTypeGrants deviceTypeGrants = deviceTypeGrantsMapper.selectById(id); + if (deviceTypeGrants == null) { + throw new RuntimeException("设备类型不存在"); + } + + DeviceType deviceType = deviceTypeMapper.selectById(deviceTypeGrants.getDeviceTypeId()); + if (deviceType == null) { + throw new RuntimeException("设备类型不存在"); + } + // + // if (!Objects.equals(deviceType.getCustomerId(), LoginHelper.getUserId())) { + // throw new RuntimeException("无权获取该设备类型通讯方式"); + // } + return deviceType; + } + + +} diff --git a/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/UserAppServiceImpl.java b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/UserAppServiceImpl.java new file mode 100644 index 0000000..59a6e27 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/java/com/fuyuanshen/equipment/service/impl/UserAppServiceImpl.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.equipment.service.impl; + +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.equipment.domain.UserApp; +import com.fuyuanshen.equipment.domain.bo.UserAppBo; +import com.fuyuanshen.equipment.mapper.UserAppMapper; +import com.fuyuanshen.equipment.service.AppUserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collection; + +/** + * Service业务层处理 + * + * @author Lion Li + * @date 2025-06-27 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class UserAppServiceImpl implements AppUserService { + + private final UserAppMapper baseMapper; + + + /** + * 修改APP用户信息 + * + * @param bo APP用户信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(UserAppBo bo) { + UserApp update = MapstructUtils.convert(bo, UserApp.class); + return baseMapper.updateById(update) > 0; + } + + +} diff --git a/fys-modules/fys-equipment/src/main/resources/application.properties b/fys-modules/fys-equipment/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/AppUserMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/AppUserMapper.xml new file mode 100644 index 0000000..a12ad16 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/AppUserMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceAssignmentsMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceAssignmentsMapper.xml new file mode 100644 index 0000000..6055d0e --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceAssignmentsMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + id + ,device_id,from_customer_id,to_customer_id,assigned_at,device_type_granted + + + + + + diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml new file mode 100644 index 0000000..7426896 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceMapper.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + id + , device_type, customer_id, device_name, device_pic, device_mac, device_sn, + remark, create_by, update_by, create_time, update_time + + + + d + . + id + ,d.device_type, + d.customer_id, d.device_name, d.device_pic, d.device_mac, + d.device_sn, d.remark, d.create_by, d.update_by, d.create_time, d.update_time, + d.binding_status, d.device_status, d.current_owner_id, d.original_owner_id, d.customer_name, + d.device_imei, d.longitude, d.latitude, d.tenant_id + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeGrantsMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeGrantsMapper.xml new file mode 100644 index 0000000..3f52045 --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeGrantsMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + id,device_type_id,customer_id,grantor_customer_id,granted_at,assignment_id + + diff --git a/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeMapper.xml b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeMapper.xml new file mode 100644 index 0000000..56f47cc --- /dev/null +++ b/fys-modules/fys-equipment/src/main/resources/mapper/equipment/DeviceTypeMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + id + , type_name, is_support_ble, locate_mode, network_way, create_by, update_by, create_time, update_time,communication_mode + + + + + + + + \ No newline at end of file diff --git a/fys-modules/fys-generator/pom.xml b/fys-modules/fys-generator/pom.xml new file mode 100644 index 0000000..be9d9c2 --- /dev/null +++ b/fys-modules/fys-generator/pom.xml @@ -0,0 +1,84 @@ + + + + com.fuyuanshen + fys-modules + ${revision} + + 4.0.0 + + fys-generator + + + generator 代码生成 + + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-mybatis + + + + com.fuyuanshen + fys-common-web + + + + com.fuyuanshen + fys-common-log + + + + + org.apache.velocity + velocity-engine-core + + + + org.anyline + anyline-environment-spring-data-jdbc + ${anyline.version} + + + + org.anyline + anyline-data-jdbc-mysql + ${anyline.version} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/GenConfig.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/GenConfig.java new file mode 100644 index 0000000..972f2b0 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author fys + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = {"classpath:generator.yml"}, encoding = "UTF-8") +public class GenConfig { + + /** + * 作者 + */ + public static String author; + + /** + * 生成包路径 + */ + public static String packageName; + + /** + * 自动去除表前缀,默认是false + */ + public static boolean autoRemovePre; + + /** + * 表前缀(类名不会包含表前缀) + */ + public static String tablePrefix; + + public static String getAuthor() { + return author; + } + + @Value("${author}") + public void setAuthor(String author) { + GenConfig.author = author; + } + + public static String getPackageName() { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/MyBatisDataSourceMonitor.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/MyBatisDataSourceMonitor.java new file mode 100644 index 0000000..ffdb3e1 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/config/MyBatisDataSourceMonitor.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.generator.config; + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.anyline.data.datasource.DataSourceMonitor; +import org.anyline.data.runtime.DataRuntime; +import org.anyline.util.ConfigTable; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.util.HashMap; +import java.util.Map; + +/** + * anyline 适配 动态数据源改造 + * + * @author Lion Li + */ +@Slf4j +@Component +public class MyBatisDataSourceMonitor implements DataSourceMonitor { + + public MyBatisDataSourceMonitor() { + // 调整执行模式为自定义 + ConfigTable.KEEP_ADAPTER = 2; + // 禁用缓存 + ConfigTable.METADATA_CACHE_SCOPE = 0; + } + + private final Map features = new HashMap<>(); + + /** + * 数据源特征 用来定准 adapter 包含数据库或JDBC协议关键字
+ * 一般会通过 产品名_url 合成 如果返回null 上层方法会通过driver_产品名_url合成 + * + * @param datasource 数据源 + * @return String 返回null由上层自动提取 + */ + @Override + public String feature(DataRuntime runtime, Object datasource) { + String feature = null; + if (datasource instanceof JdbcTemplate jdbc) { + DataSource ds = jdbc.getDataSource(); + if (ds instanceof DynamicRoutingDataSource) { + String key = DynamicDataSourceContextHolder.peek(); + feature = features.get(key); + if (null == feature) { + Connection con = null; + try { + con = DataSourceUtils.getConnection(ds); + DatabaseMetaData meta = con.getMetaData(); + String url = meta.getURL(); + feature = meta.getDatabaseProductName().toLowerCase().replace(" ", "") + "_" + url; + features.put(key, feature); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (null != con && !DataSourceUtils.isConnectionTransactional(con, ds)) { + DataSourceUtils.releaseConnection(con, ds); + } + } + } + } + } + return feature; + } + + /** + * 数据源唯一标识 如果不实现则默认feature + * @param datasource 数据源 + * @return String 返回null由上层自动提取 + */ + @Override + public String key(DataRuntime runtime, Object datasource) { + if(datasource instanceof JdbcTemplate jdbc){ + DataSource ds = jdbc.getDataSource(); + if(ds instanceof DynamicRoutingDataSource){ + return DynamicDataSourceContextHolder.peek(); + } + } + return runtime.getKey(); + } + + /** + * ConfigTable.KEEP_ADAPTER=2 : 根据当前接口判断是否保持同一个数据源绑定同一个adapter
+ * DynamicRoutingDataSource类型的返回false,因为同一个DynamicRoutingDataSource可能对应多类数据库, 如果项目中只有一种数据库 应该直接返回true + * + * @param datasource 数据源 + * @return boolean + */ + @Override + public boolean keepAdapter(DataRuntime runtime, Object datasource) { + if (datasource instanceof JdbcTemplate jdbc) { + DataSource ds = jdbc.getDataSource(); + return !(ds instanceof DynamicRoutingDataSource); + } + return true; + } + +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/constant/GenConstants.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/constant/GenConstants.java new file mode 100644 index 0000000..7b60274 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/constant/GenConstants.java @@ -0,0 +1,186 @@ +package com.fuyuanshen.generator.constant; + +/** + * 代码生成通用常量 + * + * @author fys + */ +public interface GenConstants { + /** + * 单表(增删改查) + */ + String TPL_CRUD = "crud"; + + /** + * 树表(增删改查) + */ + String TPL_TREE = "tree"; + + /** + * 树编码字段 + */ + String TREE_CODE = "treeCode"; + + /** + * 树父编码字段 + */ + String TREE_PARENT_CODE = "treeParentCode"; + + /** + * 树名称字段 + */ + String TREE_NAME = "treeName"; + + /** + * 上级菜单ID字段 + */ + String PARENT_MENU_ID = "parentMenuId"; + + /** + * 上级菜单名称字段 + */ + String PARENT_MENU_NAME = "parentMenuName"; + + /** + * 数据库字符串类型 + */ + String[] COLUMNTYPE_STR = {"char", "varchar", "enum", "set", "nchar", "nvarchar", "varchar2", "nvarchar2"}; + + /** + * 数据库文本类型 + */ + String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext", "binary", "varbinary", "blob", + "ntext", "image", "bytea"}; + + /** + * 数据库时间类型 + */ + String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp", "year", "interval", + "smalldatetime", "datetime2", "datetimeoffset", "timestamptz"}; + + /** + * 数据库数字类型 + */ + String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "int2", "int4", "int8", "number", "integer", + "bit", "bigint", "float", "float4", "float8", "double", "decimal", "numeric", "real", "double precision", + "smallserial", "serial", "bigserial", "money", "smallmoney"}; + + /** + * BO对象 不需要添加字段 + */ + String[] COLUMNNAME_NOT_ADD = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * BO对象 不需要编辑字段 + */ + String[] COLUMNNAME_NOT_EDIT = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * VO对象 不需要返回字段 + */ + String[] COLUMNNAME_NOT_LIST = {"create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "version", "tenant_id"}; + + /** + * BO对象 不需要查询字段 + */ + String[] COLUMNNAME_NOT_QUERY = {"id", "create_dept", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark", "version", "tenant_id"}; + + /** + * Entity基类字段 + */ + String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime", "tenantId"}; + + /** + * 文本框 + */ + String HTML_INPUT = "input"; + + /** + * 文本域 + */ + String HTML_TEXTAREA = "textarea"; + + /** + * 下拉框 + */ + String HTML_SELECT = "select"; + + /** + * 单选框 + */ + String HTML_RADIO = "radio"; + + /** + * 复选框 + */ + String HTML_CHECKBOX = "checkbox"; + + /** + * 日期控件 + */ + String HTML_DATETIME = "datetime"; + + /** + * 图片上传控件 + */ + String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** + * 文件上传控件 + */ + String HTML_FILE_UPLOAD = "fileUpload"; + + /** + * 富文本控件 + */ + String HTML_EDITOR = "editor"; + + /** + * 字符串类型 + */ + String TYPE_STRING = "String"; + + /** + * 整型 + */ + String TYPE_INTEGER = "Integer"; + + /** + * 长整型 + */ + String TYPE_LONG = "Long"; + + /** + * 浮点型 + */ + String TYPE_DOUBLE = "Double"; + + /** + * 高精度计算类型 + */ + String TYPE_BIGDECIMAL = "BigDecimal"; + + /** + * 时间类型 + */ + String TYPE_DATE = "Date"; + + /** + * 模糊查询 + */ + String QUERY_LIKE = "LIKE"; + + /** + * 相等查询 + */ + String QUERY_EQ = "EQ"; + + /** + * 需要 + */ + String REQUIRE = "1"; +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/controller/GenController.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/controller/GenController.java new file mode 100644 index 0000000..e2c1eb2 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/controller/GenController.java @@ -0,0 +1,217 @@ +package com.fuyuanshen.generator.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.generator.domain.GenTable; +import com.fuyuanshen.generator.domain.GenTableColumn; +import com.fuyuanshen.generator.service.IGenTableService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController { + + private final IGenTableService genTableService; + + /** + * 查询代码生成列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageGenTableList(genTable, pageQuery); + } + + /** + * 修改代码生成业务 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:query") + @GetMapping(value = "/{tableId}") + public R> getInfo(@PathVariable Long tableId) { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap<>(3); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return R.ok(map); + } + + /** + * 查询数据库列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable, PageQuery pageQuery) { + return genTableService.selectPageDbTableList(genTable, pageQuery); + } + + /** + * 查询数据表字段列表 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:list") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(@PathVariable("tableId") Long tableId) { + TableDataInfo dataInfo = new TableDataInfo<>(); + List list = genTableService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + * + * @param tables 表名串 + */ + @SaCheckPermission("tool:gen:import") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public R importTableSave(String tables, String dataName) { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames, dataName); + genTableService.importGenTable(tableList, dataName); + return R.ok(); + } + + /** + * 修改保存代码生成业务 + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public R editSave(@Validated @RequestBody GenTable genTable) { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return R.ok(); + } + + /** + * 删除代码生成 + * + * @param tableIds 表ID串 + */ + @SaCheckPermission("tool:gen:remove") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public R remove(@PathVariable Long[] tableIds) { + genTableService.deleteGenTableByIds(tableIds); + return R.ok(); + } + + /** + * 预览代码 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:preview") + @GetMapping("/preview/{tableId}") + public R> preview(@PathVariable("tableId") Long tableId) throws IOException { + Map dataMap = genTableService.previewCode(tableId); + return R.ok(dataMap); + } + + /** + * 生成代码(下载方式) + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableId}") + public void download(HttpServletResponse response, @PathVariable("tableId") Long tableId) throws IOException { + byte[] data = genTableService.downloadCode(tableId); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableId}") + public R genCode(@PathVariable("tableId") Long tableId) { + genTableService.generatorCode(tableId); + return R.ok(); + } + + /** + * 同步数据库 + * + * @param tableId 表ID + */ + @SaCheckPermission("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableId}") + public R synchDb(@PathVariable("tableId") Long tableId) { + genTableService.synchDb(tableId); + return R.ok(); + } + + /** + * 批量生成代码 + * + * @param tableIdStr 表ID串 + */ + @SaCheckPermission("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tableIdStr) throws IOException { + String[] tableIds = Convert.toStrArray(tableIdStr); + byte[] data = genTableService.downloadCode(tableIds); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"fys.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IoUtil.write(response.getOutputStream(), false, data); + } + + /** + * 查询数据源名称列表 + */ + @SaCheckPermission("tool:gen:list") + @GetMapping(value = "/getDataNames") + public R getCurrentDataSourceNameList(){ + return R.ok(DataBaseHelper.getDataSourceNameList()); + } +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTable.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTable.java new file mode 100644 index 0000000..85787ab --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTable.java @@ -0,0 +1,196 @@ +package com.fuyuanshen.generator.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.generator.constant.GenConstants; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 业务表 gen_table + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table") +public class GenTable extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "table_id") + private Long tableId; + + /** + * 数据源名称 + */ + @NotBlank(message = "数据源名称不能为空") + private String dataName; + + /** + * 表名称 + */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** + * 表描述 + */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** + * 关联父表的表名 + */ + private String subTableName; + + /** + * 本表关联父表的外键名 + */ + private String subTableFkName; + + /** + * 实体类名称(首字母大写) + */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** + * 使用的模板(crud单表操作 tree树表操作 sub主子表操作) + */ + private String tplCategory; + + /** + * 生成包路径 + */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** + * 生成模块名 + */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** + * 生成业务名 + */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** + * 生成功能名 + */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** + * 生成作者 + */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** + * 生成代码方式(0zip压缩包 1自定义路径) + */ + private String genType; + + /** + * 生成路径(不填默认项目路径) + */ + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) + private String genPath; + + /** + * 主键信息 + */ + @TableField(exist = false) + private GenTableColumn pkColumn; + + /** + * 表列信息 + */ + @Valid + @TableField(exist = false) + private List columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 备注 + */ + private String remark; + + /** + * 树编码字段 + */ + @TableField(exist = false) + private String treeCode; + + /** + * 树父编码字段 + */ + @TableField(exist = false) + private String treeParentCode; + + /** + * 树名称字段 + */ + @TableField(exist = false) + private String treeName; + + /* + * 菜单id列表 + */ + @TableField(exist = false) + private List menuIds; + + /** + * 上级菜单ID字段 + */ + @TableField(exist = false) + private Long parentMenuId; + + /** + * 上级菜单名称字段 + */ + @TableField(exist = false) + private String parentMenuName; + + public boolean isTree() { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTableColumn.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..55effd6 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/domain/GenTableColumn.java @@ -0,0 +1,222 @@ +package com.fuyuanshen.generator.domain; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.ibatis.type.JdbcType; + +import jakarta.validation.constraints.NotBlank; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("gen_table_column") +public class GenTableColumn extends BaseEntity { + + /** + * 编号 + */ + @TableId(value = "column_id") + private Long columnId; + + /** + * 归属表编号 + */ + private Long tableId; + + /** + * 列名称 + */ + private String columnName; + + /** + * 列描述 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String columnComment; + + /** + * 列类型 + */ + private String columnType; + + /** + * JAVA类型 + */ + private String javaType; + + /** + * JAVA字段名 + */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** + * 是否主键(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isPk; + + /** + * 是否自增(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isIncrement; + + /** + * 是否必填(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isRequired; + + /** + * 是否为插入字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isInsert; + + /** + * 是否编辑字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isEdit; + + /** + * 是否列表字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isList; + + /** + * 是否查询字段(1是) + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR) + private String isQuery; + + /** + * 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) + */ + private String queryType; + + /** + * 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) + */ + private String htmlType; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 排序 + */ + private Integer sort; + + public String getCapJavaField() { + return StringUtils.capitalize(javaField); + } + + public boolean isPk() { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) { + return isPk != null && StringUtils.equals("1", isPk); + } + + public boolean isIncrement() { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public boolean isRequired() { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public boolean isInsert() { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public boolean isEdit() { + return isEdit(this.isEdit); + } + + public boolean isEdit(String isEdit) { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public boolean isList() { + return isList(this.isList); + } + + public boolean isList(String isList) { + return isList != null && StringUtils.equals("1", isList); + } + + public boolean isQuery() { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public boolean isSuperColumn() { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", + // TreeEntity + "parentName", "parentId"); + } + + public boolean isUsableColumn() { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) { + for (String value : remarks.split(" ")) { + if (StringUtils.isNotEmpty(value)) { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableColumnMapper.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..a467907 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.generator.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface GenTableColumnMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableMapper.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..1036632 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/mapper/GenTableMapper.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.generator.mapper; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.generator.domain.GenTable; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author Lion Li + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface GenTableMapper extends BaseMapperPlus { + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + GenTable selectGenTableByName(String tableName); + + /** + * 查询指定数据源下的所有表名列表 + * + * @param dataName 数据源名称,用于选择不同的数据源 + * @return 当前数据库中的表名列表 + * + * @DS("") 使用默认数据源执行查询操作 + */ + @DS("") + List selectTableNameList(String dataName); +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/GenTableServiceImpl.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..007370e --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/GenTableServiceImpl.java @@ -0,0 +1,576 @@ +package com.fuyuanshen.generator.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.anyline.metadata.Column; +import org.anyline.metadata.Table; +import org.anyline.proxy.ServiceProxy; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.file.FileUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.generator.constant.GenConstants; +import com.fuyuanshen.generator.domain.GenTable; +import com.fuyuanshen.generator.domain.GenTableColumn; +import com.fuyuanshen.generator.mapper.GenTableColumnMapper; +import com.fuyuanshen.generator.mapper.GenTableMapper; +import com.fuyuanshen.generator.util.GenUtils; +import com.fuyuanshen.generator.util.VelocityInitializer; +import com.fuyuanshen.generator.util.VelocityUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 业务 服务层实现 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class GenTableServiceImpl implements IGenTableService { + + private final GenTableMapper baseMapper; + private final GenTableColumnMapper genTableColumnMapper; + private final IdentifierGenerator identifierGenerator; + + private static final String[] TABLE_IGNORE = new String[]{"sj_", "flow_", "gen_"}; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) { + return genTableColumnMapper.selectList(new LambdaQueryWrapper() + .eq(GenTableColumn::getTableId, tableId) + .orderByAsc(GenTableColumn::getSort)); + } + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) { + GenTable genTable = baseMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + @Override + public TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery) { + Page page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); + return TableDataInfo.build(page); + } + + private QueryWrapper buildGenTableQueryWrapper(GenTable genTable) { + Map params = genTable.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper + .eq(StringUtils.isNotEmpty(genTable.getDataName()), "data_name", genTable.getDataName()) + .like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName())) + .like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment())) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "create_time", params.get("beginTime"), params.get("endTime")) + .orderByDesc("update_time"); + return wrapper; + } + + /** + * 查询数据库列表 + * + * @param genTable 包含查询条件的GenTable对象 + * @param pageQuery 包含分页信息的PageQuery对象 + * @return 包含分页结果的TableDataInfo对象 + */ + @DS("#genTable.dataName") + @Override + public TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery) { + // 获取查询条件 + String tableName = genTable.getTableName(); + String tableComment = genTable.getTableComment(); + + LinkedHashMap> tablesMap = ServiceProxy.metadata().tables(); + if (CollUtil.isEmpty(tablesMap)) { + return TableDataInfo.build(); + } + List tableNames = baseMapper.selectTableNameList(genTable.getDataName()); + String[] tableArrays; + if (CollUtil.isNotEmpty(tableNames)) { + tableArrays = tableNames.toArray(new String[0]); + } else { + tableArrays = new String[0]; + } + // 过滤并转换表格数据 + List tables = tablesMap.values().stream() + .filter(x -> !StringUtils.startWithAnyIgnoreCase(x.getName(), TABLE_IGNORE)) + .filter(x -> { + if (CollUtil.isEmpty(tableNames)) { + return true; + } + return !StringUtils.equalsAnyIgnoreCase(x.getName(), tableArrays); + }) + .filter(x -> { + boolean nameMatches = true; + boolean commentMatches = true; + // 进行表名称的模糊查询 + if (StringUtils.isNotBlank(tableName)) { + nameMatches = StringUtils.containsIgnoreCase(x.getName(), tableName); + } + // 进行表描述的模糊查询 + if (StringUtils.isNotBlank(tableComment)) { + commentMatches = StringUtils.containsIgnoreCase(x.getComment(), tableComment); + } + // 同时匹配名称和描述 + return nameMatches && commentMatches; + }) + .map(x -> { + GenTable gen = new GenTable(); + gen.setTableName(x.getName()); + gen.setTableComment(x.getComment()); + // postgresql的表元数据没有创建时间这个东西(好奇葩) 只能new Date代替 + gen.setCreateTime(ObjectUtil.defaultIfNull(x.getCreateTime(), new Date())); + gen.setUpdateTime(x.getUpdateTime()); + return gen; + }).sorted(Comparator.comparing(GenTable::getCreateTime).reversed()) + .toList(); + return TableDataInfo.build(tables, pageQuery.build()); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @param dataName 数据源名称 + * @return 数据库表集合 + */ + @DS("#dataName") + @Override + public List selectDbTableListByNames(String[] tableNames, String dataName) { + Set tableNameSet = new HashSet<>(List.of(tableNames)); + LinkedHashMap> tablesMap = ServiceProxy.metadata().tables(); + + if (CollUtil.isEmpty(tablesMap)) { + return new ArrayList<>(); + } + + List> tableList = tablesMap.values().stream() + .filter(x -> !StringUtils.startWithAnyIgnoreCase(x.getName(), TABLE_IGNORE)) + .filter(x -> tableNameSet.contains(x.getName())).toList(); + + if (CollUtil.isEmpty(tableList)) { + return new ArrayList<>(); + } + return tableList.stream().map(x -> { + GenTable gen = new GenTable(); + gen.setDataName(dataName); + gen.setTableName(x.getName()); + gen.setTableComment(x.getComment()); + gen.setCreateTime(x.getCreateTime()); + gen.setUpdateTime(x.getUpdateTime()); + return gen; + }).toList(); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() { + return baseMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void updateGenTable(GenTable genTable) { + String options = JsonUtils.toJsonString(genTable.getParams()); + genTable.setOptions(options); + int row = baseMapper.updateById(genTable); + if (row > 0) { + for (GenTableColumn cenTableColumn : genTable.getColumns()) { + genTableColumnMapper.updateById(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteGenTableByIds(Long[] tableIds) { + List ids = Arrays.asList(tableIds); + baseMapper.deleteByIds(ids); + genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + * @param dataName 数据源名称 + */ + @DSTransactional + @Override + public void importGenTable(List tableList, String dataName) { + try { + for (GenTable table : tableList) { + String tableName = table.getTableName(); + GenUtils.initTable(table); + table.setDataName(dataName); + int row = baseMapper.insert(table); + if (row > 0) { + // 保存列信息 + List genTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(tableName, dataName); + List saveColumns = new ArrayList<>(); + for (GenTableColumn column : genTableColumns) { + GenUtils.initColumnField(column, table); + saveColumns.add(column); + } + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertBatch(saveColumns); + } + } + } + } catch (Exception e) { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @param dataName 数据源名称 + * @return 列信息 + */ + @DS("#dataName") + @Override + public List selectDbTableColumnsByName(String tableName, String dataName) { + Table table = ServiceProxy.metadata().table(tableName); + if (ObjectUtil.isNull(table)) { + return new ArrayList<>(); + } + LinkedHashMap columns = table.getColumns(); + List tableColumns = new ArrayList<>(); + columns.forEach((columnName, column) -> { + GenTableColumn tableColumn = new GenTableColumn(); + tableColumn.setIsPk(String.valueOf(column.isPrimaryKey())); + tableColumn.setColumnName(column.getName()); + tableColumn.setColumnComment(column.getComment()); + tableColumn.setColumnType(column.getOriginType().toLowerCase()); + tableColumn.setSort(column.getPosition()); + tableColumn.setIsRequired(column.isNullable() == 0 ? "1" : "0"); + tableColumn.setIsIncrement(column.isAutoIncrement() == -1 ? "0" : "1"); + tableColumns.add(tableColumn); + }); + return tableColumns; + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableId 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(Long tableId) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableId, zip); + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableId 表名称 + */ + @Override + public void generatorCode(Long tableId) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + if (!StringUtils.containsAny(template, "sql.vm", "api.ts.vm", "types.ts.vm", "index.vue.vm", "index-tree.vue.vm")) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + String path = getGenPath(table, template); + FileUtils.writeUtf8String(sw.toString(), path); + } catch (Exception e) { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableId 表名称 + */ + @DSTransactional + @Override + public void synchDb(Long tableId) { + GenTable table = baseMapper.selectGenTableById(tableId); + List tableColumns = table.getColumns(); + Map tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName); + + List dbTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(table.getTableName(), table.getDataName()); + if (CollUtil.isEmpty(dbTableColumns)) { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName); + + List saveColumns = new ArrayList<>(); + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + } + saveColumns.add(column); + }); + if (CollUtil.isNotEmpty(saveColumns)) { + genTableColumnMapper.insertOrUpdateBatch(saveColumns); + } + List delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName())); + if (CollUtil.isNotEmpty(delColumns)) { + List ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId); + if (CollUtil.isNotEmpty(ids)) { + genTableColumnMapper.deleteByIds(ids); + } + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableIds 表ID数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableIds) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableId : tableIds) { + generatorCode(Long.parseLong(tableId), zip); + } + IoUtil.close(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(Long tableId, ZipOutputStream zip) { + // 查询表信息 + GenTable table = baseMapper.selectGenTableById(tableId); + List menuIds = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + menuIds.add(identifierGenerator.nextId(null).longValue()); + } + table.setMenuIds(menuIds); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString()); + IoUtil.close(sw); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) { + String options = JsonUtils.toJsonString(genTable.getParams()); + Dict paramsObj = JsonUtils.parseMap(options); + if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) { + throw new ServiceException("树编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) { + throw new ServiceException("树父编码字段不能为空"); + } else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) { + throw new ServiceException("树名称字段不能为空"); + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) { + for (GenTableColumn column : table.getColumns()) { + if (column.isPk()) { + table.setPkColumn(column); + break; + } + } + if (ObjectUtil.isNull(table.getPkColumn())) { + table.setPkColumn(table.getColumns().get(0)); + } + + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) { + Dict paramsObj = JsonUtils.parseMap(genTable.getOptions()); + if (ObjectUtil.isNotNull(paramsObj)) { + String treeCode = paramsObj.getStr(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + Long parentMenuId = paramsObj.getLong(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} + diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/IGenTableService.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/IGenTableService.java new file mode 100644 index 0000000..2bf58ec --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/service/IGenTableService.java @@ -0,0 +1,141 @@ +package com.fuyuanshen.generator.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.generator.domain.GenTable; +import com.fuyuanshen.generator.domain.GenTableColumn; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author Lion Li + */ +public interface IGenTableService { + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId(Long tableId); + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + TableDataInfo selectPageGenTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + TableDataInfo selectPageDbTableList(GenTable genTable, PageQuery pageQuery); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @param dataName 数据源名称 + * @return 数据库表集合 + */ + List selectDbTableListByNames(String[] tableNames, String dataName); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + */ + void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + */ + void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + * @param dataName 数据源名称 + */ + void importGenTable(List tableList, String dataName); + + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @param dataName 数据源名称 + * @return 列信息 + */ + List selectDbTableColumnsByName(String tableName, String dataName); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableId 表名称 + * @return 数据 + */ + byte[] downloadCode(Long tableId); + + /** + * 生成代码(自定义路径) + * + * @param tableId 表名称 + */ + void generatorCode(Long tableId); + + /** + * 同步数据库 + * + * @param tableId 表名称 + */ + void synchDb(Long tableId); + + /** + * 批量生成代码(下载方式) + * + * @param tableIds 表ID数组 + * @return 数据 + */ + byte[] downloadCode(String[] tableIds); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + void validateEdit(GenTable genTable); +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/GenUtils.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/GenUtils.java new file mode 100644 index 0000000..9685c79 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/GenUtils.java @@ -0,0 +1,219 @@ +package com.fuyuanshen.generator.util; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.RegExUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.generator.config.GenConfig; +import com.fuyuanshen.generator.constant.GenConstants; +import com.fuyuanshen.generator.domain.GenTable; +import com.fuyuanshen.generator.domain.GenTableColumn; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author fys + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GenUtils { + + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable) { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateTime(null); + genTable.setUpdateTime(null); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) { + String dataType = getDbType(column.getColumnType()); + // 统一转小写 避免有些数据库默认大写问题 如果需要特别书写方式 请在实体类增加注解标注别名 + String columnName = column.getColumnName().toLowerCase(); + column.setTableId(table.getTableId()); + column.setCreateTime(null); + column.setUpdateTime(null); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) { + column.setHtmlType(GenConstants.HTML_INPUT); + // 数据库的数字字段与java不匹配 且很多数据库的数字字段很模糊 例如oracle只有number没有细分 + // 所以默认数字类型全为Long可在界面上自行编辑想要的类型 有什么特殊需求也可以在这里特殊处理 + column.setJavaType(GenConstants.TYPE_LONG); + } + + // BO对象 默认插入勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) { + column.setIsInsert(GenConstants.REQUIRE); + } + // BO对象 默认编辑勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) { + column.setIsEdit(GenConstants.REQUIRE); + } + // VO对象 默认返回勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) { + column.setIsList(GenConstants.REQUIRE); + } + // BO对象 默认查询勾选 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) { + int firstIndex = tableName.indexOf("_"); + int nameLength = tableName.length(); + String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength); + businessName = StringUtils.toCamelCase(businessName); + return businessName; + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) { + String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + */ + public static String replaceFirst(String replacementm, String[] searchList) { + String text = replacementm; + for (String searchString : searchList) { + if (replacementm.startsWith(searchString)) { + text = replacementm.replaceFirst(searchString, StringUtils.EMPTY); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) { + if (StringUtils.indexOf(columnType, "(") > 0) { + return StringUtils.substringBefore(columnType, "("); + } else { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) { + if (StringUtils.indexOf(columnType, "(") > 0) { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } else { + return 0; + } + } +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityInitializer.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..e9ee7f6 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityInitializer.java @@ -0,0 +1,35 @@ +package com.fuyuanshen.generator.util; + +import com.fuyuanshen.common.core.constant.Constants; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.app.Velocity; + +import java.util.Properties; + +/** + * VelocityEngine工厂 + * + * @author fys + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class VelocityInitializer { + + /** + * 初始化vm方法 + */ + public static void initVelocity() { + Properties p = new Properties(); + try { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityUtils.java b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityUtils.java new file mode 100644 index 0000000..0daa046 --- /dev/null +++ b/fys-modules/fys-generator/src/main/java/com/fuyuanshen/generator/util/VelocityUtils.java @@ -0,0 +1,341 @@ +package com.fuyuanshen.generator.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Dict; +import com.fuyuanshen.generator.constant.GenConstants; +import com.fuyuanshen.common.core.utils.DateUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.generator.domain.GenTable; +import com.fuyuanshen.generator.domain.GenTableColumn; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.velocity.VelocityContext; + +import java.util.*; + +/** + * 模板处理工具类 + * + * @author fys + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class VelocityUtils { + + /** + * 项目空间路径 + */ + private static final String PROJECT_PATH = "main/java"; + + /** + * mybatis空间路径 + */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** + * 默认上级菜单,系统工具 + */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) { + setTreeVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) { + context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME)); + } + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory) { + List templates = new ArrayList<>(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/vo.java.vm"); + templates.add("vm/java/bo.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + if (DataBaseHelper.isOracle()) { + templates.add("vm/sql/oracle/sql.vm"); + } else if (DataBaseHelper.isPostgerSql()) { + templates.add("vm/sql/postgres/sql.vm"); + } else if (DataBaseHelper.isSqlServer()) { + templates.add("vm/sql/sqlserver/sql.vm"); + } else { + templates.add("vm/sql/sql.vm"); + } + templates.add("vm/ts/api.ts.vm"); + templates.add("vm/ts/types.ts.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) { + templates.add("vm/vue/index.vue.vm"); + } else if (GenConstants.TPL_TREE.equals(tplCategory)) { + templates.add("vm/vue/index-tree.vue.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("vo.java.vm")) { + fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className); + } + if (template.contains("bo.java.vm")) { + fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className); + } + if (template.contains("mapper.java.vm")) { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } else if (template.contains("service.java.vm")) { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } else if (template.contains("serviceImpl.java.vm")) { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } else if (template.contains("controller.java.vm")) { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } else if (template.contains("mapper.xml.vm")) { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } else if (template.contains("sql.vm")) { + fileName = businessName + "Menu.sql"; + } else if (template.contains("api.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/index.ts", vuePath, moduleName, businessName); + } else if (template.contains("types.ts.vm")) { + fileName = StringUtils.format("{}/api/{}/{}/types.ts", vuePath, moduleName, businessName); + } else if (template.contains("index.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } else if (template.contains("index-tree.vue.vm")) { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) { + List columns = genTable.getColumns(); + HashSet importList = new HashSet<>(); + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) { + importList.add("java.math.BigDecimal"); + } else if (!column.isSuperColumn() && "imageUpload".equals(column.getHtmlType())) { + importList.add("com.fuyuanshen.common.translation.annotation.Translation"); + importList.add("com.fuyuanshen.common.translation.constant.TransConstant"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) { + List columns = genTable.getColumns(); + Set dicts = new HashSet<>(); + addDicts(dicts, columns); + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) { + for (GenTableColumn column : columns) { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) { + return paramsObj.getStr(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(Map paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) { + return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE))); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(Dict paramsObj) { + if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) { + return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) { + String options = genTable.getOptions(); + Dict paramsObj = JsonUtils.parseMap(options); + String treeName = paramsObj.getStr(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) { + if (column.isList()) { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) { + break; + } + } + } + return num; + } +} diff --git a/fys-modules/fys-generator/src/main/resources/generator.yml b/fys-modules/fys-generator/src/main/resources/generator.yml new file mode 100644 index 0000000..05d8e09 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: Lion Li + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.fuyuanshen.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ diff --git a/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..64a57ec --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..5663f2b --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + SELECT t.table_id, t.data_name, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, + c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort + FROM gen_table t + LEFT JOIN gen_table_column c ON t.table_id = c.table_id + + + + + + + + + + diff --git a/fys-modules/fys-generator/src/main/resources/mapper/package-info.md b/fys-modules/fys-generator/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/bo.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/bo.java.vm new file mode 100644 index 0000000..7a712b9 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/bo.java.vm @@ -0,0 +1,48 @@ +package ${packageName}.domain.bo; + +import ${packageName}.domain.${ClassName}; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +#foreach ($import in $importList) +import ${import}; +#end + +/** + * ${functionName}业务对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ${ClassName}.class, reverseConvertGenerate = false) +public class ${ClassName}Bo extends BaseEntity { + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.insert || $column.edit)) + /** + * $column.columnComment + */ +#if($column.insert && $column.edit) +#set($Group="AddGroup.class, EditGroup.class") +#elseif($column.insert) +#set($Group="AddGroup.class") +#elseif($column.edit) +#set($Group="EditGroup.class") +#end +#if($column.required) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空", groups = { $Group }) +#else + @NotNull(message = "$column.columnComment不能为空", groups = { $Group }) +#end +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/controller.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..9f9c818 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.service.I${ClassName}Service; +#if($table.crud) +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName} + * + * @author ${author} + * @date ${datetime} + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController { + + private final I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud) + public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { + return ${className}Service.queryPageList(bo, pageQuery); + } +#elseif($table.tree) + public R> list(${ClassName}Bo bo) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + return R.ok(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @SaCheckPermission("${permissionPrefix}:export") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(${ClassName}Bo bo, HttpServletResponse response) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); + } + + /** + * 获取${functionName}详细信息 + * + * @param ${pkColumn.javaField} 主键 + */ + @SaCheckPermission("${permissionPrefix}:query") + @GetMapping("/{${pkColumn.javaField}}") + public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空") + @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { + return R.ok(${className}Service.queryById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @SaCheckPermission("${permissionPrefix}:add") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.insertByBo(bo)); + } + + /** + * 修改${functionName} + */ + @SaCheckPermission("${permissionPrefix}:edit") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.updateByBo(bo)); + } + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField}s 主键串 + */ + @SaCheckPermission("${permissionPrefix}:remove") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true)); + } +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/domain.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..24dbd1c --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,59 @@ +package ${packageName}.domain; + +#foreach ($column in $columns) +#if($column.javaField=='tenantId') +#set($IsTenant=1) +#end +#end +#if($IsTenant==1) +import com.fuyuanshen.common.tenant.core.TenantEntity; +#else +#end +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +#foreach ($import in $importList) +import ${import}; +#end + +import java.io.Serial; + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($IsTenant==1) +#set($Entity="TenantEntity") +#else +#set($Entity="BaseEntity") +#end +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("${tableName}") +public class ${ClassName} extends ${Entity} { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** + * $column.columnComment + */ +#if($column.javaField=='delFlag') + @TableLogic +#end +#if($column.javaField=='version') + @Version +#end +#if($column.isPk==1) + @TableId(value = "$column.columnName") +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/mapper.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..6e29693 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,15 @@ +package ${packageName}.mapper; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}, ${ClassName}Vo> { + +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/service.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..5c8d309 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,72 @@ +package ${packageName}.service; + +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +#if($table.crud) +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +#end + +import java.util.Collection; +import java.util.List; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service { + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} 主键 + * @return ${functionName} + */ + ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); + +#if($table.crud) + /** + * 分页查询${functionName}列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return ${functionName}分页列表 + */ + TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery); +#end + + /** + * 查询符合条件的${functionName}列表 + * + * @param bo 查询条件 + * @return ${functionName}列表 + */ + List<${ClassName}Vo> queryList(${ClassName}Bo bo); + + /** + * 新增${functionName} + * + * @param bo ${functionName} + * @return 是否新增成功 + */ + Boolean insertByBo(${ClassName}Bo bo); + + /** + * 修改${functionName} + * + * @param bo ${functionName} + * @return 是否修改成功 + */ + Boolean updateByBo(${ClassName}Bo bo); + + /** + * 校验并批量删除${functionName}信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/serviceImpl.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..66be075 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,159 @@ +package ${packageName}.service.impl; + +import com.fuyuanshen.common.core.utils.MapstructUtils; + #if($table.crud) +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +#end +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.${ClassName}; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.service.I${ClassName}Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service { + + private final ${ClassName}Mapper baseMapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} 主键 + * @return ${functionName} + */ + @Override + public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ + return baseMapper.selectVoById(${pkColumn.javaField}); + } + +#if($table.crud) + /** + * 分页查询${functionName}列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return ${functionName}分页列表 + */ + @Override + public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } +#end + + /** + * 查询符合条件的${functionName}列表 + * + * @param bo 查询条件 + * @return ${functionName}列表 + */ + @Override + public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); +#foreach($column in $columns) +#if($column.query) +#set($queryType=$column.queryType) +#set($javaField=$column.javaField) +#set($javaType=$column.javaType) +#set($columnName=$column.columnName) +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#set($mpMethod=$column.queryType.toLowerCase()) +#if($queryType != 'BETWEEN') +#if($javaType == 'String') +#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') +#else +#set($condition='bo.get'+$AttrName+'() != null') +#end + lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); +#else + lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, + ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName")); +#end +#end +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#if($column.isPk==1) + lqw.orderByAsc(${ClassName}::get$AttrName); +#end +#end + return lqw; + } + + /** + * 新增${functionName} + * + * @param bo ${functionName} + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(${ClassName}Bo bo) { + ${ClassName} add = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; +#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) + if (flag) { + bo.set$pk(add.get$pk()); + } + return flag; + } + + /** + * 修改${functionName} + * + * @param bo ${functionName} + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(${ClassName}Bo bo) { + ${ClassName} update = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(${ClassName} entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除${functionName}信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/java/vo.java.vm b/fys-modules/fys-generator/src/main/resources/vm/java/vo.java.vm new file mode 100644 index 0000000..62f95a7 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/java/vo.java.vm @@ -0,0 +1,66 @@ +package ${packageName}.domain.vo; + +#foreach ($import in $importList) +import ${import}; +#end +import ${packageName}.domain.${ClassName}; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * ${functionName}视图对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = ${ClassName}.class) +public class ${ClassName}Vo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if(${column.dictType} && ${column.dictType} != '') + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "${column.dictType}") +#elseif($parentheseIndex != -1) + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") +#else + @ExcelProperty(value = "${comment}") +#end + private $column.javaType $column.javaField; + +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}") + private String ${column.javaField}Url; +#end +#end +#end + +} diff --git a/fys-modules/fys-generator/src/main/resources/vm/sql/oracle/sql.vm b/fys-modules/fys-generator/src/main/resources/vm/sql/oracle/sql.vm new file mode 100644 index 0000000..f6638be --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/sql/oracle/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate, null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate, null, null, ''); diff --git a/fys-modules/fys-generator/src/main/resources/vm/sql/postgres/sql.vm b/fys-modules/fys-generator/src/main/resources/vm/sql/postgres/sql.vm new file mode 100644 index 0000000..0923392 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/sql/postgres/sql.vm @@ -0,0 +1,20 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, now(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, now(), null, null, ''); + diff --git a/fys-modules/fys-generator/src/main/resources/vm/sql/sql.vm b/fys-modules/fys-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..01824c2 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate(), null, null, ''); diff --git a/fys-modules/fys-generator/src/main/resources/vm/sql/sqlserver/sql.vm b/fys-modules/fys-generator/src/main/resources/vm/sql/sqlserver/sql.vm new file mode 100644 index 0000000..bdf166e --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/sql/sqlserver/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, getdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, getdate(), null, null, ''); diff --git a/fys-modules/fys-generator/src/main/resources/vm/ts/api.ts.vm b/fys-modules/fys-generator/src/main/resources/vm/ts/api.ts.vm new file mode 100644 index 0000000..3aa4a5f --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/ts/api.ts.vm @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ${BusinessName}VO, ${BusinessName}Form, ${BusinessName}Query } from '@/api/${moduleName}/${businessName}/types'; + +/** + * 查询${functionName}列表 + * @param query + * @returns {*} + */ + +export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<${BusinessName}VO[]> => { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }); +}; + +/** + * 查询${functionName}详细 + * @param ${pkColumn.javaField} + */ +export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }); +}; + +/** + * 新增${functionName} + * @param data + */ +export const add${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }); +}; + +/** + * 修改${functionName} + * @param data + */ +export const update${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }); +}; + +/** + * 删除${functionName} + * @param ${pkColumn.javaField} + */ +export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array) => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }); +}; diff --git a/fys-modules/fys-generator/src/main/resources/vm/ts/types.ts.vm b/fys-modules/fys-generator/src/main/resources/vm/ts/types.ts.vm new file mode 100644 index 0000000..35a468e --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/ts/types.ts.vm @@ -0,0 +1,64 @@ +export interface ${BusinessName}VO { +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ + $column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + ${column.javaField}Url: string; +#end +#end +#end +#if ($table.tree) + /** + * 子对象 + */ + children: ${BusinessName}VO[]; +#end +} + +export interface ${BusinessName}Form extends BaseEntity { +#foreach ($column in $columns) +#if($column.insert || $column.edit) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} + +export interface ${BusinessName}Query #if(!${treeCode})extends PageQuery #end{ + +#foreach ($column in $columns) +#if($column.query) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end + /** + * 日期范围参数 + */ + params?: any; +} + + + diff --git a/fys-modules/fys-generator/src/main/resources/vm/vue/index-tree.vue.vm b/fys-modules/fys-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4570d46 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,499 @@ + + + diff --git a/fys-modules/fys-generator/src/main/resources/vm/vue/index.vue.vm b/fys-modules/fys-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..df51ed0 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,459 @@ + + + diff --git a/fys-modules/fys-generator/src/main/resources/vm/xml/mapper.xml.vm b/fys-modules/fys-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..9fb48d9 --- /dev/null +++ b/fys-modules/fys-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-job/pom.xml b/fys-modules/fys-job/pom.xml new file mode 100644 index 0000000..9c01560 --- /dev/null +++ b/fys-modules/fys-job/pom.xml @@ -0,0 +1,34 @@ + + + + com.fuyuanshen + fys-modules + ${revision} + + 4.0.0 + jar + fys-job + + + 任务调度 + + + + + + + com.fuyuanshen + fys-common-json + + + + com.fuyuanshen + fys-common-job + + + + + + diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/entity/BillDto.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/entity/BillDto.java new file mode 100644 index 0000000..a2b8a90 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/entity/BillDto.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.job.entity; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class BillDto { + + /** + * 账单ID + */ + private Long billId; + + /** + * 账单渠道 + */ + private String billChannel; + + /** + * 账单日期 + */ + private String billDate; + + /** + * 账单金额 + */ + private BigDecimal billAmount; + +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/package-info.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/package-info.java new file mode 100644 index 0000000..38cc859 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/package-info.java @@ -0,0 +1 @@ +package com.fuyuanshen.job; diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/AlipayBillTask.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/AlipayBillTask.java new file mode 100644 index 0000000..c50f5ca --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/AlipayBillTask.java @@ -0,0 +1,42 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟支付宝账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "alipayBillTask") +public class AlipayBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(23456789L); + billDto.setBillChannel("alipay"); + // 设置清算日期 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("2345.67")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("alipay", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/SummaryBillTask.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/SummaryBillTask.java new file mode 100644 index 0000000..fe9cafa --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/SummaryBillTask.java @@ -0,0 +1,45 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟汇总账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "summaryBillTask") +public class SummaryBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + // 获得微信账单 + BigDecimal wechatAmount = BigDecimal.valueOf(0); + String wechat = (String) jobArgs.getWfContext("wechat"); + if (StrUtil.isNotBlank(wechat)) { + BillDto wechatBillDto = JsonUtils.parseObject(wechat, BillDto.class); + wechatAmount = wechatBillDto.getBillAmount(); + } + // 获得支付宝账单 + BigDecimal alipayAmount = BigDecimal.valueOf(0); + String alipay = (String) jobArgs.getWfContext("alipay"); + if (StrUtil.isNotBlank(alipay)) { + BillDto alipayBillDto = JsonUtils.parseObject(alipay, BillDto.class); + alipayAmount = alipayBillDto.getBillAmount(); + } + // 汇总账单 + BigDecimal totalAmount = wechatAmount.add(alipayAmount); + SnailJobLog.REMOTE.info("总金额: {}", totalAmount); + return ExecuteResult.success(totalAmount); + } + +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestAnnoJobExecutor.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestAnnoJobExecutor.java new file mode 100644 index 0000000..49a847b --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestAnnoJobExecutor.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.job.snailjob; + +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.core.util.JsonUtil; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +/** + * 正常任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testJobExecutor") +public class TestAnnoJobExecutor { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + SnailJobLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + SnailJobLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs)); + return ExecuteResult.success("测试成功"); + } +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestBroadcastJob.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestBroadcastJob.java new file mode 100644 index 0000000..3788f16 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestBroadcastJob.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.util.RandomUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 广播任务 + * + * + * @author 老马 + */ +@Slf4j +@Component +@JobExecutor(name = "testBroadcastJob") +public class TestBroadcastJob { + + @Value("${snail-job.port}") + private int clientPort; + + public ExecuteResult jobExecute(JobArgs jobArgs) { + int randomInt = RandomUtil.randomInt(100); + log.info("随机数: {}", randomInt); + SnailJobLog.REMOTE.info("随机数: {},客户端端口:{}", randomInt, clientPort); + if (randomInt < 50) { + throw new RuntimeException("随机数小于50,收集日志任务执行失败"); + } + // 获得jobArgs 中传入的相加的两个数 + return ExecuteResult.success("随机数大于50,收集日志任务执行成功"); + } + +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestClassJobExecutor.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestClassJobExecutor.java new file mode 100644 index 0000000..6c8ea56 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestClassJobExecutor.java @@ -0,0 +1,19 @@ +package com.fuyuanshen.job.snailjob; + +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.job.core.executor.AbstractJobExecutor; +import com.aizuda.snailjob.client.model.ExecuteResult; +import org.springframework.stereotype.Component; + +/** + * @author opensnail + * @date 2024-05-17 + */ +@Component +public class TestClassJobExecutor extends AbstractJobExecutor { + + @Override + protected ExecuteResult doJobExecute(JobArgs jobArgs) { + return ExecuteResult.success("TestJobExecutor测试成功"); + } +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapJobAnnotation.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapJobAnnotation.java new file mode 100644 index 0000000..fd528a1 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapJobAnnotation.java @@ -0,0 +1,53 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Map任务 动态分配 只分片不关注结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapJobAnnotation") +public class TestMapJobAnnotation { + + @MapExecutor + public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + // 生成1~200数值并分片 + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapReduceAnnotation1.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapReduceAnnotation1.java new file mode 100644 index 0000000..471fe8f --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestMapReduceAnnotation1.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.MapHandler; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; +import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor; +import com.aizuda.snailjob.client.job.core.dto.MapArgs; +import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * MapReduce任务 动态分配 分片后合并结果 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testMapReduceAnnotation1") +public class TestMapReduceAnnotation1 { + + @MapExecutor + public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) { + int partitionSize = 50; + List> partition = IntStream.rangeClosed(1, 200) + .boxed() + .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + .values() + .stream() + .toList(); + SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); + return mapHandler.doMap(partition, "doCalc"); + } + + @MapExecutor(taskName = "doCalc") + public ExecuteResult doCalc(MapArgs mapArgs) { + List sourceList = (List) mapArgs.getMapResult(); + // 遍历sourceList的每一个元素,计算出一个累加值partitionTotal + int partitionTotal = sourceList.stream().mapToInt(i -> i).sum(); + // 打印日志到服务器 + ThreadUtil.sleep(3, TimeUnit.SECONDS); + SnailJobLog.REMOTE.info("端口:{},partitionTotal:{}", SpringUtil.getProperty("server.port"), partitionTotal); + return ExecuteResult.success(partitionTotal); + } + + @ReduceExecutor + public ExecuteResult reduceExecute(ReduceArgs reduceArgs) { + int reduceTotal = reduceArgs.getMapResult().stream().mapToInt(i -> Integer.parseInt((String) i)).sum(); + SnailJobLog.REMOTE.info("端口:{},reduceTotal:{}", SpringUtil.getProperty("server.port"), reduceTotal); + return ExecuteResult.success(reduceTotal); + } +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestStaticShardingJob.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestStaticShardingJob.java new file mode 100644 index 0000000..1c915c7 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/TestStaticShardingJob.java @@ -0,0 +1,36 @@ +package com.fuyuanshen.job.snailjob; + +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import org.springframework.stereotype.Component; + +/** + * 静态分片 根据服务端任务参数分片 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "testStaticShardingJob") +public class TestStaticShardingJob { + + public ExecuteResult jobExecute(JobArgs jobArgs) { + String jobParams = String.valueOf(jobArgs.getJobParams()); + SnailJobLog.LOCAL.info("开始执行分片任务,参数:{}", jobParams); + // 获得jobArgs 中传入的开始id和结束id + String[] split = jobParams.split(","); + Long fromId = Long.parseLong(split[0]); + Long toId = Long.parseLong(split[1]); + // 模拟数据库操作,对范围id,进行加密处理 + try { + SnailJobLog.REMOTE.info("开始对id范围:{}进行加密处理", fromId + "-" + toId); + Thread.sleep(3000); + SnailJobLog.REMOTE.info("对id范围:{}进行加密处理完成", fromId + "-" + toId); + } catch (InterruptedException e) { + return ExecuteResult.failure("任务执行失败"); + } + return ExecuteResult.success("执行分片任务完成"); + } +} diff --git a/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/WechatBillTask.java b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/WechatBillTask.java new file mode 100644 index 0000000..3d56119 --- /dev/null +++ b/fys-modules/fys-job/src/main/java/com/fuyuanshen/job/snailjob/WechatBillTask.java @@ -0,0 +1,43 @@ +package com.fuyuanshen.job.snailjob; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.client.job.core.dto.JobArgs; +import com.aizuda.snailjob.client.model.ExecuteResult; +import com.aizuda.snailjob.common.log.SnailJobLog; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.job.entity.BillDto; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * DAG工作流任务-模拟微信账单任务 + * + * + * @author 老马 + */ +@Component +@JobExecutor(name = "wechatBillTask") +public class WechatBillTask { + + public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { + BillDto billDto = new BillDto(); + billDto.setBillId(123456789L); + billDto.setBillChannel("wechat"); + // 从上下文中获得清算日期并设置,如果上下文中清算日期 + // 是sysdate设置为当前日期;否则取管理页面设置的值 + String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); + if (StrUtil.equals(settlementDate, "sysdate")) { + settlementDate = DateUtil.today(); + } + billDto.setBillDate(settlementDate); + billDto.setBillAmount(new BigDecimal("1234.56")); + // 把billDto对象放入上下文进行传递 + jobArgs.appendContext("wechat", JsonUtils.toJsonString(billDto)); + SnailJobLog.REMOTE.info("上下文: {}", jobArgs.getWfContext()); + return ExecuteResult.success(billDto); + } + +} diff --git a/fys-modules/fys-system/pom.xml b/fys-modules/fys-system/pom.xml new file mode 100644 index 0000000..a99e837 --- /dev/null +++ b/fys-modules/fys-system/pom.xml @@ -0,0 +1,118 @@ + + + + com.fuyuanshen + fys-modules + ${revision} + + 4.0.0 + + fys-system + + + system系统模块 + + + + + + + + + + + com.fuyuanshen + fys-common-core + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-mybatis + + + + com.fuyuanshen + fys-common-translation + + + + + com.fuyuanshen + fys-common-oss + + + + com.fuyuanshen + fys-common-log + + + + + com.fuyuanshen + fys-common-excel + + + + + com.fuyuanshen + fys-common-sms + + + + com.fuyuanshen + fys-common-tenant + + + + com.fuyuanshen + fys-common-security + + + + com.fuyuanshen + fys-common-web + + + + com.fuyuanshen + fys-common-idempotent + + + + com.fuyuanshen + fys-common-sensitive + + + + com.fuyuanshen + fys-common-encrypt + + + + com.fuyuanshen + fys-common-websocket + + + + com.fuyuanshen + fys-common-sse + + + + org.springframework.boot + spring-boot-starter-integration + + + org.springframework.integration + spring-integration-mqtt + + + + diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/CacheController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/CacheController.java new file mode 100644 index 0000000..d22ac0e --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/CacheController.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; + +/** + * 缓存监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/cache") +public class CacheController { + + private final RedissonConnectionFactory connectionFactory; + + /** + * 获取缓存监控列表 + */ + @SaCheckPermission("monitor:cache:list") + @GetMapping() + public R getInfo() throws Exception { + RedisConnection connection = connectionFactory.getConnection(); + Properties commandStats = connection.commands().info("commandstats"); + + List> pieList = new ArrayList<>(); + if (commandStats != null) { + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + } + + return R.ok(new CacheListInfoVo( + connection.commands().info(), + connection.commands().dbSize(), pieList)); + } + + public record CacheListInfoVo(Properties info, Long dbSize, List> commandStats) {} + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysLogininforController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..ee1babf --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysLogininforController.java @@ -0,0 +1,89 @@ +package com.fuyuanshen.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysLogininforBo; +import com.fuyuanshen.system.domain.vo.SysLogininforVo; +import com.fuyuanshen.system.service.ISysLogininforService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 系统访问记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController { + + private final ISysLogininforService logininforService; + + /** + * 获取系统访问记录列表 + */ + @SaCheckPermission("monitor:logininfor:list") + @GetMapping("/list") + public TableDataInfo list(SysLogininforBo logininfor, PageQuery pageQuery) { + return logininforService.selectPageLogininforList(logininfor, pageQuery); + } + + /** + * 导出系统访问记录列表 + */ + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:logininfor:export") + @PostMapping("/export") + public void export(SysLogininforBo logininfor, HttpServletResponse response) { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil.exportExcel(list, "登录日志", SysLogininforVo.class, response); + } + + /** + * 批量删除登录日志 + * @param infoIds 日志ids + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public R remove(@PathVariable Long[] infoIds) { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + /** + * 清理系统访问记录 + */ + @SaCheckPermission("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public R clean() { + logininforService.cleanLogininfor(); + return R.ok(); + } + + @SaCheckPermission("monitor:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public R unlock(@PathVariable("userName") String userName) { + String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName; + if (RedisUtils.hasKey(loginName)) { + RedisUtils.deleteObject(loginName); + } + return R.ok(); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysOperlogController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..e6cb153 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysOperlogController.java @@ -0,0 +1,75 @@ +package com.fuyuanshen.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.system.domain.bo.SysOperLogBo; +import com.fuyuanshen.system.domain.vo.SysOperLogVo; +import com.fuyuanshen.system.service.ISysOperLogService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController { + + private final ISysOperLogService operLogService; + + /** + * 获取操作日志记录列表 + */ + @SaCheckPermission("monitor:operlog:list") + @GetMapping("/list") + public TableDataInfo list(SysOperLogBo operLog, PageQuery pageQuery) { + return operLogService.selectPageOperLogList(operLog, pageQuery); + } + + /** + * 导出操作日志记录列表 + */ + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @SaCheckPermission("monitor:operlog:export") + @PostMapping("/export") + public void export(SysOperLogBo operLog, HttpServletResponse response) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil.exportExcel(list, "操作日志", SysOperLogVo.class, response); + } + + /** + * 批量删除操作日志记录 + * @param operIds 日志ids + */ + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/{operIds}") + public R remove(@PathVariable Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + /** + * 清理操作日志记录 + */ + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @SaCheckPermission("monitor:operlog:remove") + @DeleteMapping("/clean") + public R clean() { + operLogService.cleanOperLog(); + return R.ok(); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysUserOnlineController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..99e6438 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,131 @@ +package com.fuyuanshen.system.controller.monitor; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.dto.UserOnlineDTO; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.SysUserOnline; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 在线用户监控 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController { + + /** + * 获取在线用户监控列表 + * + * @param ipaddr IP地址 + * @param userName 用户名 + */ + @SaCheckPermission("monitor:online:list") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) { + // 获取所有未过期的 token + Collection keys = RedisUtils.keys(CacheConstants.ONLINE_TOKEN_KEY + "*"); + List userOnlineDTOList = new ArrayList<>(); + for (String key : keys) { + String token = StringUtils.substringAfterLast(key, ":"); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { + continue; + } + userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)); + } + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) && + StringUtils.equals(userName, userOnline.getUserName()) + ); + } else if (StringUtils.isNotEmpty(ipaddr)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(ipaddr, userOnline.getIpaddr()) + ); + } else if (StringUtils.isNotEmpty(userName)) { + userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> + StringUtils.equals(userName, userOnline.getUserName()) + ); + } + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退用户 + * + * @param tokenId token值 + */ + @SaCheckPermission("monitor:online:forceLogout") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public R forceLogout(@PathVariable String tokenId) { + try { + StpUtil.kickoutByTokenValue(tokenId); + } catch (NotLoginException ignored) { + } + return R.ok(); + } + + /** + * 获取当前用户登录在线设备 + */ + @GetMapping() + public TableDataInfo getInfo() { + // 获取指定账号 id 的 token 集合 + List tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + List userOnlineDTOList = tokenIds.stream() + .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) + .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token)) + .collect(Collectors.toList()); + //复制和处理 SysUserOnline 对象列表 + Collections.reverse(userOnlineDTOList); + userOnlineDTOList.removeAll(Collections.singleton(null)); + List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); + return TableDataInfo.build(userOnlineList); + } + + /** + * 强退当前在线设备 + * + * @param tokenId token值 + */ + @Log(title = "在线设备", businessType = BusinessType.FORCE) + @DeleteMapping("/myself/{tokenId}") + public R remove(@PathVariable("tokenId") String tokenId) { + try { + // 获取指定账号 id 的 token 集合 + List keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); + keys.stream() + .filter(key -> key.equals(tokenId)) + .findFirst() + .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId)); + } catch (NotLoginException ignored) { + } + return R.ok(); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysClientController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysClientController.java new file mode 100644 index 0000000..8d01b76 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysClientController.java @@ -0,0 +1,115 @@ +package com.fuyuanshen.system.controller.system; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.bo.SysClientBo; +import com.fuyuanshen.system.service.ISysClientService; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; + +/** + * 客户端管理 + * + * @author Michelle.Chung + * @date 2023-06-18 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/client") +public class SysClientController extends BaseController { + + private final ISysClientService sysClientService; + + /** + * 查询客户端管理列表 + */ + @SaCheckPermission("system:client:list") + @GetMapping("/list") + public TableDataInfo list(SysClientBo bo, PageQuery pageQuery) { + return sysClientService.queryPageList(bo, pageQuery); + } + + /** + * 导出客户端管理列表 + */ + @SaCheckPermission("system:client:export") + @Log(title = "客户端管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysClientBo bo, HttpServletResponse response) { + List list = sysClientService.queryList(bo); + ExcelUtil.exportExcel(list, "客户端管理", SysClientVo.class, response); + } + + /** + * 获取客户端管理详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("system:client:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(sysClientService.queryById(id)); + } + + /** + * 新增客户端管理 + */ + @SaCheckPermission("system:client:add") + @Log(title = "客户端管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysClientBo bo) { + return toAjax(sysClientService.insertByBo(bo)); + } + + /** + * 修改客户端管理 + */ + @SaCheckPermission("system:client:edit") + @Log(title = "客户端管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysClientBo bo) { + return toAjax(sysClientService.updateByBo(bo)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:client:edit") + @Log(title = "客户端管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysClientBo bo) { + return toAjax(sysClientService.updateClientStatus(bo.getClientId(), bo.getStatus())); + } + + /** + * 删除客户端管理 + * + * @param ids 主键串 + */ + @SaCheckPermission("system:client:remove") + @Log(title = "客户端管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(sysClientService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysConfigController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysConfigController.java new file mode 100644 index 0000000..851cb15 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysConfigController.java @@ -0,0 +1,150 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysConfigBo; +import com.fuyuanshen.system.domain.vo.SysConfigVo; +import com.fuyuanshen.system.service.ISysConfigService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController { + + private final ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @SaCheckPermission("system:config:list") + @GetMapping("/list") + public TableDataInfo list(SysConfigBo config, PageQuery pageQuery) { + return configService.selectPageConfigList(config, pageQuery); + } + + /** + * 导出参数配置列表 + */ + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:config:export") + @PostMapping("/export") + public void export(SysConfigBo config, HttpServletResponse response) { + List list = configService.selectConfigList(config); + ExcelUtil.exportExcel(list, "参数数据", SysConfigVo.class, response); + } + + /** + * 根据参数编号获取详细信息 + * + * @param configId 参数ID + */ + @SaCheckPermission("system:config:query") + @GetMapping(value = "/{configId}") + public R getInfo(@PathVariable Long configId) { + return R.ok(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + * + * @param configKey 参数Key + */ + @GetMapping(value = "/configKey/{configKey}") + public R getConfigKey(@PathVariable String configKey) { + return R.ok("操作成功", configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @SaCheckPermission("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysConfigBo config) { + if (!configService.checkConfigKeyUnique(config)) { + return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.insertConfig(config); + return R.ok(); + } + + /** + * 修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysConfigBo config) { + if (!configService.checkConfigKeyUnique(config)) { + return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + configService.updateConfig(config); + return R.ok(); + } + + /** + * 根据参数键名修改参数配置 + */ + @SaCheckPermission("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping("/updateByKey") + public R updateByKey(@RequestBody SysConfigBo config) { + configService.updateConfig(config); + return R.ok(); + } + + /** + * 删除参数配置 + * + * @param configIds 参数ID串 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public R remove(@PathVariable Long[] configIds) { + configService.deleteConfigByIds(configIds); + return R.ok(); + } + + /** + * 刷新参数缓存 + */ + @SaCheckPermission("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + configService.resetConfigCache(); + return R.ok(); + } + + + /** + * 清空所有客户端缓存 + */ + @GetMapping(value = "/clearAllClientCache") + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) + public void clearAllClientCache() { + + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDeptController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDeptController.java new file mode 100644 index 0000000..3b10b56 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDeptController.java @@ -0,0 +1,140 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.vo.SysDeptVo; +import com.fuyuanshen.system.service.ISysDeptService; +import com.fuyuanshen.system.service.ISysPostService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController { + + private final ISysDeptService deptService; + private final ISysPostService postService; + + /** + * 获取部门列表 + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list") + public R> list(SysDeptBo dept) { + List depts = deptService.selectDeptList(dept); + return R.ok(depts); + } + + /** + * 查询部门列表(排除节点) + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:list") + @GetMapping("/list/exclude/{deptId}") + public R> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDeptBo()); + depts.removeIf(d -> d.getDeptId().equals(deptId) + || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId))); + return R.ok(depts); + } + + /** + * 根据部门编号获取详细信息 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:query") + @GetMapping(value = "/{deptId}") + public R getInfo(@PathVariable Long deptId) { + deptService.checkDeptDataScope(deptId); + return R.ok(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @SaCheckPermission("system:dept:add") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDeptBo dept) { + if (!deptService.checkDeptNameUnique(dept)) { + return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @SaCheckPermission("system:dept:edit") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDeptBo dept) { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } else if (dept.getParentId().equals(deptId)) { + return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } else if (StringUtils.equals(SystemConstants.DISABLE, dept.getStatus())) { + if (deptService.selectNormalChildrenDeptById(deptId) > 0) { + return R.fail("该部门包含未停用的子部门!"); + } else if (deptService.checkDeptExistUser(deptId)) { + return R.fail("该部门下存在已分配用户,不能禁用!"); + } + } + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + * + * @param deptId 部门ID + */ + @SaCheckPermission("system:dept:remove") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public R remove(@PathVariable Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return R.warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return R.warn("部门存在用户,不允许删除"); + } + if (postService.countPostByDeptId(deptId) > 0) { + return R.warn("部门存在岗位,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } + + /** + * 获取部门选择框列表 + * + * @param deptIds 部门ID串 + */ + @SaCheckPermission("system:dept:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] deptIds) { + return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds))); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java new file mode 100644 index 0000000..ff3b6dc --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictDataController.java @@ -0,0 +1,123 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.system.domain.bo.SysDictDataBo; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; +import com.fuyuanshen.system.service.ISysDictDataService; +import com.fuyuanshen.system.service.ISysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController { + + private final ISysDictDataService dictDataService; + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典数据列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictDataBo dictData, PageQuery pageQuery) { + return dictDataService.selectPageDictDataList(dictData, pageQuery); + } + + /** + * 导出字典数据列表 + */ + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictDataBo dictData, HttpServletResponse response) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil.exportExcel(list, "字典数据", SysDictDataVo.class, response); + } + + /** + * 查询字典数据详细 + * + * @param dictCode 字典code + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictCode}") + public R getInfo(@PathVariable Long dictCode) { + return R.ok(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + * + * @param dictType 字典类型 + */ + @GetMapping(value = "/type/{dictType}") + public R> dictType(@PathVariable String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (ObjectUtil.isNull(data)) { + data = new ArrayList<>(); + } + return R.ok(data); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictDataBo dict) { + if (!dictDataService.checkDictDataUnique(dict)) { + return R.fail("新增字典数据'" + dict.getDictValue() + "'失败,字典键值已存在"); + } + dictDataService.insertDictData(dict); + return R.ok(); + } + + /** + * 修改保存字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictDataBo dict) { + if (!dictDataService.checkDictDataUnique(dict)) { + return R.fail("修改字典数据'" + dict.getDictValue() + "'失败,字典键值已存在"); + } + dictDataService.updateDictData(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictCodes 字典code串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public R remove(@PathVariable Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return R.ok(); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictTypeController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..cd951bd --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysDictTypeController.java @@ -0,0 +1,125 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysDictTypeBo; +import com.fuyuanshen.system.domain.vo.SysDictTypeVo; +import com.fuyuanshen.system.service.ISysDictTypeService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 数据字典信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController { + + private final ISysDictTypeService dictTypeService; + + /** + * 查询字典类型列表 + */ + @SaCheckPermission("system:dict:list") + @GetMapping("/list") + public TableDataInfo list(SysDictTypeBo dictType, PageQuery pageQuery) { + return dictTypeService.selectPageDictTypeList(dictType, pageQuery); + } + + /** + * 导出字典类型列表 + */ + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:dict:export") + @PostMapping("/export") + public void export(SysDictTypeBo dictType, HttpServletResponse response) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil.exportExcel(list, "字典类型", SysDictTypeVo.class, response); + } + + /** + * 查询字典类型详细 + * + * @param dictId 字典ID + */ + @SaCheckPermission("system:dict:query") + @GetMapping(value = "/{dictId}") + public R getInfo(@PathVariable Long dictId) { + return R.ok(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @SaCheckPermission("system:dict:add") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysDictTypeBo dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.insertDictType(dict); + return R.ok(); + } + + /** + * 修改字典类型 + */ + @SaCheckPermission("system:dict:edit") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysDictTypeBo dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dictTypeService.updateDictType(dict); + return R.ok(); + } + + /** + * 删除字典类型 + * + * @param dictIds 字典ID串 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public R remove(@PathVariable Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return R.ok(); + } + + /** + * 刷新字典缓存 + */ + @SaCheckPermission("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public R refreshCache() { + dictTypeService.resetDictCache(); + return R.ok(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public R> optionselect() { + List dictTypes = dictTypeService.selectDictTypeAll(); + return R.ok(dictTypes); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysMenuController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysMenuController.java new file mode 100644 index 0000000..9f2a0d6 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysMenuController.java @@ -0,0 +1,196 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.annotation.SaMode; +import cn.hutool.core.lang.tree.Tree; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.SysMenu; +import com.fuyuanshen.system.domain.bo.SysMenuBo; +import com.fuyuanshen.system.domain.vo.RouterVo; +import com.fuyuanshen.system.domain.vo.SysMenuVo; +import com.fuyuanshen.system.service.ISysMenuService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 菜单信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController { + + private final ISysMenuService menuService; + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("/getRouters") + public R> getRouters() { + List menus = menuService.selectMenuTreeByUserId(LoginHelper.getUserId()); + return R.ok(menuService.buildMenus(menus)); + } + + /** + * 获取菜单列表 + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY, + TenantConstants.FYS_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:list") + @GetMapping("/list") + public R> list(SysMenuBo menu) { + List menus = menuService.selectMenuList(menu, LoginHelper.getUserId()); + return R.ok(menus); + } + + /** + * 根据菜单编号获取详细信息 + * + * @param menuId 菜单ID + */ + @SaCheckRole(value = { + TenantConstants.SUPER_ADMIN_ROLE_KEY, + TenantConstants.TENANT_ADMIN_ROLE_KEY, + TenantConstants.FYS_ROLE_KEY + }, mode = SaMode.OR) + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/{menuId}") + public R getInfo(@PathVariable Long menuId) { + return R.ok(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @SaCheckPermission("system:menu:query") + @GetMapping("/treeselect") + public R>> treeselect(SysMenuBo menu) { + List menus = menuService.selectMenuList(menu, LoginHelper.getUserId()); + return R.ok(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public R roleMenuTreeselect(@PathVariable("roleId") Long roleId) { + List menus = menuService.selectMenuList(LoginHelper.getUserId()); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByRoleId(roleId), + menuService.buildMenuTreeSelect(menus)); + return R.ok(selectVo); + } + + /** + * 加载对应租户套餐菜单列表树 + * + * @param packageId 租户套餐ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:query") + @GetMapping(value = "/tenantPackageMenuTreeselect/{packageId}") + public R tenantPackageMenuTreeselect(@PathVariable("packageId") Long packageId) { + List menus = menuService.selectMenuList(LoginHelper.getUserId()); + MenuTreeSelectVo selectVo = new MenuTreeSelectVo( + menuService.selectMenuListByPackageId(packageId), + menuService.buildMenuTreeSelect(menus)); + return R.ok(selectVo); + } + + /** + * 新增菜单 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:add") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysMenuBo menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (SystemConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:edit") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysMenuBo menu) { + if (!menuService.checkMenuNameUnique(menu)) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } else if (SystemConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + * + * @param menuId 菜单ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public R remove(@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return R.warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return R.warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } + + public record MenuTreeSelectVo(List checkedKeys, List> menus) { + } + + /** + * 批量级联删除菜单 + * + * @param menuIds 菜单ID串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/cascade/{menuIds}") + public R remove(@PathVariable("menuIds") Long[] menuIds) { + List menuIdList = List.of(menuIds); + if (menuService.hasChildByMenuId(menuIdList)) { + return R.warn("存在子菜单,不允许删除"); + } + menuService.deleteMenuById(menuIdList); + return R.ok(); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysNoticeController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysNoticeController.java new file mode 100644 index 0000000..001f0c3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysNoticeController.java @@ -0,0 +1,90 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.sse.utils.SseMessageUtils; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysNoticeBo; +import com.fuyuanshen.system.domain.vo.SysNoticeVo; +import com.fuyuanshen.system.service.ISysNoticeService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 公告 信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController { + + private final ISysNoticeService noticeService; + private final DictService dictService; + + /** + * 获取通知公告列表 + */ + @SaCheckPermission("system:notice:list") + @GetMapping("/list") + public TableDataInfo list(SysNoticeBo notice, PageQuery pageQuery) { + return noticeService.selectPageNoticeList(notice, pageQuery); + } + + /** + * 根据通知公告编号获取详细信息 + * + * @param noticeId 公告ID + */ + @SaCheckPermission("system:notice:query") + @GetMapping(value = "/{noticeId}") + public R getInfo(@PathVariable Long noticeId) { + return R.ok(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @SaCheckPermission("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysNoticeBo notice) { + int rows = noticeService.insertNotice(notice); + if (rows <= 0) { + return R.fail(); + } + String type = dictService.getDictLabel("sys_notice_type", notice.getNoticeType()); + SseMessageUtils.publishAll("[" + type + "] " + notice.getNoticeTitle()); + return R.ok(); + } + + /** + * 修改通知公告 + */ + @SaCheckPermission("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysNoticeBo notice) { + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + * + * @param noticeIds 公告ID串 + */ + @SaCheckPermission("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public R remove(@PathVariable Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssConfigController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssConfigController.java new file mode 100644 index 0000000..0a79bc6 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssConfigController.java @@ -0,0 +1,105 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.core.validate.QueryGroup; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysOssConfigBo; +import com.fuyuanshen.system.domain.vo.SysOssConfigVo; +import com.fuyuanshen.system.service.ISysOssConfigService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 对象存储配置 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resource/oss/config") +public class SysOssConfigController extends BaseController { + + private final ISysOssConfigService ossConfigService; + + /** + * 查询对象存储配置列表 + */ + @SaCheckPermission("system:ossConfig:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) { + return ossConfigService.queryPageList(bo, pageQuery); + } + + /** + * 获取对象存储配置详细信息 + * + * @param ossConfigId OSS配置ID + */ + @SaCheckPermission("system:ossConfig:list") + @GetMapping("/{ossConfigId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long ossConfigId) { + return R.ok(ossConfigService.queryById(ossConfigId)); + } + + /** + * 新增对象存储配置 + */ + @SaCheckPermission("system:ossConfig:add") + @Log(title = "对象存储配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.insertByBo(bo)); + } + + /** + * 修改对象存储配置 + */ + @SaCheckPermission("system:ossConfig:edit") + @Log(title = "对象存储配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.updateByBo(bo)); + } + + /** + * 删除对象存储配置 + * + * @param ossConfigIds OSS配置ID串 + */ + @SaCheckPermission("system:ossConfig:remove") + @Log(title = "对象存储配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ossConfigIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossConfigIds) { + return toAjax(ossConfigService.deleteWithValidByIds(List.of(ossConfigIds), true)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:ossConfig:edit") + @Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysOssConfigBo bo) { + return toAjax(ossConfigService.updateOssConfigStatus(bo)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssController.java new file mode 100644 index 0000000..aebb507 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysOssController.java @@ -0,0 +1,108 @@ +package com.fuyuanshen.system.controller.system; + + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.QueryGroup; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysOssBo; +import com.fuyuanshen.system.domain.vo.SysOssUploadVo; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import com.fuyuanshen.system.service.ISysOssService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * 文件上传 控制层 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resource/oss") +public class SysOssController extends BaseController { + + private final ISysOssService ossService; + + /** + * 查询OSS对象存储列表 + */ + @SaCheckPermission("system:oss:list") + @GetMapping("/list") + public TableDataInfo list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) { + return ossService.queryPageList(bo, pageQuery); + } + + /** + * 查询OSS对象基于id串 + * + * @param ossIds OSS对象ID串 + */ + @SaCheckPermission("system:oss:query") + @GetMapping("/listByIds/{ossIds}") + public R> listByIds(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossIds) { + List list = ossService.listByIds(Arrays.asList(ossIds)); + return R.ok(list); + } + + /** + * 上传OSS对象存储 + * + * @param file 文件 + */ + @SaCheckPermission("system:oss:upload") + @Log(title = "OSS对象存储", businessType = BusinessType.INSERT) + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R upload(@RequestPart("file") MultipartFile file) { + if (ObjectUtil.isNull(file)) { + return R.fail("上传文件不能为空"); + } + SysOssVo oss = ossService.upload(file); + SysOssUploadVo uploadVo = new SysOssUploadVo(); + uploadVo.setUrl(oss.getUrl()); + uploadVo.setFileName(oss.getOriginalName()); + uploadVo.setOssId(oss.getOssId().toString()); + return R.ok(uploadVo); + } + + /** + * 下载OSS对象 + * + * @param ossId OSS对象ID + */ + @SaCheckPermission("system:oss:download") + @GetMapping("/download/{ossId}") + public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException { + ossService.download(ossId, response); + } + + /** + * 删除OSS对象存储 + * + * @param ossIds OSS对象ID串 + */ + @SaCheckPermission("system:oss:remove") + @Log(title = "OSS对象存储", businessType = BusinessType.DELETE) + @DeleteMapping("/{ossIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ossIds) { + return toAjax(ossService.deleteWithValidByIds(List.of(ossIds), true)); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysPostController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysPostController.java new file mode 100644 index 0000000..d71cd01 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysPostController.java @@ -0,0 +1,133 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysPostBo; +import com.fuyuanshen.system.domain.vo.SysPostVo; +import com.fuyuanshen.system.service.ISysPostService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController { + + private final ISysPostService postService; + + /** + * 获取岗位列表 + */ + @SaCheckPermission("system:post:list") + @GetMapping("/list") + public TableDataInfo list(SysPostBo post, PageQuery pageQuery) { + return postService.selectPagePostList(post, pageQuery); + } + + /** + * 导出岗位列表 + */ + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:post:export") + @PostMapping("/export") + public void export(SysPostBo post, HttpServletResponse response) { + List list = postService.selectPostList(post); + ExcelUtil.exportExcel(list, "岗位数据", SysPostVo.class, response); + } + + /** + * 根据岗位编号获取详细信息 + * + * @param postId 岗位ID + */ + @SaCheckPermission("system:post:query") + @GetMapping(value = "/{postId}") + public R getInfo(@PathVariable Long postId) { + return R.ok(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @SaCheckPermission("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysPostBo post) { + if (!postService.checkPostNameUnique(post)) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @SaCheckPermission("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysPostBo post) { + if (!postService.checkPostNameUnique(post)) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } else if (SystemConstants.DISABLE.equals(post.getStatus()) + && postService.countUserPostById(post.getPostId()) > 0) { + return R.fail("该岗位下存在已分配用户,不能禁用!"); + } + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + * + * @param postIds 岗位ID串 + */ + @SaCheckPermission("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public R remove(@PathVariable Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + * + * @param postIds 岗位ID串 + * @param deptId 部门id + */ + @SaCheckPermission("system:post:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] postIds, @RequestParam(required = false) Long deptId) { + List list = new ArrayList<>(); + if (ObjectUtil.isNotNull(deptId)) { + SysPostBo post = new SysPostBo(); + post.setDeptId(deptId); + list = postService.selectPostList(post); + } else if (postIds != null) { + list = postService.selectPostByIds(List.of(postIds)); + } + return R.ok(list); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysProfileController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysProfileController.java new file mode 100644 index 0000000..c897594 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysProfileController.java @@ -0,0 +1,133 @@ +package com.fuyuanshen.system.controller.system; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.crypto.digest.BCrypt; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.file.MimeTypeUtils; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.helper.DataPermissionHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.bo.SysUserPasswordBo; +import com.fuyuanshen.system.domain.bo.SysUserProfileBo; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.service.ISysOssService; +import com.fuyuanshen.system.service.ISysUserService; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; + +/** + * 个人信息 业务处理 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController { + + private final ISysUserService userService; + private final ISysOssService ossService; + + /** + * 个人信息 + */ + @GetMapping + public R profile() { + SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); + String roleGroup = userService.selectUserRoleGroup(user.getUserId()); + String postGroup = userService.selectUserPostGroup(user.getUserId()); + ProfileVo profileVo = new ProfileVo(user, roleGroup, postGroup); + return R.ok(profileVo); + } + + /** + * 修改用户信息 + */ + @RepeatSubmit + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public R updateProfile(@Validated @RequestBody SysUserProfileBo profile) { + SysUserBo user = BeanUtil.toBean(profile, SysUserBo.class); + user.setUserId(LoginHelper.getUserId()); + String username = LoginHelper.getUsername(); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("修改用户'" + username + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("修改用户'" + username + "'失败,邮箱账号已存在"); + } + int rows = DataPermissionHelper.ignore(() -> userService.updateUserProfile(user)); + if (rows > 0) { + return R.ok(); + } + return R.fail("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + * + * @param bo 新旧密码 + */ + @RepeatSubmit + @ApiEncrypt + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public R updatePwd(@Validated @RequestBody SysUserPasswordBo bo) { + SysUserVo user = userService.selectUserById(LoginHelper.getUserId()); + String password = user.getPassword(); + if (!BCrypt.checkpw(bo.getOldPassword(), password)) { + return R.fail("修改密码失败,旧密码错误"); + } + if (BCrypt.checkpw(bo.getNewPassword(), password)) { + return R.fail("新密码不能与旧密码相同"); + } + int rows = DataPermissionHelper.ignore(() -> userService.resetUserPwd(user.getUserId(), BCrypt.hashpw(bo.getNewPassword()))); + if (rows > 0) { + return R.ok(); + } + return R.fail("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + * + * @param avatarfile 用户头像 + */ + @RepeatSubmit + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { + if (!avatarfile.isEmpty()) { + String extension = FileUtil.extName(avatarfile.getOriginalFilename()); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + SysOssVo oss = ossService.upload(avatarfile); + String avatar = oss.getUrl(); + boolean updateSuccess = DataPermissionHelper.ignore(() -> userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())); + if (updateSuccess) { + return R.ok(new AvatarVo(avatar)); + } + } + return R.fail("上传图片异常,请联系管理员"); + } + + public record AvatarVo(String imgUrl) {} + + public record ProfileVo(SysUserVo user, String roleGroup, String postGroup) {} + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysRoleController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysRoleController.java new file mode 100644 index 0000000..edf8334 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysRoleController.java @@ -0,0 +1,232 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.SysUserRole; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.bo.SysRoleBo; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.SysRoleVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.service.ISysDeptService; +import com.fuyuanshen.system.service.ISysRoleService; +import com.fuyuanshen.system.service.ISysUserService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 角色信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController { + + private final ISysRoleService roleService; + private final ISysUserService userService; + private final ISysDeptService deptService; + + /** + * 获取角色信息列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/list") + public TableDataInfo list(SysRoleBo role, PageQuery pageQuery) { + return roleService.selectPageRoleList(role, pageQuery); + } + + /** + * 导出角色信息列表 + */ + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:role:export") + @PostMapping("/export") + public void export(SysRoleBo role, HttpServletResponse response) { + List list = roleService.selectRoleList(role); + ExcelUtil.exportExcel(list, "角色数据", SysRoleVo.class, response); + } + + /** + * 根据角色编号获取详细信息 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:query") + @GetMapping(value = "/{roleId}") + public R getInfo(@PathVariable Long roleId) { + roleService.checkRoleDataScope(roleId); + return R.ok(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @SaCheckPermission("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role); + if (!roleService.checkRoleNameUnique(role)) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + + if (roleService.updateRole(role) > 0) { + roleService.cleanOnlineUserByRole(role.getRoleId()); + return R.ok(); + } + return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public R dataScope(@RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysRoleBo role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.updateRoleStatus(role.getRoleId(), role.getStatus())); + } + + /** + * 删除角色 + * + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public R remove(@PathVariable Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + * + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:role:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] roleIds) { + return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds))); + } + + /** + * 查询已分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUserBo user, PageQuery pageQuery) { + return userService.selectAllocatedList(user, pageQuery); + } + + /** + * 查询未分配用户角色列表 + */ + @SaCheckPermission("system:role:list") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUserBo user, PageQuery pageQuery) { + return userService.selectUnallocatedList(user, pageQuery); + } + + /** + * 取消授权用户 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public R cancelAuthUser(@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public R cancelAuthUserAll(Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + * + * @param roleId 角色ID + * @param userIds 用户ID串 + */ + @SaCheckPermission("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public R selectAuthUserAll(Long roleId, Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + * + * @param roleId 角色ID + */ + @SaCheckPermission("system:role:list") + @GetMapping(value = "/deptTree/{roleId}") + public R roleDeptTreeselect(@PathVariable("roleId") Long roleId) { + DeptTreeSelectVo selectVo = new DeptTreeSelectVo( + deptService.selectDeptListByRoleId(roleId), + deptService.selectDeptTreeList(new SysDeptBo())); + return R.ok(selectVo); + } + + public record DeptTreeSelectVo(List checkedKeys, List> depts) {} + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysSocialController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysSocialController.java new file mode 100644 index 0000000..7f53b1f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysSocialController.java @@ -0,0 +1,38 @@ +package com.fuyuanshen.system.controller.system; + +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.vo.SysSocialVo; +import com.fuyuanshen.system.service.ISysSocialService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 社会化关系 + * + * @author thiszhc + * @date 2023-06-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/social") +public class SysSocialController extends BaseController { + + private final ISysSocialService socialUserService; + + /** + * 查询社会化关系列表 + */ + @GetMapping("/list") + public R> list() { + return R.ok(socialUserService.queryListByUserId(LoginHelper.getUserId())); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantController.java new file mode 100644 index 0000000..c405bde --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantController.java @@ -0,0 +1,193 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import com.baomidou.lock.annotation.Lock4j; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysTenantBo; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.service.ISysTenantService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 租户管理 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/tenant") +@ConditionalOnProperty(value = "tenant.enable", havingValue = "true") +public class SysTenantController extends BaseController { + + private final ISysTenantService tenantService; + + /** + * 查询租户列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:list") + @GetMapping("/list") + public TableDataInfo list(SysTenantBo bo, PageQuery pageQuery) { + return tenantService.queryPageList(bo, pageQuery); + } + + /** + * 导出租户列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:export") + @Log(title = "租户管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysTenantBo bo, HttpServletResponse response) { + List list = tenantService.queryList(bo); + ExcelUtil.exportExcel(list, "租户", SysTenantVo.class, response); + } + + /** + * 获取租户详细信息 + * + * @param id 主键 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(tenantService.queryById(id)); + } + + /** + * 新增租户 + */ + @ApiEncrypt + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:add") + @Log(title = "租户管理", businessType = BusinessType.INSERT) + @Lock4j + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysTenantBo bo) { + if (!tenantService.checkCompanyNameUnique(bo)) { + return R.fail("新增租户'" + bo.getCompanyName() + "'失败,企业名称已存在"); + } + return toAjax(TenantHelper.ignore(() -> tenantService.insertByBo(bo))); + } + + /** + * 修改租户 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysTenantBo bo) { + tenantService.checkTenantAllowed(bo.getTenantId()); + if (!tenantService.checkCompanyNameUnique(bo)) { + return R.fail("修改租户'" + bo.getCompanyName() + "'失败,公司名称已存在"); + } + return toAjax(tenantService.updateByBo(bo)); + } + + /** + * 状态修改 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysTenantBo bo) { + tenantService.checkTenantAllowed(bo.getTenantId()); + return toAjax(tenantService.updateTenantStatus(bo)); + } + + /** + * 删除租户 + * + * @param ids 主键串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:remove") + @Log(title = "租户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(tenantService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 动态切换租户 + * + * @param tenantId 租户ID + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @GetMapping("/dynamic/{tenantId}") + public R dynamicTenant(@NotBlank(message = "租户ID不能为空") @PathVariable String tenantId) { + TenantHelper.setDynamic(tenantId, true); + return R.ok(); + } + + /** + * 清除动态租户 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @GetMapping("/dynamic/clear") + public R dynamicClear() { + TenantHelper.clearDynamic(); + return R.ok(); + } + + + /** + * 同步租户套餐 + * + * @param tenantId 租户id + * @param packageId 套餐id + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenant:edit") + @Log(title = "租户管理", businessType = BusinessType.UPDATE) + @GetMapping("/syncTenantPackage") + public R syncTenantPackage(@NotBlank(message = "租户ID不能为空") String tenantId, + @NotNull(message = "套餐ID不能为空") Long packageId) { + return toAjax(TenantHelper.ignore(() -> tenantService.syncTenantPackage(tenantId, packageId))); + } + + /** + * 同步租户字典 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @Log(title = "租户管理", businessType = BusinessType.INSERT) + @GetMapping("/syncTenantDict") + public R syncTenantDict() { + if (!TenantHelper.isEnable()) { + return R.fail("当前未开启租户模式"); + } + tenantService.syncTenantDict(); + return R.ok("同步租户字典成功"); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantPackageController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantPackageController.java new file mode 100644 index 0000000..ce45b33 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysTenantPackageController.java @@ -0,0 +1,142 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysTenantPackageBo; +import com.fuyuanshen.system.domain.vo.SysTenantPackageVo; +import com.fuyuanshen.system.service.ISysTenantPackageService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 租户套餐管理 + * + * @author Michelle.Chung + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/tenant/package") +@ConditionalOnProperty(value = "tenant.enable", havingValue = "true") +public class SysTenantPackageController extends BaseController { + + private final ISysTenantPackageService tenantPackageService; + + /** + * 查询租户套餐列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:list") + @GetMapping("/list") + public TableDataInfo list(SysTenantPackageBo bo, PageQuery pageQuery) { + return tenantPackageService.queryPageList(bo, pageQuery); + } + + /** + * 查询租户套餐下拉选列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:list") + @GetMapping("/selectList") + public R> selectList() { + return R.ok(tenantPackageService.selectList()); + } + + /** + * 导出租户套餐列表 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:export") + @Log(title = "租户套餐", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysTenantPackageBo bo, HttpServletResponse response) { + List list = tenantPackageService.queryList(bo); + ExcelUtil.exportExcel(list, "租户套餐", SysTenantPackageVo.class, response); + } + + /** + * 获取租户套餐详细信息 + * + * @param packageId 主键 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:query") + @GetMapping("/{packageId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long packageId) { + return R.ok(tenantPackageService.queryById(packageId)); + } + + /** + * 新增租户套餐 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:add") + @Log(title = "租户套餐", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody SysTenantPackageBo bo) { + if (!tenantPackageService.checkPackageNameUnique(bo)) { + return R.fail("新增套餐'" + bo.getPackageName() + "'失败,套餐名称已存在"); + } + return toAjax(tenantPackageService.insertByBo(bo)); + } + + /** + * 修改租户套餐 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:edit") + @Log(title = "租户套餐", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody SysTenantPackageBo bo) { + if (!tenantPackageService.checkPackageNameUnique(bo)) { + return R.fail("修改套餐'" + bo.getPackageName() + "'失败,套餐名称已存在"); + } + return toAjax(tenantPackageService.updateByBo(bo)); + } + + /** + * 状态修改 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:edit") + @Log(title = "租户套餐", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysTenantPackageBo bo) { + return toAjax(tenantPackageService.updatePackageStatus(bo)); + } + + /** + * 删除租户套餐 + * + * @param packageIds 主键串 + */ + @SaCheckRole(TenantConstants.SUPER_ADMIN_ROLE_KEY) + @SaCheckPermission("system:tenantPackage:remove") + @Log(title = "租户套餐", businessType = BusinessType.DELETE) + @DeleteMapping("/{packageIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] packageIds) { + return toAjax(tenantPackageService.deleteWithValidByIds(List.of(packageIds), true)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysUserController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysUserController.java new file mode 100644 index 0000000..3001e3c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/controller/system/SysUserController.java @@ -0,0 +1,300 @@ +package com.fuyuanshen.system.controller.system; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt; +import com.fuyuanshen.common.excel.core.ExcelResult; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.bo.SysPostBo; +import com.fuyuanshen.system.domain.bo.SysRoleBo; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.*; +import com.fuyuanshen.system.listener.SysUserImportListener; +import com.fuyuanshen.system.service.*; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用户信息 + * + * @author Lion Li + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + + private final ISysUserService userService; + private final ISysRoleService roleService; + private final ISysPostService postService; + private final ISysDeptService deptService; + private final ISysTenantService tenantService; + + /** + * 获取用户列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/list") + public TableDataInfo list(SysUserBo user, PageQuery pageQuery) { + return userService.selectPageUserList(user, pageQuery); + } + + /** + * 导出用户列表 + */ + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @SaCheckPermission("system:user:export") + @PostMapping("/export") + public void export(SysUserBo user, HttpServletResponse response) { + List list = userService.selectUserExportList(user); + ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response); + } + + /** + * 导入数据 + * + * @param file 导入文件 + * @param updateSupport 是否更新已存在数据 + */ + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @SaCheckPermission("system:user:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { + ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); + return R.ok(result.getAnalysis()); + } + + /** + * 获取导入模板 + */ + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("/getInfo") + public R getInfo() { + UserInfoVo userInfoVo = new UserInfoVo(); + LoginUser loginUser = LoginHelper.getLoginUser(); + if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { + // 超级管理员 如果重新加载用户信息需清除动态租户 + TenantHelper.clearDynamic(); + } + SysUserVo user = userService.selectUserById(loginUser.getUserId()); + if (ObjectUtil.isNull(user)) { + return R.fail("没有权限访问用户数据!"); + } + userInfoVo.setUser(user); + userInfoVo.setPermissions(loginUser.getMenuPermission()); + userInfoVo.setRoles(loginUser.getRolePermission()); + return R.ok(userInfoVo); + } + + /** + * 根据用户编号获取详细信息 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping(value = {"/", "/{userId}"}) + public R getInfo(@PathVariable(value = "userId", required = false) Long userId) { + SysUserInfoVo userInfoVo = new SysUserInfoVo(); + if (ObjectUtil.isNotNull(userId)) { + userService.checkUserDataScope(userId); + SysUserVo sysUser = userService.selectUserById(userId); + userInfoVo.setUser(sysUser); + userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId)); + Long deptId = sysUser.getDeptId(); + if (ObjectUtil.isNotNull(deptId)) { + SysPostBo postBo = new SysPostBo(); + postBo.setDeptId(deptId); + userInfoVo.setPosts(postService.selectPostList(postBo)); + userInfoVo.setPostIds(postService.selectPostListByUserId(userId)); + } + } + SysRoleBo roleBo = new SysRoleBo(); + roleBo.setStatus(SystemConstants.NORMAL); + List roles = roleService.selectRoleList(roleBo); + userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); + return R.ok(userInfoVo); + } + + /** + * 新增用户 + */ + @SaCheckPermission("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated @RequestBody SysUserBo user) { + deptService.checkDeptDataScope(user.getDeptId()); + if (!userService.checkUserNameUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (TenantHelper.isEnable()) { + if (!tenantService.checkAccountBalance(TenantHelper.getTenantId())) { + return R.fail("当前租户下用户名额不足,请联系管理员"); + } + } + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated @RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + if (!userService.checkUserNameUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + * + * @param userIds 角色ID串 + */ + @SaCheckPermission("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public R remove(@PathVariable Long[] userIds) { + if (ArrayUtil.contains(userIds, LoginHelper.getUserId())) { + return R.fail("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 根据用户ID串批量获取用户基础信息 + * + * @param userIds 用户ID串 + * @param deptId 部门ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/optionselect") + public R> optionselect(@RequestParam(required = false) Long[] userIds, + @RequestParam(required = false) Long deptId) { + return R.ok(userService.selectUserByIds(ArrayUtil.isEmpty(userIds) ? null : List.of(userIds), deptId)); + } + + /** + * 重置密码 + */ + @ApiEncrypt + @SaCheckPermission("system:user:resetPwd") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public R resetPwd(@RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(BCrypt.hashpw(user.getPassword())); + return toAjax(userService.resetUserPwd(user.getUserId(), user.getPassword())); + } + + /** + * 状态修改 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public R changeStatus(@RequestBody SysUserBo user) { + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + return toAjax(userService.updateUserStatus(user.getUserId(), user.getStatus())); + } + + /** + * 根据用户编号获取授权角色 + * + * @param userId 用户ID + */ + @SaCheckPermission("system:user:query") + @GetMapping("/authRole/{userId}") + public R authRole(@PathVariable Long userId) { + userService.checkUserDataScope(userId); + SysUserVo user = userService.selectUserById(userId); + List roles = roleService.selectRolesAuthByUserId(userId); + SysUserInfoVo userInfoVo = new SysUserInfoVo(); + userInfoVo.setUser(user); + userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin())); + return R.ok(userInfoVo); + } + + /** + * 用户授权角色 + * + * @param userId 用户Id + * @param roleIds 角色ID串 + */ + @SaCheckPermission("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public R insertAuthRole(Long userId, Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return R.ok(); + } + + /** + * 获取部门树列表 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/deptTree") + public R>> deptTree(SysDeptBo dept) { + return R.ok(deptService.selectDeptTreeList(dept)); + } + + /** + * 获取部门下的所有用户信息 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/list/dept/{deptId}") + public R> listByDept(@PathVariable @NotNull Long deptId) { + return R.ok(userService.selectUserListByDept(deptId)); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysCache.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysCache.java new file mode 100644 index 0000000..dcf092b --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysCache.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.system.domain; + +import com.fuyuanshen.common.core.utils.StringUtils; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 缓存信息 + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +public class SysCache { + + /** + * 缓存名称 + */ + private String cacheName = ""; + + /** + * 缓存键名 + */ + private String cacheKey = ""; + + /** + * 缓存内容 + */ + private String cacheValue = ""; + + /** + * 备注 + */ + private String remark = ""; + + public SysCache(String cacheName, String remark) { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysClient.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysClient.java new file mode 100644 index 0000000..b5cbde2 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysClient.java @@ -0,0 +1,77 @@ +package com.fuyuanshen.system.domain; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 授权管理对象 sys_client + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_client") +public class SysClient extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "id") + private Long id; + + /** + * 客户端id + */ + private String clientId; + + /** + * 客户端key + */ + private String clientKey; + + /** + * 客户端秘钥 + */ + private String clientSecret; + + /** + * 授权类型 + */ + private String grantType; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * token活跃超时时间 + */ + private Long activeTimeout; + + /** + * token固定超时时间 + */ + private Long timeout; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysConfig.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysConfig.java new file mode 100644 index 0000000..63274e5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysConfig.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 参数配置表 sys_config + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_config") +public class SysConfig extends TenantEntity { + + /** + * 参数主键 + */ + @TableId(value = "config_id") + private Long configId; + + /** + * 参数名称 + */ + private String configName; + + /** + * 参数键名 + */ + private String configKey; + + /** + * 参数键值 + */ + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + private String configType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDept.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDept.java new file mode 100644 index 0000000..8d3a269 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDept.java @@ -0,0 +1,92 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +/** + * 部门表 sys_dept + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dept") +public class SysDept extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + @TableId(value = "dept_id") + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 部门类别编码 + */ + private String deptCategory; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 负责人 + */ + private Long leader; + + /** + * 联系电话 + */ + private String phone; + + /** + * 邮箱 + */ + private String email; + + /** + * 部门状态:0正常,1停用 + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 子部门 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictData.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictData.java new file mode 100644 index 0000000..db58dfb --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictData.java @@ -0,0 +1,71 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.tenant.core.TenantEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_data") +public class SysDictData extends TenantEntity { + + /** + * 字典编码 + */ + @TableId(value = "dict_code") + private Long dictCode; + + /** + * 字典排序 + */ + private Integer dictSort; + + /** + * 字典标签 + */ + private String dictLabel; + + /** + * 字典键值 + */ + private String dictValue; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + private String cssClass; + + /** + * 表格字典样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 备注 + */ + private String remark; + + public boolean getDefault() { + return SystemConstants.YES.equals(this.isDefault); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictType.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictType.java new file mode 100644 index 0000000..a35f2c4 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysDictType.java @@ -0,0 +1,41 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典类型表 sys_dict_type + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_dict_type") +public class SysDictType extends TenantEntity { + + /** + * 字典主键 + */ + @TableId(value = "dict_id") + private Long dictId; + + /** + * 字典名称 + */ + private String dictName; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysLogininfor.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysLogininfor.java new file mode 100644 index 0000000..aca3123 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysLogininfor.java @@ -0,0 +1,85 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 系统访问记录表 sys_logininfor + * + * @author Lion Li + */ + +@Data +@TableName("sys_logininfor") +public class SysLogininfor implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "info_id") + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysMenu.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysMenu.java new file mode 100644 index 0000000..d80f234 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysMenu.java @@ -0,0 +1,191 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_menu") +public class SysMenu extends BaseEntity { + + /** + * 菜单ID + */ + @TableId(value = "menu_id") + private Long menuId; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 类型(M目录 C菜单 F按钮) + */ + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限字符串 + */ + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 备注 + */ + private String remark; + + /** + * 父菜单名称 + */ + @TableField(exist = false) + private String parentName; + + /** + * 子菜单 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + + /** + * 获取路由名称 + */ + public String getRouteName() { + String routerName = StringUtils.capitalize(path); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame()) { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + */ + public String getRouterPath() { + String routerPath = this.path; + // 内链打开外网方式 + if (getParentId() != 0L && isInnerLink()) { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0L == getParentId() && SystemConstants.TYPE_DIR.equals(getMenuType()) + && SystemConstants.NO_FRAME.equals(getIsFrame())) { + routerPath = "/" + this.path; + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame()) { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + */ + public String getComponentInfo() { + String component = SystemConstants.LAYOUT; + if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) { + component = this.component; + } else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) { + component = SystemConstants.INNER_LINK; + } else if (StringUtils.isEmpty(this.component) && isParentView()) { + component = SystemConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + */ + public boolean isMenuFrame() { + return getParentId() == 0L && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + */ + public boolean isInnerLink() { + return isFrame.equals(SystemConstants.NO_FRAME) && StringUtils.ishttp(path); + } + + /** + * 是否为parent_view组件 + */ + public boolean isParentView() { + return getParentId() != 0L && SystemConstants.TYPE_DIR.equals(menuType); + } + + /** + * 内链域名特殊字符替换 + */ + public static String innerLinkReplaceEach(String path) { + return StringUtils.replaceEach(path, new String[]{Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":"}, + new String[]{"", "", "", "/", "/"}); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysNotice.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysNotice.java new file mode 100644 index 0000000..d366a6a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysNotice.java @@ -0,0 +1,51 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + + +/** + * 通知公告表 sys_notice + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_notice") +public class SysNotice extends TenantEntity { + + /** + * 公告ID + */ + @TableId(value = "notice_id") + private Long noticeId; + + /** + * 公告标题 + */ + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOperLog.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOperLog.java new file mode 100644 index 0000000..a7c227d --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOperLog.java @@ -0,0 +1,115 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志记录表 oper_log + * + * @author Lion Li + */ + +@Data +@TableName("sys_oper_log") +public class SysOperLog implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @TableId(value = "oper_id") + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOss.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOss.java new file mode 100644 index 0000000..b360436 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOss.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储对象 + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss") +public class SysOss extends TenantEntity { + + /** + * 对象存储主键 + */ + @TableId(value = "oss_id") + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 服务商 + */ + private String service; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOssConfig.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOssConfig.java new file mode 100644 index 0000000..036f4af --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysOssConfig.java @@ -0,0 +1,89 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +/** + * 对象存储配置对象 sys_oss_config + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_oss_config") +public class SysOssConfig extends BaseEntity { + + /** + * 主键 + */ + @TableId(value = "oss_config_id") + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(0否 1是) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysPost.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysPost.java new file mode 100644 index 0000000..00a8469 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysPost.java @@ -0,0 +1,61 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 岗位表 sys_post + * + * @author Lion Li + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_post") +public class SysPost extends TenantEntity { + + /** + * 岗位序号 + */ + @TableId(value = "post_id") + private Long postId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 岗位编码 + */ + private String postCode; + + /** + * 岗位名称 + */ + private String postName; + + /** + * 岗位类别编码 + */ + private String postCategory; + + /** + * 岗位排序 + */ + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRole.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRole.java new file mode 100644 index 0000000..5a7ae14 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRole.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 角色表 sys_role + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_role") +public class SysRole extends TenantEntity { + + /** + * 角色ID + */ + @TableId(value = "role_id") + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 角色排序 + */ + private Integer roleSort; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) + */ + private String dataScope; + + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) + */ + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 备注 + */ + private String remark; + + public SysRole(Long roleId) { + this.roleId = roleId; + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleDept.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleDept.java new file mode 100644 index 0000000..b13e6ca --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleDept.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和部门关联 sys_role_dept + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_dept") +public class SysRoleDept { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleMenu.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..1a73bc0 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysRoleMenu.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author Lion Li + */ + +@Data +@TableName("sys_role_menu") +public class SysRoleMenu { + + /** + * 角色ID + */ + @TableId(type = IdType.INPUT) + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysSocial.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysSocial.java new file mode 100644 index 0000000..3e94f13 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysSocial.java @@ -0,0 +1,136 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 社会化关系对象 sys_social + * + * @author thiszhc + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_social") +public class SysSocial extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 的唯一ID + */ + private String authId; + + /** + * 用户来源 + */ + private String source; + + /** + * 用户的授权令牌 + */ + private String accessToken; + + /** + * 用户的授权令牌的有效期,部分平台可能没有 + */ + private int expireIn; + + /** + * 刷新令牌,部分平台可能没有 + */ + private String refreshToken; + + /** + * 用户的 open id + */ + private String openId; + + /** + * 授权的第三方账号 + */ + private String userName; + + /** + * 授权的第三方昵称 + */ + private String nickName; + + /** + * 授权的第三方邮箱 + */ + private String email; + + /** + * 授权的第三方头像地址 + */ + private String avatar; + + /** + * 平台的授权信息,部分平台可能没有 + */ + private String accessCode; + + /** + * 用户的 unionid + */ + private String unionId; + + /** + * 授予的权限,部分平台可能没有 + */ + private String scope; + + /** + * 个别平台的授权信息,部分平台可能没有 + */ + private String tokenType; + + /** + * id token,部分平台可能没有 + */ + private String idToken; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macAlgorithm; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macKey; + + /** + * 用户的授权code,部分平台可能没有 + */ + private String code; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthToken; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthTokenSecret; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenant.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenant.java new file mode 100644 index 0000000..55c51a3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenant.java @@ -0,0 +1,103 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 租户对象 sys_tenant + * + * @author Michelle.Chung + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_tenant") +public class SysTenant extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "id") + private Long id; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 联系人 + */ + private String contactUserName; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 统一社会信用代码 + */ + private String licenseNumber; + + /** + * 地址 + */ + private String address; + + /** + * 域名 + */ + private String domain; + + /** + * 企业简介 + */ + private String intro; + + /** + * 备注 + */ + private String remark; + + /** + * 租户套餐编号 + */ + private Long packageId; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenantPackage.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenantPackage.java new file mode 100644 index 0000000..e9cc7e1 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysTenantPackage.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.io.Serial; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +/** + * 租户套餐对象 sys_tenant_package + * + * @author Michelle.Chung + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_tenant_package") +public class SysTenantPackage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户套餐id + */ + @TableId(value = "package_id") + private Long packageId; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 关联菜单id + */ + private String menuIds; + + /** + * 备注 + */ + private String remark; + + /** + * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) + */ + private Boolean menuCheckStrictly; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUser.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUser.java new file mode 100644 index 0000000..fa0d4c2 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUser.java @@ -0,0 +1,117 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.tenant.core.TenantEntity; + +import java.util.Date; + +/** + * 用户对象 sys_user + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_user") +public class SysUser extends TenantEntity { + + /** + * 用户ID + */ + @TableId(value = "user_id") + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别 + */ + private String sex; + + /** + * 用户头像 + */ + private Long avatar; + + /** + * 密码 + */ + @TableField( + insertStrategy = FieldStrategy.NOT_EMPTY, + updateStrategy = FieldStrategy.NOT_EMPTY, + whereStrategy = FieldStrategy.NOT_EMPTY + ) + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + private Byte userLevel; + + + public SysUser(Long userId) { + this.userId = userId; + } + + public boolean isSuperAdmin() { + return SystemConstants.SUPER_ADMIN_ID.equals(this.userId); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserOnline.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserOnline.java new file mode 100644 index 0000000..12363be --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserOnline.java @@ -0,0 +1,63 @@ +package com.fuyuanshen.system.domain; + +import lombok.Data; + +/** + * 当前在线会话 + * + * @author Lion Li + */ +@Data +public class SysUserOnline { + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserPost.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserPost.java new file mode 100644 index 0000000..144df8a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserPost.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和岗位关联 sys_user_post + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_post") +public class SysUserPost { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserRole.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserRole.java new file mode 100644 index 0000000..b070639 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/SysUserRole.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 用户和角色关联 sys_user_role + * + * @author Lion Li + */ + +@Data +@TableName("sys_user_role") +public class SysUserRole { + + /** + * 用户ID + */ + @TableId(type = IdType.INPUT) + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysClientBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysClientBo.java new file mode 100644 index 0000000..c4eb9e0 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysClientBo.java @@ -0,0 +1,80 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.system.domain.SysClient; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.List; + +/** + * 授权管理业务对象 sys_client + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysClient.class, reverseConvertGenerate = false) +public class SysClientBo extends BaseEntity { + + /** + * id + */ + @NotNull(message = "id不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 客户端id + */ + private String clientId; + + /** + * 客户端key + */ + @NotBlank(message = "客户端key不能为空", groups = { AddGroup.class, EditGroup.class }) + private String clientKey; + + /** + * 客户端秘钥 + */ + @NotBlank(message = "客户端秘钥不能为空", groups = { AddGroup.class, EditGroup.class }) + private String clientSecret; + + /** + * 授权类型 + */ + @NotNull(message = "授权类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private List grantTypeList; + + /** + * 授权类型 + */ + private String grantType; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * token活跃超时时间 + */ + private Long activeTimeout; + + /** + * token固定超时时间 + */ + private Long timeout; + + /** + * 状态(0正常 1停用) + */ + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysConfigBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysConfigBo.java new file mode 100644 index 0000000..643865d --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysConfigBo.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysConfig; + +/** + * 参数配置业务对象 sys_config + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysConfig.class, reverseConvertGenerate = false) +public class SysConfigBo extends BaseEntity { + + /** + * 参数主键 + */ + private Long configId; + + /** + * 参数名称 + */ + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过{max}个字符") + private String configName; + + /** + * 参数键名 + */ + @NotBlank(message = "参数键名不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过{max}个字符") + private String configKey; + + /** + * 参数键值 + */ + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过{max}个字符") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + private String configType; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDeptBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDeptBo.java new file mode 100644 index 0000000..8f4e958 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDeptBo.java @@ -0,0 +1,81 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysDept; + +/** + * 部门业务对象 sys_dept + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDept.class, reverseConvertGenerate = false) +public class SysDeptBo extends BaseEntity { + + /** + * 部门id + */ + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符") + private String deptName; + + /** + * 部门类别编码 + */ + @Size(min = 0, max = 100, message = "部门类别编码长度不能超过{max}个字符") + private String deptCategory; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer orderNum; + + /** + * 负责人 + */ + private Long leader; + + /** + * 联系电话 + */ + @Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符") + private String phone; + + /** + * 邮箱 + */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 部门状态(0正常 1停用) + */ + private String status; + + /** + * 归属部门id(部门树) + */ + private Long belongDeptId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictDataBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictDataBo.java new file mode 100644 index 0000000..a73b2fc --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictDataBo.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysDictData; + +/** + * 字典数据业务对象 sys_dict_data + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDictData.class, reverseConvertGenerate = false) +public class SysDictDataBo extends BaseEntity { + + /** + * 字典编码 + */ + private Long dictCode; + + /** + * 字典排序 + */ + private Integer dictSort; + + /** + * 字典标签 + */ + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符") + private String dictLabel; + + /** + * 字典键值 + */ + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符") + private String dictValue; + + /** + * 字典类型 + */ + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + @Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符") + private String cssClass; + + /** + * 表格回显样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 创建部门 + */ + private Long createDept; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictTypeBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictTypeBo.java new file mode 100644 index 0000000..52bd835 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysDictTypeBo.java @@ -0,0 +1,50 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.constant.RegexConstants; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysDictType; + +/** + * 字典类型业务对象 sys_dict_type + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysDictType.class, reverseConvertGenerate = false) +public class SysDictTypeBo extends BaseEntity { + + /** + * 字典主键 + */ + private Long dictId; + + /** + * 字典名称 + */ + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符") + private String dictName; + + /** + * 字典类型 + */ + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符") + @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + private String dictType; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysLogininforBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysLogininforBo.java new file mode 100644 index 0000000..a9a932f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysLogininforBo.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.system.domain.SysLogininfor; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 系统访问记录业务对象 sys_logininfor + * + * @author Michelle.Chung + */ + +@Data +@AutoMapper(target = SysLogininfor.class, reverseConvertGenerate = false) +public class SysLogininforBo { + + /** + * 访问ID + */ + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录状态(0成功 1失败) + */ + private String status; + + /** + * 提示消息 + */ + private String msg; + + /** + * 访问时间 + */ + private Date loginTime; + + /** + * 请求参数 + */ + private Map params = new HashMap<>(); + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysMenuBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysMenuBo.java new file mode 100644 index 0000000..c33861f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysMenuBo.java @@ -0,0 +1,110 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.constant.RegexConstants; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysMenu; + +/** + * 菜单权限业务对象 sys_menu + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysMenu.class, reverseConvertGenerate = false) +public class SysMenuBo extends BaseEntity { + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 菜单名称 + */ + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符") + private String menuName; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer orderNum; + + /** + * 路由地址 + */ + @Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符") + private String path; + + /** + * 组件路径 + */ + @Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符") + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 菜单类型(M目录 C菜单 F按钮) + */ + @NotBlank(message = "菜单类型不能为空") + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限标识 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符") + @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "权限标识必须符合 tool:build:list 格式") + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysNoticeBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysNoticeBo.java new file mode 100644 index 0000000..765d5ee --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysNoticeBo.java @@ -0,0 +1,61 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.xss.Xss; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysNotice; + +/** + * 通知公告业务对象 sys_notice + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysNotice.class, reverseConvertGenerate = false) +public class SysNoticeBo extends BaseEntity { + + /** + * 公告ID + */ + private Long noticeId; + + /** + * 公告标题 + */ + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过{max}个字符") + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 创建人名称 + */ + private String createByName; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOperLogBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOperLogBo.java new file mode 100644 index 0000000..e54dbb3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOperLogBo.java @@ -0,0 +1,127 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.common.log.event.OperLogEvent; +import com.fuyuanshen.system.domain.SysOperLog; +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMappers; +import lombok.Data; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 操作日志记录业务对象 sys_oper_log + * + * @author Michelle.Chung + * @date 2023-02-07 + */ + +@Data +@AutoMappers({ + @AutoMapper(target = SysOperLog.class, reverseConvertGenerate = false), + @AutoMapper(target = OperLogEvent.class) +}) +public class SysOperLogBo { + + /** + * 日志主键 + */ + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 模块标题 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 方法名称 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求URL + */ + private String operUrl; + + /** + * 主机地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; + + /** + * 请求参数 + */ + private Map params = new HashMap<>(); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssBo.java new file mode 100644 index 0000000..5ca51ba --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssBo.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysOss; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * OSS对象存储分页查询对象 sys_oss + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysOss.class, reverseConvertGenerate = false) +public class SysOssBo extends BaseEntity { + + /** + * ossId + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 服务商 + */ + private String service; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssConfigBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssConfigBo.java new file mode 100644 index 0000000..8c77d04 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysOssConfigBo.java @@ -0,0 +1,109 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysOssConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 对象存储配置业务对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysOssConfig.class, reverseConvertGenerate = false) +public class SysOssConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long ossConfigId; + + /** + * 配置key + */ + @NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间") + private String configKey; + + /** + * accessKey + */ + @NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间") + private String accessKey; + + /** + * 秘钥 + */ + @NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间") + private String secretKey; + + /** + * 桶名称 + */ + @NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间") + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + @NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class}) + @Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间") + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 域 + */ + private String region; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String accessPolicy; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysPostBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysPostBo.java new file mode 100644 index 0000000..4e18a90 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysPostBo.java @@ -0,0 +1,75 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysPost; + +/** + * 岗位信息业务对象 sys_post + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysPost.class, reverseConvertGenerate = false) +public class SysPostBo extends BaseEntity { + + /** + * 岗位ID + */ + private Long postId; + + /** + * 部门id(单部门) + */ + @NotNull(message = "部门id不能为空") + private Long deptId; + + /** + * 归属部门id(部门树) + */ + private Long belongDeptId; + + /** + * 岗位编码 + */ + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过{max}个字符") + private String postCode; + + /** + * 岗位名称 + */ + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过{max}个字符") + private String postName; + + /** + * 岗位类别编码 + */ + @Size(min = 0, max = 100, message = "类别编码长度不能超过{max}个字符") + private String postCategory; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysRoleBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysRoleBo.java new file mode 100644 index 0000000..e48bab7 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysRoleBo.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysRole; + +/** + * 角色信息业务对象 sys_role + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysRole.class, reverseConvertGenerate = false) +public class SysRoleBo extends BaseEntity { + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色名称 + */ + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符") + private String roleName; + + /** + * 角色权限字符串 + */ + @NotBlank(message = "角色权限字符串不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符") + private String roleKey; + + /** + * 显示顺序 + */ + @NotNull(message = "显示顺序不能为空") + private Integer roleSort; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) + */ + private String dataScope; + + /** + * 菜单树选择项是否关联显示 + */ + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示 + */ + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 菜单组 + */ + private Long[] menuIds; + + /** + * 部门组(数据权限) + */ + private Long[] deptIds; + + public SysRoleBo(Long roleId) { + this.roleId = roleId; + } + + public boolean isSuperAdmin() { + return SystemConstants.SUPER_ADMIN_ID.equals(this.roleId); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysSocialBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysSocialBo.java new file mode 100644 index 0000000..2551252 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysSocialBo.java @@ -0,0 +1,142 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import com.fuyuanshen.system.domain.SysSocial; + +/** + * 社会化关系业务对象 sys_social + * + * @author Lion Li + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysSocial.class, reverseConvertGenerate = false) +public class SysSocialBo extends TenantEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 认证唯一ID + */ + @NotBlank(message = "认证唯一ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String authId; + + /** + * 用户来源 + */ + @NotBlank(message = "用户来源不能为空", groups = { AddGroup.class, EditGroup.class }) + private String source; + + /** + * 用户的授权令牌 + */ + @NotBlank(message = "用户的授权令牌不能为空", groups = { AddGroup.class, EditGroup.class }) + private String accessToken; + + /** + * 用户的授权令牌的有效期,部分平台可能没有 + */ + private int expireIn; + + /** + * 刷新令牌,部分平台可能没有 + */ + private String refreshToken; + + /** + * 平台唯一id + */ + private String openId; + + /** + * 用户的 ID + */ + @NotBlank(message = "用户的ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long userId; + + /** + * 平台的授权信息,部分平台可能没有 + */ + private String accessCode; + + /** + * 用户的 unionid + */ + private String unionId; + + /** + * 授予的权限,部分平台可能没有 + */ + private String scope; + + /** + * 授权的第三方账号 + */ + private String userName; + + /** + * 授权的第三方昵称 + */ + private String nickName; + + /** + * 授权的第三方邮箱 + */ + private String email; + + /** + * 授权的第三方头像地址 + */ + private String avatar; + + /** + * 个别平台的授权信息,部分平台可能没有 + */ + private String tokenType; + + /** + * id token,部分平台可能没有 + */ + private String idToken; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macAlgorithm; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macKey; + + /** + * 用户的授权code,部分平台可能没有 + */ + private String code; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthToken; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthTokenSecret; + + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantBo.java new file mode 100644 index 0000000..a62c58d --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantBo.java @@ -0,0 +1,114 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.system.domain.SysTenant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +/** + * 租户业务对象 sys_tenant + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysTenant.class, reverseConvertGenerate = false) +public class SysTenantBo extends BaseEntity { + + /** + * id + */ + @NotNull(message = "id不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 联系人 + */ + @NotBlank(message = "联系人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String contactUserName; + + /** + * 联系电话 + */ + @NotBlank(message = "联系电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String contactPhone; + + /** + * 企业名称 + */ + @NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String companyName; + + /** + * 用户名(创建系统用户) + */ + @NotBlank(message = "用户名不能为空", groups = { AddGroup.class }) + private String username; + + /** + * 密码(创建系统用户) + */ + @NotBlank(message = "密码不能为空", groups = { AddGroup.class }) + private String password; + + /** + * 统一社会信用代码 + */ + private String licenseNumber; + + /** + * 地址 + */ + private String address; + + /** + * 域名 + */ + private String domain; + + /** + * 企业简介 + */ + private String intro; + + /** + * 备注 + */ + private String remark; + + /** + * 租户套餐编号 + */ + @NotNull(message = "租户套餐不能为空", groups = { AddGroup.class }) + private Long packageId; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantPackageBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantPackageBo.java new file mode 100644 index 0000000..a32c06c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysTenantPackageBo.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.system.domain.bo; + +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.system.domain.SysTenantPackage; +import io.github.linpeilie.annotations.AutoMapper; +import io.github.linpeilie.annotations.AutoMapping; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +/** + * 租户套餐业务对象 sys_tenant_package + * + * @author Michelle.Chung + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysTenantPackage.class, reverseConvertGenerate = false) +public class SysTenantPackageBo extends BaseEntity { + + /** + * 租户套餐id + */ + @NotNull(message = "租户套餐id不能为空", groups = { EditGroup.class }) + private Long packageId; + + /** + * 套餐名称 + */ + @NotBlank(message = "套餐名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String packageName; + + /** + * 关联菜单id + */ + @AutoMapping(target = "menuIds", expression = "java(com.fuyuanshen.common.core.utils.StringUtils.join(source.getMenuIds(), \",\"))") + private Long[] menuIds; + + /** + * 备注 + */ + private String remark; + + /** + * 菜单树选择项是否关联显示 + */ + private Boolean menuCheckStrictly; + + /** + * 状态(0正常 1停用) + */ + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserBo.java new file mode 100644 index 0000000..60fbdb1 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserBo.java @@ -0,0 +1,124 @@ +package com.fuyuanshen.system.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.xss.Xss; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.system.domain.SysUser; + +/** + * 用户信息业务对象 sys_user + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysUser.class, reverseConvertGenerate = false) +public class SysUserBo extends BaseEntity { + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") + private String userName; + + /** + * 用户昵称 + */ + @Xss(message = "用户昵称不能包含脚本字符") + @NotBlank(message = "用户昵称不能为空") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 角色组 + */ + @Size(min = 1, message = "用户角色不能为空") + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 用户ID + */ + private String userIds; + + /** + * 排除不查询的用户(工作流用) + */ + private String excludeUserIds; + + public SysUserBo(Long userId) { + this.userId = userId; + } + + public boolean isSuperAdmin() { + return SystemConstants.SUPER_ADMIN_ID.equals(this.userId); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserPasswordBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserPasswordBo.java new file mode 100644 index 0000000..99f9000 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserPasswordBo.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户密码修改bo + */ +@Data +public class SysUserPasswordBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 旧密码 + */ + @NotBlank(message = "旧密码不能为空") + private String oldPassword; + + /** + * 新密码 + */ + @NotBlank(message = "新密码不能为空") + private String newPassword; +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserProfileBo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserProfileBo.java new file mode 100644 index 0000000..517dc0e --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/bo/SysUserProfileBo.java @@ -0,0 +1,53 @@ +package com.fuyuanshen.system.domain.bo; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import com.fuyuanshen.common.core.constant.RegexConstants; +import com.fuyuanshen.common.core.xss.Xss; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.common.sensitive.annotation.Sensitive; +import com.fuyuanshen.common.sensitive.core.SensitiveStrategy; + +/** + * 个人信息业务处理 + * + * @author Michelle.Chung + */ + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysUserProfileBo extends BaseEntity { + + /** + * 用户昵称 + */ + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") + private String nickName; + + /** + * 用户邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL) + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") + private String email; + + /** + * 手机号码 + */ + @Pattern(regexp = RegexConstants.MOBILE, message = "手机号格式不正确") + @Sensitive(strategy = SensitiveStrategy.PHONE) + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/MetaVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..ae7818c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/MetaVo.java @@ -0,0 +1,61 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fuyuanshen.common.core.utils.StringUtils; +import lombok.Data; + +/** + * 路由显示信息 + * + * @author fys + */ + +@Data +public class MetaVo { + + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private Boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo(String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, Boolean noCache) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, Boolean noCache, String link) { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) { + this.link = link; + } + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/RouterVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..812f9ae --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/RouterVo.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author Lion Li + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private Boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysClientVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysClientVo.java new file mode 100644 index 0000000..44241d9 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysClientVo.java @@ -0,0 +1,90 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fuyuanshen.system.domain.SysClient; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + + +/** + * 授权管理视图对象 sys_client + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysClient.class) +public class SysClientVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @ExcelProperty(value = "id") + private Long id; + + /** + * 客户端id + */ + @ExcelProperty(value = "客户端id") + private String clientId; + + /** + * 客户端key + */ + @ExcelProperty(value = "客户端key") + private String clientKey; + + /** + * 客户端秘钥 + */ + @ExcelProperty(value = "客户端秘钥") + private String clientSecret; + + /** + * 授权类型 + */ + @ExcelProperty(value = "授权类型") + private List grantTypeList; + + /** + * 授权类型 + */ + private String grantType; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * token活跃超时时间 + */ + @ExcelProperty(value = "token活跃超时时间") + private Long activeTimeout; + + /** + * token固定超时时间 + */ + @ExcelProperty(value = "token固定超时时间") + private Long timeout; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysConfigVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysConfigVo.java new file mode 100644 index 0000000..6285c83 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysConfigVo.java @@ -0,0 +1,72 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 参数配置视图对象 sys_config + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysConfig.class) +public class SysConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 参数主键 + */ + @ExcelProperty(value = "参数主键") + private Long configId; + + /** + * 参数名称 + */ + @ExcelProperty(value = "参数名称") + private String configName; + + /** + * 参数键名 + */ + @ExcelProperty(value = "参数键名") + private String configKey; + + /** + * 参数键值 + */ + @ExcelProperty(value = "参数键值") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @ExcelProperty(value = "系统内置", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String configType; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDeptVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDeptVo.java new file mode 100644 index 0000000..e69f872 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDeptVo.java @@ -0,0 +1,109 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysDept; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 部门视图对象 sys_dept + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDept.class) +public class SysDeptVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 父部门id + */ + private Long parentId; + + /** + * 父部门名称 + */ + private String parentName; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 部门类别编码 + */ + @ExcelProperty(value = "部门类别编码") + private String deptCategory; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 负责人ID + */ + private Long leader; + + /** + * 负责人 + */ + @ExcelProperty(value = "负责人") + private String leaderName; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String phone; + + /** + * 邮箱 + */ + @ExcelProperty(value = "邮箱") + private String email; + + /** + * 部门状态(0正常 1停用) + */ + @ExcelProperty(value = "部门状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 子部门 + */ + private List children = new ArrayList<>(); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictDataVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictDataVo.java new file mode 100644 index 0000000..071f409 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictDataVo.java @@ -0,0 +1,88 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysDictData; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 字典数据视图对象 sys_dict_data + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDictData.class) +public class SysDictDataVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典编码 + */ + @ExcelProperty(value = "字典编码") + private Long dictCode; + + /** + * 字典排序 + */ + @ExcelProperty(value = "字典排序") + private Integer dictSort; + + /** + * 字典标签 + */ + @ExcelProperty(value = "字典标签") + private String dictLabel; + + /** + * 字典键值 + */ + @ExcelProperty(value = "字典键值") + private String dictValue; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + private String dictType; + + /** + * 样式属性(其他样式扩展) + */ + private String cssClass; + + /** + * 表格回显样式 + */ + private String listClass; + + /** + * 是否默认(Y是 N否) + */ + @ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private String isDefault; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictTypeVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictTypeVo.java new file mode 100644 index 0000000..696b933 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysDictTypeVo.java @@ -0,0 +1,57 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.system.domain.SysDictType; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 字典类型视图对象 sys_dict_type + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysDictType.class) +public class SysDictTypeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典主键 + */ + @ExcelProperty(value = "字典主键") + private Long dictId; + + /** + * 字典名称 + */ + @ExcelProperty(value = "字典名称") + private String dictName; + + /** + * 字典类型 + */ + @ExcelProperty(value = "字典类型") + private String dictType; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysLogininforVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysLogininforVo.java new file mode 100644 index 0000000..6b3121a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysLogininforVo.java @@ -0,0 +1,106 @@ +package com.fuyuanshen.system.domain.vo; + +import java.util.Date; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysLogininfor; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + + +/** + * 系统访问记录视图对象 sys_logininfor + * + * @author Michelle.Chung + * @date 2023-02-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysLogininfor.class) +public class SysLogininforVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 访问ID + */ + @ExcelProperty(value = "序号") + private Long infoId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "用户账号") + private String userName; + + /** + * 客户端 + */ + @ExcelProperty(value = "客户端") + private String clientKey; + + /** + * 设备类型 + */ + @ExcelProperty(value = "设备类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_device_type") + private String deviceType; + + /** + * 登录状态(0成功 1失败) + */ + @ExcelProperty(value = "登录状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private String status; + + /** + * 登录IP地址 + */ + @ExcelProperty(value = "登录地址") + private String ipaddr; + + /** + * 登录地点 + */ + @ExcelProperty(value = "登录地点") + private String loginLocation; + + /** + * 浏览器类型 + */ + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 操作系统 + */ + @ExcelProperty(value = "操作系统") + private String os; + + + /** + * 提示消息 + */ + @ExcelProperty(value = "提示消息") + private String msg; + + /** + * 访问时间 + */ + @ExcelProperty(value = "访问时间") + private Date loginTime; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysMenuVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysMenuVo.java new file mode 100644 index 0000000..8244553 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysMenuVo.java @@ -0,0 +1,116 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fuyuanshen.system.domain.SysMenu; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + + +/** + * 菜单权限视图对象 sys_menu + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysMenu.class) +public class SysMenuVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 菜单ID + */ + private Long menuId; + + /** + * 菜单名称 + */ + private String menuName; + + /** + * 父菜单ID + */ + private Long parentId; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 路由地址 + */ + private String path; + + /** + * 组件路径 + */ + private String component; + + /** + * 路由参数 + */ + private String queryParam; + + /** + * 是否为外链(0是 1否) + */ + private String isFrame; + + /** + * 是否缓存(0缓存 1不缓存) + */ + private String isCache; + + /** + * 菜单类型(M目录 C菜单 F按钮) + */ + private String menuType; + + /** + * 显示状态(0显示 1隐藏) + */ + private String visible; + + /** + * 菜单状态(0正常 1停用) + */ + private String status; + + /** + * 权限标识 + */ + private String perms; + + /** + * 菜单图标 + */ + private String icon; + + /** + * 创建部门 + */ + private Long createDept; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 子菜单 + */ + private List children = new ArrayList<>(); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysNoticeVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysNoticeVo.java new file mode 100644 index 0000000..38a3509 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysNoticeVo.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.system.domain.SysNotice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 通知公告视图对象 sys_notice + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysNotice.class) +public class SysNoticeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 公告ID + */ + private Long noticeId; + + /** + * 公告标题 + */ + private String noticeTitle; + + /** + * 公告类型(1通知 2公告) + */ + private String noticeType; + + /** + * 公告内容 + */ + private String noticeContent; + + /** + * 公告状态(0正常 1关闭) + */ + private String status; + + /** + * 备注 + */ + private String remark; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 创建人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + private String createByName; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOperLogVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOperLogVo.java new file mode 100644 index 0000000..04201ac --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOperLogVo.java @@ -0,0 +1,144 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysOperLog; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 操作日志记录视图对象 sys_oper_log + * + * @author Michelle.Chung + * @date 2023-02-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysOperLog.class) +public class SysOperLogVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @ExcelProperty(value = "日志主键") + private Long operId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 模块标题 + */ + @ExcelProperty(value = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @ExcelProperty(value = "业务类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_oper_type") + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 方法名称 + */ + @ExcelProperty(value = "请求方法") + private String method; + + /** + * 请求方式 + */ + @ExcelProperty(value = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @ExcelProperty(value = "操作类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** + * 操作人员 + */ + @ExcelProperty(value = "操作人员") + private String operName; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 请求URL + */ + @ExcelProperty(value = "请求地址") + private String operUrl; + + /** + * 主机地址 + */ + @ExcelProperty(value = "操作地址") + private String operIp; + + /** + * 操作地点 + */ + @ExcelProperty(value = "操作地点") + private String operLocation; + + /** + * 请求参数 + */ + @ExcelProperty(value = "请求参数") + private String operParam; + + /** + * 返回参数 + */ + @ExcelProperty(value = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_common_status") + private Integer status; + + /** + * 错误消息 + */ + @ExcelProperty(value = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @ExcelProperty(value = "操作时间") + private Date operTime; + + /** + * 消耗时间 + */ + @ExcelProperty(value = "消耗时间") + private Long costTime; +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssConfigVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssConfigVo.java new file mode 100644 index 0000000..42f3627 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssConfigVo.java @@ -0,0 +1,97 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import com.fuyuanshen.system.domain.SysOssConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 对象存储配置视图对象 sys_oss_config + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysOssConfig.class) +public class SysOssConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long ossConfigId; + + /** + * 配置key + */ + private String configKey; + + /** + * accessKey + */ + private String accessKey; + + /** + * 秘钥 + */ + private String secretKey; + + /** + * 桶名称 + */ + private String bucketName; + + /** + * 前缀 + */ + private String prefix; + + /** + * 访问站点 + */ + private String endpoint; + + /** + * 自定义域名 + */ + private String domain; + + /** + * 是否https(Y=是,N=否) + */ + private String isHttps; + + /** + * 域 + */ + private String region; + + /** + * 是否默认(0=是,1=否) + */ + private String status; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 备注 + */ + private String remark; + + /** + * 桶权限类型(0private 1public 2custom) + */ + private String accessPolicy; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssUploadVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssUploadVo.java new file mode 100644 index 0000000..235c9ea --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssUploadVo.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.system.domain.vo; + +import lombok.Data; + +/** + * 上传对象信息 + * + * @author Michelle.Chung + */ +@Data +public class SysOssUploadVo { + + /** + * URL地址 + */ + private String url; + + /** + * 文件名 + */ + private String fileName; + + /** + * 对象存储主键 + */ + private String ossId; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssVo.java new file mode 100644 index 0000000..2e612a4 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysOssVo.java @@ -0,0 +1,77 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.system.domain.SysOss; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * OSS对象存储视图对象 sys_oss + * + * @author Lion Li + */ +@Data +@AutoMapper(target = SysOss.class) +public class SysOssVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 对象存储主键 + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + + /** + * 扩展字段 + */ + private String ext1; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 上传人 + */ + private Long createBy; + + /** + * 上传人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy") + private String createByName; + + /** + * 服务商 + */ + private String service; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysPostVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysPostVo.java new file mode 100644 index 0000000..5d4e17c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysPostVo.java @@ -0,0 +1,91 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.system.domain.SysPost; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 岗位信息视图对象 sys_post + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysPost.class) +public class SysPostVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + @ExcelProperty(value = "岗位序号") + private Long postId; + + /** + * 部门id + */ + @ExcelProperty(value = "部门id") + private Long deptId; + + /** + * 岗位编码 + */ + @ExcelProperty(value = "岗位编码") + private String postCode; + + /** + * 岗位名称 + */ + @ExcelProperty(value = "岗位名称") + private String postName; + + /** + * 岗位类别编码 + */ + @ExcelProperty(value = "类别编码") + private String postCategory; + + /** + * 显示顺序 + */ + @ExcelProperty(value = "岗位排序") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 部门名 + */ + @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId") + private String deptName; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysRoleVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysRoleVo.java new file mode 100644 index 0000000..8149ce8 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysRoleVo.java @@ -0,0 +1,100 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysRole; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 角色信息视图对象 sys_role + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysRole.class) +public class SysRoleVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + @ExcelProperty(value = "角色序号") + private Long roleId; + + /** + * 角色名称 + */ + @ExcelProperty(value = "角色名称") + private String roleName; + + /** + * 角色权限字符串 + */ + @ExcelProperty(value = "角色权限") + private String roleKey; + + /** + * 显示顺序 + */ + @ExcelProperty(value = "角色排序") + private Integer roleSort; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) + */ + @ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=全部数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限,6=部门及以下或本人数据权限") + private String dataScope; + + /** + * 菜单树选择项是否关联显示 + */ + @ExcelProperty(value = "菜单树选择项是否关联显示") + private Boolean menuCheckStrictly; + + /** + * 部门树选择项是否关联显示 + */ + @ExcelProperty(value = "部门树选择项是否关联显示") + private Boolean deptCheckStrictly; + + /** + * 角色状态(0正常 1停用) + */ + @ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 用户是否存在此角色标识 默认不存在 + */ + private boolean flag = false; + + public boolean isSuperAdmin() { + return SystemConstants.SUPER_ADMIN_ID.equals(this.roleId); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysSocialVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysSocialVo.java new file mode 100644 index 0000000..a9108b4 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysSocialVo.java @@ -0,0 +1,144 @@ +package com.fuyuanshen.system.domain.vo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.system.domain.SysSocial; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 社会化关系视图对象 sys_social + * + * @author thiszhc + */ +@Data +@AutoMapper(target = SysSocial.class) +public class SysSocialVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 的唯一ID + */ + private String authId; + + /** + * 用户来源 + */ + private String source; + + /** + * 用户的授权令牌 + */ + private String accessToken; + + /** + * 用户的授权令牌的有效期,部分平台可能没有 + */ + private int expireIn; + + /** + * 刷新令牌,部分平台可能没有 + */ + private String refreshToken; + + /** + * 用户的 open id + */ + private String openId; + + /** + * 授权的第三方账号 + */ + private String userName; + + /** + * 授权的第三方昵称 + */ + private String nickName; + + /** + * 授权的第三方邮箱 + */ + private String email; + + /** + * 授权的第三方头像地址 + */ + private String avatar; + + + /** + * 平台的授权信息,部分平台可能没有 + */ + private String accessCode; + + /** + * 用户的 unionid + */ + private String unionId; + + /** + * 授予的权限,部分平台可能没有 + */ + private String scope; + + /** + * 个别平台的授权信息,部分平台可能没有 + */ + private String tokenType; + + /** + * id token,部分平台可能没有 + */ + private String idToken; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macAlgorithm; + + /** + * 小米平台用户的附带属性,部分平台可能没有 + */ + private String macKey; + + /** + * 用户的授权code,部分平台可能没有 + */ + private String code; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthToken; + + /** + * Twitter平台用户的附带属性,部分平台可能没有 + */ + private String oauthTokenSecret; + + /** + * 创建时间 + */ + private Date createTime; +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantPackageVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantPackageVo.java new file mode 100644 index 0000000..897ed67 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantPackageVo.java @@ -0,0 +1,66 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysTenantPackage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 租户套餐视图对象 sys_tenant_package + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysTenantPackage.class) +public class SysTenantPackageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户套餐id + */ + @ExcelProperty(value = "租户套餐id") + private Long packageId; + + /** + * 套餐名称 + */ + @ExcelProperty(value = "套餐名称") + private String packageName; + + /** + * 关联菜单id + */ + @ExcelProperty(value = "关联菜单id") + private String menuIds; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 菜单树选择项是否关联显示 + */ + @ExcelProperty(value = "菜单树选择项是否关联显示") + private Boolean menuCheckStrictly; + + /** + * 状态(0正常 1停用) + */ + @ExcelProperty(value = "状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantVo.java new file mode 100644 index 0000000..ce167ce --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysTenantVo.java @@ -0,0 +1,115 @@ +package com.fuyuanshen.system.domain.vo; + +import java.util.Date; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import com.fuyuanshen.system.domain.SysTenant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 租户视图对象 sys_tenant + * + * @author Michelle.Chung + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysTenant.class) +public class SysTenantVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @ExcelProperty(value = "id") + private Long id; + + /** + * 租户编号 + */ + @ExcelProperty(value = "租户编号") + private String tenantId; + + /** + * 联系人 + */ + @ExcelProperty(value = "联系人") + private String contactUserName; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String contactPhone; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String companyName; + + /** + * 统一社会信用代码 + */ + @ExcelProperty(value = "统一社会信用代码") + private String licenseNumber; + + /** + * 地址 + */ + @ExcelProperty(value = "地址") + private String address; + + /** + * 域名 + */ + @ExcelProperty(value = "域名") + private String domain; + + /** + * 企业简介 + */ + @ExcelProperty(value = "企业简介") + private String intro; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 租户套餐编号 + */ + @ExcelProperty(value = "租户套餐编号") + private Long packageId; + + /** + * 过期时间 + */ + @ExcelProperty(value = "过期时间") + private Date expireTime; + + /** + * 用户数量(-1不限制) + */ + @ExcelProperty(value = "用户数量") + private Long accountCount; + + /** + * 租户状态(0正常 1停用) + */ + @ExcelProperty(value = "租户状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=正常,1=停用") + private String status; + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserExportVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserExportVo.java new file mode 100644 index 0000000..44517ae --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserExportVo.java @@ -0,0 +1,94 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 用户对象导出VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +public class SysUserExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + + /** + * 最后登录IP + */ + @ExcelProperty(value = "最后登录IP") + private String loginIp; + + /** + * 最后登录时间 + */ + @ExcelProperty(value = "最后登录时间") + private Date loginDate; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 负责人 + */ + @ExcelProperty(value = "部门负责人") + private String leaderName; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserImportVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserImportVo.java new file mode 100644 index 0000000..c30a11d --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserImportVo.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.system.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fuyuanshen.common.excel.annotation.ExcelDictFormat; +import com.fuyuanshen.common.excel.convert.ExcelDictConvert; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户对象导入VO + * + * @author Lion Li + */ + +@Data +@NoArgsConstructor +// @Accessors(chain = true) // 导入不允许使用 会找不到set方法 +public class SysUserImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户序号") + private Long userId; + + /** + * 部门ID + */ + @ExcelProperty(value = "部门编号") + private Long deptId; + + /** + * 用户账号 + */ + @ExcelProperty(value = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @ExcelProperty(value = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_normal_disable") + private String status; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserInfoVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserInfoVo.java new file mode 100644 index 0000000..7e104f5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserInfoVo.java @@ -0,0 +1,40 @@ +package com.fuyuanshen.system.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 用户信息 + * + * @author Michelle.Chung + */ +@Data +public class SysUserInfoVo { + + /** + * 用户信息 + */ + private SysUserVo user; + + /** + * 角色ID列表 + */ + private List roleIds; + + /** + * 角色列表 + */ + private List roles; + + /** + * 岗位ID列表 + */ + private List postIds; + + /** + * 岗位列表 + */ + private List posts; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserVo.java new file mode 100644 index 0000000..0f1cfac --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/SysUserVo.java @@ -0,0 +1,145 @@ +package com.fuyuanshen.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fuyuanshen.common.sensitive.annotation.Sensitive; +import com.fuyuanshen.common.sensitive.core.SensitiveStrategy; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.system.domain.SysUser; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 用户信息视图对象 sys_user + * + * @author Michelle.Chung + */ +@Data +@AutoMapper(target = SysUser.class) +public class SysUserVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + private Long pid; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + @Sensitive(strategy = SensitiveStrategy.EMAIL, perms = "system:user:edit") + private String email; + + /** + * 手机号码 + */ + @Sensitive(strategy = SensitiveStrategy.PHONE, perms = "system:user:edit") + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 头像地址 + */ + @Translation(type = TransConstant.OSS_ID_TO_URL) + private Long avatar; + + /** + * 密码 + */ + @JsonIgnore + @JsonProperty + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 最后登录IP + */ + private String loginIp; + + /** + * 最后登录时间 + */ + private Date loginDate; + + /** + * 备注 + */ + private String remark; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 部门名 + */ + @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId") + private String deptName; + + /** + * 角色对象 + */ + private List roles; + + /** + * 角色组 + */ + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + private Byte userLevel; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/UserInfoVo.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/UserInfoVo.java new file mode 100644 index 0000000..5559f5a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/domain/vo/UserInfoVo.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.system.domain.vo; + +import lombok.Data; + +import java.util.Set; + +/** + * 登录用户信息 + * + * @author Michelle.Chung + */ +@Data +public class UserInfoVo { + + /** + * 用户基本信息 + */ + private SysUserVo user; + + /** + * 菜单权限 + */ + private Set permissions; + + /** + * 角色权限 + */ + private Set roles; + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/listener/SysUserImportListener.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/listener/SysUserImportListener.java new file mode 100644 index 0000000..b3fe43b --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/listener/SysUserImportListener.java @@ -0,0 +1,127 @@ +package com.fuyuanshen.system.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.digest.BCrypt; +import cn.hutool.http.HtmlUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.excel.core.ExcelListener; +import com.fuyuanshen.common.excel.core.ExcelResult; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.SysUserImportVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.service.ISysConfigService; +import com.fuyuanshen.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * 系统用户自定义导入 + * + * @author Lion Li + */ +@Slf4j +public class SysUserImportListener extends AnalysisEventListener implements ExcelListener { + + private final ISysUserService userService; + + private final String password; + + private final Boolean isUpdateSupport; + + private final Long operUserId; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public SysUserImportListener(Boolean isUpdateSupport) { + String initPassword = SpringUtils.getBean(ISysConfigService.class).selectConfigByKey("sys.user.initPassword"); + this.userService = SpringUtils.getBean(ISysUserService.class); + this.password = BCrypt.hashpw(initPassword); + this.isUpdateSupport = isUpdateSupport; + this.operUserId = LoginHelper.getUserId(); + } + + @Override + public void invoke(SysUserImportVo userVo, AnalysisContext context) { + SysUserVo sysUser = this.userService.selectUserByUserName(userVo.getUserName()); + try { + // 验证是否存在这个用户 + if (ObjectUtil.isNull(sysUser)) { + SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class); + ValidatorUtils.validate(user); + user.setPassword(password); + user.setCreateBy(operUserId); + userService.insertUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 导入成功"); + } else if (isUpdateSupport) { + Long userId = sysUser.getUserId(); + SysUserBo user = BeanUtil.toBean(userVo, SysUserBo.class); + user.setUserId(userId); + ValidatorUtils.validate(user); + userService.checkUserAllowed(user.getUserId()); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(operUserId); + userService.updateUser(user); + successNum++; + successMsg.append("
").append(successNum).append("、账号 ").append(user.getUserName()).append(" 更新成功"); + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、账号 ").append(sysUser.getUserName()).append(" 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、账号 " + HtmlUtil.cleanHtmlTag(userVo.getUserName()) + " 导入失败:"; + String message = e.getMessage(); + if (e instanceof ConstraintViolationException cvException) { + message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage, ", "); + } + failureMsg.append(msg).append(message); + log.error(msg, e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult<>() { + + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysClientMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysClientMapper.java new file mode 100644 index 0000000..2f4898b --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysClientMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.system.domain.SysClient; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 授权管理Mapper接口 + * + * @author Michelle.Chung + * @date 2023-05-15 + */ +public interface SysClientMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysConfigMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..472f8e3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysConfigMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysConfig; +import com.fuyuanshen.system.domain.vo.SysConfigVo; + +/** + * 参数配置 数据层 + * + * @author Lion Li + */ +public interface SysConfigMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDeptMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..e33995b --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDeptMapper.java @@ -0,0 +1,78 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.system.domain.SysDept; +import com.fuyuanshen.system.domain.vo.SysDeptVo; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author Lion Li + */ +public interface SysDeptMapper extends BaseMapperPlus { + + /** + * 查询部门管理数据 + * + * @param queryWrapper 查询条件 + * @return 部门信息集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id") + }) + List selectDeptList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 分页查询部门管理数据 + * + * @param queryWrapper 查询条件 + * @return 部门信息集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + }) + Page selectPageDeptList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 统计指定部门ID的部门数量 + * + * @param deptId 部门ID + * @return 该部门ID的部门数量 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id") + }) + long countDeptById(Long deptId); + + /** + * 根据父部门ID查询其所有子部门的列表 + * + * @param parentId 父部门ID + * @return 包含子部门的列表 + */ + default List selectListByParentId(Long parentId) { + return this.selectList(new LambdaQueryWrapper() + .select(SysDept::getDeptId) + .apply(DataBaseHelper.findInSet(parentId, "ancestors"))); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictDataMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..23c41ee --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictDataMapper.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysDictData; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictDataMapper extends BaseMapperPlus { + + /** + * 根据字典类型查询字典数据列表 + * + * @param dictType 字典类型 + * @return 符合条件的字典数据列表 + */ + default List selectDictDataByType(String dictType) { + return selectVoList( + new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType) + .orderByAsc(SysDictData::getDictSort)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictTypeMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..2dcc044 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.system.domain.SysDictType; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.vo.SysDictTypeVo; + +/** + * 字典表 数据层 + * + * @author Lion Li + */ +public interface SysDictTypeMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysLogininforMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..ac0e21c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysLogininforMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysLogininfor; +import com.fuyuanshen.system.domain.vo.SysLogininforVo; + +/** + * 系统访问日志情况信息 数据层 + * + * @author Lion Li + */ +public interface SysLogininforMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysMenuMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..32083c8 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysMenuMapper.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysMenu; +import com.fuyuanshen.system.domain.vo.SysMenuVo; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author Lion Li + */ +public interface SysMenuMapper extends BaseMapperPlus { + + /** + * 根据用户查询系统菜单列表 + * + * @param queryWrapper 查询条件 + * @return 菜单列表 + */ + List selectMenuListByUserId(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + List selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + default List selectMenuTreeAll() { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) + .eq(SysMenu::getStatus, SystemConstants.NORMAL) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum); + return this.selectList(lqw); + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysNoticeMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..7945410 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysNoticeMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysNotice; +import com.fuyuanshen.system.domain.vo.SysNoticeVo; + +/** + * 通知公告表 数据层 + * + * @author Lion Li + */ +public interface SysNoticeMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOperLogMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..b936dd2 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOperLogMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysOperLog; +import com.fuyuanshen.system.domain.vo.SysOperLogVo; + +/** + * 操作日志 数据层 + * + * @author Lion Li + */ +public interface SysOperLogMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssConfigMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssConfigMapper.java new file mode 100644 index 0000000..3d68426 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssConfigMapper.java @@ -0,0 +1,16 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysOssConfig; +import com.fuyuanshen.system.domain.vo.SysOssConfigVo; + +/** + * 对象存储配置Mapper接口 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +public interface SysOssConfigMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssMapper.java new file mode 100644 index 0000000..f08ecd3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysOssMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysOss; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import org.apache.ibatis.annotations.Mapper; + +/** + * 文件上传 数据层 + * + * @author Lion Li + */ +@Mapper +public interface SysOssMapper extends BaseMapperPlus { +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysPostMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..94677c4 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysPostMapper.java @@ -0,0 +1,43 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysPost; +import com.fuyuanshen.system.domain.vo.SysPostVo; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author Lion Li + */ +public interface SysPostMapper extends BaseMapperPlus { + + /** + * 分页查询岗位列表 + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 包含岗位信息的分页结果 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "create_by") + }) + Page selectPagePostList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 结果 + */ + List selectPostsByUserId(Long userId); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleDeptMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..ccc40e5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,13 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleDeptMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..4e613aa --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMapper.java @@ -0,0 +1,75 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysRole; +import com.fuyuanshen.system.domain.vo.SysRoleVo; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMapper extends BaseMapperPlus { + + /** + * 分页查询角色列表 + * + * @param page 分页对象 + * @param queryWrapper 查询条件 + * @return 包含角色信息的分页结果 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "r.create_by") + }) + Page selectPageRoleList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询角色数据 + * + * @param queryWrapper 查询条件 + * @return 角色数据集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "r.create_by") + }) + List selectRoleList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据角色ID查询角色信息 + * + * @param roleId 角色ID + * @return 对应的角色信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "r.create_by") + }) + SysRoleVo selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolePermissionByUserId(Long userId); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesByUserId(Long userId); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMenuMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..e4a87af --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysRoleMenu; + +import java.util.List; + +/** + * 角色与菜单关联表 数据层 + * + * @author Lion Li + */ +public interface SysRoleMenuMapper extends BaseMapperPlus { + + /** + * 根据菜单ID串删除关联关系 + * + * @return 结果 + */ + default int deleteByMenuIds(List menuIds) { + LambdaUpdateWrapper lqw = new LambdaUpdateWrapper() + .in(SysRoleMenu::getMenuId, menuIds); + return this.delete(lqw); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysSocialMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysSocialMapper.java new file mode 100644 index 0000000..98230ca --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysSocialMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysSocial; +import com.fuyuanshen.system.domain.vo.SysSocialVo; + +/** + * 社会化关系Mapper接口 + * + * @author thiszhc + */ +public interface SysSocialMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantMapper.java new file mode 100644 index 0000000..95fa503 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.system.domain.SysTenant; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 租户Mapper接口 + * + * @author Michelle.Chung + */ +public interface SysTenantMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantPackageMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantPackageMapper.java new file mode 100644 index 0000000..dfc0644 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysTenantPackageMapper.java @@ -0,0 +1,14 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysTenantPackage; +import com.fuyuanshen.system.domain.vo.SysTenantPackageVo; + +/** + * 租户套餐Mapper接口 + * + * @author Michelle.Chung + */ +public interface SysTenantPackageMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..9970a6a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserMapper.java @@ -0,0 +1,123 @@ +package com.fuyuanshen.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.vo.SysUserExportVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author Lion Li + */ +public interface SysUserMapper extends BaseMapperPlus { + + /** + * 分页查询用户列表,并进行数据权限控制 + * + * @param page 分页参数 + * @param queryWrapper 查询条件 + * @return 分页的用户信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "u.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectPageUserList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 查询用户列表,并进行数据权限控制 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + List selectUserList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询用户列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + List selectUserExportList(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectAllocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param queryWrapper 查询条件 + * @return 用户信息集合信息 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "d.dept_id"), + @DataColumn(key = "userName", value = "u.user_id") + }) + Page selectUnallocatedList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 根据用户ID统计用户数量 + * + * @param userId 用户ID + * @return 用户数量 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + long countUserById(Long userId); + + /** + * 根据条件更新用户数据 + * + * @param user 要更新的用户实体 + * @param updateWrapper 更新条件封装器 + * @return 更新操作影响的行数 + */ + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int update(@Param(Constants.ENTITY) SysUser user, @Param(Constants.WRAPPER) Wrapper updateWrapper); + + /** + * 根据用户ID更新用户数据 + * + * @param user 要更新的用户实体 + * @return 更新操作影响的行数 + */ + @Override + @DataPermission({ + @DataColumn(key = "deptName", value = "dept_id"), + @DataColumn(key = "userName", value = "user_id") + }) + int updateById(@Param(Constants.ENTITY) SysUser user); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserPostMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..9dc4a18 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserPostMapper.java @@ -0,0 +1,13 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserPostMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserRoleMapper.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..a55e969 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,23 @@ +package com.fuyuanshen.system.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.system.domain.SysUserRole; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author Lion Li + */ +public interface SysUserRoleMapper extends BaseMapperPlus { + + /** + * 根据角色ID查询关联的用户ID列表 + * + * @param roleId 角色ID + * @return 关联到指定角色的用户ID列表 + */ + List selectUserIdsByRoleId(Long roleId); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java new file mode 100644 index 0000000..002b6b5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttConfiguration.java @@ -0,0 +1,36 @@ +package com.fuyuanshen.system.mqtt.config; + +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 14:40 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Configuration +public class MqttConfiguration { + @Autowired + private MqttPropertiesConfig mqttPropertiesConfig; + /** 创建连接工厂 **/ + @Bean + public MqttPahoClientFactory mqttPahoClientFactory(){ + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + MqttConnectOptions options = new MqttConnectOptions(); + options.setCleanSession(true); //设置新会话 + options.setUserName(mqttPropertiesConfig.getUsername()); + options.setPassword(mqttPropertiesConfig.getPassword().toCharArray()); + options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()}); + options.setAutomaticReconnect(true); // 启用自动重连 + options.setConnectionTimeout(10); // 设置连接超时时间 + options.setKeepAliveInterval(60); // 设置心跳间隔 + factory.setConnectionOptions(options); + return factory; + } + +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttGateway.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttGateway.java new file mode 100644 index 0000000..0f24318 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttGateway.java @@ -0,0 +1,17 @@ +package com.fuyuanshen.system.mqtt.config; + +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.handler.annotation.Header; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 17:06 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") +public interface MqttGateway { + public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, String payload); + public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload ); +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java new file mode 100644 index 0000000..14a83ec --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttInboundConfiguration.java @@ -0,0 +1,63 @@ +package com.fuyuanshen.system.mqtt.config; + + +import cn.hutool.core.lang.UUID; +import com.fuyuanshen.system.mqtt.receiver.ReceiverMessageHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageProducer; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHandler; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 14:54 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Configuration +public class MqttInboundConfiguration { + @Autowired + private MqttPropertiesConfig mqttPropertiesConfig; + @Autowired + private MqttPahoClientFactory mqttPahoClientFactory; + @Autowired + private ReceiverMessageHandler receiverMessageHandler; + //消息通道 + @Bean + public MessageChannel messageInboundChannel(){ + return new DirectChannel(); + } + + /** + * 配置入站适配器 + * 作用: 设置订阅主题,以及指定消息的通道 等相关属性 + * */ + @Bean + public MessageProducer messageProducer(){ + // 生成一个不重复的随机数 + String clientId = mqttPropertiesConfig.getSubClientId() + "_" + UUID.fastUUID(); + MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter( + mqttPropertiesConfig.getUrl(), + clientId, + mqttPahoClientFactory, + mqttPropertiesConfig.getSubTopic().split(",") + ); + mqttPahoMessageDrivenChannelAdapter.setQos(1); + mqttPahoMessageDrivenChannelAdapter.setConverter(new DefaultPahoMessageConverter()); + mqttPahoMessageDrivenChannelAdapter.setOutputChannel(messageInboundChannel()); + return mqttPahoMessageDrivenChannelAdapter; + } + /** 指定处理消息来自哪个通道 */ + @Bean + @ServiceActivator(inputChannel = "messageInboundChannel") + public MessageHandler messageHandler(){ + return receiverMessageHandler; + } +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java new file mode 100644 index 0000000..66c345f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttOutboundConfiguration.java @@ -0,0 +1,52 @@ +package com.fuyuanshen.system.mqtt.config; + +import cn.hutool.core.lang.UUID; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHandler; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 15:46 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Configuration +@Slf4j +public class MqttOutboundConfiguration { + @Autowired + private MqttPropertiesConfig mqttPropertiesConfig; + @Autowired + private MqttPahoClientFactory mqttPahoClientFactory; + + // 消息通道 + @Bean + public MessageChannel mqttOutboundChannel(){ + return new DirectChannel(); + } + + + /** 配置出站消息处理器 */ + @Bean + @ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理 + public MessageHandler mqttOutboundMessageHandler(){ + String clientId = mqttPropertiesConfig.getPubClientId() + "_" + UUID.fastUUID(); + MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler( + mqttPropertiesConfig.getUrl(), + clientId, + mqttPahoClientFactory + ); + mqttPahoMessageHandler.setDefaultQos(1); + mqttPahoMessageHandler.setDefaultTopic("worker/location"); + mqttPahoMessageHandler.setAsync(true); + return mqttPahoMessageHandler; + } + +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java new file mode 100644 index 0000000..9ae3374 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/config/MqttPropertiesConfig.java @@ -0,0 +1,24 @@ +package com.fuyuanshen.system.mqtt.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 14:32 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Data +@ConfigurationProperties(prefix = "mqtt") +@Component +public class MqttPropertiesConfig { + private String username; + private String password; + private String url; + private String subClientId; + private String subTopic; + private String pubClientId; + private String pubTopic; +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/DeviceDataController.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/DeviceDataController.java new file mode 100644 index 0000000..e879402 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/DeviceDataController.java @@ -0,0 +1,23 @@ +package com.fuyuanshen.system.mqtt.publish; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/") +@Slf4j +public class DeviceDataController { + + @Autowired + private MqttClientTest mqttClientTest; + +// @PostMapping("/{deviceId}/command") + public ResponseEntity sendCommand() { + + mqttClientTest.sendMsg(); + return ResponseEntity.ok("success"); + } +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttClientTest.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttClientTest.java new file mode 100644 index 0000000..2332809 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttClientTest.java @@ -0,0 +1,22 @@ +package com.fuyuanshen.system.mqtt.publish; + + +import com.fuyuanshen.system.mqtt.config.MqttGateway; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class MqttClientTest { + @Autowired + private MqttGateway mqttGateway; + + public void sendMsg() { + mqttGateway.sendMsgToMqtt("worker/location/1", "hello mqtt spring boot"); + log.info("message is send"); + + mqttGateway.sendMsgToMqtt("worker/alert/2", "hello mqtt spring boot2"); + log.info("message is send2"); + } +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttMessageSender.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttMessageSender.java new file mode 100644 index 0000000..05bf77e --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/publish/MqttMessageSender.java @@ -0,0 +1,25 @@ +package com.fuyuanshen.system.mqtt.publish; + +import com.fuyuanshen.system.mqtt.config.MqttGateway; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Service; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 16:16 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Service +public class MqttMessageSender { + @Autowired + private MqttGateway mqttGateway; + public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, String payload) { + mqttGateway.sendMsgToMqtt(topic,payload); + } + public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload) { + mqttGateway.sendMsgToMqtt(topic,qos,payload); + } +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java new file mode 100644 index 0000000..9b8a9f6 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/mqtt/receiver/ReceiverMessageHandler.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.system.mqtt.receiver; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.MessagingException; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @Author: HarryLin + * @Date: 2025/3/20 15:24 + * @Company: 北京红山信息科技研究院有限公司 + * @Email: linyun@***.com.cn + **/ +@Service +@Slf4j +public class ReceiverMessageHandler implements MessageHandler { + @Override + public void handleMessage(Message message) throws MessagingException{ + Object payload = message.getPayload(); + MessageHeaders headers = message.getHeaders(); + String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString(); + String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString(); + String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString(); + log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}" + ,payload,receivedTopic,receivedQos,timestamp); + } +} \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/runner/SystemApplicationRunner.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/runner/SystemApplicationRunner.java new file mode 100644 index 0000000..ba3ebf9 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/runner/SystemApplicationRunner.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.system.runner; + +import com.fuyuanshen.system.service.ISysOssConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +/** + * 初始化 system 模块对应业务数据 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Component +public class SystemApplicationRunner implements ApplicationRunner { + + private final ISysOssConfigService ossConfigService; + + @Override + public void run(ApplicationArguments args) throws Exception { + ossConfigService.init(); + log.info("初始化OSS配置成功"); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysClientService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysClientService.java new file mode 100644 index 0000000..6a5e897 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysClientService.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.bo.SysClientBo; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 客户端管理Service接口 + * + * @author Michelle.Chung + * @date 2023-06-18 + */ +public interface ISysClientService { + + /** + * 查询客户端管理 + */ + SysClientVo queryById(Long id); + + /** + * 查询客户端信息基于客户端id + */ + SysClientVo queryByClientId(String clientId); + + /** + * 查询客户端管理列表 + */ + TableDataInfo queryPageList(SysClientBo bo, PageQuery pageQuery); + + /** + * 查询客户端管理列表 + */ + List queryList(SysClientBo bo); + + /** + * 新增客户端管理 + */ + Boolean insertByBo(SysClientBo bo); + + /** + * 修改客户端管理 + */ + Boolean updateByBo(SysClientBo bo); + + /** + * 修改状态 + */ + int updateClientStatus(String clientId, String status); + + /** + * 校验并批量删除客户端管理信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysConfigService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysConfigService.java new file mode 100644 index 0000000..e0f6ff8 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysConfigService.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysConfigBo; +import com.fuyuanshen.system.domain.vo.SysConfigVo; + +import java.util.List; + +/** + * 参数配置 服务层 + * + * @author Lion Li + */ +public interface ISysConfigService { + + + TableDataInfo selectPageConfigList(SysConfigBo config, PageQuery pageQuery); + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + SysConfigVo selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + String selectConfigByKey(String configKey); + + /** + * 获取注册开关 + * @param tenantId 租户id + * @return true开启,false关闭 + */ + boolean selectRegisterEnabled(String tenantId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + List selectConfigList(SysConfigBo config); + + /** + * 新增参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + String insertConfig(SysConfigBo bo); + + /** + * 修改参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + String updateConfig(SysConfigBo bo); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + void deleteConfigByIds(Long[] configIds); + + /** + * 重置参数缓存数据 + */ + void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + boolean checkConfigKeyUnique(SysConfigBo config); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDataScopeService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDataScopeService.java new file mode 100644 index 0000000..48d8b0c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDataScopeService.java @@ -0,0 +1,26 @@ +package com.fuyuanshen.system.service; + +/** + * 通用 数据权限 服务 + * + * @author Lion Li + */ +public interface ISysDataScopeService { + + /** + * 获取角色自定义权限 + * + * @param roleId 角色id + * @return 部门id组 + */ + String getRoleCustom(Long roleId); + + /** + * 获取部门及以下权限 + * + * @param deptId 部门id + * @return 部门id组 + */ + String getDeptAndChild(Long deptId); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDeptService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDeptService.java new file mode 100644 index 0000000..40b6ffd --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDeptService.java @@ -0,0 +1,137 @@ +package com.fuyuanshen.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.vo.SysDeptVo; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author Lion Li + */ +public interface ISysDeptService { + + /** + * 分页查询部门管理数据 + * + * @param dept 部门信息 + * @param pageQuery 分页对象 + * @return 部门信息集合 + */ + TableDataInfo selectPageDeptList(SysDeptBo dept, PageQuery pageQuery); + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + List selectDeptList(SysDeptBo dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + List> selectDeptTreeList(SysDeptBo dept); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + List> buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + SysDeptVo selectDeptById(Long deptId); + + /** + * 通过部门ID串查询部门 + * + * @param deptIds 部门id串 + * @return 部门列表信息 + */ + List selectDeptByIds(List deptIds); + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + long selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + boolean checkDeptNameUnique(SysDeptBo dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + int insertDept(SysDeptBo bo); + + /** + * 修改保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + int updateDept(SysDeptBo bo); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + int deleteDeptById(Long deptId); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java new file mode 100644 index 0000000..1f8fc32 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictDataService.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysDictDataBo; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictDataService { + + + TableDataInfo selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery); + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + List selectDictDataList(SysDictDataBo dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + SysDictDataVo selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + List insertDictData(SysDictDataBo bo); + + /** + * 修改保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + List updateDictData(SysDictDataBo bo); + + /** + * 校验字典键值是否唯一 + * + * @param dict 字典数据 + * @return 结果 + */ + boolean checkDictDataUnique(SysDictDataBo dict); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..004b6e5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysDictTypeService.java @@ -0,0 +1,95 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysDictTypeBo; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; +import com.fuyuanshen.system.domain.vo.SysDictTypeVo; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author Lion Li + */ +public interface ISysDictTypeService { + + + TableDataInfo selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery); + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + List selectDictTypeList(SysDictTypeBo dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + SysDictTypeVo selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + SysDictTypeVo selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + void deleteDictTypeByIds(Long[] dictIds); + + /** + * 重置字典缓存数据 + */ + void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + List insertDictType(SysDictTypeBo bo); + + /** + * 修改保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + List updateDictType(SysDictTypeBo bo); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + boolean checkDictTypeUnique(SysDictTypeBo dictType); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysLogininforService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysLogininforService.java new file mode 100644 index 0000000..7809d7b --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysLogininforService.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysLogininforBo; +import com.fuyuanshen.system.domain.vo.SysLogininforVo; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author Lion Li + */ +public interface ISysLogininforService { + + + TableDataInfo selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery); + + /** + * 新增系统登录日志 + * + * @param bo 访问日志对象 + */ + void insertLogininfor(SysLogininforBo bo); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + List selectLogininforList(SysLogininforBo logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + void cleanLogininfor(); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysMenuService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysMenuService.java new file mode 100644 index 0000000..4da8a90 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysMenuService.java @@ -0,0 +1,163 @@ +package com.fuyuanshen.system.service; + +import cn.hutool.core.lang.tree.Tree; +import com.fuyuanshen.system.domain.SysMenu; +import com.fuyuanshen.system.domain.bo.SysMenuBo; +import com.fuyuanshen.system.domain.vo.RouterVo; +import com.fuyuanshen.system.domain.vo.SysMenuVo; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author Lion Li + */ +public interface ISysMenuService { + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuList(SysMenuBo menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(Long roleId); + + /** + * 根据租户套餐ID查询菜单树信息 + * + * @param packageId 租户套餐ID + * @return 选中菜单列表 + */ + List selectMenuListByPackageId(Long packageId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + List buildMenus(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + List> buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + SysMenuVo selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(List menuIds); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + int insertMenu(SysMenuBo bo); + + /** + * 修改保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + int updateMenu(SysMenuBo bo); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + int deleteMenuById(Long menuId); + + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + void deleteMenuById(List menuIds); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + boolean checkMenuNameUnique(SysMenuBo menu); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysNoticeService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysNoticeService.java new file mode 100644 index 0000000..332ca3f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysNoticeService.java @@ -0,0 +1,67 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysNoticeBo; +import com.fuyuanshen.system.domain.vo.SysNoticeVo; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author Lion Li + */ +public interface ISysNoticeService { + + + TableDataInfo selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery); + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + SysNoticeVo selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + List selectNoticeList(SysNoticeBo notice); + + /** + * 新增公告 + * + * @param bo 公告信息 + * @return 结果 + */ + int insertNotice(SysNoticeBo bo); + + /** + * 修改公告 + * + * @param bo 公告信息 + * @return 结果 + */ + int updateNotice(SysNoticeBo bo); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOperLogService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOperLogService.java new file mode 100644 index 0000000..1327dfb --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOperLogService.java @@ -0,0 +1,54 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysOperLogBo; +import com.fuyuanshen.system.domain.vo.SysOperLogVo; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author Lion Li + */ +public interface ISysOperLogService { + + TableDataInfo selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery); + + /** + * 新增操作日志 + * + * @param bo 操作日志对象 + */ + void insertOperlog(SysOperLogBo bo); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + List selectOperLogList(SysOperLogBo operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + SysOperLogVo selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + void cleanOperLog(); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssConfigService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssConfigService.java new file mode 100644 index 0000000..bb63d81 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssConfigService.java @@ -0,0 +1,64 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysOssConfigBo; +import com.fuyuanshen.system.domain.vo.SysOssConfigVo; + +import java.util.Collection; + +/** + * 对象存储配置Service接口 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +public interface ISysOssConfigService { + + /** + * 初始化OSS配置 + */ + void init(); + + /** + * 查询单个 + */ + SysOssConfigVo queryById(Long ossConfigId); + + /** + * 查询列表 + */ + TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery); + + /** + * 根据新增业务对象插入对象存储配置 + * + * @param bo 对象存储配置新增业务对象 + * @return 结果 + */ + Boolean insertByBo(SysOssConfigBo bo); + + /** + * 根据编辑业务对象修改对象存储配置 + * + * @param bo 对象存储配置编辑业务对象 + * @return 结果 + */ + Boolean updateByBo(SysOssConfigBo bo); + + /** + * 校验并删除数据 + * + * @param ids 主键集合 + * @param isValid 是否校验,true-删除前校验,false-不校验 + * @return 结果 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 启用停用状态 + */ + int updateOssConfigStatus(SysOssConfigBo bo); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java new file mode 100644 index 0000000..fe0d466 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysOssService.java @@ -0,0 +1,83 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysOssBo; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +/** + * 文件上传 服务层 + * + * @author Lion Li + */ +public interface ISysOssService { + + /** + * 查询OSS对象存储列表 + * + * @param sysOss OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ + TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery); + + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ + List listByIds(Collection ossIds); + + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ + SysOssVo getById(Long ossId); + + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ + SysOssVo upload(MultipartFile file); + + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ + SysOssVo upload(File file); + + + public SysOssVo upload(byte[] data, String fileName); + + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ + void download(Long ossId, HttpServletResponse response) throws IOException; + + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPermissionService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPermissionService.java new file mode 100644 index 0000000..25f77c8 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPermissionService.java @@ -0,0 +1,28 @@ +package com.fuyuanshen.system.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author Lion Li + */ +public interface ISysPermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPostService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPostService.java new file mode 100644 index 0000000..68b0074 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysPostService.java @@ -0,0 +1,130 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysPostBo; +import com.fuyuanshen.system.domain.vo.SysPostVo; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author Lion Li + */ +public interface ISysPostService { + + + TableDataInfo selectPagePostList(SysPostBo post, PageQuery pageQuery); + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + List selectPostList(SysPostBo post); + + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 岗位ID + */ + List selectPostsByUserId(Long userId); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + SysPostVo selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + List selectPostByIds(List postIds); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + boolean checkPostNameUnique(SysPostBo post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + boolean checkPostCodeUnique(SysPostBo post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + long countUserPostById(Long postId); + + /** + * 通过部门ID查询岗位使用数量 + * + * @param deptId 部门id + * @return 结果 + */ + long countPostByDeptId(Long deptId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + int insertPost(SysPostBo bo); + + /** + * 修改保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + int updatePost(SysPostBo bo); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysRoleService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysRoleService.java new file mode 100644 index 0000000..e15f2c9 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysRoleService.java @@ -0,0 +1,202 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysUserRole; +import com.fuyuanshen.system.domain.bo.SysRoleBo; +import com.fuyuanshen.system.domain.vo.SysRoleVo; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author Lion Li + */ +public interface ISysRoleService { + + + TableDataInfo selectPageRoleList(SysRoleBo role, PageQuery pageQuery); + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + List selectRoleList(SysRoleBo role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + List selectRolesAuthByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + SysRoleVo selectRoleById(Long roleId); + + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + List selectRoleByIds(List roleIds); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + boolean checkRoleNameUnique(SysRoleBo role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + boolean checkRoleKeyUnique(SysRoleBo role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + void checkRoleAllowed(SysRoleBo role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + long countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int insertRole(SysRoleBo bo); + + /** + * 修改保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int updateRole(SysRoleBo bo); + + /** + * 修改角色状态 + * + * @param roleId 角色ID + * @param status 角色状态 + * @return 结果 + */ + int updateRoleStatus(Long roleId, String status); + + /** + * 修改数据权限信息 + * + * @param bo 角色信息 + * @return 结果 + */ + int authDataScope(SysRoleBo bo); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + int insertAuthUsers(Long roleId, Long[] userIds); + + void cleanOnlineUserByRole(Long roleId); + + void cleanOnlineUser(List userIds); + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysSocialService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysSocialService.java new file mode 100644 index 0000000..ab01af9 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysSocialService.java @@ -0,0 +1,53 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.system.domain.bo.SysSocialBo; +import com.fuyuanshen.system.domain.vo.SysSocialVo; + +import java.util.List; + +/** + * 社会化关系Service接口 + * + * @author thiszhc + */ +public interface ISysSocialService { + + + /** + * 查询社会化关系 + */ + SysSocialVo queryById(String id); + + /** + * 查询社会化关系列表 + */ + List queryList(SysSocialBo bo); + + /** + * 查询社会化关系列表 + */ + List queryListByUserId(Long userId); + + /** + * 新增授权关系 + */ + Boolean insertByBo(SysSocialBo bo); + + /** + * 更新社会化关系 + */ + Boolean updateByBo(SysSocialBo bo); + + /** + * 删除社会化关系信息 + */ + Boolean deleteWithValidById(Long id); + + + /** + * 根据 authId 查询 + */ + List selectByAuthId(String authId); + + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantPackageService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantPackageService.java new file mode 100644 index 0000000..0ed20ba --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantPackageService.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysTenantPackageBo; +import com.fuyuanshen.system.domain.vo.SysTenantPackageVo; + +import java.util.Collection; +import java.util.List; + +/** + * 租户套餐Service接口 + * + * @author Michelle.Chung + */ +public interface ISysTenantPackageService { + + /** + * 查询租户套餐 + */ + SysTenantPackageVo queryById(Long packageId); + + /** + * 查询租户套餐列表 + */ + TableDataInfo queryPageList(SysTenantPackageBo bo, PageQuery pageQuery); + + /** + * 查询租户套餐已启用列表 + */ + List selectList(); + + /** + * 查询租户套餐列表 + */ + List queryList(SysTenantPackageBo bo); + + /** + * 新增租户套餐 + */ + Boolean insertByBo(SysTenantPackageBo bo); + + /** + * 修改租户套餐 + */ + Boolean updateByBo(SysTenantPackageBo bo); + + /** + * 校验套餐名称是否唯一 + */ + boolean checkPackageNameUnique(SysTenantPackageBo bo); + + /** + * 修改套餐状态 + */ + int updatePackageStatus(SysTenantPackageBo bo); + + /** + * 校验并批量删除租户套餐信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantService.java new file mode 100644 index 0000000..903dbf5 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysTenantService.java @@ -0,0 +1,87 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.domain.bo.SysTenantBo; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 租户Service接口 + * + * @author Michelle.Chung + */ +public interface ISysTenantService { + + /** + * 查询租户 + */ + SysTenantVo queryById(Long id); + + /** + * 基于租户ID查询租户 + */ + SysTenantVo queryByTenantId(String tenantId); + + /** + * 查询租户列表 + */ + TableDataInfo queryPageList(SysTenantBo bo, PageQuery pageQuery); + + /** + * 查询租户列表 + */ + List queryList(SysTenantBo bo); + + /** + * 新增租户 + */ + Boolean insertByBo(SysTenantBo bo); + + /** + * 修改租户 + */ + Boolean updateByBo(SysTenantBo bo); + + /** + * 修改租户状态 + */ + int updateTenantStatus(SysTenantBo bo); + + /** + * 校验租户是否允许操作 + */ + void checkTenantAllowed(String tenantId); + + /** + * 校验并批量删除租户信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 校验企业名称是否唯一 + */ + boolean checkCompanyNameUnique(SysTenantBo bo); + + /** + * 校验账号余额 + */ + boolean checkAccountBalance(String tenantId); + + /** + * 校验有效期 + */ + boolean checkExpireTime(String tenantId); + + /** + * 同步租户套餐 + */ + Boolean syncTenantPackage(String tenantId, Long packageId); + + /** + * 同步租户字典 + */ + void syncTenantDict(); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysUserService.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysUserService.java new file mode 100644 index 0000000..7682640 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/ISysUserService.java @@ -0,0 +1,231 @@ +package com.fuyuanshen.system.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.SysUserExportVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author Lion Li + */ +public interface ISysUserService { + + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @param pageQuery 发呢也 + * @return 用户信息 + */ + TableDataInfo selectPageUserList(SysUserBo user, PageQuery pageQuery); + + /** + * 导出用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + List selectUserExportList(SysUserBo user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @param pageQuery 分页 + * @return 用户信息集合信息 + */ + TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @param pageQuery 分页 + * @return 用户信息集合信息 + */ + TableDataInfo selectUnallocatedList(SysUserBo user, PageQuery pageQuery); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + SysUserVo selectUserByUserName(String userName); + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + SysUserVo selectUserByPhonenumber(String phonenumber); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + SysUserVo selectUserById(Long userId); + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + List selectUserByIds(List userIds, Long deptId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userId 用户ID + * @return 结果 + */ + String selectUserRoleGroup(Long userId); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userId 用户ID + * @return 结果 + */ + String selectUserPostGroup(Long userId); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkUserNameUnique(SysUserBo user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkPhoneUnique(SysUserBo user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + boolean checkEmailUnique(SysUserBo user); + + /** + * 校验用户是否允许操作 + * + * @param userId 用户ID + */ + void checkUserAllowed(Long userId); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int insertUser(SysUserBo user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + boolean registerUser(SysUserBo user, String tenantId); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUser(SysUserBo user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param userId 用户ID + * @param status 帐号状态 + * @return 结果 + */ + int updateUserStatus(Long userId, String status); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + int updateUserProfile(SysUserBo user); + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + boolean updateUserAvatar(Long userId, Long avatar); + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + int resetUserPwd(Long userId, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + + /** + * 通过部门id查询当前部门所有用户 + * + * @param deptId 部门id + * @return 结果 + */ + List selectUserListByDept(Long deptId); +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysClientServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysClientServiceImpl.java new file mode 100644 index 0000000..efe7dd1 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysClientServiceImpl.java @@ -0,0 +1,152 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.crypto.SecureUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysClient; +import com.fuyuanshen.system.domain.bo.SysClientBo; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.mapper.SysClientMapper; +import com.fuyuanshen.system.service.ISysClientService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 客户端管理Service业务层处理 + * + * @author Michelle.Chung + * @date 2023-06-18 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysClientServiceImpl implements ISysClientService { + + private final SysClientMapper baseMapper; + + /** + * 查询客户端管理 + */ + @Override + public SysClientVo queryById(Long id) { + SysClientVo vo = baseMapper.selectVoById(id); + vo.setGrantTypeList(List.of(vo.getGrantType().split(","))); + return vo; + } + + + /** + * 查询客户端管理 + */ + @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") + @Override + public SysClientVo queryByClientId(String clientId) { + log.debug("【queryByClientId】缓存未命中,开始执行数据库查询,clientId={}", clientId); + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); + } + + /** + * 查询客户端管理列表 + */ + @Override + public TableDataInfo queryPageList(SysClientBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + result.getRecords().forEach(r -> r.setGrantTypeList(List.of(r.getGrantType().split(",")))); + return TableDataInfo.build(result); + } + + /** + * 查询客户端管理列表 + */ + @Override + public List queryList(SysClientBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysClientBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getClientId()), SysClient::getClientId, bo.getClientId()); + lqw.eq(StringUtils.isNotBlank(bo.getClientKey()), SysClient::getClientKey, bo.getClientKey()); + lqw.eq(StringUtils.isNotBlank(bo.getClientSecret()), SysClient::getClientSecret, bo.getClientSecret()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysClient::getStatus, bo.getStatus()); + lqw.orderByAsc(SysClient::getId); + return lqw; + } + + /** + * 新增客户端管理 + */ + @Override + public Boolean insertByBo(SysClientBo bo) { + SysClient add = MapstructUtils.convert(bo, SysClient.class); + validEntityBeforeSave(add); + add.setGrantType(String.join(",", bo.getGrantTypeList())); + // 生成clientid + String clientKey = bo.getClientKey(); + String clientSecret = bo.getClientSecret(); + add.setClientId(SecureUtil.md5(clientKey + clientSecret)); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改客户端管理 + */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#bo.clientId") + @Override + public Boolean updateByBo(SysClientBo bo) { + SysClient update = MapstructUtils.convert(bo, SysClient.class); + validEntityBeforeSave(update); + update.setGrantType(String.join(",", bo.getGrantTypeList())); + return baseMapper.updateById(update) > 0; + } + + /** + * 修改状态 + */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") + @Override + public int updateClientStatus(String clientId, String status) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysClient::getStatus, status) + .eq(SysClient::getClientId, clientId)); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysClient entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除客户端管理 + */ + @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysConfigServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..37fcd0c --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,217 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.ConfigService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.SysConfig; +import com.fuyuanshen.system.domain.bo.SysConfigBo; +import com.fuyuanshen.system.domain.vo.SysConfigVo; +import com.fuyuanshen.system.mapper.SysConfigMapper; +import com.fuyuanshen.system.service.ISysConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 参数配置 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysConfigServiceImpl implements ISysConfigService, ConfigService { + + private final SysConfigMapper baseMapper; + + @Override + public TableDataInfo selectPageConfigList(SysConfigBo config, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(config); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DS("master") + public SysConfigVo selectConfigById(Long configId) { + return baseMapper.selectVoById(configId); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") + @Override + public String selectConfigByKey(String configKey) { + SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, configKey)); + return ObjectUtils.notNullGetter(retConfig, SysConfig::getConfigValue, StringUtils.EMPTY); + } + + /** + * 获取注册开关 + * @param tenantId 租户id + * @return true开启,false关闭 + */ + @Override + public boolean selectRegisterEnabled(String tenantId) { + SysConfig retConfig = TenantHelper.dynamic(tenantId, () -> { + return baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, "sys.account.registerUser")); + }); + if (ObjectUtil.isNull(retConfig)) { + return false; + } + return Convert.toBool(retConfig.getConfigValue()); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfigBo config) { + LambdaQueryWrapper lqw = buildQueryWrapper(config); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getConfigName()), SysConfig::getConfigName, bo.getConfigName()); + lqw.eq(StringUtils.isNotBlank(bo.getConfigType()), SysConfig::getConfigType, bo.getConfigType()); + lqw.like(StringUtils.isNotBlank(bo.getConfigKey()), SysConfig::getConfigKey, bo.getConfigKey()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); + lqw.orderByAsc(SysConfig::getConfigId); + return lqw; + } + + /** + * 新增参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#bo.configKey") + @Override + public String insertConfig(SysConfigBo bo) { + SysConfig config = MapstructUtils.convert(bo, SysConfig.class); + int row = baseMapper.insert(config); + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改参数配置 + * + * @param bo 参数配置信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#bo.configKey") + @Override + public String updateConfig(SysConfigBo bo) { + int row = 0; + SysConfig config = MapstructUtils.convert(bo, SysConfig.class); + if (config.getConfigId() != null) { + SysConfig temp = baseMapper.selectById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { + CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); + } + row = baseMapper.updateById(config); + } else { + CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); + row = baseMapper.update(config, new LambdaQueryWrapper() + .eq(SysConfig::getConfigKey, config.getConfigKey())); + } + if (row > 0) { + return config.getConfigValue(); + } + throw new ServiceException("操作失败"); + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) { + for (Long configId : configIds) { + SysConfig config = baseMapper.selectById(configId); + if (StringUtils.equals(SystemConstants.YES, config.getConfigType())) { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); + } + baseMapper.deleteByIds(Arrays.asList(configIds)); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() { + CacheUtils.clear(CacheNames.SYS_CONFIG); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfigBo config) { + long configId = ObjectUtils.notNull(config.getConfigId(), -1L); + SysConfig info = baseMapper.selectOne(new LambdaQueryWrapper().eq(SysConfig::getConfigKey, config.getConfigKey())); + if (ObjectUtil.isNotNull(info) && info.getConfigId() != configId) { + return false; + } + return true; + } + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + @Override + public String getConfigValue(String configKey) { + return SpringUtils.getAopProxy(this).selectConfigByKey(configKey); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDataScopeServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDataScopeServiceImpl.java new file mode 100644 index 0000000..0ebca1a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDataScopeServiceImpl.java @@ -0,0 +1,78 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.system.domain.SysDept; +import com.fuyuanshen.system.domain.SysRoleDept; +import com.fuyuanshen.system.mapper.SysDeptMapper; +import com.fuyuanshen.system.mapper.SysRoleDeptMapper; +import com.fuyuanshen.system.service.ISysDataScopeService; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 数据权限 实现 + *

+ * 注意: 此Service内不允许调用标注`数据权限`注解的方法 + * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service("sdss") +public class SysDataScopeServiceImpl implements ISysDataScopeService { + + private final SysRoleDeptMapper roleDeptMapper; + private final SysDeptMapper deptMapper; + + /** + * 获取角色自定义权限 + * + * @param roleId 角色Id + * @return 部门Id组 + */ + @Cacheable(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#roleId", condition = "#roleId != null") + @Override + public String getRoleCustom(Long roleId) { + if (ObjectUtil.isNull(roleId)) { + return "-1"; + } + List list = roleDeptMapper.selectList( + new LambdaQueryWrapper() + .select(SysRoleDept::getDeptId) + .eq(SysRoleDept::getRoleId, roleId)); + if (CollUtil.isNotEmpty(list)) { + return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); + } + return "-1"; + } + + /** + * 获取部门及以下权限 + * + * @param deptId 部门Id + * @return 部门Id组 + */ + @Cacheable(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId", condition = "#deptId != null") + @Override + public String getDeptAndChild(Long deptId) { + if (ObjectUtil.isNull(deptId)) { + return "-1"; + } + List deptList = deptMapper.selectListByParentId(deptId); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(deptId); + if (CollUtil.isNotEmpty(ids)) { + return StreamUtils.join(ids, Convert::toStr); + } + return "-1"; + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDeptServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..58b30b4 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,418 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.dto.DeptDTO; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.DeptService; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.SysDept; +import com.fuyuanshen.system.domain.SysRole; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.vo.SysDeptVo; +import com.fuyuanshen.system.mapper.SysDeptMapper; +import com.fuyuanshen.system.mapper.SysRoleMapper; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.system.service.ISysDeptService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 部门管理 服务实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDeptServiceImpl implements ISysDeptService, DeptService { + + private final SysDeptMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysUserMapper userMapper; + + /** + * 分页查询部门管理数据 + * + * @param dept 部门信息 + * @param pageQuery 分页对象 + * @return 部门信息集合 + */ + @Override + public TableDataInfo selectPageDeptList(SysDeptBo dept, PageQuery pageQuery) { + Page page = baseMapper.selectPageDeptList(pageQuery.build(), buildQueryWrapper(dept)); + return TableDataInfo.build(page); + } + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + public List selectDeptList(SysDeptBo dept) { + LambdaQueryWrapper lqw = buildQueryWrapper(dept); + return baseMapper.selectDeptList(lqw); + } + + /** + * 查询部门树结构信息 + * + * @param bo 部门信息 + * @return 部门树信息集合 + */ + @Override + public List> selectDeptTreeList(SysDeptBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List depts = baseMapper.selectDeptList(lqw); + return buildDeptTreeSelect(depts); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL); + lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); + lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); + lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); + lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDept::getCreateTime, params.get("beginTime"), params.get("endTime")); + lqw.orderByAsc(SysDept::getAncestors); + lqw.orderByAsc(SysDept::getParentId); + lqw.orderByAsc(SysDept::getOrderNum); + lqw.orderByAsc(SysDept::getDeptId); + if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { + //部门树搜索 + lqw.and(x -> { + Long parentId = bo.getBelongDeptId(); + List deptList = baseMapper.selectListByParentId(parentId); + List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); + deptIds.add(parentId); + x.in(SysDept::getDeptId, deptIds); + }); + } + return lqw; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List> buildDeptTreeSelect(List depts) { + if (CollUtil.isEmpty(depts)) { + return CollUtil.newArrayList(); + } + // 获取当前列表中每一个节点的parentId,然后在列表中查找是否有id与其parentId对应,若无对应,则表明此时节点列表中,该节点在当前列表中属于顶级节点 + List> treeList = CollUtil.newArrayList(); + for (SysDeptVo d : depts) { + Long parentId = d.getParentId(); + SysDeptVo sysDeptVo = StreamUtils.findFirst(depts, it -> it.getDeptId().longValue() == parentId); + if (ObjectUtil.isNull(sysDeptVo)) { + List> trees = TreeBuildUtils.build(depts, parentId, (dept, tree) -> + tree.setId(dept.getDeptId()) + .setParentId(dept.getParentId()) + .setName(dept.getDeptName()) + .setWeight(dept.getOrderNum()) + .putExtra("disabled", SystemConstants.DISABLE.equals(dept.getStatus()))); + Tree tree = StreamUtils.findFirst(trees, it -> it.getId().longValue() == d.getDeptId()); + treeList.add(tree); + } + } + return treeList; + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") + @Override + public SysDeptVo selectDeptById(Long deptId) { + SysDeptVo dept = baseMapper.selectVoById(deptId); + if (ObjectUtil.isNull(dept)) { + return null; + } + SysDeptVo parentDept = baseMapper.selectVoOne(new LambdaQueryWrapper() + .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId())); + dept.setParentName(ObjectUtils.notNullGetter(parentDept, SysDeptVo::getDeptName)); + return dept; + } + + @Override + public List selectDeptByIds(List deptIds) { + return baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) + .eq(SysDept::getStatus, SystemConstants.NORMAL) + .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds)); + } + + /** + * 通过部门ID查询部门名称 + * + * @param deptIds 部门ID串逗号分隔 + * @return 部门名称串逗号分隔 + */ + @Override + public String selectDeptNameByIds(String deptIds) { + List list = new ArrayList<>(); + for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) { + SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(id); + if (ObjectUtil.isNotNull(vo)) { + list.add(vo.getDeptName()); + } + } + return String.join(StringUtils.SEPARATOR, list); + } + + /** + * 根据部门ID查询部门负责人 + * + * @param deptId 部门ID,用于指定需要查询的部门 + * @return 返回该部门的负责人ID + */ + @Override + public Long selectDeptLeaderById(Long deptId) { + SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(deptId); + return vo.getLeader(); + } + + /** + * 查询部门 + * + * @return 部门列表 + */ + @Override + public List selectDeptsByList() { + List list = baseMapper.selectDeptList(new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId) + .eq(SysDept::getStatus, SystemConstants.NORMAL)); + return BeanUtil.copyToList(list, DeptDTO.class); + } + + /** + * 根据ID查询所有子部门数(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public long selectNormalChildrenDeptById(Long deptId) { + return baseMapper.selectCount(new LambdaQueryWrapper() + .eq(SysDept::getStatus, SystemConstants.NORMAL) + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) { + return baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getParentId, deptId)); + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) { + return userMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getDeptId, deptId)); + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDeptBo dept) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDept::getDeptName, dept.getDeptName()) + .eq(SysDept::getParentId, dept.getParentId()) + .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); + return !exist; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) { + if (ObjectUtil.isNull(deptId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + if (baseMapper.countDeptById(deptId) == 0) { + throw new ServiceException("没有权限访问部门数据!"); + } + } + + /** + * 新增保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, allEntries = true) + @Override + public int insertDept(SysDeptBo bo) { + SysDept info = baseMapper.selectById(bo.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!SystemConstants.NORMAL.equals(info.getStatus())) { + throw new ServiceException("部门停用,不允许新增"); + } + SysDept dept = MapstructUtils.convert(bo, SysDept.class); + dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId()); + return baseMapper.insert(dept); + } + + /** + * 修改保存部门信息 + * + * @param bo 部门信息 + * @return 结果 + */ + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#bo.deptId"), + @CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, allEntries = true) + }) + @Override + @Transactional(rollbackFor = Exception.class) + public int updateDept(SysDeptBo bo) { + SysDept dept = MapstructUtils.convert(bo, SysDept.class); + SysDept oldDept = baseMapper.selectById(dept.getDeptId()); + if (ObjectUtil.isNull(oldDept)) { + throw new ServiceException("部门不存在,无法修改"); + } + if (!oldDept.getParentId().equals(dept.getParentId())) { + // 如果是新父部门 则校验是否具有新父部门权限 避免越权 + this.checkDeptDataScope(dept.getParentId()); + SysDept newParentDept = baseMapper.selectById(dept.getParentId()); + if (ObjectUtil.isNotNull(newParentDept)) { + String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + } else { + dept.setAncestors(oldDept.getAncestors()); + } + int result = baseMapper.updateById(dept); + // 如果部门状态为启用,且部门祖级列表不为空,且部门祖级列表不等于根部门祖级列表(如果部门祖级列表不等于根部门祖级列表,则说明存在上级部门) + if (SystemConstants.NORMAL.equals(dept.getStatus()) + && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals(SystemConstants.ROOT_DEPT_ANCESTORS, dept.getAncestors())) { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + baseMapper.update(null, new LambdaUpdateWrapper() + .set(SysDept::getStatus, SystemConstants.NORMAL) + .in(SysDept::getDeptId, Arrays.asList(deptIds))); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { + List children = baseMapper.selectList(new LambdaQueryWrapper() + .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List list = new ArrayList<>(); + for (SysDept child : children) { + SysDept dept = new SysDept(); + dept.setDeptId(child.getDeptId()); + dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + list.add(dept); + } + if (CollUtil.isNotEmpty(list)) { + if (baseMapper.updateBatchById(list)) { + list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId())); + } + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Caching(evict = { + @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId"), + @CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId") + }) + @Override + public int deleteDeptById(Long deptId) { + return baseMapper.deleteById(deptId); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..adcf47d --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,157 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.system.domain.SysDictData; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.system.domain.bo.SysDictDataBo; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; +import com.fuyuanshen.system.mapper.SysDictDataMapper; +import com.fuyuanshen.system.service.ISysDictDataService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CachePut; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictDataServiceImpl implements ISysDictDataService { + + private final SysDictDataMapper baseMapper; + + @Override + public TableDataInfo selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictData); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictDataBo dictData) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictData); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDictDataBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getDictSort() != null, SysDictData::getDictSort, bo.getDictSort()); + lqw.like(StringUtils.isNotBlank(bo.getDictLabel()), SysDictData::getDictLabel, bo.getDictLabel()); + lqw.eq(StringUtils.isNotBlank(bo.getDictType()), SysDictData::getDictType, bo.getDictType()); + lqw.orderByAsc(SysDictData::getDictSort, SysDictData::getDictCode); + return lqw; + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysDictData::getDictLabel) + .eq(SysDictData::getDictType, dictType) + .eq(SysDictData::getDictValue, dictValue)) + .getDictLabel(); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictDataVo selectDictDataById(Long dictCode) { + return baseMapper.selectVoById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) { + for (Long dictCode : dictCodes) { + SysDictData data = baseMapper.selectById(dictCode); + baseMapper.deleteById(dictCode); + CacheUtils.evict(CacheNames.SYS_DICT, data.getDictType()); + } + } + + /** + * 新增保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List insertDictData(SysDictDataBo bo) { + SysDictData data = MapstructUtils.convert(bo, SysDictData.class); + int row = baseMapper.insert(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典数据信息 + * + * @param bo 字典数据信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List updateDictData(SysDictDataBo bo) { + SysDictData data = MapstructUtils.convert(bo, SysDictData.class); + int row = baseMapper.updateById(data); + if (row > 0) { + return baseMapper.selectDictDataByType(data.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 校验字典键值是否唯一 + * + * @param dict 字典数据 + * @return 结果 + */ + @Override + public boolean checkDictDataUnique(SysDictDataBo dict) { + Long dictCode = ObjectUtils.notNull(dict.getDictCode(), -1L); + SysDictData entity = baseMapper.selectOne(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dict.getDictType()).eq(SysDictData::getDictValue, dict.getDictValue())); + if (ObjectUtil.isNotNull(entity) && !dictCode.equals(entity.getDictCode())) { + return false; + } + return true; + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..822a2b8 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,297 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.domain.dto.DictDataDTO; +import com.fuyuanshen.common.core.domain.dto.DictTypeDTO; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.system.domain.SysDictData; +import com.fuyuanshen.system.domain.SysDictType; +import com.fuyuanshen.system.domain.bo.SysDictTypeBo; +import com.fuyuanshen.system.domain.vo.SysDictDataVo; +import com.fuyuanshen.system.domain.vo.SysDictTypeVo; +import com.fuyuanshen.system.mapper.SysDictDataMapper; +import com.fuyuanshen.system.mapper.SysDictTypeMapper; +import com.fuyuanshen.system.service.ISysDictTypeService; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 字典 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService { + + private final SysDictTypeMapper baseMapper; + private final SysDictDataMapper dictDataMapper; + + @Override + public TableDataInfo selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictType); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictTypeBo dictType) { + LambdaQueryWrapper lqw = buildQueryWrapper(dictType); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysDictTypeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getDictName()), SysDictType::getDictName, bo.getDictName()); + lqw.like(StringUtils.isNotBlank(bo.getDictType()), SysDictType::getDictType, bo.getDictType()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + SysDictType::getCreateTime, params.get("beginTime"), params.get("endTime")); + lqw.orderByAsc(SysDictType::getDictId); + return lqw; + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() { + return baseMapper.selectVoList(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType") + @Override + public List selectDictDataByType(String dictType) { + List dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (CollUtil.isNotEmpty(dictDatas)) { + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictTypeVo selectDictTypeById(Long dictId) { + return baseMapper.selectVoById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Cacheable(cacheNames = CacheNames.SYS_DICT_TYPE, key = "#dictType") + @Override + public SysDictTypeVo selectDictTypeByType(String dictType) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) { + for (Long dictId : dictIds) { + SysDictType dictType = baseMapper.selectById(dictId); + if (dictDataMapper.exists(new LambdaQueryWrapper() + .eq(SysDictData::getDictType, dictType.getDictType()))) { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + CacheUtils.evict(CacheNames.SYS_DICT, dictType.getDictType()); + CacheUtils.evict(CacheNames.SYS_DICT_TYPE, dictType.getDictType()); + } + baseMapper.deleteByIds(Arrays.asList(dictIds)); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() { + CacheUtils.clear(CacheNames.SYS_DICT); + CacheUtils.clear(CacheNames.SYS_DICT_TYPE); + } + + /** + * 新增保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + public List insertDictType(SysDictTypeBo bo) { + SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); + int row = baseMapper.insert(dict); + if (row > 0) { + // 新增 type 下无 data 数据 返回空防止缓存穿透 + return new ArrayList<>(); + } + throw new ServiceException("操作失败"); + } + + /** + * 修改保存字典类型信息 + * + * @param bo 字典类型信息 + * @return 结果 + */ + @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType") + @Override + @Transactional(rollbackFor = Exception.class) + public List updateDictType(SysDictTypeBo bo) { + SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); + SysDictType oldDict = baseMapper.selectById(dict.getDictId()); + dictDataMapper.update(null, new LambdaUpdateWrapper() + .set(SysDictData::getDictType, dict.getDictType()) + .eq(SysDictData::getDictType, oldDict.getDictType())); + int row = baseMapper.updateById(dict); + if (row > 0) { + CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); + CacheUtils.evict(CacheNames.SYS_DICT_TYPE, oldDict.getDictType()); + return dictDataMapper.selectDictDataByType(dict.getDictType()); + } + throw new ServiceException("操作失败"); + } + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictTypeBo dictType) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysDictType::getDictType, dictType.getDictType()) + .ne(ObjectUtil.isNotNull(dictType.getDictId()), SysDictType::getDictId, dictType.getDictId())); + return !exist; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + @Override + public String getDictLabel(String dictType, String dictValue, String separator) { + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel); + if (StringUtils.containsAny(dictValue, separator)) { + return Arrays.stream(dictValue.split(separator)) + .map(v -> map.getOrDefault(v, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictValue, StringUtils.EMPTY); + } + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + @Override + public String getDictValue(String dictType, String dictLabel, String separator) { + List datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + Map map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue); + if (StringUtils.containsAny(dictLabel, separator)) { + return Arrays.stream(dictLabel.split(separator)) + .map(l -> map.getOrDefault(l, StringUtils.EMPTY)) + .collect(Collectors.joining(separator)); + } else { + return map.getOrDefault(dictLabel, StringUtils.EMPTY); + } + } + + /** + * 获取字典下所有的字典值与标签 + * + * @param dictType 字典类型 + * @return dictValue为key,dictLabel为值组成的Map + */ + @Override + public Map getAllDictByDictType(String dictType) { + List list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + // 保证顺序 + LinkedHashMap map = new LinkedHashMap<>(); + for (SysDictDataVo vo : list) { + map.put(vo.getDictValue(), vo.getDictLabel()); + } + return map; + } + + /** + * 根据字典类型查询详细信息 + * + * @param dictType 字典类型 + * @return 字典类型详细信息 + */ + @Override + public DictTypeDTO getDictType(String dictType) { + SysDictTypeVo vo = SpringUtils.getAopProxy(this).selectDictTypeByType(dictType); + return BeanUtil.toBean(vo, DictTypeDTO.class); + } + + /** + * 根据字典类型查询字典数据列表 + * + * @param dictType 字典类型 + * @return 字典数据列表 + */ + @Override + public List getDictData(String dictType) { + List list = SpringUtils.getAopProxy(this).selectDictDataByType(dictType); + return BeanUtil.copyToList(list, DictDataDTO.class); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysLogininforServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..23b18ad --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,175 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ServletUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ip.AddressUtils; +import com.fuyuanshen.common.log.event.LogininforEvent; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.SysLogininfor; +import com.fuyuanshen.system.domain.bo.SysLogininforBo; +import com.fuyuanshen.system.domain.vo.SysClientVo; +import com.fuyuanshen.system.domain.vo.SysLogininforVo; +import com.fuyuanshen.system.mapper.SysLogininforMapper; +import com.fuyuanshen.system.service.ISysClientService; +import com.fuyuanshen.system.service.ISysLogininforService; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Slf4j +@Service +public class SysLogininforServiceImpl implements ISysLogininforService { + + private final SysLogininforMapper baseMapper; + + private final ISysClientService clientService; + + /** + * 记录登录信息 + * + * @param logininforEvent 登录事件 + */ + @Async + @EventListener + public void recordLogininfor(LogininforEvent logininforEvent) { + HttpServletRequest request = logininforEvent.getRequest(); + final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); + final String ip = ServletUtils.getClientIP(request); + // 客户端信息 + String clientId = request.getHeader(LoginHelper.CLIENT_KEY); + SysClientVo client = null; + if (StringUtils.isNotBlank(clientId)) { + client = clientService.queryByClientId(clientId); + } + + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(getBlock(ip)); + s.append(address); + s.append(getBlock(logininforEvent.getUsername())); + s.append(getBlock(logininforEvent.getStatus())); + s.append(getBlock(logininforEvent.getMessage())); + // 打印信息到日志 + log.info(s.toString(), logininforEvent.getArgs()); + // 获取客户端操作系统 + String os = userAgent.getOs().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininforBo logininfor = new SysLogininforBo(); + logininfor.setTenantId(logininforEvent.getTenantId()); + logininfor.setUserName(logininforEvent.getUsername()); + if (ObjectUtil.isNotNull(client)) { + logininfor.setClientKey(client.getClientKey()); + logininfor.setDeviceType(client.getDeviceType()); + } + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(logininforEvent.getMessage()); + // 日志状态 + if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.SUCCESS); + } else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + insertLogininfor(logininfor); + } + + private String getBlock(Object msg) { + if (msg == null) { + msg = ""; + } + return "[" + msg.toString() + "]"; + } + + @Override + public TableDataInfo selectPageLogininforList(SysLogininforBo logininfor, PageQuery pageQuery) { + Map params = logininfor.getParams(); + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + lqw.orderByDesc(SysLogininfor::getInfoId); + } + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 新增系统登录日志 + * + * @param bo 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininforBo bo) { + SysLogininfor logininfor = MapstructUtils.convert(bo, SysLogininfor.class); + logininfor.setLoginTime(new Date()); + baseMapper.insert(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininforBo logininfor) { + Map params = logininfor.getParams(); + return baseMapper.selectVoList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(logininfor.getIpaddr()), SysLogininfor::getIpaddr, logininfor.getIpaddr()) + .eq(StringUtils.isNotBlank(logininfor.getStatus()), SysLogininfor::getStatus, logininfor.getStatus()) + .like(StringUtils.isNotBlank(logininfor.getUserName()), SysLogininfor::getUserName, logininfor.getUserName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysLogininfor::getLoginTime, params.get("beginTime"), params.get("endTime")) + .orderByDesc(SysLogininfor::getInfoId)); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) { + return baseMapper.deleteByIds(Arrays.asList(infoIds)); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysMenuServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..3fb894a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,405 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.TreeBuildUtils; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.SysMenu; +import com.fuyuanshen.system.domain.SysRole; +import com.fuyuanshen.system.domain.SysRoleMenu; +import com.fuyuanshen.system.domain.SysTenantPackage; +import com.fuyuanshen.system.domain.bo.SysMenuBo; +import com.fuyuanshen.system.domain.vo.MetaVo; +import com.fuyuanshen.system.domain.vo.RouterVo; +import com.fuyuanshen.system.domain.vo.SysMenuVo; +import com.fuyuanshen.system.mapper.SysMenuMapper; +import com.fuyuanshen.system.mapper.SysRoleMapper; +import com.fuyuanshen.system.mapper.SysRoleMenuMapper; +import com.fuyuanshen.system.mapper.SysTenantPackageMapper; +import com.fuyuanshen.system.service.ISysMenuService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 菜单 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysMenuServiceImpl implements ISysMenuService { + + private final SysMenuMapper baseMapper; + private final SysRoleMapper roleMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysTenantPackageMapper tenantPackageMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) { + return selectMenuList(new SysMenuBo(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenuBo menu, Long userId) { + List menuList; + // 管理员显示所有菜单信息 + if (LoginHelper.isSuperAdmin(userId)) { + menuList = baseMapper.selectVoList(new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId()) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum)); + } else { + QueryWrapper wrapper = Wrappers.query(); + wrapper.inSql("r.role_id", "select role_id from sys_user_role where user_id = " + userId) + .like(StringUtils.isNotBlank(menu.getMenuName()), "m.menu_name", menu.getMenuName()) + .eq(StringUtils.isNotBlank(menu.getVisible()), "m.visible", menu.getVisible()) + .eq(StringUtils.isNotBlank(menu.getStatus()), "m.status", menu.getStatus()) + .eq(StringUtils.isNotBlank(menu.getMenuType()), "m.menu_type", menu.getMenuType()) + .eq(ObjectUtil.isNotNull(menu.getParentId()), "m.parent_id", menu.getParentId()) + .orderByAsc("m.parent_id") + .orderByAsc("m.order_num"); + List list = baseMapper.selectMenuListByUserId(wrapper); + menuList = MapstructUtils.convert(list, SysMenuVo.class); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) { + List perms = baseMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(StringUtils.splitList(perm.trim())); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) { + List perms = baseMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(StringUtils.splitList(perm.trim())); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) { + List menus; + if (LoginHelper.isSuperAdmin(userId)) { + menus = baseMapper.selectMenuTreeAll(); + } else { + menus = baseMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) { + SysRole role = roleMapper.selectById(roleId); + return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); + } + + /** + * 根据租户套餐ID查询菜单树信息 + * + * @param packageId 租户套餐ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByPackageId(Long packageId) { + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + if (CollUtil.isEmpty(menuIds)) { + return List.of(); + } + List parentIds = null; + if (tenantPackage.getMenuCheckStrictly()) { + parentIds = baseMapper.selectObjs(new LambdaQueryWrapper() + .select(SysMenu::getParentId) + .in(SysMenu::getMenuId, menuIds), x -> { + return Convert.toLong(x); + }); + } + return baseMapper.selectObjs(new LambdaQueryWrapper() + .in(SysMenu::getMenuId, menuIds) + .notIn(CollUtil.isNotEmpty(parentIds), SysMenu::getMenuId, parentIds), x -> { + return Convert.toLong(x); + }); + } + + /** + * 构建前端路由所需要的菜单 + * 路由name命名规则 path首字母转大写 + id + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) { + List routers = new LinkedList<>(); + for (SysMenu menu : menus) { + String name = menu.getRouteName() + menu.getMenuId(); + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(name); + router.setPath(menu.getRouterPath()); + router.setComponent(menu.getComponentInfo()); + router.setQuery(menu.getQueryParam()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (CollUtil.isNotEmpty(cMenus) && SystemConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } else if (menu.isMenuFrame()) { + String frameName = StringUtils.capitalize(menu.getPath()) + menu.getMenuId(); + router.setMeta(null); + List childrenList = new ArrayList<>(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(frameName); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQueryParam()); + childrenList.add(children); + router.setChildren(childrenList); + } else if (menu.getParentId().intValue() == 0 && menu.isInnerLink()) { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList<>(); + RouterVo children = new RouterVo(); + String routerPath = SysMenu.innerLinkReplaceEach(menu.getPath()); + String innerLinkName = StringUtils.capitalize(routerPath) + menu.getMenuId(); + children.setPath(routerPath); + children.setComponent(SystemConstants.INNER_LINK); + children.setName(innerLinkName); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List> buildMenuTreeSelect(List menus) { + if (CollUtil.isEmpty(menus)) { + return CollUtil.newArrayList(); + } + return TreeBuildUtils.build(menus, (menu, tree) -> { + Tree menuTree = tree.setId(menu.getMenuId()) + .setParentId(menu.getParentId()) + .setName(menu.getMenuName()) + .setWeight(menu.getOrderNum()); + menuTree.put("menuType", menu.getMenuType()); + menuTree.put("icon", menu.getIcon()); + }); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenuVo selectMenuById(Long menuId) { + return baseMapper.selectVoById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) { + return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); + } + + /** + * 是否存在菜单子节点 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(List menuIds) { + return baseMapper.exists(new LambdaQueryWrapper().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds)); + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) { + return roleMenuMapper.exists(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); + } + + /** + * 新增保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenuBo bo) { + SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); + return baseMapper.insert(menu); + } + + /** + * 修改保存菜单信息 + * + * @param bo 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenuBo bo) { + SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); + return baseMapper.updateById(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) { + return baseMapper.deleteById(menuId); + } + + /** + * 批量删除菜单管理信息 + * + * @param menuIds 菜单ID串 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteMenuById(List menuIds) { + baseMapper.deleteByIds(menuIds); + roleMenuMapper.deleteByMenuIds(menuIds); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenuBo menu) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysMenu::getMenuName, menu.getMenuName()) + .eq(SysMenu::getParentId, menu.getParentId()) + .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId())); + return !exist; + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + private List getChildPerms(List list, int parentId) { + List returnList = new ArrayList<>(); + for (SysMenu t : list) { + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysMenu t) { + // 得到子节点列表 + List childList = StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId())); + t.setChildren(childList); + for (SysMenu tChild : childList) { + // 判断是否有子节点 + if (list.stream().anyMatch(n -> n.getParentId().equals(tChild.getMenuId()))) { + recursionFn(list, tChild); + } + } + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysNoticeServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..f62d513 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,124 @@ +package com.fuyuanshen.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysNotice; +import com.fuyuanshen.system.domain.SysUser; +import com.fuyuanshen.system.domain.bo.SysNoticeBo; +import com.fuyuanshen.system.domain.vo.SysNoticeVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.SysNoticeMapper; +import com.fuyuanshen.system.mapper.SysUserMapper; +import com.fuyuanshen.system.service.ISysNoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysNoticeServiceImpl implements ISysNoticeService { + + private final SysNoticeMapper baseMapper; + private final SysUserMapper userMapper; + + @Override + public TableDataInfo selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(notice); + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNoticeVo selectNoticeById(Long noticeId) { + return baseMapper.selectVoById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNoticeBo notice) { + LambdaQueryWrapper lqw = buildQueryWrapper(notice); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysNoticeBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); + if (StringUtils.isNotBlank(bo.getCreateByName())) { + SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, bo.getCreateByName())); + lqw.eq(SysNotice::getCreateBy, ObjectUtils.notNullGetter(sysUser, SysUserVo::getUserId)); + } + lqw.orderByAsc(SysNotice::getNoticeId); + return lqw; + } + + /** + * 新增公告 + * + * @param bo 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNoticeBo bo) { + SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); + return baseMapper.insert(notice); + } + + /** + * 修改公告 + * + * @param bo 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNoticeBo bo) { + SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); + return baseMapper.updateById(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) { + return baseMapper.deleteById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) { + return baseMapper.deleteByIds(Arrays.asList(noticeIds)); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOperLogServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..ea6805f --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,134 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.util.ArrayUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ip.AddressUtils; +import com.fuyuanshen.common.log.event.OperLogEvent; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysOperLog; +import com.fuyuanshen.system.domain.bo.SysOperLogBo; +import com.fuyuanshen.system.domain.vo.SysOperLogVo; +import com.fuyuanshen.system.mapper.SysOperLogMapper; +import com.fuyuanshen.system.service.ISysOperLogService; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 操作日志 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysOperLogServiceImpl implements ISysOperLogService { + + private final SysOperLogMapper baseMapper; + + /** + * 操作日志记录 + * + * @param operLogEvent 操作日志事件 + */ + @Async + @EventListener + public void recordOper(OperLogEvent operLogEvent) { + SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class); + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + insertOperlog(operLog); + } + + @Override + public TableDataInfo selectPageOperLogList(SysOperLogBo operLog, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(operLog); + if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { + lqw.orderByDesc(SysOperLog::getOperId); + } + Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + private LambdaQueryWrapper buildQueryWrapper(SysOperLogBo operLog) { + Map params = operLog.getParams(); + return new LambdaQueryWrapper() + .like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp()) + .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, + SysOperLog::getBusinessType, operLog.getBusinessType()) + .func(f -> { + if (ArrayUtil.isNotEmpty(operLog.getBusinessTypes())) { + f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); + } + }) + .eq(operLog.getStatus() != null, + SysOperLog::getStatus, operLog.getStatus()) + .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")); + } + + /** + * 新增操作日志 + * + * @param bo 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLogBo bo) { + SysOperLog operLog = MapstructUtils.convert(bo, SysOperLog.class); + operLog.setOperTime(new Date()); + baseMapper.insert(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLogBo operLog) { + LambdaQueryWrapper lqw = buildQueryWrapper(operLog); + return baseMapper.selectVoList(lqw.orderByDesc(SysOperLog::getOperId)); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) { + return baseMapper.deleteByIds(Arrays.asList(operIds)); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLogVo selectOperLogById(Long operId) { + return baseMapper.selectVoById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() { + baseMapper.delete(new LambdaQueryWrapper<>()); + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssConfigServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssConfigServiceImpl.java new file mode 100644 index 0000000..e676445 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssConfigServiceImpl.java @@ -0,0 +1,177 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.ObjectUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.oss.constant.OssConstant; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.common.redis.utils.RedisUtils; +import com.fuyuanshen.system.domain.SysOssConfig; +import com.fuyuanshen.system.domain.bo.SysOssConfigBo; +import com.fuyuanshen.system.domain.vo.SysOssConfigVo; +import com.fuyuanshen.system.mapper.SysOssConfigMapper; +import com.fuyuanshen.system.service.ISysOssConfigService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +/** + * 对象存储配置Service业务层处理 + * + * @author Lion Li + * @author 孤舟烟雨 + * @date 2021-08-13 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysOssConfigServiceImpl implements ISysOssConfigService { + + private final SysOssConfigMapper baseMapper; + + /** + * 项目启动时,初始化参数到缓存,加载配置类 + */ + @Override + public void init() { + List list = baseMapper.selectList(); + // 加载OSS初始化配置 + for (SysOssConfig config : list) { + String configKey = config.getConfigKey(); + if ("0".equals(config.getStatus())) { + RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey); + } + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + } + + @Override + public SysOssConfigVo queryById(Long ossConfigId) { + return baseMapper.selectVoById(ossConfigId); + } + + @Override + public TableDataInfo queryPageList(SysOssConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + + private LambdaQueryWrapper buildQueryWrapper(SysOssConfigBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey()); + lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus()); + lqw.orderByAsc(SysOssConfig::getOssConfigId); + return lqw; + } + + @Override + public Boolean insertByBo(SysOssConfigBo bo) { + SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); + validEntityBeforeSave(config); + boolean flag = baseMapper.insert(config) > 0; + if (flag) { + // 从数据库查询完整的数据做缓存 + config = baseMapper.selectById(config.getOssConfigId()); + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + return flag; + } + + @Override + public Boolean updateByBo(SysOssConfigBo bo) { + SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); + validEntityBeforeSave(config); + LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); + luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); + luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); + luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); + luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); + luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); + boolean flag = baseMapper.update(config, luw) > 0; + if (flag) { + // 从数据库查询完整的数据做缓存 + config = baseMapper.selectById(config.getOssConfigId()); + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysOssConfig entity) { + if (StringUtils.isNotEmpty(entity.getConfigKey()) + && !checkConfigKeyUnique(entity)) { + throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, 配置key已存在!"); + } + } + + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) { + throw new ServiceException("系统内置, 不可删除!"); + } + } + List list = CollUtil.newArrayList(); + for (Long configId : ids) { + SysOssConfig config = baseMapper.selectById(configId); + list.add(config); + } + boolean flag = baseMapper.deleteByIds(ids) > 0; + if (flag) { + list.forEach(sysOssConfig -> + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); + } + return flag; + } + + /** + * 判断configKey是否唯一 + */ + private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { + long ossConfigId = ObjectUtils.notNull(sysOssConfig.getOssConfigId(), -1L); + SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) + .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey())); + if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { + return false; + } + return true; + } + + /** + * 启用禁用状态 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateOssConfigStatus(SysOssConfigBo bo) { + SysOssConfig sysOssConfig = MapstructUtils.convert(bo, SysOssConfig.class); + int row = baseMapper.update(null, new LambdaUpdateWrapper() + .set(SysOssConfig::getStatus, "1")); + row += baseMapper.updateById(sysOssConfig); + if (row > 0) { + RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); + } + return row; + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java new file mode 100644 index 0000000..a46dfe1 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysOssServiceImpl.java @@ -0,0 +1,299 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.domain.dto.OssDTO; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.OssService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.file.FileUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.oss.core.OssClient; +import com.fuyuanshen.common.oss.entity.UploadResult; +import com.fuyuanshen.common.oss.enums.AccessPolicyType; +import com.fuyuanshen.common.oss.factory.OssFactory; +import com.fuyuanshen.system.domain.SysOss; +import com.fuyuanshen.system.domain.bo.SysOssBo; +import com.fuyuanshen.system.domain.vo.SysOssVo; +import com.fuyuanshen.system.mapper.SysOssMapper; +import com.fuyuanshen.system.service.ISysOssService; +import org.jetbrains.annotations.NotNull; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 文件上传 服务层实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysOssServiceImpl implements ISysOssService, OssService { + + private final SysOssMapper baseMapper; + + /** + * 查询OSS对象存储列表 + * + * @param bo OSS对象存储分页查询对象 + * @param pageQuery 分页查询实体类 + * @return 结果 + */ + @Override + public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + List filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl); + result.setRecords(filterResult); + return TableDataInfo.build(result); + } + + /** + * 根据一组 ossIds 获取对应的 SysOssVo 列表 + * + * @param ossIds 一组文件在数据库中的唯一标识集合 + * @return 包含 SysOssVo 对象的列表 + */ + @Override + public List listByIds(Collection ossIds) { + List list = new ArrayList<>(); + SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); + for (Long id : ossIds) { + SysOssVo vo = ossService.getById(id); + if (ObjectUtil.isNotNull(vo)) { + try { + list.add(this.matchingUrl(vo)); + } catch (Exception ignored) { + // 如果oss异常无法连接则将数据直接返回 + list.add(vo); + } + } + } + return list; + } + + /** + * 根据一组 ossIds 获取对应文件的 URL 列表 + * + * @param ossIds 以逗号分隔的 ossId 字符串 + * @return 以逗号分隔的文件 URL 字符串 + */ + @Override + public String selectUrlByIds(String ossIds) { + List list = new ArrayList<>(); + SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); + for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { + SysOssVo vo = ossService.getById(id); + if (ObjectUtil.isNotNull(vo)) { + try { + list.add(this.matchingUrl(vo).getUrl()); + } catch (Exception ignored) { + // 如果oss异常无法连接则将数据直接返回 + list.add(vo.getUrl()); + } + } + } + return String.join(StringUtils.SEPARATOR, list); + } + + @Override + public List selectByIds(String ossIds) { + List list = new ArrayList<>(); + for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { + SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + if (ObjectUtil.isNotNull(vo)) { + try { + vo.setUrl(this.matchingUrl(vo).getUrl()); + list.add(BeanUtil.toBean(vo, OssDTO.class)); + } catch (Exception ignored) { + // 如果oss异常无法连接则将数据直接返回 + list.add(BeanUtil.toBean(vo, OssDTO.class)); + } + } + } + return list; + } + + private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); + lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); + lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); + lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); + lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); + lqw.orderByAsc(SysOss::getOssId); + return lqw; + } + + /** + * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象 + * + * @param ossId 文件在数据库中的唯一标识 + * @return SysOssVo 对象,包含文件信息 + */ + @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") + @Override + public SysOssVo getById(Long ossId) { + return baseMapper.selectVoById(ossId); + } + + + /** + * 文件下载方法,支持一次性下载完整文件 + * + * @param ossId OSS对象ID + * @param response HttpServletResponse对象,用于设置响应头和向客户端发送文件内容 + */ + @Override + public void download(Long ossId, HttpServletResponse response) throws IOException { + SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); + if (ObjectUtil.isNull(sysOss)) { + throw new ServiceException("文件数据不存在!"); + } + FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); + OssClient storage = OssFactory.instance(sysOss.getService()); + storage.download(sysOss.getFileName(), response.getOutputStream(), response::setContentLengthLong); + } + + /** + * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的 MultipartFile 对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ + @Override + public SysOssVo upload(MultipartFile file) { + String originalfileName = file.getOriginalFilename(); + String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); + OssClient storage = OssFactory.instance(); + UploadResult uploadResult; + try { + uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); + } catch (IOException e) { + throw new ServiceException(e.getMessage()); + } + // 保存文件信息 + return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); + } + + /** + * 上传文件到对象存储服务,并保存文件信息到数据库 + * + * @param file 要上传的文件对象 + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + */ + @Override + public SysOssVo upload(File file) { + String originalfileName = file.getName(); + String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); + OssClient storage = OssFactory.instance(); + UploadResult uploadResult = storage.uploadSuffix(file, suffix); + // 保存文件信息 + return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult); + } + + + + /** + * 上传二进制数据到对象存储服务,并保存文件信息到数据库 + * + * @param data 要上传的二进制数据 + * @param fileName 要上传的文件名(包括扩展名) + * @return 上传成功后的 SysOssVo 对象,包含文件信息 + * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常 + */ + @Override + public SysOssVo upload(byte[] data, String fileName) { + if (data == null || data.length == 0) { + throw new ServiceException("上传的数据为空"); + } + if (StringUtils.isBlank(fileName)) { + throw new ServiceException("文件名不能为空"); + } + + String suffix = StringUtils.substring(fileName, fileName.lastIndexOf("."), fileName.length()); + OssClient storage = OssFactory.instance(); + + UploadResult uploadResult; + uploadResult = storage.uploadSuffix(data, suffix, "image/jpeg"); // 假设是图片类型,可以根据实际需要修改 + + // 保存文件信息 + return buildResultEntity(fileName, suffix, storage.getConfigKey(), uploadResult); + } + + + @NotNull + private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult) { + SysOss oss = new SysOss(); + oss.setUrl(uploadResult.getUrl()); + oss.setFileSuffix(suffix); + oss.setFileName(uploadResult.getFilename()); + oss.setOriginalName(originalfileName); + oss.setService(configKey); + baseMapper.insert(oss); + SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); + return this.matchingUrl(sysOssVo); + } + + /** + * 删除OSS对象存储 + * + * @param ids OSS对象ID串 + * @param isValid 判断是否需要校验 + * @return 结果 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + } + List list = baseMapper.selectByIds(ids); + for (SysOss sysOss : list) { + OssClient storage = OssFactory.instance(sysOss.getService()); + storage.delete(sysOss.getUrl()); + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 桶类型为 private 的URL 修改为临时URL时长为120s + * + * @param oss OSS对象 + * @return oss 匹配Url的OSS对象 + */ + private SysOssVo matchingUrl(SysOssVo oss) { + OssClient storage = OssFactory.instance(oss.getService()); + // 仅修改桶类型为 private 的URL,临时URL时长为120s + if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { + oss.setUrl(storage.getPrivateUrl(oss.getFileName(), Duration.ofSeconds(120))); + } + return oss; + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPermissionServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 0000000..9d81bed --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,62 @@ +package com.fuyuanshen.system.service.impl; + +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.service.PermissionService; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.service.ISysMenuService; +import com.fuyuanshen.system.service.ISysPermissionService; +import com.fuyuanshen.system.service.ISysRoleService; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +/** + * 用户权限处理 + * + * @author fys + */ +@RequiredArgsConstructor +@Service +public class SysPermissionServiceImpl implements ISysPermissionService, PermissionService { + + private final ISysRoleService roleService; + private final ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + @Override + public Set getRolePermission(Long userId) { + Set roles = new HashSet<>(); + // 管理员拥有所有权限 + if (LoginHelper.isSuperAdmin(userId)) { + roles.add(TenantConstants.SUPER_ADMIN_ROLE_KEY); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(userId)); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + @Override + public Set getMenuPermission(Long userId) { + Set perms = new HashSet<>(); + // 管理员拥有所有权限 + if (LoginHelper.isSuperAdmin(userId)) { + perms.add("*:*:*"); + } else { + perms.addAll(menuService.selectMenuPermsByUserId(userId)); + } + return perms; + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPostServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..5be6b39 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,254 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.PostService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysDept; +import com.fuyuanshen.system.domain.SysPost; +import com.fuyuanshen.system.domain.SysUserPost; +import com.fuyuanshen.system.domain.bo.SysPostBo; +import com.fuyuanshen.system.domain.vo.SysPostVo; +import com.fuyuanshen.system.mapper.SysDeptMapper; +import com.fuyuanshen.system.mapper.SysPostMapper; +import com.fuyuanshen.system.mapper.SysUserPostMapper; +import com.fuyuanshen.system.service.ISysPostService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 岗位信息 服务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysPostServiceImpl implements ISysPostService, PostService { + + private final SysPostMapper baseMapper; + private final SysDeptMapper deptMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPagePostList(SysPostBo post, PageQuery pageQuery) { + Page page = baseMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post)); + return TableDataInfo.build(page); + } + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPostBo post) { + return baseMapper.selectVoList(buildQueryWrapper(post)); + } + + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 岗位ID + */ + @Override + public List selectPostsByUserId(Long userId) { + return baseMapper.selectPostsByUserId(userId); + } + + /** + * 根据查询条件构建查询包装器 + * + * @param bo 查询条件对象 + * @return 构建好的查询包装器 + */ + private LambdaQueryWrapper buildQueryWrapper(SysPostBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode()) + .like(StringUtils.isNotBlank(bo.getPostCategory()), SysPost::getPostCategory, bo.getPostCategory()) + .like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName()) + .eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + SysPost::getCreateTime, params.get("beginTime"), params.get("endTime")) + .orderByAsc(SysPost::getPostSort); + if (ObjectUtil.isNotNull(bo.getDeptId())) { + //优先单部门搜索 + wrapper.eq(SysPost::getDeptId, bo.getDeptId()); + } else if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { + //部门树搜索 + wrapper.and(x -> { + List deptList = deptMapper.selectListByParentId(bo.getBelongDeptId()); + List deptIds = StreamUtils.toList(deptList, SysDept::getDeptId); + deptIds.add(bo.getBelongDeptId()); + x.in(SysPost::getDeptId, deptIds); + }); + } + return wrapper; + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() { + return baseMapper.selectVoList(new QueryWrapper<>()); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPostVo selectPostById(Long postId) { + return baseMapper.selectVoById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) { + List list = baseMapper.selectPostsByUserId(userId); + return StreamUtils.toList(list, SysPostVo::getPostId); + } + + /** + * 通过岗位ID串查询岗位 + * + * @param postIds 岗位id串 + * @return 岗位列表信息 + */ + @Override + public List selectPostByIds(List postIds) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) + .eq(SysPost::getStatus, SystemConstants.NORMAL) + .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds)); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPostBo post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostName, post.getPostName()) + .eq(SysPost::getDeptId, post.getDeptId()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + return !exist; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPostBo post) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysPost::getPostCode, post.getPostCode()) + .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + return !exist; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public long countUserPostById(Long postId) { + return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); + } + + /** + * 通过部门ID查询岗位使用数量 + * + * @param deptId 部门id + * @return 结果 + */ + @Override + public long countPostByDeptId(Long deptId) { + return baseMapper.selectCount(new LambdaQueryWrapper().eq(SysPost::getDeptId, deptId)); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) { + return baseMapper.deleteById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) { + for (Long postId : postIds) { + SysPost post = baseMapper.selectById(postId); + if (countUserPostById(postId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除!", post.getPostName())); + } + } + return baseMapper.deleteByIds(Arrays.asList(postIds)); + } + + /** + * 新增保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPostBo bo) { + SysPost post = MapstructUtils.convert(bo, SysPost.class); + return baseMapper.insert(post); + } + + /** + * 修改保存岗位信息 + * + * @param bo 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPostBo bo) { + SysPost post = MapstructUtils.convert(bo, SysPost.class); + return baseMapper.updateById(post); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysRoleServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..a67a1b3 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,552 @@ +package com.fuyuanshen.system.service.impl; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.domain.model.LoginUser; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.RoleService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.SysRole; +import com.fuyuanshen.system.domain.SysRoleDept; +import com.fuyuanshen.system.domain.SysRoleMenu; +import com.fuyuanshen.system.domain.SysUserRole; +import com.fuyuanshen.system.domain.bo.SysRoleBo; +import com.fuyuanshen.system.domain.vo.SysRoleVo; +import com.fuyuanshen.system.mapper.SysRoleDeptMapper; +import com.fuyuanshen.system.mapper.SysRoleMapper; +import com.fuyuanshen.system.mapper.SysRoleMenuMapper; +import com.fuyuanshen.system.mapper.SysUserRoleMapper; +import com.fuyuanshen.system.service.ISysRoleService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysRoleServiceImpl implements ISysRoleService, RoleService { + + private final SysRoleMapper baseMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysRoleDeptMapper roleDeptMapper; + + @Override + public TableDataInfo selectPageRoleList(SysRoleBo role, PageQuery pageQuery) { + Page page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + public List selectRoleList(SysRoleBo role) { + return baseMapper.selectRoleList(this.buildQueryWrapper(role)); + } + + private Wrapper buildQueryWrapper(SysRoleBo bo) { + Map params = bo.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("r.del_flag", SystemConstants.NORMAL) + .eq(ObjectUtil.isNotNull(bo.getRoleId()), "r.role_id", bo.getRoleId()) + .like(StringUtils.isNotBlank(bo.getRoleName()), "r.role_name", bo.getRoleName()) + .eq(StringUtils.isNotBlank(bo.getStatus()), "r.status", bo.getStatus()) + .like(StringUtils.isNotBlank(bo.getRoleKey()), "r.role_key", bo.getRoleKey()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "r.create_time", params.get("beginTime"), params.get("endTime")) + .orderByAsc("r.role_sort").orderByAsc("r.create_time"); + return wrapper; + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) { + return baseMapper.selectRolesByUserId(userId); + } + + /** + * 根据用户ID查询角色列表(包含被授权状态) + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesAuthByUserId(Long userId) { + List userRoles = baseMapper.selectRolesByUserId(userId); + List roles = selectRoleAll(); + // 使用HashSet提高查找效率 + Set userRoleIds = StreamUtils.toSet(userRoles, SysRoleVo::getRoleId); + for (SysRoleVo role : roles) { + if (userRoleIds.contains(role.getRoleId())) { + role.setFlag(true); + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) { + List perms = baseMapper.selectRolesByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRoleVo perm : perms) { + if (ObjectUtil.isNotNull(perm)) { + permsSet.addAll(StringUtils.splitList(perm.getRoleKey().trim())); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() { + return this.selectRoleList(new SysRoleBo()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) { + List list = baseMapper.selectRolesByUserId(userId); + return StreamUtils.toList(list, SysRoleVo::getRoleId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRoleVo selectRoleById(Long roleId) { + return baseMapper.selectRoleById(roleId); + } + + /** + * 通过角色ID串查询角色 + * + * @param roleIds 角色ID串 + * @return 角色列表信息 + */ + @Override + public List selectRoleByIds(List roleIds) { + return baseMapper.selectRoleList(new QueryWrapper() + .eq("r.status", SystemConstants.NORMAL) + .in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds)); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRoleBo role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleName, role.getRoleName()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + return !exist; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRoleBo role) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysRole::getRoleKey, role.getRoleKey()) + .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + return !exist; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRoleBo role) { + if (ObjectUtil.isNotNull(role.getRoleId()) && LoginHelper.isSuperAdmin(role.getRoleId())) { + throw new ServiceException("不允许操作超级管理员角色"); + } + String[] keys = new String[]{TenantConstants.SUPER_ADMIN_ROLE_KEY, TenantConstants.TENANT_ADMIN_ROLE_KEY}; + // 新增不允许使用 管理员标识符 + if (ObjectUtil.isNull(role.getRoleId()) + && StringUtils.equalsAny(role.getRoleKey(), keys)) { + throw new ServiceException("不允许使用系统内置管理员角色标识符!"); + } + // 修改不允许修改 管理员标识符 + if (ObjectUtil.isNotNull(role.getRoleId())) { + SysRole sysRole = baseMapper.selectById(role.getRoleId()); + // 如果标识符不相等 判断为修改了管理员标识符 + if (!StringUtils.equals(sysRole.getRoleKey(), role.getRoleKey())) { + if (StringUtils.equalsAny(sysRole.getRoleKey(), keys)) { + throw new ServiceException("不允许修改系统内置管理员角色标识符!"); + } else if (StringUtils.equalsAny(role.getRoleKey(), keys)) { + throw new ServiceException("不允许使用系统内置管理员角色标识符!"); + } + } + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) { + if (ObjectUtil.isNull(roleId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + List roles = this.selectRoleList(new SysRoleBo(roleId)); + if (CollUtil.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色数据!"); + } + + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public long countUserRoleByRoleId(Long roleId) { + return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + } + + /** + * 新增保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertRole(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + // 新增角色信息 + baseMapper.insert(role); + bo.setRoleId(role.getRoleId()); + return insertRoleMenu(bo); + } + + /** + * 修改保存角色信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + + if (SystemConstants.DISABLE.equals(role.getStatus()) && this.countUserRoleByRoleId(role.getRoleId()) > 0) { + throw new ServiceException("角色已分配,不能禁用!"); + } + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); + return insertRoleMenu(bo); + } + + /** + * 修改角色状态 + * + * @param roleId 角色ID + * @param status 角色状态 + * @return 结果 + */ + @Override + public int updateRoleStatus(Long roleId, String status) { + if (SystemConstants.DISABLE.equals(status) && this.countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException("角色已分配,不能禁用!"); + } + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysRole::getStatus, status) + .eq(SysRole::getRoleId, roleId)); + } + + /** + * 修改数据权限信息 + * + * @param bo 角色信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#bo.roleId") + @Override + @Transactional(rollbackFor = Exception.class) + public int authDataScope(SysRoleBo bo) { + SysRole role = MapstructUtils.convert(bo, SysRole.class); + // 修改角色信息 + baseMapper.updateById(role); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); + // 新增角色和部门信息(数据权限) + return insertRoleDept(bo); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + private int insertRoleMenu(SysRoleBo role) { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList<>(); + for (Long menuId : role.getMenuIds()) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + private int insertRoleDept(SysRoleBo role) { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList<>(); + for (Long deptId : role.getDeptIds()) { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) { + rows = roleDeptMapper.insertBatch(list) ? list.size() : 0; + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#roleId") + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleById(Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); + return baseMapper.deleteById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_ROLE_CUSTOM, allEntries = true) + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds(Long[] roleIds) { + for (Long roleId : roleIds) { + SysRole role = baseMapper.selectById(roleId); + checkRoleAllowed(BeanUtil.toBean(role, SysRoleBo.class)); + checkRoleDataScope(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除!", role.getRoleName())); + } + } + List ids = Arrays.asList(roleIds); + // 删除角色与菜单关联 + roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, ids)); + // 删除角色与部门关联 + roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, ids)); + return baseMapper.deleteByIds(ids); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) { + int rows = userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, userRole.getRoleId()) + .eq(SysUserRole::getUserId, userRole.getUserId())); + if (rows > 0) { + cleanOnlineUser(List.of(userRole.getUserId())); + } + return rows; + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) { + List ids = List.of(userIds); + int rows = userRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getRoleId, roleId) + .in(SysUserRole::getUserId, ids)); + if (rows > 0) { + cleanOnlineUser(ids); + } + return rows; + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) { + // 新增用户与角色管理 + int rows = 1; + List ids = List.of(userIds); + List list = StreamUtils.toList(ids, userId -> { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + return ur; + }); + if (CollUtil.isNotEmpty(list)) { + rows = userRoleMapper.insertBatch(list) ? list.size() : 0; + } + if (rows > 0) { + cleanOnlineUser(ids); + } + return rows; + } + + @Override + public void cleanOnlineUserByRole(Long roleId) { + // 如果角色未绑定用户 直接返回 + Long num = userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + if (num == 0) { + return; + } + List keys = StpUtil.searchTokenValue("", 0, -1, false); + if (CollUtil.isEmpty(keys)) { + return; + } + // 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作 + keys.parallelStream().forEach(key -> { + String token = StringUtils.substringAfterLast(key, ":"); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { + return; + } + LoginUser loginUser = LoginHelper.getLoginUser(token); + if (ObjectUtil.isNull(loginUser) || CollUtil.isEmpty(loginUser.getRoles())) { + return; + } + if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(roleId))) { + try { + StpUtil.logoutByTokenValue(token); + } catch (NotLoginException ignored) { + } + } + }); + } + + @Override + public void cleanOnlineUser(List userIds) { + List keys = StpUtil.searchTokenValue("", 0, -1, false); + if (CollUtil.isEmpty(keys)) { + return; + } + // 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作 + keys.parallelStream().forEach(key -> { + String token = StringUtils.substringAfterLast(key, ":"); + // 如果已经过期则跳过 + if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { + return; + } + LoginUser loginUser = LoginHelper.getLoginUser(token); + if (ObjectUtil.isNull(loginUser)) { + return; + } + if (userIds.contains(loginUser.getUserId())) { + try { + StpUtil.logoutByTokenValue(token); + } catch (NotLoginException ignored) { + } + } + }); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSensitiveServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSensitiveServiceImpl.java new file mode 100644 index 0000000..7ed0707 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSensitiveServiceImpl.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.system.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.ArrayUtil; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.common.sensitive.core.SensitiveService; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import org.springframework.stereotype.Service; + +/** + * 脱敏服务 + * 默认管理员不过滤 + * 需自行根据业务重写实现 + * + * @author Lion Li + * @version 3.6.0 + */ +@Service +public class SysSensitiveServiceImpl implements SensitiveService { + + /** + * 是否脱敏 + */ + @Override + public boolean isSensitive(String[] roleKey, String[] perms) { + if (!LoginHelper.isLogin()) { + return true; + } + boolean roleExist = ArrayUtil.isNotEmpty(roleKey); + boolean permsExist = ArrayUtil.isNotEmpty(perms); + if (roleExist && permsExist) { + if (StpUtil.hasRoleOr(roleKey) && StpUtil.hasPermissionOr(perms)) { + return false; + } + } else if (roleExist && StpUtil.hasRoleOr(roleKey)) { + return false; + } else if (permsExist && StpUtil.hasPermissionOr(perms)) { + return false; + } + + if (TenantHelper.isEnable()) { + return !LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin(); + } + return !LoginHelper.isSuperAdmin(); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSocialServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSocialServiceImpl.java new file mode 100644 index 0000000..5b4e0a7 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysSocialServiceImpl.java @@ -0,0 +1,112 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.system.domain.SysSocial; +import com.fuyuanshen.system.domain.bo.SysSocialBo; +import com.fuyuanshen.system.domain.vo.SysSocialVo; +import com.fuyuanshen.system.mapper.SysSocialMapper; +import com.fuyuanshen.system.service.ISysSocialService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 社会化关系Service业务层处理 + * + * @author thiszhc + * @date 2023-06-12 + */ +@RequiredArgsConstructor +@Service +public class SysSocialServiceImpl implements ISysSocialService { + + private final SysSocialMapper baseMapper; + + + /** + * 查询社会化关系 + */ + @Override + public SysSocialVo queryById(String id) { + return baseMapper.selectVoById(id); + } + + /** + * 授权列表 + */ + @Override + public List queryList(SysSocialBo bo) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .eq(ObjectUtil.isNotNull(bo.getUserId()), SysSocial::getUserId, bo.getUserId()) + .eq(StringUtils.isNotBlank(bo.getAuthId()), SysSocial::getAuthId, bo.getAuthId()) + .eq(StringUtils.isNotBlank(bo.getSource()), SysSocial::getSource, bo.getSource()); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryListByUserId(Long userId) { + return baseMapper.selectVoList(new LambdaQueryWrapper().eq(SysSocial::getUserId, userId)); + } + + + /** + * 新增社会化关系 + */ + @Override + public Boolean insertByBo(SysSocialBo bo) { + SysSocial add = MapstructUtils.convert(bo, SysSocial.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + if (add != null) { + bo.setId(add.getId()); + } else { + return false; + } + } + return flag; + } + + /** + * 更新社会化关系 + */ + @Override + public Boolean updateByBo(SysSocialBo bo) { + SysSocial update = MapstructUtils.convert(bo, SysSocial.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysSocial entity) { + //TODO 做一些数据校验,如唯一约束 + } + + + /** + * 删除社会化关系 + */ + @Override + public Boolean deleteWithValidById(Long id) { + return baseMapper.deleteById(id) > 0; + } + + + /** + * 根据 authId 查询用户信息 + * + * @param authId 认证id + * @return 授权信息 + */ + @Override + public List selectByAuthId(String authId) { + return baseMapper.selectVoList(new LambdaQueryWrapper().eq(SysSocial::getAuthId, authId)); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTaskAssigneeServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTaskAssigneeServiceImpl.java new file mode 100644 index 0000000..9605bc0 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTaskAssigneeServiceImpl.java @@ -0,0 +1,134 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.dto.TaskAssigneeDTO; +import com.fuyuanshen.common.core.domain.model.TaskAssigneeBody; +import com.fuyuanshen.common.core.service.TaskAssigneeService; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.bo.SysDeptBo; +import com.fuyuanshen.system.domain.bo.SysPostBo; +import com.fuyuanshen.system.domain.bo.SysRoleBo; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.SysDeptVo; +import com.fuyuanshen.system.domain.vo.SysPostVo; +import com.fuyuanshen.system.domain.vo.SysRoleVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.service.ISysDeptService; +import com.fuyuanshen.system.service.ISysPostService; +import com.fuyuanshen.system.service.ISysRoleService; +import com.fuyuanshen.system.service.ISysUserService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 工作流设计器获取任务执行人 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysTaskAssigneeServiceImpl implements TaskAssigneeService { + + // 上级Service注入下级Service 其他Service永远不可能注入当前类 避免循环注入 + private final ISysPostService postService; + private final ISysDeptService deptService; + private final ISysUserService userService; + private final ISysRoleService roleService; + + /** + * 查询角色并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + @Override + public TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery) { + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); + SysRoleBo bo = new SysRoleBo(); + bo.setRoleKey(taskQuery.getHandlerCode()); + bo.setRoleName(taskQuery.getHandlerName()); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + TableDataInfo page = roleService.selectPageRoleList(bo, pageQuery); + // 使用封装的字段映射方法进行转换 + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + SysRoleVo::getRoleId, SysRoleVo::getRoleKey, SysRoleVo::getRoleName, null, SysRoleVo::getCreateTime); + return new TaskAssigneeDTO(page.getTotal(), handlers); + } + + /** + * 查询岗位并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + @Override + public TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery) { + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); + SysPostBo bo = new SysPostBo(); + bo.setPostCategory(taskQuery.getHandlerCode()); + bo.setPostName(taskQuery.getHandlerName()); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setBelongDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = postService.selectPagePostList(bo, pageQuery); + // 使用封装的字段映射方法进行转换 + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + SysPostVo::getPostId, SysPostVo::getPostCategory, SysPostVo::getPostName, SysPostVo::getDeptId, SysPostVo::getCreateTime); + return new TaskAssigneeDTO(page.getTotal(), handlers); + } + + /** + * 查询部门并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + @Override + public TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery) { + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); + SysDeptBo bo = new SysDeptBo(); + bo.setDeptCategory(taskQuery.getHandlerCode()); + bo.setDeptName(taskQuery.getHandlerName()); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setBelongDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = deptService.selectPageDeptList(bo, pageQuery); + // 使用封装的字段映射方法进行转换 + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + SysDeptVo::getDeptId, SysDeptVo::getDeptCategory, SysDeptVo::getDeptName, SysDeptVo::getParentId, SysDeptVo::getCreateTime); + return new TaskAssigneeDTO(page.getTotal(), handlers); + } + + + /** + * 查询用户并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + @Override + public TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery) { + PageQuery pageQuery = new PageQuery(taskQuery.getPageSize(), taskQuery.getPageNum()); + SysUserBo bo = new SysUserBo(); + bo.setUserName(taskQuery.getHandlerCode()); + bo.setNickName(taskQuery.getHandlerName()); + Map params = bo.getParams(); + params.put("beginTime", taskQuery.getBeginTime()); + params.put("endTime", taskQuery.getEndTime()); + bo.setDeptId(Convert.toLong(taskQuery.getGroupId())); + TableDataInfo page = userService.selectPageUserList(bo, pageQuery); + // 使用封装的字段映射方法进行转换 + List handlers = TaskAssigneeDTO.convertToHandlerList(page.getRows(), + SysUserVo::getUserId, SysUserVo::getUserName, SysUserVo::getNickName, SysUserVo::getDeptId, SysUserVo::getCreateTime); + return new TaskAssigneeDTO(page.getTotal(), handlers); + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantPackageServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantPackageServiceImpl.java new file mode 100644 index 0000000..6168841 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantPackageServiceImpl.java @@ -0,0 +1,157 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.system.domain.SysTenant; +import com.fuyuanshen.system.domain.SysTenantPackage; +import com.fuyuanshen.system.domain.bo.SysTenantPackageBo; +import com.fuyuanshen.system.domain.vo.SysTenantPackageVo; +import com.fuyuanshen.system.mapper.SysTenantMapper; +import com.fuyuanshen.system.mapper.SysTenantPackageMapper; +import com.fuyuanshen.system.service.ISysTenantPackageService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * 租户套餐Service业务层处理 + * + * @author Michelle.Chung + */ +@RequiredArgsConstructor +@Service +public class SysTenantPackageServiceImpl implements ISysTenantPackageService { + + private final SysTenantPackageMapper baseMapper; + private final SysTenantMapper tenantMapper; + + /** + * 查询租户套餐 + */ + @Override + public SysTenantPackageVo queryById(Long packageId){ + return baseMapper.selectVoById(packageId); + } + + /** + * 查询租户套餐列表 + */ + @Override + public TableDataInfo queryPageList(SysTenantPackageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List selectList() { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .eq(SysTenantPackage::getStatus, SystemConstants.NORMAL)); + } + + /** + * 查询租户套餐列表 + */ + @Override + public List queryList(SysTenantPackageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysTenantPackageBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), SysTenantPackage::getPackageName, bo.getPackageName()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenantPackage::getStatus, bo.getStatus()); + lqw.orderByAsc(SysTenantPackage::getPackageId); + return lqw; + } + + /** + * 新增租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysTenantPackageBo bo) { + SysTenantPackage add = MapstructUtils.convert(bo, SysTenantPackage.class); + // 保存菜单id + List menuIds = Arrays.asList(bo.getMenuIds()); + if (CollUtil.isNotEmpty(menuIds)) { + add.setMenuIds(StringUtils.join(menuIds, ", ")); + } else { + add.setMenuIds(""); + } + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setPackageId(add.getPackageId()); + } + return flag; + } + + /** + * 修改租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(SysTenantPackageBo bo) { + SysTenantPackage update = MapstructUtils.convert(bo, SysTenantPackage.class); + // 保存菜单id + List menuIds = Arrays.asList(bo.getMenuIds()); + if (CollUtil.isNotEmpty(menuIds)) { + update.setMenuIds(StringUtils.join(menuIds, ", ")); + } else { + update.setMenuIds(""); + } + return baseMapper.updateById(update) > 0; + } + + /** + * 校验套餐名称是否唯一 + */ + @Override + public boolean checkPackageNameUnique(SysTenantPackageBo bo) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysTenantPackage::getPackageName, bo.getPackageName()) + .ne(ObjectUtil.isNotNull(bo.getPackageId()), SysTenantPackage::getPackageId, bo.getPackageId())); + return !exist; + } + + /** + * 修改套餐状态 + * + * @param bo 套餐信息 + * @return 结果 + */ + @Override + public int updatePackageStatus(SysTenantPackageBo bo) { + SysTenantPackage tenantPackage = MapstructUtils.convert(bo, SysTenantPackage.class); + return baseMapper.updateById(tenantPackage); + } + + /** + * 批量删除租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + boolean exists = tenantMapper.exists(new LambdaQueryWrapper().in(SysTenant::getPackageId, ids)); + if (exists) { + throw new ServiceException("租户套餐已被使用"); + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantServiceImpl.java new file mode 100644 index 0000000..663b83a --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysTenantServiceImpl.java @@ -0,0 +1,498 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.constant.Constants; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.constant.TenantConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.WorkflowService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.redis.utils.CacheUtils; +import com.fuyuanshen.common.tenant.core.TenantEntity; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import com.fuyuanshen.system.domain.*; +import com.fuyuanshen.system.domain.bo.SysTenantBo; +import com.fuyuanshen.system.domain.vo.SysTenantVo; +import com.fuyuanshen.system.mapper.*; +import com.fuyuanshen.system.service.ISysTenantService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 租户Service业务层处理 + * + * @author Michelle.Chung + */ +@RequiredArgsConstructor +@Service +public class SysTenantServiceImpl implements ISysTenantService { + + private final SysTenantMapper baseMapper; + private final SysTenantPackageMapper tenantPackageMapper; + private final SysUserMapper userMapper; + private final SysDeptMapper deptMapper; + private final SysRoleMapper roleMapper; + private final SysRoleMenuMapper roleMenuMapper; + private final SysRoleDeptMapper roleDeptMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysDictTypeMapper dictTypeMapper; + private final SysDictDataMapper dictDataMapper; + private final SysConfigMapper configMapper; + + /** + * 查询租户 + */ + @Override + public SysTenantVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 基于租户ID查询租户 + */ + @Cacheable(cacheNames = CacheNames.SYS_TENANT, key = "#tenantId") + @Override + public SysTenantVo queryByTenantId(String tenantId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysTenant::getTenantId, tenantId)); + } + + /** + * 查询租户列表 + */ + @Override + public TableDataInfo queryPageList(SysTenantBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询租户列表 + */ + @Override + public List queryList(SysTenantBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysTenantBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId()); + lqw.like(StringUtils.isNotBlank(bo.getContactUserName()), SysTenant::getContactUserName, bo.getContactUserName()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), SysTenant::getContactPhone, bo.getContactPhone()); + lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), SysTenant::getCompanyName, bo.getCompanyName()); + lqw.eq(StringUtils.isNotBlank(bo.getLicenseNumber()), SysTenant::getLicenseNumber, bo.getLicenseNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getAddress()), SysTenant::getAddress, bo.getAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getIntro()), SysTenant::getIntro, bo.getIntro()); + lqw.like(StringUtils.isNotBlank(bo.getDomain()), SysTenant::getDomain, bo.getDomain()); + lqw.eq(bo.getPackageId() != null, SysTenant::getPackageId, bo.getPackageId()); + lqw.eq(bo.getExpireTime() != null, SysTenant::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getAccountCount() != null, SysTenant::getAccountCount, bo.getAccountCount()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenant::getStatus, bo.getStatus()); + lqw.orderByAsc(SysTenant::getId); + return lqw; + } + + /** + * 新增租户 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysTenantBo bo) { + SysTenant add = MapstructUtils.convert(bo, SysTenant.class); + + // 获取所有租户编号 + List tenantIds = baseMapper.selectObjs( + new LambdaQueryWrapper().select(SysTenant::getTenantId), x -> { + return Convert.toStr(x); + }); + String tenantId = generateTenantId(tenantIds); + add.setTenantId(tenantId); + boolean flag = baseMapper.insert(add) > 0; + if (!flag) { + throw new ServiceException("创建租户失败"); + } + bo.setId(add.getId()); + + // 根据套餐创建角色 + Long roleId = createTenantRole(tenantId, bo.getPackageId()); + + // 创建部门: 公司名是部门名称 + SysDept dept = new SysDept(); + dept.setTenantId(tenantId); + dept.setDeptName(bo.getCompanyName()); + dept.setParentId(Constants.TOP_PARENT_ID); + dept.setAncestors(Constants.TOP_PARENT_ID.toString()); + deptMapper.insert(dept); + Long deptId = dept.getDeptId(); + + // 角色和部门关联表 + SysRoleDept roleDept = new SysRoleDept(); + roleDept.setRoleId(roleId); + roleDept.setDeptId(deptId); + roleDeptMapper.insert(roleDept); + + // 创建系统用户 + SysUser user = new SysUser(); + user.setTenantId(tenantId); + user.setUserName(bo.getUsername()); + user.setNickName(bo.getUsername()); + user.setPassword(BCrypt.hashpw(bo.getPassword())); + user.setDeptId(deptId); + userMapper.insert(user); + //新增系统用户后,默认当前用户为部门的负责人 + SysDept sd = new SysDept(); + sd.setLeader(user.getUserId()); + sd.setDeptId(deptId); + deptMapper.updateById(sd); + + // 用户和角色关联表 + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(user.getUserId()); + userRole.setRoleId(roleId); + userRoleMapper.insert(userRole); + + String defaultTenantId = TenantConstants.DEFAULT_TENANT_ID; + List dictTypeList = dictTypeMapper.selectList( + new LambdaQueryWrapper().eq(SysDictType::getTenantId, defaultTenantId)); + List dictDataList = dictDataMapper.selectList( + new LambdaQueryWrapper().eq(SysDictData::getTenantId, defaultTenantId)); + for (SysDictType dictType : dictTypeList) { + dictType.setDictId(null); + dictType.setTenantId(tenantId); + dictType.setCreateDept(null); + dictType.setCreateBy(null); + dictType.setCreateTime(null); + dictType.setUpdateBy(null); + dictType.setUpdateTime(null); + } + for (SysDictData dictData : dictDataList) { + dictData.setDictCode(null); + dictData.setTenantId(tenantId); + dictData.setCreateDept(null); + dictData.setCreateBy(null); + dictData.setCreateTime(null); + dictData.setUpdateBy(null); + dictData.setUpdateTime(null); + } + dictTypeMapper.insertBatch(dictTypeList); + dictDataMapper.insertBatch(dictDataList); + + List sysConfigList = configMapper.selectList( + new LambdaQueryWrapper().eq(SysConfig::getTenantId, defaultTenantId)); + for (SysConfig config : sysConfigList) { + config.setConfigId(null); + config.setTenantId(tenantId); + config.setCreateDept(null); + config.setCreateBy(null); + config.setCreateTime(null); + config.setUpdateBy(null); + config.setUpdateTime(null); + } + configMapper.insertBatch(sysConfigList); + + // 未开启工作流不执行下方操作 + if (SpringUtils.getProperty("warm-flow.enabled", Boolean.class, false)) { + WorkflowService workflowService = SpringUtils.getBean(WorkflowService.class); + // 新增租户流程定义 + workflowService.syncDef(tenantId); + } + return true; + } + + /** + * 生成租户id + * + * @param tenantIds 已有租户id列表 + * @return 租户id + */ + private String generateTenantId(List tenantIds) { + // 随机生成6位 + String numbers = RandomUtil.randomNumbers(6); + // 判断是否存在,如果存在则重新生成 + if (tenantIds.contains(numbers)) { + return generateTenantId(tenantIds); + } + return numbers; + } + + /** + * 根据租户菜单创建租户角色 + * + * @param tenantId 租户编号 + * @param packageId 租户套餐id + * @return 角色id + */ + private Long createTenantRole(String tenantId, Long packageId) { + // 获取租户套餐 + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + if (ObjectUtil.isNull(tenantPackage)) { + throw new ServiceException("套餐不存在"); + } + // 获取套餐菜单id + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + + // 创建角色 + SysRole role = new SysRole(); + role.setTenantId(tenantId); + role.setRoleName(TenantConstants.TENANT_ADMIN_ROLE_NAME); + role.setRoleKey(TenantConstants.TENANT_ADMIN_ROLE_KEY); + role.setRoleSort(1); + role.setStatus(SystemConstants.NORMAL); + roleMapper.insert(role); + Long roleId = role.getRoleId(); + + // 创建角色菜单 + List roleMenus = new ArrayList<>(menuIds.size()); + menuIds.forEach(menuId -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(roleId); + roleMenu.setMenuId(menuId); + roleMenus.add(roleMenu); + }); + roleMenuMapper.insertBatch(roleMenus); + + return roleId; + } + + /** + * 修改租户 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId") + @Override + public Boolean updateByBo(SysTenantBo bo) { + SysTenant tenant = MapstructUtils.convert(bo, SysTenant.class); + tenant.setTenantId(null); + tenant.setPackageId(null); + return baseMapper.updateById(tenant) > 0; + } + + /** + * 修改租户状态 + * + * @param bo 租户信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, key = "#bo.tenantId") + @Override + public int updateTenantStatus(SysTenantBo bo) { + SysTenant tenant = new SysTenant(); + tenant.setId(bo.getId()); + tenant.setStatus(bo.getStatus()); + return baseMapper.updateById(tenant); + } + + /** + * 校验租户是否允许操作 + * + * @param tenantId 租户ID + */ + @Override + public void checkTenantAllowed(String tenantId) { + if (ObjectUtil.isNotNull(tenantId) && TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + throw new ServiceException("不允许操作管理租户"); + } + } + + /** + * 批量删除租户 + */ + @CacheEvict(cacheNames = CacheNames.SYS_TENANT, allEntries = true) + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + if (ids.contains(TenantConstants.SUPER_ADMIN_ID)) { + throw new ServiceException("超管租户不能删除"); + } + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 校验企业名称是否唯一 + */ + @Override + public boolean checkCompanyNameUnique(SysTenantBo bo) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysTenant::getCompanyName, bo.getCompanyName()) + .ne(ObjectUtil.isNotNull(bo.getTenantId()), SysTenant::getTenantId, bo.getTenantId())); + return !exist; + } + + /** + * 校验账号余额 + */ + @Override + public boolean checkAccountBalance(String tenantId) { + SysTenantVo tenant = SpringUtils.getAopProxy(this).queryByTenantId(tenantId); + // 如果余额为-1代表不限制 + if (tenant.getAccountCount() == -1) { + return true; + } + Long userNumber = userMapper.selectCount(new LambdaQueryWrapper<>()); + // 如果余额大于0代表还有可用名额 + return tenant.getAccountCount() - userNumber > 0; + } + + /** + * 校验有效期 + */ + @Override + public boolean checkExpireTime(String tenantId) { + SysTenantVo tenant = SpringUtils.getAopProxy(this).queryByTenantId(tenantId); + // 如果未设置过期时间代表不限制 + if (ObjectUtil.isNull(tenant.getExpireTime())) { + return true; + } + // 如果当前时间在过期时间之前则通过 + return new Date().before(tenant.getExpireTime()); + } + + /** + * 同步租户套餐 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean syncTenantPackage(String tenantId, Long packageId) { + SysTenantPackage tenantPackage = tenantPackageMapper.selectById(packageId); + List roles = roleMapper.selectList( + new LambdaQueryWrapper().eq(SysRole::getTenantId, tenantId)); + List roleIds = new ArrayList<>(roles.size() - 1); + List menuIds = StringUtils.splitTo(tenantPackage.getMenuIds(), Convert::toLong); + roles.forEach(item -> { + if (TenantConstants.TENANT_ADMIN_ROLE_KEY.equals(item.getRoleKey())) { + List roleMenus = new ArrayList<>(menuIds.size()); + menuIds.forEach(menuId -> { + SysRoleMenu roleMenu = new SysRoleMenu(); + roleMenu.setRoleId(item.getRoleId()); + roleMenu.setMenuId(menuId); + roleMenus.add(roleMenu); + }); + roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, item.getRoleId())); + roleMenuMapper.insertBatch(roleMenus); + } else { + roleIds.add(item.getRoleId()); + } + }); + if (!roleIds.isEmpty()) { + roleMenuMapper.delete( + new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, roleIds).notIn(!menuIds.isEmpty(), SysRoleMenu::getMenuId, menuIds)); + } + return true; + } + + /** + * 同步租户字典 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void syncTenantDict() { + // 查询超管 所有字典数据 + List dictTypeList = new ArrayList<>(); + List dictDataList = new ArrayList<>(); + TenantHelper.ignore(() -> { + dictTypeList.addAll(dictTypeMapper.selectList()); + dictDataList.addAll(dictDataMapper.selectList()); + }); + Map> typeMap = StreamUtils.groupByKey(dictTypeList, TenantEntity::getTenantId); + Map>> typeDataMap = StreamUtils.groupBy2Key( + dictDataList, TenantEntity::getTenantId, SysDictData::getDictType); + // 管理租户字典数据 + List defaultTypeMap = typeMap.get(TenantConstants.DEFAULT_TENANT_ID); + Map> defaultTypeDataMap = typeDataMap.get(TenantConstants.DEFAULT_TENANT_ID); + + // 获取所有租户编号 + List tenantIds = baseMapper.selectObjs( + new LambdaQueryWrapper().select(SysTenant::getTenantId) + .eq(SysTenant::getStatus, SystemConstants.NORMAL), x -> { + return Convert.toStr(x); + }); + List saveTypeList = new ArrayList<>(); + List saveDataList = new ArrayList<>(); + Set set = new HashSet<>(); + for (String tenantId : tenantIds) { + if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { + continue; + } + for (SysDictType dictType : defaultTypeMap) { + List typeList = StreamUtils.toList(typeMap.get(tenantId), SysDictType::getDictType); + List dataList = defaultTypeDataMap.get(dictType.getDictType()); + if (typeList.contains(dictType.getDictType())) { + List dataListTenant = typeDataMap.get(tenantId).get(dictType.getDictType()); + Map map = StreamUtils.toIdentityMap(dataListTenant, SysDictData::getDictValue); + for (SysDictData dictData : dataList) { + if (!map.containsKey(dictData.getDictValue())) { + SysDictData data = BeanUtil.toBean(dictData, SysDictData.class); + // 设置字典编码为 null + data.setDictCode(null); + data.setTenantId(tenantId); + data.setCreateTime(null); + data.setUpdateTime(null); + data.setCreateDept(null); + data.setCreateBy(null); + data.setUpdateBy(null); + set.add(tenantId); + saveDataList.add(data); + } + } + } else { + SysDictType type = BeanUtil.toBean(dictType, SysDictType.class); + type.setDictId(null); + type.setTenantId(tenantId); + type.setCreateTime(null); + type.setUpdateTime(null); + set.add(tenantId); + saveTypeList.add(type); + if (CollUtil.isNotEmpty(dataList)) { + // 筛选出 dictType 对应的 data + for (SysDictData dictData : dataList) { + SysDictData data = BeanUtil.toBean(dictData, SysDictData.class); + // 设置字典编码为 null + data.setDictCode(null); + data.setTenantId(tenantId); + data.setCreateTime(null); + data.setUpdateTime(null); + data.setCreateDept(null); + data.setCreateBy(null); + data.setUpdateBy(null); + set.add(tenantId); + saveDataList.add(data); + } + } + } + } + } + TenantHelper.ignore(() -> { + if (CollUtil.isNotEmpty(saveTypeList)) { + dictTypeMapper.insertBatch(saveTypeList); + } + if (CollUtil.isNotEmpty(saveDataList)) { + dictDataMapper.insertBatch(saveDataList); + } + }); + for (String tenantId : set) { + TenantHelper.dynamic(tenantId, () -> CacheUtils.clear(CacheNames.SYS_DICT)); + } + } + +} diff --git a/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysUserServiceImpl.java b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..d4b1a08 --- /dev/null +++ b/fys-modules/fys-system/src/main/java/com/fuyuanshen/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,798 @@ +package com.fuyuanshen.system.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.constant.CacheNames; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.UserService; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.system.domain.*; +import com.fuyuanshen.system.domain.bo.SysUserBo; +import com.fuyuanshen.system.domain.vo.SysPostVo; +import com.fuyuanshen.system.domain.vo.SysRoleVo; +import com.fuyuanshen.system.domain.vo.SysUserExportVo; +import com.fuyuanshen.system.domain.vo.SysUserVo; +import com.fuyuanshen.system.mapper.*; +import com.fuyuanshen.system.service.ISysUserService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户 业务层处理 + * + * @author Lion Li + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysUserServiceImpl implements ISysUserService, UserService { + + private final SysUserMapper baseMapper; + private final SysDeptMapper deptMapper; + private final SysRoleMapper roleMapper; + private final SysPostMapper postMapper; + private final SysUserRoleMapper userRoleMapper; + private final SysUserPostMapper userPostMapper; + + @Override + public TableDataInfo selectPageUserList(SysUserBo user, PageQuery pageQuery) { + Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public List selectUserExportList(SysUserBo user) { + return baseMapper.selectUserExportList(this.buildQueryWrapper(user)); + } + + private Wrapper buildQueryWrapper(SysUserBo user) { + Map params = user.getParams(); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", SystemConstants.NORMAL) + .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId()) + .in(StringUtils.isNotBlank(user.getUserIds()), "u.user_id", StringUtils.splitTo(user.getUserIds(), Convert::toLong)) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getNickName()), "u.nick_name", user.getNickName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .between(params.get("beginTime") != null && params.get("endTime") != null, + "u.create_time", params.get("beginTime"), params.get("endTime")) + .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { + List deptList = deptMapper.selectListByParentId(user.getDeptId()); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + }).orderByAsc("u.user_id"); + if (StringUtils.isNotBlank(user.getExcludeUserIds())) { + wrapper.notIn("u.user_id", StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); + } + return wrapper; + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectAllocatedList(SysUserBo user, PageQuery pageQuery) { + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", SystemConstants.NORMAL) + .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId()) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); + Page page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + public TableDataInfo selectUnallocatedList(SysUserBo user, PageQuery pageQuery) { + List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); + QueryWrapper wrapper = Wrappers.query(); + wrapper.eq("u.del_flag", SystemConstants.NORMAL) + .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id")) + .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds) + .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName()) + .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber()) + .orderByAsc("u.user_id"); + Page page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper); + return TableDataInfo.build(page); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserByUserName(String userName) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName)); + } + + /** + * 通过手机号查询用户 + * + * @param phonenumber 手机号 + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserByPhonenumber(String phonenumber) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, phonenumber)); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUserVo selectUserById(Long userId) { + SysUserVo user = baseMapper.selectVoById(userId); + if (ObjectUtil.isNull(user)) { + return user; + } + user.setRoles(roleMapper.selectRolesByUserId(user.getUserId())); + return user; + } + + /** + * 通过用户ID串查询用户 + * + * @param userIds 用户ID串 + * @param deptId 部门id + * @return 用户列表信息 + */ + @Override + public List selectUserByIds(List userIds, Long deptId) { + return baseMapper.selectUserList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) + .eq(SysUser::getStatus, SystemConstants.NORMAL) + .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId) + .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); + } + + /** + * 查询用户所属角色组 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + public String selectUserRoleGroup(Long userId) { + List list = roleMapper.selectRolesByUserId(userId); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysRoleVo::getRoleName); + } + + /** + * 查询用户所属岗位组 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + public String selectUserPostGroup(Long userId) { + List list = postMapper.selectPostsByUserId(userId); + if (CollUtil.isEmpty(list)) { + return StringUtils.EMPTY; + } + return StreamUtils.join(list, SysPostVo::getPostName); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getUserName, user.getUserName()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + */ + @Override + public boolean checkPhoneUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, user.getPhonenumber()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + */ + @Override + public boolean checkEmailUnique(SysUserBo user) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(SysUser::getEmail, user.getEmail()) + .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + return !exist; + } + + /** + * 校验用户是否允许操作 + * + * @param userId 用户ID + */ + @Override + public void checkUserAllowed(Long userId) { + if (ObjectUtil.isNotNull(userId) && LoginHelper.isSuperAdmin(userId)) { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) { + if (ObjectUtil.isNull(userId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + if (baseMapper.countUserById(userId) == 0) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser(SysUserBo user) { + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + // 新增用户信息 + int rows = baseMapper.insert(sysUser); + user.setUserId(sysUser.getUserId()); + // 新增用户岗位关联 + insertUserPost(user, false); + // 新增用户与角色管理 + insertUserRole(user, false); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUserBo user, String tenantId) { + user.setCreateBy(0L); + user.setUpdateBy(0L); + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + sysUser.setTenantId(tenantId); + return baseMapper.insert(sysUser) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @CacheEvict(cacheNames = CacheNames.SYS_NICKNAME, key = "#user.userId") + @Transactional(rollbackFor = Exception.class) + public int updateUser(SysUserBo user) { + // 新增用户与角色管理 + insertUserRole(user, true); + // 新增用户与岗位管理 + insertUserPost(user, true); + SysUser sysUser = MapstructUtils.convert(user, SysUser.class); + // 防止错误更新后导致的数据误删除 + int flag = baseMapper.updateById(sysUser); + if (flag < 1) { + throw new ServiceException("修改用户" + user.getUserName() + "信息失败"); + } + return flag; + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void insertUserAuth(Long userId, Long[] roleIds) { + insertUserRole(userId, roleIds, true); + } + + /** + * 修改用户状态 + * + * @param userId 用户ID + * @param status 帐号状态 + * @return 结果 + */ + @Override + public int updateUserStatus(Long userId, String status) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId)); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @CacheEvict(cacheNames = CacheNames.SYS_NICKNAME, key = "#user.userId") + @Override + public int updateUserProfile(SysUserBo user) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhonenumber, user.getPhonenumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getSex, user.getSex()) + .eq(SysUser::getUserId, user.getUserId())); + } + + /** + * 修改用户头像 + * + * @param userId 用户ID + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(Long userId, Long avatar) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId)) > 0; + } + + /** + * 重置用户密码 + * + * @param userId 用户ID + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(Long userId, String password) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId)); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + * @param clear 清除已存在的关联数据 + */ + public void insertUserRole(SysUserBo user, boolean clear) { + this.insertUserRole(user.getUserId(), user.getRoleIds(), clear); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + * @param clear 清除已存在的关联数据 + */ + private void insertUserPost(SysUserBo user, boolean clear) { + Long[] posts = user.getPostIds(); + if (ArrayUtil.isNotEmpty(posts)) { + if (clear) { + // 删除用户与岗位关联 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, user.getUserId())); + } + // 新增用户与岗位管理 + List list = StreamUtils.toList(List.of(posts), postId -> { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + return up; + }); + userPostMapper.insertBatch(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + * @param clear 清除已存在的关联数据 + */ + private void insertUserRole(Long userId, Long[] roleIds, boolean clear) { + if (ArrayUtil.isNotEmpty(roleIds)) { + List roleList = new ArrayList<>(List.of(roleIds)); + if (!LoginHelper.isSuperAdmin(userId)) { + roleList.remove(SystemConstants.SUPER_ADMIN_ID); + } + // 判断是否具有此角色的操作权限 + List roles = roleMapper.selectRoleList( + new QueryWrapper().in("r.role_id", roleList)); + if (CollUtil.isEmpty(roles)) { + throw new ServiceException("没有权限访问角色的数据"); + } + if (clear) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + } + // 新增用户与角色管理 + List list = StreamUtils.toList(roleList, roleId -> { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + return ur; + }); + userRoleMapper.insertBatch(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById(Long userId) { + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); + // 防止更新失败导致的数据删除 + int flag = baseMapper.deleteById(userId); + if (flag < 1) { + throw new ServiceException("删除用户失败!"); + } + return flag; + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds(Long[] userIds) { + for (Long userId : userIds) { + checkUserAllowed(userId); + checkUserDataScope(userId); + } + List ids = List.of(userIds); + // 删除用户与角色关联 + userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); + // 删除用户与岗位表 + userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); + // 防止更新失败导致的数据删除 + int flag = baseMapper.deleteByIds(ids); + if (flag < 1) { + throw new ServiceException("删除用户失败!"); + } + return flag; + } + + /** + * 通过部门id查询当前部门所有用户 + * + * @param deptId 部门ID + * @return 用户信息集合信息 + */ + @Override + public List selectUserListByDept(Long deptId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysUser::getDeptId, deptId); + lqw.orderByAsc(SysUser::getUserId); + return baseMapper.selectVoList(lqw); + } + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ + @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") + @Override + public String selectUserNameById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + return ObjectUtils.notNullGetter(sysUser, SysUser::getUserName); + } + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ + @Override + @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") + public String selectNicknameById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); + return ObjectUtils.notNullGetter(sysUser, SysUser::getNickName); + } + + /** + * 通过用户ID查询用户账户 + * + * @param userIds 用户ID 多个用逗号隔开 + * @return 用户账户 + */ + @Override + public String selectNicknameByIds(String userIds) { + List list = new ArrayList<>(); + for (Long id : StringUtils.splitTo(userIds, Convert::toLong)) { + String nickname = SpringUtils.getAopProxy(this).selectNicknameById(id); + if (StringUtils.isNotBlank(nickname)) { + list.add(nickname); + } + } + return String.join(StringUtils.SEPARATOR, list); + } + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + @Override + public String selectPhonenumberById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getPhonenumber).eq(SysUser::getUserId, userId)); + return ObjectUtils.notNullGetter(sysUser, SysUser::getPhonenumber); + } + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + @Override + public String selectEmailById(Long userId) { + SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() + .select(SysUser::getEmail).eq(SysUser::getUserId, userId)); + return ObjectUtils.notNullGetter(sysUser, SysUser::getEmail); + } + + /** + * 通过用户ID查询用户列表 + * + * @param userIds 用户ids + * @return 用户列表 + */ + @Override + public List selectListByIds(List userIds) { + if (CollUtil.isEmpty(userIds)) { + return List.of(); + } + List list = baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber) + .eq(SysUser::getStatus, SystemConstants.NORMAL) + .in(SysUser::getUserId, userIds)); + return BeanUtil.copyToList(list, UserDTO.class); + } + + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + @Override + public List selectUserIdsByRoleIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return List.of(); + } + List userRoles = userRoleMapper.selectList( + new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + return StreamUtils.toList(userRoles, SysUserRole::getUserId); + } + + /** + * 通过角色ID查询用户 + * + * @param roleIds 角色ids + * @return 用户 + */ + @Override + public List selectUsersByRoleIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return List.of(); + } + + // 通过角色ID获取用户角色信息 + List userRoles = userRoleMapper.selectList( + new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + + // 获取用户ID列表 + Set userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId); + + return selectListByIds(new ArrayList<>(userIds)); + } + + /** + * 通过部门ID查询用户 + * + * @param deptIds 部门ids + * @return 用户 + */ + @Override + public List selectUsersByDeptIds(List deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return List.of(); + } + List list = baseMapper.selectVoList(new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhonenumber) + .eq(SysUser::getStatus, SystemConstants.NORMAL) + .in(SysUser::getDeptId, deptIds)); + return BeanUtil.copyToList(list, UserDTO.class); + } + + /** + * 通过岗位ID查询用户 + * + * @param postIds 岗位ids + * @return 用户 + */ + @Override + public List selectUsersByPostIds(List postIds) { + if (CollUtil.isEmpty(postIds)) { + return List.of(); + } + + // 通过岗位ID获取用户岗位信息 + List userPosts = userPostMapper.selectList( + new LambdaQueryWrapper().in(SysUserPost::getPostId, postIds)); + + // 获取用户ID列表 + Set userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId); + + return selectListByIds(new ArrayList<>(userIds)); + } + + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + @Override + public Map selectUserNamesByIds(List userIds) { + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyMap(); + } + return baseMapper.selectList( + new LambdaQueryWrapper() + .select(SysUser::getUserId, SysUser::getNickName) + .in(SysUser::getUserId, userIds) + ).stream() + .collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName)); + } + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + @Override + public Map selectRoleNamesByIds(List roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptyMap(); + } + return roleMapper.selectList( + new LambdaQueryWrapper() + .select(SysRole::getRoleId, SysRole::getRoleName) + .in(SysRole::getRoleId, roleIds) + ).stream() + .collect(Collectors.toMap(SysRole::getRoleId, SysRole::getRoleName)); + } + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + @Override + public Map selectDeptNamesByIds(List deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyMap(); + } + return deptMapper.selectList( + new LambdaQueryWrapper() + .select(SysDept::getDeptId, SysDept::getDeptName) + .in(SysDept::getDeptId, deptIds) + ).stream() + .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName)); + } + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + @Override + public Map selectPostNamesByIds(List postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyMap(); + } + return postMapper.selectList( + new LambdaQueryWrapper() + .select(SysPost::getPostId, SysPost::getPostName) + .in(SysPost::getPostId, postIds) + ).stream() + .collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName)); + } + +} diff --git a/fys-modules/fys-system/src/main/resources/mapper/package-info.md b/fys-modules/fys-system/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysClientMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysClientMapper.xml new file mode 100644 index 0000000..9b9727f --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysClientMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysConfigMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..08d7ef5 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysDeptMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..e34b8f1 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..1455c8a --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..6558dbd --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..e60f97e --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysMenuMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..917e466 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..0690691 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..319355b --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysOssConfigMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysOssConfigMapper.xml new file mode 100644 index 0000000..fcf3bc1 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysOssConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysOssMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysOssMapper.xml new file mode 100644 index 0000000..16ad66b --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysOssMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysPostMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..1f535c7 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..2753663 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..361c8bc --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + select distinct r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.menu_check_strictly, + r.dept_check_strictly, + r.status, + r.del_flag, + r.create_time, + r.remark + from sys_role r + left join sys_user_role sur on sur.role_id = r.role_id + left join sys_user u on u.user_id = sur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..367f5bc --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysSocialMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysSocialMapper.xml new file mode 100644 index 0000000..b574d4b --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysSocialMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantMapper.xml new file mode 100644 index 0000000..b05d145 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml new file mode 100644 index 0000000..3f8b9cd --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysTenantPackageMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysUserMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..2dcb316 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..efd543e --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/fys-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..14d9e35 --- /dev/null +++ b/fys-modules/fys-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/fys-modules/fys-workflow/pom.xml b/fys-modules/fys-workflow/pom.xml new file mode 100644 index 0000000..059e41b --- /dev/null +++ b/fys-modules/fys-workflow/pom.xml @@ -0,0 +1,84 @@ + + + + com.fuyuanshen + fys-modules + ${revision} + ../pom.xml + + 4.0.0 + jar + fys-workflow + + + 工作流模块 + + + + + + com.fuyuanshen + fys-common-sse + + + + com.fuyuanshen + fys-common-doc + + + + com.fuyuanshen + fys-common-mail + + + + com.fuyuanshen + fys-common-sms + + + + com.fuyuanshen + fys-common-mybatis + + + com.fuyuanshen + fys-common-web + + + com.fuyuanshen + fys-common-log + + + com.fuyuanshen + fys-common-idempotent + + + com.fuyuanshen + fys-common-excel + + + com.fuyuanshen + fys-common-translation + + + com.fuyuanshen + fys-common-tenant + + + com.fuyuanshen + fys-common-security + + + org.dromara.warm + warm-flow-mybatis-plus-sb3-starter + + + org.dromara.warm + warm-flow-plugin-ui-sb-web + + + + + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/ConditionalOnEnable.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/ConditionalOnEnable.java new file mode 100644 index 0000000..b0d4288 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/ConditionalOnEnable.java @@ -0,0 +1,29 @@ +package com.fuyuanshen.workflow.common; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义条件注解,用于基于配置启用或禁用特定功能 + *

+ * 该注解只会在配置文件中 `warm-flow.enabled=true` 时,标注了此注解的类或方法才会被 Spring 容器加载 + *

+ * 示例配置: + *

+ * warm-flow:
+ *   enabled: true  # 设置为 true 时,启用工作流功能
+ * 
+ *

+ * 使用此注解时,可以动态控制工作流功能是否启用,而不需要修改代码逻辑 + * + * @author Lion Li + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true") +public @interface ConditionalOnEnable { +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/constant/FlowConstant.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/constant/FlowConstant.java new file mode 100644 index 0000000..2f20ad7 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/constant/FlowConstant.java @@ -0,0 +1,76 @@ +package com.fuyuanshen.workflow.common.constant; + + +/** + * 工作流常量 + * + * @author may + */ +public interface FlowConstant { + + /** + * 流程发起人 + */ + String INITIATOR = "initiator"; + + /** + * 业务id + */ + String BUSINESS_ID = "businessId"; + + /** + * 委托 + */ + String DELEGATE_TASK = "delegateTask"; + + /** + * 转办 + */ + String TRANSFER_TASK = "transferTask"; + + /** + * 加签 + */ + String ADD_SIGNATURE = "addSignature"; + + /** + * 减签 + */ + String REDUCTION_SIGNATURE = "reductionSignature"; + + /** + * 流程分类Id转名称 + */ + String CATEGORY_ID_TO_NAME = "category_id_to_name"; + + /** + * 流程分类名称 + */ + String FLOW_CATEGORY_NAME = "flow_category_name#30d"; + + /** + * 默认租户OA申请分类id + */ + Long FLOW_CATEGORY_ID = 100L; + + /** + * 是否为申请人提交常量 + */ + String SUBMIT = "submit"; + + /** + * 抄送常量 + */ + String FLOW_COPY_LIST = "flowCopyList"; + + /** + * 消息类型常量 + */ + String MESSAGE_TYPE = "messageType"; + + /** + * 消息通知常量 + */ + String MESSAGE_NOTICE = "messageNotice"; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/ButtonPermissionEnum.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/ButtonPermissionEnum.java new file mode 100644 index 0000000..d1bc64d --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/ButtonPermissionEnum.java @@ -0,0 +1,65 @@ +package com.fuyuanshen.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 按钮权限枚举 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum ButtonPermissionEnum implements NodeExtEnum { + + /** + * 是否弹窗选人 + */ + POP("是否弹窗选人", "pop", false), + + /** + * 是否能委托 + */ + TRUST("是否能委托", "trust", false), + + /** + * 是否能转办 + */ + TRANSFER("是否能转办", "transfer", false), + + /** + * 是否能抄送 + */ + COPY("是否能抄送", "copy", false), + + /** + * 是否显示退回 + */ + BACK("是否显示退回", "back", true), + + /** + * 是否能加签 + */ + ADD_SIGN("是否能加签", "addSign", false), + + /** + * 是否能减签 + */ + SUB_SIGN("是否能减签", "subSign", false), + + /** + * 是否能终止 + */ + TERMINATION("是否能终止", "termination", true), + + /** + * 是否能上传附件 + */ + FILE("是否能上传附件", "file", true); + + private final String label; + private final String value; + private final boolean selected; + +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/MessageTypeEnum.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/MessageTypeEnum.java new file mode 100644 index 0000000..5339d46 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/MessageTypeEnum.java @@ -0,0 +1,53 @@ +package com.fuyuanshen.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 消息类型枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum MessageTypeEnum { + + /** + * 站内信 + */ + SYSTEM_MESSAGE("1", "站内信"), + + /** + * 邮箱 + */ + EMAIL_MESSAGE("2", "邮箱"), + + /** + * 短信 + */ + SMS_MESSAGE("3", "短信"); + + private final String code; + + private final String desc; + + private static final Map MESSAGE_TYPE_ENUM_MAP = Arrays.stream(values()) + .collect(Collectors.toConcurrentMap(MessageTypeEnum::getCode, Function.identity())); + + /** + * 根据消息类型 code 获取 MessageTypeEnum + * + * @param code 消息类型code + * @return MessageTypeEnum + */ + public static MessageTypeEnum getByCode(String code) { + return MESSAGE_TYPE_ENUM_MAP.getOrDefault(code, null); + } + +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/NodeExtEnum.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/NodeExtEnum.java new file mode 100644 index 0000000..599a15f --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/NodeExtEnum.java @@ -0,0 +1,32 @@ +package com.fuyuanshen.workflow.common.enums; + +/** + * 节点扩展属性枚举 + * + * @author AprilWind + */ +public interface NodeExtEnum { + + /** + * 选项label + * + * @return 选项label + */ + String getLabel(); + + /** + * 选项值 + * + * @return 选项值 + */ + String getValue(); + + /** + * 是否默认选中 + * + * @return 是否默认选中 + */ + boolean isSelected(); + +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeEnum.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeEnum.java new file mode 100644 index 0000000..3160471 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeEnum.java @@ -0,0 +1,109 @@ +package com.fuyuanshen.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import com.fuyuanshen.common.core.exception.ServiceException; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 任务分配人枚举 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum TaskAssigneeEnum { + + /** + * 用户 + */ + USER("用户", ""), + + /** + * 角色 + */ + ROLE("角色", "role:"), + + /** + * 部门 + */ + DEPT("部门", "dept:"), + + /** + * 岗位 + */ + POST("岗位", "post:"); + + private final String desc; + private final String code; + + /** + * 根据描述获取对应的枚举类型 + *

+ * 通过传入描述,查找并返回匹配的枚举项。如果未找到匹配项,会抛出 {@link ServiceException}。 + *

+ * + * @param desc 描述,用于匹配对应的枚举项 + * @return TaskAssigneeEnum 返回对应的枚举类型 + * @throws ServiceException 如果未找到匹配的枚举项 + */ + public static TaskAssigneeEnum fromDesc(String desc) { + for (TaskAssigneeEnum type : values()) { + if (type.getDesc().equals(desc)) { + return type; + } + } + throw new ServiceException("未知的办理人类型: " + desc); + } + + /** + * 根据代码获取对应的枚举类型 + *

+ * 通过传入代码,查找并返回匹配的枚举项。如果未找到匹配项,会抛出 {@link ServiceException}。 + *

+ * + * @param code 代码,用于匹配对应的枚举项 + * @return TaskAssigneeEnum 返回对应的枚举类型 + * @throws IllegalArgumentException 如果未找到匹配的枚举项 + */ + public static TaskAssigneeEnum fromCode(String code) { + for (TaskAssigneeEnum type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + throw new ServiceException("未知的办理人类型代码: " + code); + } + + /** + * 获取所有办理人类型的描述列表 + *

+ * 获取当前枚举类所有项的描述字段列表,通常用于展示选择项。 + *

+ * + * @return List 返回所有办理人类型的描述列表 + */ + public static List getAssigneeTypeList() { + return Arrays.stream(values()) + .map(TaskAssigneeEnum::getDesc) + .collect(Collectors.toList()); + } + + /** + * 获取所有办理人类型的代码列表 + *

+ * 获取当前枚举类所有项的代码字段列表,通常用于程序内部逻辑的判断。 + *

+ * + * @return List 返回所有办理人类型的代码列表 + */ + public static List getAssigneeCodeList() { + return Arrays.stream(values()) + .map(TaskAssigneeEnum::getCode) + .collect(Collectors.toList()); + } +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeType.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeType.java new file mode 100644 index 0000000..6e84e68 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskAssigneeType.java @@ -0,0 +1,49 @@ +package com.fuyuanshen.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 人员类型 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum TaskAssigneeType { + + /** + * 待办任务的审批人权限 + *

该权限表示用户是待办任务的审批人,负责审核任务的执行情况。

+ */ + APPROVER("1", "待办任务的审批人权限"), + + /** + * 待办任务的转办人权限 + *

该权限表示用户是待办任务的转办人,负责将任务分配给其他人员。

+ */ + TRANSFER("2", "待办任务的转办人权限"), + + /** + * 待办任务的委托人权限 + *

该权限表示用户是待办任务的委托人,能够委托其他人代为处理任务。

+ */ + DELEGATE("3", "待办任务的委托人权限"), + + /** + * 待办任务的抄送人权限 + *

该权限表示用户是待办任务的抄送人,仅接收任务信息的通知,不参与任务的审批或处理。

+ */ + COPY("4", "待办任务的抄送人权限"); + + /** + * 类型 + */ + private final String code; + + /** + * 描述 + */ + private final String description; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskStatusEnum.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskStatusEnum.java new file mode 100644 index 0000000..a97d4d8 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/common/enums/TaskStatusEnum.java @@ -0,0 +1,104 @@ +package com.fuyuanshen.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum TaskStatusEnum { + + /** + * 撤销 + */ + CANCEL("cancel", "撤销"), + + /** + * 通过 + */ + PASS("pass", "通过"), + + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + + /** + * 作废 + */ + INVALID("invalid", "作废"), + + /** + * 退回 + */ + BACK("back", "退回"), + + /** + * 终止 + */ + TERMINATION("termination", "终止"), + + /** + * 转办 + */ + TRANSFER("transfer", "转办"), + + /** + * 委托 + */ + DEPUTE("depute", "委托"), + + /** + * 抄送 + */ + COPY("copy", "抄送"), + + /** + * 加签 + */ + SIGN("sign", "加签"), + + /** + * 减签 + */ + SIGN_OFF("sign_off", "减签"), + + /** + * 超时 + */ + TIMEOUT("timeout", "超时"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + private static final Map STATUS_DESC_MAP = Arrays.stream(values()) + .collect(Collectors.toConcurrentMap(TaskStatusEnum::getStatus, TaskStatusEnum::getDesc)); + + /** + * 任务业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + // 从缓存中直接获取描述 + return STATUS_DESC_MAP.getOrDefault(status, StrUtil.EMPTY); + } + +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/config/WarmFlowConfig.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/config/WarmFlowConfig.java new file mode 100644 index 0000000..580f5c2 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/config/WarmFlowConfig.java @@ -0,0 +1,16 @@ +package com.fuyuanshen.workflow.config; + +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import org.springframework.context.annotation.Configuration; + +/** + * warmFlow配置 + * + * @author may + */ +@ConditionalOnEnable +@Configuration +public class WarmFlowConfig { + +} + diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwCategoryController.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwCategoryController.java new file mode 100644 index 0000000..51336ab --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwCategoryController.java @@ -0,0 +1,136 @@ +package com.fuyuanshen.workflow.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.hutool.core.lang.tree.Tree; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.domain.bo.FlowCategoryBo; +import com.fuyuanshen.workflow.domain.vo.FlowCategoryVo; +import com.fuyuanshen.workflow.service.IFlwCategoryService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 流程分类 + * + * @author may + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/category") +public class FlwCategoryController extends BaseController { + + private final IFlwCategoryService flwCategoryService; + + /** + * 查询流程分类列表 + */ + @SaCheckPermission("workflow:category:list") + @GetMapping("/list") + public R> list(FlowCategoryBo bo) { + List list = flwCategoryService.queryList(bo); + return R.ok(list); + } + + /** + * 导出流程分类列表 + */ + @SaCheckPermission("workflow:category:export") + @Log(title = "流程分类", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(FlowCategoryBo bo, HttpServletResponse response) { + List list = flwCategoryService.queryList(bo); + ExcelUtil.exportExcel(list, "流程分类", FlowCategoryVo.class, response); + } + + /** + * 获取流程分类详细信息 + * + * @param categoryId 主键 + */ + @SaCheckPermission("workflow:category:query") + @GetMapping("/{categoryId}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long categoryId) { + flwCategoryService.checkCategoryDataScope(categoryId); + return R.ok(flwCategoryService.queryById(categoryId)); + } + + /** + * 新增流程分类 + */ + @SaCheckPermission("workflow:category:add") + @Log(title = "流程分类", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody FlowCategoryBo category) { + if (!flwCategoryService.checkCategoryNameUnique(category)) { + return R.fail("新增流程分类'" + category.getCategoryName() + "'失败,流程分类名称已存在"); + } + return toAjax(flwCategoryService.insertByBo(category)); + } + + /** + * 修改流程分类 + */ + @SaCheckPermission("workflow:category:edit") + @Log(title = "流程分类", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody FlowCategoryBo category) { + Long categoryId = category.getCategoryId(); + flwCategoryService.checkCategoryDataScope(categoryId); + if (!flwCategoryService.checkCategoryNameUnique(category)) { + return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,流程分类名称已存在"); + } else if (category.getParentId().equals(categoryId)) { + return R.fail("修改流程分类'" + category.getCategoryName() + "'失败,上级流程分类不能是自己"); + } + return toAjax(flwCategoryService.updateByBo(category)); + } + + /** + * 删除流程分类 + * + * @param categoryId 主键 + */ + @SaCheckPermission("workflow:category:remove") + @Log(title = "流程分类", businessType = BusinessType.DELETE) + @DeleteMapping("/{categoryId}") + public R remove(@PathVariable Long categoryId) { + if (FlowConstant.FLOW_CATEGORY_ID.equals(categoryId)) { + return R.warn("默认流程分类,不允许删除"); + } + if (flwCategoryService.hasChildByCategoryId(categoryId)) { + return R.warn("存在下级流程分类,不允许删除"); + } + if (flwCategoryService.checkCategoryExistDefinition(categoryId)) { + return R.warn("流程分类存在流程定义,不允许删除"); + } + return toAjax(flwCategoryService.deleteWithValidById(categoryId)); + } + + /** + * 获取流程分类树列表 + * + * @param categoryBo 流程分类 + */ + @GetMapping("/categoryTree") + public R>> categoryTree(FlowCategoryBo categoryBo) { + return R.ok(flwCategoryService.selectCategoryTreeList(categoryBo)); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwDefinitionController.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwDefinitionController.java new file mode 100644 index 0000000..f5bc64d --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwDefinitionController.java @@ -0,0 +1,194 @@ +package com.fuyuanshen.workflow.controller; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.vo.FlowDefinitionVo; +import com.fuyuanshen.workflow.service.IFlwDefinitionService; +import org.dromara.warm.flow.core.entity.Definition; +import org.dromara.warm.flow.core.service.DefService; +import org.dromara.warm.flow.orm.entity.FlowDefinition; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +/** + * 流程定义管理 控制层 + * + * @author may + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/definition") +public class FlwDefinitionController extends BaseController { + + private final DefService defService; + private final IFlwDefinitionService flwDefinitionService; + + /** + * 查询流程定义列表 + * + * @param flowDefinition 参数 + * @param pageQuery 分页 + */ + @GetMapping("/list") + public TableDataInfo list(FlowDefinition flowDefinition, PageQuery pageQuery) { + return flwDefinitionService.queryList(flowDefinition, pageQuery); + } + + /** + * 查询未发布的流程定义列表 + * + * @param flowDefinition 参数 + * @param pageQuery 分页 + */ + @GetMapping("/unPublishList") + public TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) { + return flwDefinitionService.unPublishList(flowDefinition, pageQuery); + } + + /** + * 获取流程定义详细信息 + * + * @param id 流程定义id + */ + @GetMapping(value = "/{id}") + public R getInfo(@PathVariable Long id) { + return R.ok(defService.getById(id)); + } + + /** + * 新增流程定义 + * + * @param flowDefinition 参数 + */ + @Log(title = "流程定义", businessType = BusinessType.INSERT) + @PostMapping + @RepeatSubmit() + @Transactional(rollbackFor = Exception.class) + public R add(@RequestBody FlowDefinition flowDefinition) { + return R.ok(defService.checkAndSave(flowDefinition)); + } + + /** + * 修改流程定义 + * + * @param flowDefinition 参数 + */ + @Log(title = "流程定义", businessType = BusinessType.UPDATE) + @PutMapping + @RepeatSubmit() + @Transactional(rollbackFor = Exception.class) + public R edit(@RequestBody FlowDefinition flowDefinition) { + return R.ok(defService.updateById(flowDefinition)); + } + + /** + * 发布流程定义 + * + * @param id 流程定义id + */ + @Log(title = "流程定义", businessType = BusinessType.INSERT) + @PutMapping("/publish/{id}") + @RepeatSubmit() + public R publish(@PathVariable Long id) { + return R.ok(flwDefinitionService.publish(id)); + } + + /** + * 取消发布流程定义 + * + * @param id 流程定义id + */ + @Log(title = "流程定义", businessType = BusinessType.INSERT) + @PutMapping("/unPublish/{id}") + @RepeatSubmit() + @Transactional(rollbackFor = Exception.class) + public R unPublish(@PathVariable Long id) { + return R.ok(defService.unPublish(id)); + } + + /** + * 删除流程定义 + */ + @Log(title = "流程定义", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@PathVariable List ids) { + return toAjax(flwDefinitionService.removeDef(ids)); + } + + /** + * 复制流程定义 + * + * @param id 流程定义id + */ + @Log(title = "流程定义", businessType = BusinessType.INSERT) + @PostMapping("/copy/{id}") + @RepeatSubmit() + @Transactional(rollbackFor = Exception.class) + public R copy(@PathVariable Long id) { + return R.ok(defService.copyDef(id)); + } + + /** + * 导入流程定义 + * + * @param file 文件 + * @param category 分类 + */ + @Log(title = "流程定义", businessType = BusinessType.IMPORT) + @PostMapping("/importDef") + public R importDef(MultipartFile file, String category) { + return R.ok(flwDefinitionService.importJson(file, category)); + } + + /** + * 导出流程定义 + * + * @param id 流程定义id + * @param response 响应 + * @throws IOException 异常 + */ + @Log(title = "流程定义", businessType = BusinessType.EXPORT) + @PostMapping("/exportDef/{id}") + public void exportDef(@PathVariable Long id, HttpServletResponse response) throws IOException { + flwDefinitionService.exportDef(id, response); + } + + /** + * 获取流程定义JSON字符串 + * + * @param id 流程定义id + */ + @GetMapping("/xmlString/{id}") + public R xmlString(@PathVariable Long id) { + return R.ok("操作成功", defService.exportJson(id)); + } + + /** + * 激活/挂起流程定义 + * + * @param id 流程定义id + * @param active 激活/挂起 + */ + @RepeatSubmit() + @PutMapping("/active/{id}") + @Transactional(rollbackFor = Exception.class) + public R active(@PathVariable Long id, @RequestParam boolean active) { + return R.ok(active ? defService.active(id) : defService.unActive(id)); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwInstanceController.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwInstanceController.java new file mode 100644 index 0000000..4070543 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwInstanceController.java @@ -0,0 +1,157 @@ +package com.fuyuanshen.workflow.controller; + +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.bo.FlowCancelBo; +import com.fuyuanshen.workflow.domain.bo.FlowInstanceBo; +import com.fuyuanshen.workflow.domain.bo.FlowInvalidBo; +import com.fuyuanshen.workflow.domain.vo.FlowInstanceVo; +import com.fuyuanshen.workflow.service.IFlwInstanceService; +import org.dromara.warm.flow.core.service.InsService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 流程实例管理 控制层 + * + * @author may + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/instance") +public class FlwInstanceController extends BaseController { + + private final InsService insService; + private final IFlwInstanceService flwInstanceService; + + /** + * 查询正在运行的流程实例列表 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + */ + @GetMapping("/pageByRunning") + public TableDataInfo selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { + return flwInstanceService.selectRunningInstanceList(flowInstanceBo, pageQuery); + } + + /** + * 查询已结束的流程实例列表 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + */ + @GetMapping("/pageByFinish") + public TableDataInfo selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { + return flwInstanceService.selectFinishInstanceList(flowInstanceBo, pageQuery); + } + + /** + * 根据业务id查询流程实例详细信息 + * + * @param businessId 业务id + */ + @GetMapping("/getInfo/{businessId}") + public R getInfo(@PathVariable Long businessId) { + return R.ok(flwInstanceService.queryByBusinessId(businessId)); + } + + /** + * 按照业务id删除流程实例 + * + * @param businessIds 业务id + */ + @DeleteMapping("/deleteByBusinessIds/{businessIds}") + public R deleteByBusinessIds(@PathVariable List businessIds) { + return toAjax(flwInstanceService.deleteByBusinessIds(businessIds)); + } + + /** + * 按照实例id删除流程实例 + * + * @param instanceIds 实例id + */ + @DeleteMapping("/deleteByInstanceIds/{instanceIds}") + public R deleteByInstanceIds(@PathVariable List instanceIds) { + return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds)); + } + + /** + * 撤销流程 + * + * @param bo 参数 + */ + @RepeatSubmit() + @PutMapping("/cancelProcessApply") + public R cancelProcessApply(@RequestBody FlowCancelBo bo) { + return toAjax(flwInstanceService.cancelProcessApply(bo)); + } + + /** + * 激活/挂起流程实例 + * + * @param id 流程实例id + * @param active 激活/挂起 + */ + @RepeatSubmit() + @PutMapping("/active/{id}") + public R active(@PathVariable Long id, @RequestParam boolean active) { + return R.ok(active ? insService.active(id) : insService.unActive(id)); + } + + /** + * 获取当前登陆人发起的流程实例 + * + * @param flowInstanceBo 参数 + * @param pageQuery 分页 + */ + @GetMapping("/pageByCurrent") + public TableDataInfo selectCurrentInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { + return flwInstanceService.selectCurrentInstanceList(flowInstanceBo, pageQuery); + } + + /** + * 获取流程图,流程记录 + * + * @param businessId 业务id + */ + @GetMapping("/flowHisTaskList/{businessId}") + public R> flowHisTaskList(@PathVariable String businessId) { + return R.ok(flwInstanceService.flowHisTaskList(businessId)); + } + + /** + * 获取流程变量 + * + * @param instanceId 流程实例id + */ + @GetMapping("/instanceVariable/{instanceId}") + public R> instanceVariable(@PathVariable Long instanceId) { + return R.ok(flwInstanceService.instanceVariable(instanceId)); + } + + /** + * 作废流程 + * + * @param bo 参数 + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/invalid") + public R invalid(@Validated @RequestBody FlowInvalidBo bo) { + return R.ok(flwInstanceService.processInvalid(bo)); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwTaskController.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwTaskController.java new file mode 100644 index 0000000..0237dd0 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/FlwTaskController.java @@ -0,0 +1,212 @@ +package com.fuyuanshen.workflow.controller; + +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.domain.dto.StartProcessReturnDTO; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import org.dromara.warm.flow.core.entity.Node; +import org.dromara.warm.flow.orm.entity.FlowNode; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.bo.*; +import com.fuyuanshen.workflow.domain.vo.FlowHisTaskVo; +import com.fuyuanshen.workflow.domain.vo.FlowTaskVo; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 任务管理 控制层 + * + * @author may + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/task") +public class FlwTaskController extends BaseController { + + private final IFlwTaskService flwTaskService; + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/startWorkFlow") + public R startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) { + StartProcessReturnDTO startProcessReturn = flwTaskService.startWorkFlow(startProcessBo); + return R.ok("提交成功", startProcessReturn); + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/completeTask") + public R completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) { + return toAjax(flwTaskService.completeTask(completeTaskBo)); + } + + /** + * 查询当前用户的待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @GetMapping("/pageByTaskWait") + public TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + return flwTaskService.pageByTaskWait(flowTaskBo, pageQuery); + } + + /** + * 查询当前用户的已办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + + @GetMapping("/pageByTaskFinish") + public TableDataInfo pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + return flwTaskService.pageByTaskFinish(flowTaskBo, pageQuery); + } + + /** + * 查询待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @GetMapping("/pageByAllTaskWait") + public TableDataInfo pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + return flwTaskService.pageByAllTaskWait(flowTaskBo, pageQuery); + } + + /** + * 查询已办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @GetMapping("/pageByAllTaskFinish") + public TableDataInfo pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + return flwTaskService.pageByAllTaskFinish(flowTaskBo, pageQuery); + } + + /** + * 查询当前用户的抄送 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @GetMapping("/pageByTaskCopy") + public TableDataInfo pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + return flwTaskService.pageByTaskCopy(flowTaskBo, pageQuery); + } + + /** + * 根据taskId查询代表任务 + * + * @param taskId 任务id + */ + @GetMapping("/getTask/{taskId}") + public R getTask(@PathVariable Long taskId) { + return R.ok(flwTaskService.selectById(taskId)); + } + + /** + * 获取下一节点信息 + * + * @param bo 参数 + */ + @PostMapping("/getNextNodeList") + public R> getNextNodeList(@RequestBody FlowNextNodeBo bo) { + return R.ok(flwTaskService.getNextNodeList(bo)); + } + + /** + * 终止任务 + * + * @param bo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/terminationTask") + public R terminationTask(@RequestBody FlowTerminationBo bo) { + return R.ok(flwTaskService.terminationTask(bo)); + } + + /** + * 任务操作 + * + * @param bo 参数 + * @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + */ + @Log(title = "任务管理", businessType = BusinessType.UPDATE) + @RepeatSubmit + @PostMapping("/taskOperation/{taskOperation}") + public R taskOperation(@Validated @RequestBody TaskOperationBo bo, @PathVariable String taskOperation) { + return toAjax(flwTaskService.taskOperation(bo, taskOperation)); + } + + /** + * 修改任务办理人 + * + * @param taskIdList 任务id + * @param userId 办理人id + */ + @Log(title = "任务管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateAssignee/{userId}") + public R updateAssignee(@RequestBody List taskIdList, @PathVariable String userId) { + return toAjax(flwTaskService.updateAssignee(taskIdList, userId)); + } + + /** + * 驳回审批 + * + * @param bo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/backProcess") + public R backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo bo) { + return toAjax(flwTaskService.backProcess(bo)); + } + + /** + * 获取可驳回的前置节点 + * + * @param definitionId 流程定义id + * @param nowNodeCode 当前节点 + */ + @GetMapping("/getBackTaskNode/{definitionId}/{nowNodeCode}") + public R> getBackTaskNode(@PathVariable Long definitionId, @PathVariable String nowNodeCode) { + return R.ok(flwTaskService.getBackTaskNode(definitionId, nowNodeCode)); + } + + /** + * 获取当前任务的所有办理人 + * + * @param taskId 任务id + */ + @GetMapping("/currentTaskAllUser/{taskId}") + public R> currentTaskAllUser(@PathVariable Long taskId) { + return R.ok(flwTaskService.currentTaskAllUser(taskId)); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/TestLeaveController.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/TestLeaveController.java new file mode 100644 index 0000000..5817e5e --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/controller/TestLeaveController.java @@ -0,0 +1,108 @@ +package com.fuyuanshen.workflow.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.R; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.excel.utils.ExcelUtil; +import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit; +import com.fuyuanshen.common.log.annotation.Log; +import com.fuyuanshen.common.log.enums.BusinessType; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.web.core.BaseController; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.bo.TestLeaveBo; +import com.fuyuanshen.workflow.domain.vo.TestLeaveVo; +import com.fuyuanshen.workflow.service.ITestLeaveService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 请假 + * + * @author may + * @date 2023-07-21 + */ +@ConditionalOnEnable +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/leave") +public class TestLeaveController extends BaseController { + + private final ITestLeaveService testLeaveService; + + /** + * 查询请假列表 + */ + @SaCheckPermission("workflow:leave:list") + @GetMapping("/list") + public TableDataInfo list(TestLeaveBo bo, PageQuery pageQuery) { + return testLeaveService.queryPageList(bo, pageQuery); + } + + /** + * 导出请假列表 + */ + @SaCheckPermission("workflow:leave:export") + @Log(title = "请假", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(TestLeaveBo bo, HttpServletResponse response) { + List list = testLeaveService.queryList(bo); + ExcelUtil.exportExcel(list, "请假", TestLeaveVo.class, response); + } + + /** + * 获取请假详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("workflow:leave:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(testLeaveService.queryById(id)); + } + + /** + * 新增请假 + */ + @SaCheckPermission("workflow:leave:add") + @Log(title = "请假", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) { + return R.ok(testLeaveService.insertByBo(bo)); + } + + /** + * 修改请假 + */ + @SaCheckPermission("workflow:leave:edit") + @Log(title = "请假", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) { + return R.ok(testLeaveService.updateByBo(bo)); + } + + /** + * 删除请假 + * + * @param ids 主键串 + */ + @SaCheckPermission("workflow:leave:remove") + @Log(title = "请假", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testLeaveService.deleteWithValidByIds(List.of(ids))); + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/FlowCategory.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/FlowCategory.java new file mode 100644 index 0000000..0349ae7 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/FlowCategory.java @@ -0,0 +1,67 @@ +package com.fuyuanshen.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.tenant.core.TenantEntity; + +import java.io.Serial; +import java.util.ArrayList; +import java.util.List; + +/** + * 流程分类对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("flow_category") +public class FlowCategory extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程分类ID + */ + @TableId(value = "category_id") + private Long categoryId; + + /** + * 父流程分类id + */ + private Long parentId; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 流程分类名称 + */ + private String categoryName; + + /** + * 显示顺序 + */ + private Long orderNum; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + /** + * 子菜单 + */ + @TableField(exist = false) + private List children = new ArrayList<>(); + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/TestLeave.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/TestLeave.java new file mode 100644 index 0000000..9101057 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/TestLeave.java @@ -0,0 +1,63 @@ +package com.fuyuanshen.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 请假对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_leave") +public class TestLeave extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 请假类型 + */ + private String leaveType; + + /** + * 开始时间 + */ + private Date startDate; + + /** + * 结束时间 + */ + private Date endDate; + + /** + * 请假天数 + */ + private Integer leaveDays; + + /** + * 请假原因 + */ + private String remark; + + /** + * 状态 + */ + private String status; + + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/BackProcessBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/BackProcessBo.java new file mode 100644 index 0000000..f5aec91 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/BackProcessBo.java @@ -0,0 +1,69 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * 驳回参数请求 + * + * @author may + */ +@Data +public class BackProcessBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @NotNull(message = "任务ID不能为空", groups = AddGroup.class) + private Long taskId; + + /** + * 附件id + */ + private String fileId; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 驳回的节点id(目前未使用,直接驳回到申请人) + */ + private String nodeCode; + + /** + * 办理意见 + */ + private String message; + + /** + * 通知 + */ + private String notice; + + /** + * 流程变量 + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/CompleteTaskBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/CompleteTaskBo.java new file mode 100644 index 0000000..a8897db --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/CompleteTaskBo.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 办理任务请求对象 + * + * @author may + */ +@Data +public class CompleteTaskBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotNull(message = "任务id不能为空", groups = {AddGroup.class}) + private Long taskId; + + /** + * 附件id + */ + private String fileId; + + /** + * 抄送人员 + */ + private List flowCopyList; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 办理意见 + */ + private String message; + + /** + * 消息通知 + */ + private String notice; + + /** + * 流程变量 + */ + private Map variables; + + /** + * 弹窗选择的办理人 + */ + private Map assigneeMap; + + /** + * 扩展变量(此处为逗号分隔的ossId) + */ + private String ext; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCancelBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCancelBo.java new file mode 100644 index 0000000..64ef9ca --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCancelBo.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 撤销任务请求对象 + * + * @author may + */ +@Data +public class FlowCancelBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @NotBlank(message = "业务ID不能为空", groups = AddGroup.class) + private String businessId; + + /** + * 办理意见 + */ + private String message; +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCategoryBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCategoryBo.java new file mode 100644 index 0000000..fa5bf66 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCategoryBo.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.workflow.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.workflow.domain.FlowCategory; + +/** + * 流程分类业务对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = FlowCategory.class, reverseConvertGenerate = false) +public class FlowCategoryBo extends BaseEntity { + + /** + * 流程分类ID + */ + @NotNull(message = "流程分类ID不能为空", groups = { EditGroup.class }) + private Long categoryId; + + /** + * 父流程分类id + */ + @NotNull(message = "父流程分类id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long parentId; + + /** + * 流程分类名称 + */ + @NotBlank(message = "流程分类名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String categoryName; + + /** + * 显示顺序 + */ + private Long orderNum; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCopyBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCopyBo.java new file mode 100644 index 0000000..e2e2bd9 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowCopyBo.java @@ -0,0 +1,30 @@ +package com.fuyuanshen.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 抄送 + * + * @author may + */ +@Data +public class FlowCopyBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名称 + */ + private String userName; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInstanceBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInstanceBo.java new file mode 100644 index 0000000..d966845 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInstanceBo.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 流程实例请求对象 + * + * @author may + */ +@Data +public class FlowInstanceBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 任务发起人 + */ + private String startUserId; + + /** + * 业务id + */ + private String businessId; + + /** + * 流程分类id + */ + private String category; + + /** + * 任务名称 + */ + private String nodeName; + + /** + * 申请人Ids + */ + private List createByIds; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInvalidBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInvalidBo.java new file mode 100644 index 0000000..0625605 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowInvalidBo.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 作废请求对象 + * + * @author may + */ +@Data +public class FlowInvalidBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + @NotNull(message = "流程实例id为空", groups = AddGroup.class) + private Long id; + + /** + * 审批意见 + */ + private String comment; +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowNextNodeBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowNextNodeBo.java new file mode 100644 index 0000000..9212470 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowNextNodeBo.java @@ -0,0 +1,38 @@ +package com.fuyuanshen.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 下一节点信息 + * + * @author may + */ +@Data +public class FlowNextNodeBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 任务id + */ + private Long taskId; + + /** + * 流程变量 + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTaskBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTaskBo.java new file mode 100644 index 0000000..9b099ba --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTaskBo.java @@ -0,0 +1,55 @@ +package com.fuyuanshen.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 任务请求对象 + * + * @author may + */ +@Data +public class FlowTaskBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务名称 + */ + private String nodeName; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 流程分类id + */ + private String category; + + /** + * 流程实例id + */ + private Long instanceId; + + /** + * 权限列表 + */ + private List permissionList; + + /** + * 申请人Ids + */ + private List createByIds; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTerminationBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTerminationBo.java new file mode 100644 index 0000000..b8ff7ba --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/FlowTerminationBo.java @@ -0,0 +1,31 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 终止任务请求对象 + * + * @author may + */ +@Data +public class FlowTerminationBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotNull(message = "任务id为空", groups = AddGroup.class) + private Long taskId; + + /** + * 审批意见 + */ + private String comment; +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/StartProcessBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/StartProcessBo.java new file mode 100644 index 0000000..fe76d29 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/StartProcessBo.java @@ -0,0 +1,49 @@ +package com.fuyuanshen.workflow.domain.bo; + + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 启动流程对象 + * + * @author may + */ +@Data +public class StartProcessBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 业务唯一值id + */ + @NotBlank(message = "业务ID不能为空", groups = {AddGroup.class}) + private String businessId; + + /** + * 流程定义编码 + */ + @NotBlank(message = "流程定义编码不能为空", groups = {AddGroup.class}) + private String flowCode; + + /** + * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TaskOperationBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TaskOperationBo.java new file mode 100644 index 0000000..0fb4ccf --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TaskOperationBo.java @@ -0,0 +1,48 @@ +package com.fuyuanshen.workflow.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + + +/** + * 任务操作业务对象,用于描述任务委派、转办、加签等操作的必要参数 + * 包含了用户ID、任务ID、任务相关的消息、以及加签/减签的用户ID + * + * @author AprilWind + */ +@Data +public class TaskOperationBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 委派/转办人的用户ID(必填,准对委派/转办人操作) + */ + @NotNull(message = "委派/转办人id不能为空", groups = {AddGroup.class}) + private String userId; + + /** + * 加签/减签人的用户ID列表(必填,针对加签/减签操作) + */ + @NotNull(message = "加签/减签id不能为空", groups = {EditGroup.class}) + private List userIds; + + /** + * 任务ID(必填) + */ + @NotNull(message = "任务id不能为空") + private Long taskId; + + /** + * 意见或备注信息(可选) + */ + private String message; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TestLeaveBo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TestLeaveBo.java new file mode 100644 index 0000000..10a2174 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/bo/TestLeaveBo.java @@ -0,0 +1,82 @@ +package com.fuyuanshen.workflow.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.workflow.domain.TestLeave; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 请假业务对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestLeave.class, reverseConvertGenerate = false) +public class TestLeaveBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 请假类型 + */ + @NotBlank(message = "请假类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String leaveType; + + /** + * 开始时间 + */ + @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + /** + * 结束时间 + */ + @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + /** + * 请假天数 + */ + private Integer leaveDays; + + /** + * 开始时间 + */ + private Integer startLeaveDays; + + /** + * 结束时间 + */ + private Integer endLeaveDays; + + /** + * 请假原因 + */ + private String remark; + + /** + * 状态 + */ + private String status; + + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/ButtonPermissionVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/ButtonPermissionVo.java new file mode 100644 index 0000000..6877a94 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/ButtonPermissionVo.java @@ -0,0 +1,43 @@ +package com.fuyuanshen.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 按钮权限 + * + * @author may + * @date 2025-02-28 + */ +@Data +public class ButtonPermissionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 唯一编码 + */ + private String code; + + /** + * 选项值 + */ + private String value; + + /** + * 是否显示 + */ + private Boolean show; + + public ButtonPermissionVo() { + } + + public ButtonPermissionVo(String code, Boolean show) { + this.code = code; + this.show = show; + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowCategoryVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowCategoryVo.java new file mode 100644 index 0000000..4e82ffb --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowCategoryVo.java @@ -0,0 +1,74 @@ +package com.fuyuanshen.workflow.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.workflow.domain.FlowCategory; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + + +/** + * 流程分类视图对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = FlowCategory.class) +public class FlowCategoryVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程分类ID + */ + @ExcelProperty(value = "流程分类ID") + private Long categoryId; + + /** + * 父级id + */ + private Long parentId; + + /** + * 父类别名称 + */ + private String parentName; + + /** + * 祖级列表 + */ + private String ancestors; + + /** + * 流程分类名称 + */ + @ExcelProperty(value = "流程分类名称") + private String categoryName; + + /** + * 显示顺序 + */ + @ExcelProperty(value = "显示顺序") + private Long orderNum; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 子菜单 + */ + private List children = new ArrayList<>(); + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowDefinitionVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowDefinitionVo.java new file mode 100644 index 0000000..ee8a5dc --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowDefinitionVo.java @@ -0,0 +1,104 @@ +package com.fuyuanshen.workflow.domain.vo; + +import lombok.Data; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.workflow.common.constant.FlowConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程定义视图 + * + * @author may + */ +@Data +public class FlowDefinitionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 删除标记 + */ + private String delFlag; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 流程分类id + */ + private String category; + + /** + * 流程分类名称 + */ + @Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category") + private String categoryName; + + /** + * 流程版本 + */ + private String version; + + /** + * 是否发布(0未发布 1已发布 9失效) + */ + private Integer isPublish; + + /** + * 审批表单是否自定义(Y是 N否) + */ + private String formCustom; + + /** + * 审批表单路径 + */ + private String formPath; + + /** + * 流程激活状态(0挂起 1激活) + */ + private Integer activityStatus; + + /** + * 监听器类型 + */ + private String listenerType; + + /** + * 监听器路径 + */ + private String listenerPath; + + /** + * 扩展字段,预留给业务系统使用 + */ + private String ext; +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowHisTaskVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowHisTaskVo.java new file mode 100644 index 0000000..60c5fc1 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowHisTaskVo.java @@ -0,0 +1,244 @@ +package com.fuyuanshen.workflow.domain.vo; + +import lombok.Data; +import com.fuyuanshen.common.core.utils.DateUtils; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import org.dromara.warm.flow.core.enums.CooperateType; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 历史任务视图 + * + * @author may + */ +@Data +public class FlowHisTaskVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 删除标记 + */ + private String delFlag; + + /** + * 对应flow_definition表的id + */ + private Long definitionId; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 流程实例表id + */ + private Long instanceId; + + /** + * 任务表id + */ + private Long taskId; + + /** + * 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签) + */ + private Integer cooperateType; + + /** + * 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签) + */ + private String cooperateTypeName; + + /** + * 业务id + */ + private String businessId; + + /** + * 开始节点编码 + */ + private String nodeCode; + + /** + * 开始节点名称 + */ + private String nodeName; + + /** + * 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 目标节点编码 + */ + private String targetNodeCode; + + /** + * 结束节点名称 + */ + private String targetNodeName; + + /** + * 审批者 + */ + private String approver; + + /** + * 审批者 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "approver") + private String approveName; + + /** + * 协作人(只有转办、会签、票签、委派) + */ + private String collaborator; + + /** + * 权限标识 permissionFlag的list形式 + */ + private List permissionList; + + /** + * 跳转类型(PASS通过 REJECT退回 NONE无动作) + */ + private String skipType; + + /** + * 流程状态 + */ + private String flowStatus; + + /** + * 任务状态 + */ + private String flowTaskStatus; + + /** + * 流程状态 + */ + private String flowStatusName; + + /** + * 审批意见 + */ + private String message; + + /** + * 业务详情 存业务类的json + */ + private String ext; + + /** + * 创建者 + */ + private String createBy; + + /** + * 申请人 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy") + private String createByName; + + /** + * 流程分类id + */ + private String category; + + /** + * 流程分类名称 + */ + @Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category") + private String categoryName; + + /** + * 审批表单是否自定义(Y是 N否) + */ + private String formCustom; + + /** + * 审批表单路径 + */ + private String formPath; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 流程版本号 + */ + private String version; + + /** + * 运行时长 + */ + private String runDuration; + + /** + * 设置创建时间并计算任务运行时长 + * + * @param createTime 创建时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + updateRunDuration(); + } + + /** + * 设置更新时间并计算任务运行时长 + * + * @param updateTime 更新时间 + */ + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + updateRunDuration(); + } + + /** + * 更新运行时长 + */ + private void updateRunDuration() { + // 如果创建时间和更新时间均不为空,计算它们之间的时长 + if (this.updateTime != null && this.createTime != null) { + this.runDuration = DateUtils.getTimeDifference(this.updateTime, this.createTime); + } + } + + /** + * 设置协作方式,并通过协作方式获取名称 + */ + public void setCooperateType(Integer cooperateType) { + this.cooperateType = cooperateType; + this.cooperateTypeName = CooperateType.getValueByKey(cooperateType); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowInstanceVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowInstanceVo.java new file mode 100644 index 0000000..76c1377 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowInstanceVo.java @@ -0,0 +1,137 @@ +package com.fuyuanshen.workflow.domain.vo; + +import lombok.Data; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.workflow.common.constant.FlowConstant; + +import java.util.Date; + +/** + * 流程实例视图 + * + * @author may + */ +@Data +public class FlowInstanceVo { + + private Long id; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 删除标记 + */ + private String delFlag; + + /** + * 对应flow_definition表的id + */ + private Long definitionId; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 业务id + */ + private String businessId; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 流程变量 + */ + private String variable; + + /** + * 流程状态(0待提交 1审批中 2 审批通过 3自动通过 8已完成 9已退回 10失效) + */ + private String flowStatus; + + /** + * 流程状态 + */ + private String flowStatusName; + + /** + * 流程激活状态(0挂起 1激活) + */ + private Integer activityStatus; + + /** + * 审批表单是否自定义(Y是 N否) + */ + private String formCustom; + + /** + * 审批表单路径 + */ + private String formPath; + + /** + * 扩展字段,预留给业务系统使用 + */ + private String ext; + + /** + * 流程定义版本 + */ + private String version; + + /** + * 创建者 + */ + private String createBy; + + /** + * 申请人 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy") + private String createByName; + + /** + * 流程分类id + */ + private String category; + + /** + * 流程分类名称 + */ + @Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category") + private String categoryName; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowTaskVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowTaskVo.java new file mode 100644 index 0000000..e347ee4 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/FlowTaskVo.java @@ -0,0 +1,187 @@ +package com.fuyuanshen.workflow.domain.vo; + +import lombok.Data; +import com.fuyuanshen.common.translation.annotation.Translation; +import com.fuyuanshen.common.translation.constant.TransConstant; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import org.dromara.warm.flow.core.entity.User; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 任务视图 + * + * @author may + */ +@Data +public class FlowTaskVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 删除标记 + */ + private String delFlag; + + /** + * 对应flow_definition表的id + */ + private Long definitionId; + + /** + * 流程实例表id + */ + private Long instanceId; + + /** + * 流程定义名称 + */ + private String flowName; + + /** + * 业务id + */ + private String businessId; + + /** + * 节点编码 + */ + private String nodeCode; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 权限标识 permissionFlag的list形式 + */ + private List permissionList; + + /** + * 流程用户列表 + */ + private List userList; + + /** + * 审批表单是否自定义(Y是 N否) + */ + private String formCustom; + + /** + * 审批表单 + */ + private String formPath; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 流程版本号 + */ + private String version; + + /** + * 流程状态 + */ + private String flowStatus; + + /** + * 流程分类id + */ + private String category; + + /** + * 流程分类名称 + */ + @Translation(type = FlowConstant.CATEGORY_ID_TO_NAME, mapper = "category") + private String categoryName; + + /** + * 流程状态 + */ + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "flowStatus", other = "wf_business_status") + private String flowStatusName; + + /** + * 办理人类型 + */ + private String type; + + /** + * 办理人ids + */ + private String assigneeIds; + + /** + * 办理人名称 + */ + private String assigneeNames; + + /** + * 抄送人id + */ + private String processedBy; + + /** + * 抄送人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "processedBy") + private String processedByName; + + /** + * 流程签署比例值 大于0为票签,会签 + */ + private BigDecimal nodeRatio; + + /** + * 申请人id + */ + private String createBy; + + /** + * 申请人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy") + private String createByName; + + /** + * 是否为申请人节点 + */ + private Boolean applyNode; + + /** + * 按钮权限 + */ + private List buttonList; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/TestLeaveVo.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/TestLeaveVo.java new file mode 100644 index 0000000..c7d5940 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/domain/vo/TestLeaveVo.java @@ -0,0 +1,73 @@ +package com.fuyuanshen.workflow.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.fuyuanshen.workflow.domain.TestLeave; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 请假视图对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestLeave.class) +public class TestLeaveVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 请假类型 + */ + @ExcelProperty(value = "请假类型") + private String leaveType; + + /** + * 开始时间 + */ + @ExcelProperty(value = "开始时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + /** + * 结束时间 + */ + @ExcelProperty(value = "结束时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + /** + * 请假天数 + */ + @ExcelProperty(value = "请假天数") + private Integer leaveDays; + + /** + * 备注 + */ + @ExcelProperty(value = "请假原因") + private String remark; + + /** + * 状态 + */ + @ExcelProperty(value = "状态") + private String status; + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/FlowProcessEventHandler.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/FlowProcessEventHandler.java new file mode 100644 index 0000000..6aedcf6 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/FlowProcessEventHandler.java @@ -0,0 +1,91 @@ +package com.fuyuanshen.workflow.handler; + +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.event.ProcessTaskEvent; +import com.fuyuanshen.common.core.domain.event.ProcessDeleteEvent; +import com.fuyuanshen.common.core.domain.event.ProcessEvent; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.tenant.helper.TenantHelper; +import org.dromara.warm.flow.core.entity.Instance; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 流程监听服务 + * + * @author may + * @date 2024-06-02 + */ +@ConditionalOnEnable +@Slf4j +@Component +public class FlowProcessEventHandler { + + /** + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) + * + * @param flowCode 流程定义编码 + * @param instance 实例数据 + * @param status 流程状态 + * @param params 办理参数 + * @param submit 当为true时为申请人节点办理 + */ + public void processHandler(String flowCode, Instance instance, String status, Map params, boolean submit) { + String tenantId = TenantHelper.getTenantId(); + log.info("【流程事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 流程状态: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 是否申请人节点: {}, 参数: {}", + tenantId, flowCode, instance.getBusinessId(), status, instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), submit, params); + ProcessEvent processEvent = new ProcessEvent(); + processEvent.setTenantId(tenantId); + processEvent.setFlowCode(flowCode); + processEvent.setBusinessId(instance.getBusinessId()); + processEvent.setNodeType(instance.getNodeType()); + processEvent.setNodeCode(instance.getNodeCode()); + processEvent.setNodeName(instance.getNodeName()); + processEvent.setStatus(status); + processEvent.setParams(params); + processEvent.setSubmit(submit); + SpringUtils.context().publishEvent(processEvent); + } + + /** + * 执行创建任务监听 + * + * @param flowCode 流程定义编码 + * @param instance 实例数据 + * @param taskId 任务id + */ + public void processTaskHandler(String flowCode, Instance instance, Long taskId) { + String tenantId = TenantHelper.getTenantId(); + log.info("【流程任务事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}, 节点类型: {}, 节点编码: {}, 节点名称: {}, 任务ID: {}", + tenantId, flowCode, instance.getBusinessId(), instance.getNodeType(), instance.getNodeCode(), instance.getNodeName(), taskId); + ProcessTaskEvent processTaskEvent = new ProcessTaskEvent(); + processTaskEvent.setTenantId(tenantId); + processTaskEvent.setFlowCode(flowCode); + processTaskEvent.setBusinessId(instance.getBusinessId()); + processTaskEvent.setNodeType(instance.getNodeType()); + processTaskEvent.setNodeCode(instance.getNodeCode()); + processTaskEvent.setNodeName(instance.getNodeName()); + processTaskEvent.setTaskId(taskId); + processTaskEvent.setStatus(instance.getFlowStatus()); + SpringUtils.context().publishEvent(processTaskEvent); + } + + /** + * 删除流程监听 + * + * @param flowCode 流程定义编码 + * @param businessId 业务ID + */ + public void processDeleteHandler(String flowCode, String businessId) { + String tenantId = TenantHelper.getTenantId(); + log.info("【流程删除事件发布】租户ID: {}, 流程编码: {}, 业务ID: {}", tenantId, flowCode, businessId); + ProcessDeleteEvent processDeleteEvent = new ProcessDeleteEvent(); + processDeleteEvent.setTenantId(tenantId); + processDeleteEvent.setFlowCode(flowCode); + processDeleteEvent.setBusinessId(businessId); + SpringUtils.context().publishEvent(processDeleteEvent); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/WorkflowPermissionHandler.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/WorkflowPermissionHandler.java new file mode 100644 index 0000000..b92fbba --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/handler/WorkflowPermissionHandler.java @@ -0,0 +1,59 @@ +package com.fuyuanshen.workflow.handler; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.handler.PermissionHandler; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.service.IFlwCommonService; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +/** + * 办理人权限处理器 + * + * @author AprilWind + */ +@ConditionalOnEnable +@RequiredArgsConstructor +@Component +@Slf4j +public class WorkflowPermissionHandler implements PermissionHandler { + + private final IFlwCommonService flwCommonService; + + /** + * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务 + * 后续在{@link FlowParams#getPermissionFlag} 中获取 + * 返回当前用户权限集合 + */ + @Override + public List permissions() { + return Collections.singletonList(LoginHelper.getUserIdStr()); + } + + /** + * 获取当前办理人 + * + * @return 当前办理人 + */ + @Override + public String getHandler() { + return LoginHelper.getUserIdStr(); + } + + /** + * 转换办理人,比如设计器中预设了能办理的人,如果其中包含角色或者部门id等,可以通过此接口进行转换成用户id + */ + @Override + public List convertPermissions(List permissions) { + if (CollUtil.isNotEmpty(permissions)) { + permissions = flwCommonService.buildUser(permissions); + } + return permissions; + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/listener/WorkflowGlobalListener.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/listener/WorkflowGlobalListener.java new file mode 100644 index 0000000..39e99d9 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/listener/WorkflowGlobalListener.java @@ -0,0 +1,193 @@ +package com.fuyuanshen.workflow.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.enums.BusinessStatusEnum; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.dromara.warm.flow.core.FlowEngine; +import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.entity.Definition; +import org.dromara.warm.flow.core.entity.Instance; +import org.dromara.warm.flow.core.entity.Task; +import org.dromara.warm.flow.core.listener.GlobalListener; +import org.dromara.warm.flow.core.listener.ListenerVariable; +import org.dromara.warm.flow.core.service.InsService; +import org.dromara.warm.flow.orm.entity.FlowInstance; +import org.dromara.warm.flow.orm.entity.FlowTask; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.common.enums.TaskStatusEnum; +import com.fuyuanshen.workflow.domain.bo.FlowCopyBo; +import com.fuyuanshen.workflow.handler.FlowProcessEventHandler; +import com.fuyuanshen.workflow.service.IFlwCommonService; +import com.fuyuanshen.workflow.service.IFlwInstanceService; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 全局任务办理监听 + * + * @author may + */ +@ConditionalOnEnable +@Component +@Slf4j +@RequiredArgsConstructor +public class WorkflowGlobalListener implements GlobalListener { + + private final IFlwTaskService flwTaskService; + private final IFlwInstanceService instanceService; + private final FlowProcessEventHandler flowProcessEventHandler; + private final IFlwCommonService flwCommonService; + private final InsService insService; + + /** + * 创建监听器,任务创建时执行 + * + * @param listenerVariable 监听器变量 + */ + @Override + public void create(ListenerVariable listenerVariable) { + + } + + /** + * 开始监听器,任务开始办理时执行 + * + * @param listenerVariable 监听器变量 + */ + @Override + public void start(ListenerVariable listenerVariable) { + } + + /** + * 分派监听器,动态修改代办任务信息 + * + * @param listenerVariable 监听器变量 + */ + @Override + public void assignment(ListenerVariable listenerVariable) { + Map variable = listenerVariable.getVariable(); + List nextTasks = listenerVariable.getNextTasks(); + FlowParams flowParams = listenerVariable.getFlowParams(); + Definition definition = listenerVariable.getDefinition(); + Instance instance = listenerVariable.getInstance(); + String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); + for (Task flowTask : nextTasks) { + // 如果办理或者退回并行存在需要指定办理人,则直接覆盖办理人 + if (variable.containsKey(flowTask.getNodeCode()) && (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) + || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus()))) { + String userIds = variable.get(flowTask.getNodeCode()).toString(); + flowTask.setPermissionList(List.of(userIds.split(StringUtils.SEPARATOR))); + variable.remove(flowTask.getNodeCode()); + } + // 如果是申请节点,则把启动人添加到办理人 + if (flowTask.getNodeCode().equals(applyNodeCode)) { + flowTask.setPermissionList(List.of(instance.getCreateBy())); + } + } + } + + /** + * 完成监听器,当前任务完成后执行 + * + * @param listenerVariable 监听器变量 + */ + @Override + public void finish(ListenerVariable listenerVariable) { + Instance instance = listenerVariable.getInstance(); + Definition definition = listenerVariable.getDefinition(); + Task task = listenerVariable.getTask(); + Map params = new HashMap<>(); + FlowParams flowParams = listenerVariable.getFlowParams(); + Map variable = new HashMap<>(); + if (ObjectUtil.isNotNull(flowParams)) { + // 历史任务扩展(通常为附件) + params.put("hisTaskExt", flowParams.getHisTaskExt()); + // 办理人 + params.put("handler", flowParams.getHandler()); + // 办理意见 + params.put("message", flowParams.getMessage()); + variable = flowParams.getVariable(); + } + //申请人提交事件 + Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); + if (submit != null && submit) { + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); + } else { + // 判断流程状态(发布:撤销,退回,作废,终止,已完成事件) + String status = determineFlowStatus(instance); + if (StringUtils.isNotBlank(status)) { + flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, status, params, false); + } + } + //发布任务事件 + if (task != null) { + flowProcessEventHandler.processTaskHandler(definition.getFlowCode(), instance, task.getId()); + } + if (ObjectUtil.isNull(flowParams)) { + return; + } + // 只有办理或者退回的时候才执行消息通知和抄送 + if (TaskStatusEnum.PASS.getStatus().equals(flowParams.getHisStatus()) + || TaskStatusEnum.BACK.getStatus().equals(flowParams.getHisStatus())) { + if (variable != null) { + if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { + List flowCopyList = (List) variable.get(FlowConstant.FLOW_COPY_LIST); + // 添加抄送人 + flwTaskService.setCopy(task, flowCopyList); + } + if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { + List messageType = (List) variable.get(FlowConstant.MESSAGE_TYPE); + String notice = (String) variable.get(FlowConstant.MESSAGE_NOTICE); + // 消息通知 + if (CollUtil.isNotEmpty(messageType)) { + flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + } + } + FlowInstance ins = new FlowInstance(); + Map variableMap = instance.getVariableMap(); + variableMap.remove(FlowConstant.FLOW_COPY_LIST); + variableMap.remove(FlowConstant.MESSAGE_TYPE); + variableMap.remove(FlowConstant.MESSAGE_NOTICE); + variableMap.remove(FlowConstant.SUBMIT); + ins.setId(instance.getId()); + ins.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); + insService.updateById(ins); + } + } + } + + /** + * 根据流程实例确定最终状态 + * + * @param instance 流程实例 + * @return 流程最终状态 + */ + private String determineFlowStatus(Instance instance) { + String flowStatus = instance.getFlowStatus(); + if (StringUtils.isNotBlank(flowStatus) && BusinessStatusEnum.initialState(flowStatus)) { + log.info("流程实例当前状态: {}", flowStatus); + return flowStatus; + } else { + Long instanceId = instance.getId(); + List flowTasks = flwTaskService.selectByInstId(instanceId); + if (CollUtil.isEmpty(flowTasks)) { + String status = BusinessStatusEnum.FINISH.getStatus(); + // 更新流程状态为已完成 + instanceService.updateStatus(instanceId, status); + log.info("流程已结束,状态更新为: {}", status); + return status; + } + return null; + } + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwCategoryMapper.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwCategoryMapper.java new file mode 100644 index 0000000..e55aa16 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwCategoryMapper.java @@ -0,0 +1,60 @@ +package com.fuyuanshen.workflow.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fuyuanshen.common.mybatis.annotation.DataColumn; +import com.fuyuanshen.common.mybatis.annotation.DataPermission; +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.workflow.domain.FlowCategory; +import com.fuyuanshen.workflow.domain.vo.FlowCategoryVo; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 流程分类Mapper接口 + * + * @author may + * @date 2023-06-27 + */ +public interface FlwCategoryMapper extends BaseMapperPlus { + + /** + * 统计指定流程分类ID的分类数量 + * + * @param categoryId 流程分类ID + * @return 该流程分类ID的分类数量 + */ + @DataPermission({ + @DataColumn(key = "deptName", value = "createDept") + }) + long countCategoryById(Long categoryId); + + /** + * 根据父流程分类ID查询其所有子流程分类的列表 + * + * @param parentId 父流程分类ID + * @return 包含子流程分类的列表 + */ + default List selectListByParentId(Long parentId) { + return this.selectList(new LambdaQueryWrapper() + .select(FlowCategory::getCategoryId) + .apply(DataBaseHelper.findInSet(parentId, "ancestors"))); + } + + /** + * 根据父流程分类ID查询包括父ID及其所有子流程分类ID的列表 + * + * @param parentId 父流程分类ID + * @return 包含父ID和子流程分类ID的列表 + */ + default List selectCategoryIdsByParentId(Long parentId) { + return Stream.concat( + this.selectListByParentId(parentId).stream() + .map(FlowCategory::getCategoryId), + Stream.of(parentId) + ).collect(Collectors.toList()); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwInstanceMapper.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwInstanceMapper.java new file mode 100644 index 0000000..7849304 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwInstanceMapper.java @@ -0,0 +1,27 @@ +package com.fuyuanshen.workflow.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.workflow.domain.bo.FlowInstanceBo; +import com.fuyuanshen.workflow.domain.vo.FlowInstanceVo; + +/** + * 实例信息Mapper接口 + * + * @author may + * @date 2024-03-02 + */ +public interface FlwInstanceMapper { + + /** + * 流程实例信息 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page selectInstanceList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwTaskMapper.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwTaskMapper.java new file mode 100644 index 0000000..a9c0507 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/FlwTaskMapper.java @@ -0,0 +1,57 @@ +package com.fuyuanshen.workflow.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import com.fuyuanshen.workflow.domain.bo.FlowTaskBo; +import com.fuyuanshen.workflow.domain.vo.FlowHisTaskVo; +import com.fuyuanshen.workflow.domain.vo.FlowTaskVo; + +import java.util.List; + + +/** + * 任务信息Mapper接口 + * + * @author may + * @date 2024-03-02 + */ +public interface FlwTaskMapper { + + /** + * 获取待办信息 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getListRunTask(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 获取待办信息 + * + * @param queryWrapper 条件 + * @return 结果 + */ + List getListRunTask(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 获取已办 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getListFinishTask(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 查询当前用户的抄送 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getTaskCopyByPage(@Param("page") Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/TestLeaveMapper.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/TestLeaveMapper.java new file mode 100644 index 0000000..3fe500c --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/mapper/TestLeaveMapper.java @@ -0,0 +1,15 @@ +package com.fuyuanshen.workflow.mapper; + +import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; +import com.fuyuanshen.workflow.domain.TestLeave; +import com.fuyuanshen.workflow.domain.vo.TestLeaveVo; + +/** + * 请假Mapper接口 + * + * @author may + * @date 2023-07-21 + */ +public interface TestLeaveMapper extends BaseMapperPlus { + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCategoryService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCategoryService.java new file mode 100644 index 0000000..c4fd272 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCategoryService.java @@ -0,0 +1,102 @@ +package com.fuyuanshen.workflow.service; + +import cn.hutool.core.lang.tree.Tree; +import com.fuyuanshen.workflow.domain.bo.FlowCategoryBo; +import com.fuyuanshen.workflow.domain.vo.FlowCategoryVo; + +import java.util.List; + +/** + * 流程分类Service接口 + * + * @author may + */ +public interface IFlwCategoryService { + + /** + * 查询流程分类 + * + * @param categoryId 主键 + * @return 流程分类 + */ + FlowCategoryVo queryById(Long categoryId); + + /** + * 根据流程分类ID查询流程分类名称 + * + * @param categoryId 流程分类ID + * @return 流程分类名称 + */ + String selectCategoryNameById(Long categoryId); + + /** + * 查询符合条件的流程分类列表 + * + * @param bo 查询条件 + * @return 流程分类列表 + */ + List queryList(FlowCategoryBo bo); + + /** + * 查询流程分类树结构信息 + * + * @param category 流程分类信息 + * @return 流程分类树信息集合 + */ + List> selectCategoryTreeList(FlowCategoryBo category); + + /** + * 校验流程分类是否有数据权限 + * + * @param categoryId 流程分类ID + */ + void checkCategoryDataScope(Long categoryId); + + /** + * 校验流程分类名称是否唯一 + * + * @param category 流程分类信息 + * @return 结果 + */ + boolean checkCategoryNameUnique(FlowCategoryBo category); + + /** + * 查询流程分类是否存在流程定义 + * + * @param categoryId 流程分类ID + * @return 结果 true 存在 false 不存在 + */ + boolean checkCategoryExistDefinition(Long categoryId); + + /** + * 是否存在流程分类子节点 + * + * @param categoryId 流程分类ID + * @return 结果 + */ + boolean hasChildByCategoryId(Long categoryId); + + /** + * 新增流程分类 + * + * @param bo 流程分类 + * @return 是否新增成功 + */ + int insertByBo(FlowCategoryBo bo); + + /** + * 修改流程分类 + * + * @param bo 流程分类 + * @return 是否修改成功 + */ + int updateByBo(FlowCategoryBo bo); + + /** + * 删除流程分类信息 + * + * @param categoryId 主键 + * @return 是否删除成功 + */ + int deleteWithValidById(Long categoryId); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCommonService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCommonService.java new file mode 100644 index 0000000..8bb3ba7 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwCommonService.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.workflow.service; + +import java.util.List; + +/** + * 通用 工作流服务 + * + * @author LionLi + */ +public interface IFlwCommonService { + + /** + * 构建工作流用户 + * + * @param permissionList 办理用户 + * @return 用户 + */ + List buildUser(List permissionList); + + /** + * 发送消息 + * + * @param flowName 流程定义名称 + * @param instId 实例id + * @param messageType 消息类型 + * @param message 消息内容,为空则发送默认配置的消息内容 + */ + void sendMessage(String flowName, Long instId, List messageType, String message); + + /** + * 申请人节点编码 + * + * @param definitionId 流程定义id + * @return 申请人节点编码 + */ + String applyNodeCode(Long definitionId); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwDefinitionService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwDefinitionService.java new file mode 100644 index 0000000..2ca3219 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwDefinitionService.java @@ -0,0 +1,79 @@ +package com.fuyuanshen.workflow.service; + +import jakarta.servlet.http.HttpServletResponse; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.domain.vo.FlowDefinitionVo; +import org.dromara.warm.flow.orm.entity.FlowDefinition; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +/** + * 流程定义 服务层 + * + * @author may + */ +public interface IFlwDefinitionService { + + /** + * 查询流程定义列表 + * + * @param flowDefinition 参数 + * @param pageQuery 分页 + * @return 返回分页列表 + */ + TableDataInfo queryList(FlowDefinition flowDefinition, PageQuery pageQuery); + + /** + * 查询未发布的流程定义列表 + * + * @param flowDefinition 参数 + * @param pageQuery 分页 + * @return 返回分页列表 + */ + TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery); + + + /** + * 发布流程定义 + * + * @param id 流程定义id + * @return 结果 + */ + boolean publish(Long id); + + /** + * 导出流程定义 + * + * @param id 流程定义id + * @param response 响应 + * @throws IOException 异常 + */ + void exportDef(Long id, HttpServletResponse response) throws IOException; + + /** + * 导入流程定义 + * + * @param file 文件 + * @param category 分类 + * @return 结果 + */ + boolean importJson(MultipartFile file, String category); + + /** + * 删除流程定义 + * + * @param ids 流程定义id + * @return 结果 + */ + boolean removeDef(List ids); + + /** + * 新增租户流程定义 + * + * @param tenantId 租户id + */ + void syncDef(String tenantId); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwInstanceService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwInstanceService.java new file mode 100644 index 0000000..af5fa32 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwInstanceService.java @@ -0,0 +1,159 @@ +package com.fuyuanshen.workflow.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.domain.bo.FlowCancelBo; +import com.fuyuanshen.workflow.domain.bo.FlowInstanceBo; +import com.fuyuanshen.workflow.domain.bo.FlowInvalidBo; +import com.fuyuanshen.workflow.domain.vo.FlowInstanceVo; +import org.dromara.warm.flow.orm.entity.FlowInstance; + +import java.util.List; +import java.util.Map; + +/** + * 流程实例 服务层 + * + * @author may + */ +public interface IFlwInstanceService { + + /** + * 分页查询正在运行的流程实例 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery); + + /** + * 分页查询已结束的流程实例 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery); + + /** + * 根据业务id查询流程实例详细信息 + * + * @param businessId 业务id + * @return 结果 + */ + FlowInstanceVo queryByBusinessId(Long businessId); + + /** + * 按照业务id查询流程实例 + * + * @param businessId 业务id + * @return 结果 + */ + FlowInstance selectInstByBusinessId(String businessId); + + /** + * 按照实例id查询流程实例 + * + * @param instanceId 实例id + * @return 结果 + */ + FlowInstance selectInstById(Long instanceId); + + /** + * 按照实例id查询流程实例 + * + * @param instanceIds 实例id + * @return 结果 + */ + List selectInstListByIdList(List instanceIds); + + /** + * 按照业务id删除流程实例 + * + * @param businessIds 业务id + * @return 结果 + */ + boolean deleteByBusinessIds(List businessIds); + + /** + * 按照实例id删除流程实例 + * + * @param instanceIds 实例id + * @return 结果 + */ + boolean deleteByInstanceIds(List instanceIds); + + /** + * 撤销流程 + * + * @param bo 参数 + * @return 结果 + */ + boolean cancelProcessApply(FlowCancelBo bo); + + /** + * 获取当前登陆人发起的流程实例 + * + * @param instanceBo 流程实例 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery); + + /** + * 获取流程图,流程记录 + * + * @param businessId 业务id + * @return 结果 + */ + Map flowHisTaskList(String businessId); + + /** + * 按照实例id更新状态 + * + * @param instanceId 实例id + * @param status 状态 + */ + void updateStatus(Long instanceId, String status); + + /** + * 获取流程变量 + * + * @param instanceId 实例id + * @return 结果 + */ + Map instanceVariable(Long instanceId); + + /** + * 设置流程变量 + * + * @param instanceId 实例id + * @param variable 流程变量 + */ + void setVariable(Long instanceId, Map variable); + + /** + * 按任务id查询实例 + * + * @param taskId 任务id + * @return 结果 + */ + FlowInstance selectByTaskId(Long taskId); + + /** + * 按任务id查询实例 + * + * @param taskIdList 任务id + * @return 结果 + */ + List selectByTaskIdList(List taskIdList); + + /** + * 作废流程 + * + * @param bo 流程实例 + * @return 结果 + */ + boolean processInvalid(FlowInvalidBo bo); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwNodeExtService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwNodeExtService.java new file mode 100644 index 0000000..77a1134 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwNodeExtService.java @@ -0,0 +1,22 @@ +package com.fuyuanshen.workflow.service; + +import com.fuyuanshen.workflow.domain.vo.ButtonPermissionVo; + +import java.util.List; + +/** + * 流程节点扩展属性 服务层 + * + * @author AprilWind + */ +public interface IFlwNodeExtService { + + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + List buildButtonPermissionsFromExt(String ext); + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskAssigneeService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskAssigneeService.java new file mode 100644 index 0000000..c5337bc --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskAssigneeService.java @@ -0,0 +1,24 @@ +package com.fuyuanshen.workflow.service; + +import com.fuyuanshen.common.core.domain.dto.UserDTO; + +import java.util.List; + +/** + * 流程设计器-获取办理人 + * + * @author AprilWind + */ +public interface IFlwTaskAssigneeService { + + /** + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 + * + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 + */ + List fetchUsersByStorageIds(String storageIds); + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskService.java new file mode 100644 index 0000000..5347ef2 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/IFlwTaskService.java @@ -0,0 +1,218 @@ +package com.fuyuanshen.workflow.service; + +import com.fuyuanshen.common.core.domain.dto.StartProcessReturnDTO; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.domain.bo.*; +import com.fuyuanshen.workflow.domain.vo.FlowHisTaskVo; +import com.fuyuanshen.workflow.domain.vo.FlowTaskVo; +import org.dromara.warm.flow.core.entity.Node; +import org.dromara.warm.flow.core.entity.Task; +import org.dromara.warm.flow.orm.entity.FlowHisTask; +import org.dromara.warm.flow.orm.entity.FlowNode; +import org.dromara.warm.flow.orm.entity.FlowTask; + +import java.util.List; +import java.util.Map; + +/** + * 任务 服务层 + * + * @author may + */ +public interface IFlwTaskService { + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + * @return 结果 + */ + StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo); + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + * @return 结果 + */ + boolean completeTask(CompleteTaskBo completeTaskBo); + + /** + * 添加抄送人 + * + * @param task 任务信息 + * @param flowCopyList 抄送人 + */ + void setCopy(Task task, List flowCopyList); + + /** + * 查询当前用户的待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery); + + /** + * 查询当前租户所有待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery); + + /** + * 查询待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery); + + /** + * 查询已办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery); + + /** + * 查询当前用户的抄送 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery); + + /** + * 修改任务办理人 + * + * @param taskIdList 任务id + * @param userId 用户id + * @return 结果 + */ + boolean updateAssignee(List taskIdList, String userId); + + /** + * 驳回审批 + * + * @param bo 参数 + * @return 结果 + */ + boolean backProcess(BackProcessBo bo); + + /** + * 获取可驳回的前置节点 + * + * @param definitionId 流程定义id + * @param nowNodeCode 当前节点 + * @return 结果 + */ + List getBackTaskNode(Long definitionId, String nowNodeCode); + + /** + * 终止任务 + * + * @param bo 参数 + * @return 结果 + */ + boolean terminationTask(FlowTerminationBo bo); + + /** + * 按照任务id查询任务 + * + * @param taskIdList 任务id + * @return 结果 + */ + List selectByIdList(List taskIdList); + + /** + * 按照任务id查询任务 + * + * @param taskId 任务id + * @return 结果 + */ + FlowTaskVo selectById(Long taskId); + + /** + * 获取下一节点信息 + * + * @param bo 参数 + * @return 结果 + */ + List getNextNodeList(FlowNextNodeBo bo); + + /** + * 按照任务id查询任务 + * + * @param taskIdList 任务id + * @return 结果 + */ + List selectHisTaskByIdList(List taskIdList); + + /** + * 按照任务id查询任务 + * + * @param taskId 任务id + * @return 结果 + */ + FlowHisTask selectHisTaskById(Long taskId); + + /** + * 按照实例id查询任务 + * + * @param instanceIdList 流程实例id + * @return 结果 + */ + List selectByInstIdList(List instanceIdList); + + /** + * 按照实例id查询任务 + * + * @param instanceId 流程实例id + * @return 结果 + */ + List selectByInstId(Long instanceId); + + /** + * 任务操作 + * + * @param bo 参数 + * @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + * @return 结果 + */ + boolean taskOperation(TaskOperationBo bo, String taskOperation); + + /** + * 获取任务所有办理人 + * + * @param taskIdList 任务id + * @return 结果 + */ + Map> currentTaskAllUser(List taskIdList); + + /** + * 获取当前任务的所有办理人 + * + * @param taskId 任务id + * @return 结果 + */ + List currentTaskAllUser(Long taskId); + + /** + * 按照节点编码查询节点 + * + * @param nodeCode 节点编码 + * @param definitionId 流程定义id + * @return 节点 + */ + FlowNode getByNodeCode(String nodeCode, Long definitionId); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/ITestLeaveService.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/ITestLeaveService.java new file mode 100644 index 0000000..44fa20a --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/ITestLeaveService.java @@ -0,0 +1,47 @@ +package com.fuyuanshen.workflow.service; + +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.domain.bo.TestLeaveBo; +import com.fuyuanshen.workflow.domain.vo.TestLeaveVo; + +import java.util.List; + +/** + * 请假Service接口 + * + * @author may + * @date 2023-07-21 + */ +public interface ITestLeaveService { + + /** + * 查询请假 + */ + TestLeaveVo queryById(Long id); + + /** + * 查询请假列表 + */ + TableDataInfo queryPageList(TestLeaveBo bo, PageQuery pageQuery); + + /** + * 查询请假列表 + */ + List queryList(TestLeaveBo bo); + + /** + * 新增请假 + */ + TestLeaveVo insertByBo(TestLeaveBo bo); + + /** + * 修改请假 + */ + TestLeaveVo updateByBo(TestLeaveBo bo); + + /** + * 校验并批量删除请假信息 + */ + Boolean deleteWithValidByIds(List ids); +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/CategoryNameTranslationImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/CategoryNameTranslationImpl.java new file mode 100644 index 0000000..42c3518 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/CategoryNameTranslationImpl.java @@ -0,0 +1,37 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.translation.annotation.TranslationType; +import com.fuyuanshen.common.translation.core.TranslationInterface; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.service.IFlwCategoryService; +import org.springframework.stereotype.Service; + +/** + * 流程分类名称翻译实现 + * + * @author AprilWind + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +@TranslationType(type = FlowConstant.CATEGORY_ID_TO_NAME) +public class CategoryNameTranslationImpl implements TranslationInterface { + + private final IFlwCategoryService flwCategoryService; + + @Override + public String translation(Object key, String other) { + Long id = null; + if (key instanceof String categoryId) { + id = Convert.toLong(categoryId); + } else if (key instanceof Long categoryId) { + id = categoryId; + } + return flwCategoryService.selectCategoryNameById(id); + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCategoryServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCategoryServiceImpl.java new file mode 100644 index 0000000..a97c422 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCategoryServiceImpl.java @@ -0,0 +1,269 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.constant.SystemConstants; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.*; +import com.fuyuanshen.common.mybatis.helper.DataBaseHelper; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.domain.FlowCategory; +import com.fuyuanshen.workflow.domain.bo.FlowCategoryBo; +import com.fuyuanshen.workflow.domain.vo.FlowCategoryVo; +import com.fuyuanshen.workflow.mapper.FlwCategoryMapper; +import com.fuyuanshen.workflow.service.IFlwCategoryService; +import org.dromara.warm.flow.core.service.DefService; +import org.dromara.warm.flow.orm.entity.FlowDefinition; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 流程分类Service业务层处理 + * + * @author may + */ +@ConditionalOnEnable +@RequiredArgsConstructor +@Service +public class FlwCategoryServiceImpl implements IFlwCategoryService { + + private final DefService defService; + private final FlwCategoryMapper baseMapper; + + /** + * 查询流程分类 + * + * @param categoryId 主键 + * @return 流程分类 + */ + @Override + public FlowCategoryVo queryById(Long categoryId) { + FlowCategoryVo category = baseMapper.selectVoById(categoryId); + if (ObjectUtil.isNull(category)) { + return null; + } + FlowCategoryVo parentCategory = baseMapper.selectVoOne(new LambdaQueryWrapper() + .select(FlowCategory::getCategoryName).eq(FlowCategory::getCategoryId, category.getParentId())); + category.setParentName(ObjectUtils.notNullGetter(parentCategory, FlowCategoryVo::getCategoryName)); + return category; + } + + /** + * 根据流程分类ID查询流程分类名称 + * + * @param categoryId 流程分类ID + * @return 流程分类名称 + */ + @Cacheable(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#categoryId") + @Override + public String selectCategoryNameById(Long categoryId) { + if (ObjectUtil.isNull(categoryId)) { + return null; + } + FlowCategory category = baseMapper.selectOne(new LambdaQueryWrapper() + .select(FlowCategory::getCategoryName).eq(FlowCategory::getCategoryId, categoryId)); + return ObjectUtils.notNullGetter(category, FlowCategory::getCategoryName); + } + + /** + * 查询符合条件的流程分类列表 + * + * @param bo 查询条件 + * @return 流程分类列表 + */ + @Override + public List queryList(FlowCategoryBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 查询流程分类树结构信息 + * + * @param category 流程分类信息 + * @return 流程分类树信息集合 + */ + @Override + public List> selectCategoryTreeList(FlowCategoryBo category) { + LambdaQueryWrapper lqw = buildQueryWrapper(category); + List categorys = baseMapper.selectVoList(lqw); + if (CollUtil.isEmpty(categorys)) { + return CollUtil.newArrayList(); + } + // 获取当前列表中每一个节点的parentId,然后在列表中查找是否有id与其parentId对应,若无对应,则表明此时节点列表中,该节点在当前列表中属于顶级节点 + List> treeList = CollUtil.newArrayList(); + for (FlowCategoryVo d : categorys) { + String parentId = d.getParentId().toString(); + FlowCategoryVo categoryVo = StreamUtils.findFirst(categorys, it -> it.getCategoryId().toString().equals(parentId)); + if (ObjectUtil.isNull(categoryVo)) { + List> trees = TreeBuildUtils.build(categorys, parentId, (dept, tree) -> + tree.setId(dept.getCategoryId().toString()) + .setParentId(dept.getParentId().toString()) + .setName(dept.getCategoryName()) + .setWeight(dept.getOrderNum())); + Tree tree = StreamUtils.findFirst(trees, it -> it.getId().equals(d.getCategoryId().toString())); + treeList.add(tree); + } + } + return treeList; + } + + /** + * 校验流程分类是否有数据权限 + * + * @param categoryId 流程分类ID + */ + @Override + public void checkCategoryDataScope(Long categoryId) { + if (ObjectUtil.isNull(categoryId)) { + return; + } + if (LoginHelper.isSuperAdmin()) { + return; + } + if (baseMapper.countCategoryById(categoryId) == 0) { + throw new ServiceException("没有权限访问流程分类数据!"); + } + } + + /** + * 校验流程分类名称是否唯一 + * + * @param category 流程分类信息 + * @return 结果 + */ + @Override + public boolean checkCategoryNameUnique(FlowCategoryBo category) { + boolean exist = baseMapper.exists(new LambdaQueryWrapper() + .eq(FlowCategory::getCategoryName, category.getCategoryName()) + .eq(FlowCategory::getParentId, category.getParentId()) + .ne(ObjectUtil.isNotNull(category.getCategoryId()), FlowCategory::getCategoryId, category.getCategoryId())); + return !exist; + } + + /** + * 查询流程分类是否存在流程定义 + * + * @param categoryId 流程分类ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkCategoryExistDefinition(Long categoryId) { + FlowDefinition definition = new FlowDefinition(); + definition.setCategory(categoryId.toString()); + return defService.exists(definition); + } + + /** + * 是否存在流程分类子节点 + * + * @param categoryId 流程分类ID + * @return 结果 + */ + @Override + public boolean hasChildByCategoryId(Long categoryId) { + return baseMapper.exists(new LambdaQueryWrapper() + .eq(FlowCategory::getParentId, categoryId)); + } + + private LambdaQueryWrapper buildQueryWrapper(FlowCategoryBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(FlowCategory::getDelFlag, SystemConstants.NORMAL); + lqw.eq(ObjectUtil.isNotNull(bo.getCategoryId()), FlowCategory::getCategoryId, bo.getCategoryId()); + lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), FlowCategory::getParentId, bo.getParentId()); + lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), FlowCategory::getCategoryName, bo.getCategoryName()); + lqw.orderByAsc(FlowCategory::getAncestors); + lqw.orderByAsc(FlowCategory::getParentId); + lqw.orderByAsc(FlowCategory::getOrderNum); + lqw.orderByAsc(FlowCategory::getCategoryId); + return lqw; + } + + /** + * 新增流程分类 + * + * @param bo 流程分类 + * @return 是否新增成功 + */ + @Override + public int insertByBo(FlowCategoryBo bo) { + FlowCategory info = baseMapper.selectById(bo.getParentId()); + FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); + category.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + category.getParentId()); + return baseMapper.insert(category); + } + + /** + * 修改流程分类 + * + * @param bo 流程分类 + * @return 是否修改成功 + */ + @CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#bo.categoryId") + @Override + public int updateByBo(FlowCategoryBo bo) { + FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); + FlowCategory oldCategory = baseMapper.selectById(category.getCategoryId()); + if (ObjectUtil.isNull(oldCategory)) { + throw new ServiceException("流程分类不存在,无法修改"); + } + if (!oldCategory.getParentId().equals(category.getParentId())) { + // 如果是新父流程分类 则校验是否具有新父流程分类权限 避免越权 + this.checkCategoryDataScope(category.getParentId()); + FlowCategory newParentCategory = baseMapper.selectById(category.getParentId()); + if (ObjectUtil.isNotNull(newParentCategory)) { + String newAncestors = newParentCategory.getAncestors() + StringUtils.SEPARATOR + newParentCategory.getCategoryId(); + String oldAncestors = oldCategory.getAncestors(); + category.setAncestors(newAncestors); + updateCategoryChildren(category.getCategoryId(), newAncestors, oldAncestors); + } + } else { + category.setAncestors(oldCategory.getAncestors()); + } + return baseMapper.updateById(category); + } + + /** + * 修改子元素关系 + * + * @param categoryId 被修改的流程分类ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + private void updateCategoryChildren(Long categoryId, String newAncestors, String oldAncestors) { + List children = baseMapper.selectList(new LambdaQueryWrapper() + .apply(DataBaseHelper.findInSet(categoryId, "ancestors"))); + List list = new ArrayList<>(); + for (FlowCategory child : children) { + FlowCategory category = new FlowCategory(); + category.setCategoryId(child.getCategoryId()); + category.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + list.add(category); + } + if (CollUtil.isNotEmpty(list)) { + baseMapper.updateBatchById(list); + } + } + + /** + * 删除流程分类信息 + * + * @param categoryId 主键 + * @return 是否删除成功 + */ + @CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#categoryId") + @Override + public int deleteWithValidById(Long categoryId) { + return baseMapper.deleteById(categoryId); + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCommonServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCommonServiceImpl.java new file mode 100644 index 0000000..83963c4 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwCommonServiceImpl.java @@ -0,0 +1,122 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.utils.SpringUtils; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mail.utils.MailUtils; +import com.fuyuanshen.common.sse.dto.SseMessageDto; +import com.fuyuanshen.common.sse.utils.SseMessageUtils; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.enums.MessageTypeEnum; +import com.fuyuanshen.workflow.service.IFlwCommonService; +import com.fuyuanshen.workflow.service.IFlwTaskAssigneeService; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.dromara.warm.flow.core.entity.Node; +import org.dromara.warm.flow.core.entity.Task; +import org.dromara.warm.flow.core.enums.SkipType; +import org.dromara.warm.flow.core.service.NodeService; +import org.dromara.warm.flow.orm.entity.FlowTask; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * 工作流工具 + * + * @author LionLi + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwCommonServiceImpl implements IFlwCommonService { + private final NodeService nodeService; + + /** + * 构建工作流用户 + * + * @param permissionList 办理用户 + * @return 用户 + */ + @Override + public List buildUser(List permissionList) { + if (CollUtil.isEmpty(permissionList)) { + return List.of(); + } + IFlwTaskAssigneeService taskAssigneeService = SpringUtils.getBean(IFlwTaskAssigneeService.class); + String processedBys = CollUtil.join(permissionList, StringUtils.SEPARATOR); + // 根据 processedBy 前缀判断处理人类型,分别获取用户列表 + List users = taskAssigneeService.fetchUsersByStorageIds(processedBys); + + return StreamUtils.toList(users, userDTO -> String.valueOf(userDTO.getUserId())); + } + + + /** + * 发送消息 + * + * @param flowName 流程定义名称 + * @param messageType 消息类型 + * @param message 消息内容,为空则发送默认配置的消息内容 + */ + @Override + public void sendMessage(String flowName, Long instId, List messageType, String message) { + IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); + List userList = new ArrayList<>(); + List list = flwTaskService.selectByInstId(instId); + if (StringUtils.isBlank(message)) { + message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。"; + } + for (Task task : list) { + List users = flwTaskService.currentTaskAllUser(task.getId()); + if (CollUtil.isNotEmpty(users)) { + userList.addAll(users); + } + } + if (CollUtil.isNotEmpty(userList)) { + for (String code : messageType) { + MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); + if (ObjectUtil.isNotEmpty(messageTypeEnum)) { + switch (messageTypeEnum) { + case SYSTEM_MESSAGE: + SseMessageDto dto = new SseMessageDto(); + dto.setUserIds(StreamUtils.toList(userList, UserDTO::getUserId).stream().distinct().collect(Collectors.toList())); + dto.setMessage(message); + SseMessageUtils.publishMessage(dto); + break; + case EMAIL_MESSAGE: + MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "单据审批提醒", message); + break; + case SMS_MESSAGE: + //todo 短信发送 + break; + default: + throw new IllegalStateException("Unexpected value: " + messageTypeEnum); + } + } + } + } + } + + + /** + * 申请人节点编码 + * + * @param definitionId 流程定义id + * @return 申请人节点编码 + */ + @Override + public String applyNodeCode(Long definitionId) { + Node startNode = nodeService.getStartNode(definitionId); + Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); + return nextNode.getNodeCode(); + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwDefinitionServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwDefinitionServiceImpl.java new file mode 100644 index 0000000..5ae6528 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -0,0 +1,272 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.domain.FlowCategory; +import com.fuyuanshen.workflow.domain.vo.FlowDefinitionVo; +import com.fuyuanshen.workflow.mapper.FlwCategoryMapper; +import com.fuyuanshen.workflow.service.IFlwCommonService; +import com.fuyuanshen.workflow.service.IFlwDefinitionService; +import org.dromara.warm.flow.core.dto.DefJson; +import org.dromara.warm.flow.core.enums.NodeType; +import org.dromara.warm.flow.core.enums.PublishStatus; +import org.dromara.warm.flow.core.service.DefService; +import org.dromara.warm.flow.orm.entity.FlowDefinition; +import org.dromara.warm.flow.orm.entity.FlowHisTask; +import org.dromara.warm.flow.orm.entity.FlowNode; +import org.dromara.warm.flow.orm.entity.FlowSkip; +import org.dromara.warm.flow.orm.mapper.FlowDefinitionMapper; +import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper; +import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; +import org.dromara.warm.flow.orm.mapper.FlowSkipMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.fuyuanshen.common.core.constant.TenantConstants.DEFAULT_TENANT_ID; + +/** + * 流程定义 服务层实现 + * + * @author may + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwDefinitionServiceImpl implements IFlwDefinitionService { + + private final DefService defService; + private final FlowDefinitionMapper flowDefinitionMapper; + private final FlowHisTaskMapper flowHisTaskMapper; + private final FlowNodeMapper flowNodeMapper; + private final FlowSkipMapper flowSkipMapper; + private final FlwCategoryMapper flwCategoryMapper; + private final IFlwCommonService flwCommonService; + + /** + * 查询流程定义列表 + * + * @param flowDefinition 流程定义信息 + * @param pageQuery 分页 + * @return 返回分页列表 + */ + @Override + public TableDataInfo queryList(FlowDefinition flowDefinition, PageQuery pageQuery) { + LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinition); + wrapper.eq(FlowDefinition::getIsPublish, PublishStatus.PUBLISHED.getKey()); + Page page = flowDefinitionMapper.selectPage(pageQuery.build(), wrapper); + TableDataInfo build = TableDataInfo.build(); + build.setRows(BeanUtil.copyToList(page.getRecords(), FlowDefinitionVo.class)); + build.setTotal(page.getTotal()); + return build; + } + + /** + * 查询未发布的流程定义列表 + * + * @param flowDefinition 流程定义信息 + * @param pageQuery 分页 + * @return 返回分页列表 + */ + @Override + public TableDataInfo unPublishList(FlowDefinition flowDefinition, PageQuery pageQuery) { + LambdaQueryWrapper wrapper = buildQueryWrapper(flowDefinition); + wrapper.in(FlowDefinition::getIsPublish, Arrays.asList(PublishStatus.UNPUBLISHED.getKey(), PublishStatus.EXPIRED.getKey())); + Page page = flowDefinitionMapper.selectPage(pageQuery.build(), wrapper); + TableDataInfo build = TableDataInfo.build(); + build.setRows(BeanUtil.copyToList(page.getRecords(), FlowDefinitionVo.class)); + build.setTotal(page.getTotal()); + return build; + } + + private LambdaQueryWrapper buildQueryWrapper(FlowDefinition flowDefinition) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.like(StringUtils.isNotBlank(flowDefinition.getFlowCode()), FlowDefinition::getFlowCode, flowDefinition.getFlowCode()); + wrapper.like(StringUtils.isNotBlank(flowDefinition.getFlowName()), FlowDefinition::getFlowName, flowDefinition.getFlowName()); + if (StringUtils.isNotBlank(flowDefinition.getCategory())) { + List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowDefinition.getCategory())); + wrapper.in(FlowDefinition::getCategory, StreamUtils.toList(categoryIds, Convert::toStr)); + } + wrapper.orderByDesc(FlowDefinition::getCreateTime); + return wrapper; + } + + /** + * 发布流程定义 + * + * @param id 流程定义id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean publish(Long id) { + List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, id)); + List errorMsg = new ArrayList<>(); + if (CollUtil.isNotEmpty(flowNodes)) { + for (FlowNode flowNode : flowNodes) { + String applyNodeCode = flwCommonService.applyNodeCode(id); + if (StringUtils.isBlank(flowNode.getPermissionFlag()) && !applyNodeCode.equals(flowNode.getNodeCode()) && NodeType.BETWEEN.getKey().equals(flowNode.getNodeType())) { + errorMsg.add(flowNode.getNodeName()); + } + } + if (CollUtil.isNotEmpty(errorMsg)) { + throw new ServiceException("节点【" + StringUtils.join(errorMsg, ",") + "】未配置办理人!"); + } + } + return defService.publish(id); + } + + /** + * 导入流程定义 + * + * @param file 文件 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean importJson(MultipartFile file, String category) { + try { + DefJson defJson = JsonUtils.parseObject(file.getBytes(), DefJson.class); + defJson.setCategory(category); + defService.importDef(defJson); + } catch (IOException e) { + log.error("读取文件流错误: {}", e.getMessage(), e); + throw new IllegalStateException("文件读取失败,请检查文件内容", e); + } + return true; + } + + /** + * 导出流程定义 + * + * @param id 流程定义id + * @param response 响应 + * @throws IOException 异常 + */ + @Override + public void exportDef(Long id, HttpServletResponse response) throws IOException { + byte[] data = defService.exportJson(id).getBytes(StandardCharsets.UTF_8); + // 设置响应头和内容类型 + response.reset(); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + response.setContentType("application/text"); + response.setHeader("Content-Disposition", "attachment;"); + response.addHeader("Content-Length", "" + data.length); + IoUtil.write(response.getOutputStream(), false, data); + } + + /** + * 删除流程定义 + * + * @param ids 流程定义id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean removeDef(List ids) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.in(FlowHisTask::getDefinitionId, ids); + List flowHisTasks = flowHisTaskMapper.selectList(wrapper); + if (CollUtil.isNotEmpty(flowHisTasks)) { + List flowDefinitions = flowDefinitionMapper.selectByIds(StreamUtils.toList(flowHisTasks, FlowHisTask::getDefinitionId)); + if (CollUtil.isNotEmpty(flowDefinitions)) { + String join = StreamUtils.join(flowDefinitions, FlowDefinition::getFlowCode); + log.info("流程定义【{}】已被使用不可被删除!", join); + throw new ServiceException("流程定义【" + join + "】已被使用不可被删除!"); + } + } + try { + defService.removeDef(ids); + } catch (Exception e) { + log.error("Error removing flow definitions: {}", e.getMessage(), e); + throw new RuntimeException("Failed to remove flow definitions", e); + } + return true; + } + + /** + * 新增租户流程定义 + * + * @param tenantId 租户id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void syncDef(String tenantId) { + List flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper().eq(FlowDefinition::getTenantId, DEFAULT_TENANT_ID)); + if (CollUtil.isEmpty(flowDefinitions)) { + return; + } + FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper() + .eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID).eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID)); + flowCategory.setCategoryId(null); + flowCategory.setTenantId(tenantId); + flowCategory.setCreateDept(null); + flowCategory.setCreateBy(null); + flowCategory.setCreateTime(null); + flowCategory.setUpdateBy(null); + flowCategory.setUpdateTime(null); + flwCategoryMapper.insert(flowCategory); + List defIds = StreamUtils.toList(flowDefinitions, FlowDefinition::getId); + List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().in(FlowNode::getDefinitionId, defIds)); + List flowSkips = flowSkipMapper.selectList(new LambdaQueryWrapper().in(FlowSkip::getDefinitionId, defIds)); + for (FlowDefinition definition : flowDefinitions) { + FlowDefinition flowDefinition = BeanUtil.toBean(definition, FlowDefinition.class); + flowDefinition.setId(null); + flowDefinition.setTenantId(tenantId); + flowDefinition.setIsPublish(0); + flowDefinition.setCategory(String.valueOf(flowCategory.getCategoryId())); + int insert = flowDefinitionMapper.insert(flowDefinition); + if (insert <= 0) { + log.info("同步流程定义【{}】失败!", definition.getFlowCode()); + continue; + } + log.info("同步流程定义【{}】成功!", definition.getFlowCode()); + Long definitionId = flowDefinition.getId(); + if (CollUtil.isNotEmpty(flowNodes)) { + List nodes = StreamUtils.filter(flowNodes, node -> node.getDefinitionId().equals(definition.getId())); + if (CollUtil.isNotEmpty(nodes)) { + List flowNodeList = BeanUtil.copyToList(nodes, FlowNode.class); + flowNodeList.forEach(e -> { + e.setId(null); + e.setDefinitionId(definitionId); + e.setTenantId(tenantId); + e.setPermissionFlag(null); + }); + flowNodeMapper.insertOrUpdate(flowNodeList); + } + } + if (CollUtil.isNotEmpty(flowSkips)) { + List skips = StreamUtils.filter(flowSkips, skip -> skip.getDefinitionId().equals(definition.getId())); + if (CollUtil.isNotEmpty(skips)) { + List flowSkipList = BeanUtil.copyToList(skips, FlowSkip.class); + flowSkipList.forEach(e -> { + e.setId(null); + e.setDefinitionId(definitionId); + e.setTenantId(tenantId); + }); + flowSkipMapper.insertOrUpdate(flowSkipList); + } + } + } + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwInstanceServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwInstanceServiceImpl.java new file mode 100644 index 0000000..d2530ca --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwInstanceServiceImpl.java @@ -0,0 +1,430 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.enums.BusinessStatusEnum; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.enums.TaskStatusEnum; +import com.fuyuanshen.workflow.domain.bo.FlowCancelBo; +import com.fuyuanshen.workflow.domain.bo.FlowInstanceBo; +import com.fuyuanshen.workflow.domain.bo.FlowInvalidBo; +import com.fuyuanshen.workflow.domain.vo.FlowHisTaskVo; +import com.fuyuanshen.workflow.domain.vo.FlowInstanceVo; +import com.fuyuanshen.workflow.handler.FlowProcessEventHandler; +import com.fuyuanshen.workflow.mapper.FlwCategoryMapper; +import com.fuyuanshen.workflow.mapper.FlwInstanceMapper; +import com.fuyuanshen.workflow.service.IFlwInstanceService; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.dromara.warm.flow.core.constant.ExceptionCons; +import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.entity.Definition; +import org.dromara.warm.flow.core.entity.Instance; +import org.dromara.warm.flow.core.entity.Task; +import org.dromara.warm.flow.core.enums.NodeType; +import org.dromara.warm.flow.core.service.DefService; +import org.dromara.warm.flow.core.service.InsService; +import org.dromara.warm.flow.core.service.TaskService; +import org.dromara.warm.flow.orm.entity.FlowHisTask; +import org.dromara.warm.flow.orm.entity.FlowInstance; +import org.dromara.warm.flow.orm.entity.FlowTask; +import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper; +import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 流程实例 服务层实现 + * + * @author may + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwInstanceServiceImpl implements IFlwInstanceService { + + private final InsService insService; + private final DefService defService; + private final TaskService taskService; + private final FlowHisTaskMapper flowHisTaskMapper; + private final FlowInstanceMapper flowInstanceMapper; + private final FlowProcessEventHandler flowProcessEventHandler; + private final IFlwTaskService flwTaskService; + private final FlwInstanceMapper flwInstanceMapper; + private final FlwCategoryMapper flwCategoryMapper; + + /** + * 分页查询正在运行的流程实例 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowInstanceBo); + queryWrapper.in("fi.flow_status", BusinessStatusEnum.runningStatus()); + Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 分页查询已结束的流程实例 + * + * @param flowInstanceBo 流程实例 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowInstanceBo); + queryWrapper.in("fi.flow_status", BusinessStatusEnum.finishStatus()); + Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 根据业务id查询流程实例详细信息 + * + * @param businessId 业务id + * @return 结果 + */ + @Override + public FlowInstanceVo queryByBusinessId(Long businessId) { + FlowInstance instance = this.selectInstByBusinessId(String.valueOf(businessId)); + FlowInstanceVo instanceVo = BeanUtil.toBean(instance, FlowInstanceVo.class); + Definition definition = defService.getById(instanceVo.getDefinitionId()); + instanceVo.setFlowName(definition.getFlowName()); + instanceVo.setFlowCode(definition.getFlowCode()); + instanceVo.setVersion(definition.getVersion()); + instanceVo.setFormCustom(definition.getFormCustom()); + instanceVo.setFormPath(definition.getFormPath()); + instanceVo.setCategory(definition.getCategory()); + return instanceVo; + } + + /** + * 通用查询条件 + * + * @param flowInstanceBo 查询条件 + * @return 查询条件构造方法 + */ + private QueryWrapper buildQueryWrapper(FlowInstanceBo flowInstanceBo) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getNodeName()), "fi.node_name", flowInstanceBo.getNodeName()); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowName()), "fd.flow_name", flowInstanceBo.getFlowName()); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowCode()), "fd.flow_code", flowInstanceBo.getFlowCode()); + if (StringUtils.isNotBlank(flowInstanceBo.getCategory())) { + List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory())); + queryWrapper.in("fd.category", StreamUtils.toList(categoryIds, Convert::toStr)); + } + queryWrapper.eq(StringUtils.isNotBlank(flowInstanceBo.getBusinessId()), "fi.business_id", flowInstanceBo.getBusinessId()); + queryWrapper.in(CollUtil.isNotEmpty(flowInstanceBo.getCreateByIds()), "fi.create_by", flowInstanceBo.getCreateByIds()); + queryWrapper.eq("fi.del_flag", "0"); + queryWrapper.orderByDesc("fi.create_time"); + return queryWrapper; + } + + /** + * 根据业务id查询流程实例 + * + * @param businessId 业务id + */ + @Override + public FlowInstance selectInstByBusinessId(String businessId) { + return flowInstanceMapper.selectOne(new LambdaQueryWrapper().eq(FlowInstance::getBusinessId, businessId)); + } + + /** + * 按照实例id查询流程实例 + * + * @param instanceId 实例id + */ + @Override + public FlowInstance selectInstById(Long instanceId) { + return flowInstanceMapper.selectById(instanceId); + } + + /** + * 按照实例id查询流程实例 + * + * @param instanceIds 实例id + */ + @Override + public List selectInstListByIdList(List instanceIds) { + return flowInstanceMapper.selectByIds(instanceIds); + } + + /** + * 按照业务id删除流程实例 + * + * @param businessIds 业务id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteByBusinessIds(List businessIds) { + List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); + if (CollUtil.isEmpty(flowInstances)) { + log.warn("未找到对应的流程实例信息,无法执行删除操作。"); + return false; + } + return insService.remove(StreamUtils.toList(flowInstances, FlowInstance::getId)); + } + + /** + * 按照实例id删除流程实例 + * + * @param instanceIds 实例id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteByInstanceIds(List instanceIds) { + // 获取实例信息 + List instances = insService.getByIds(instanceIds); + if (CollUtil.isEmpty(instances)) { + log.warn("未找到对应的流程实例信息,无法执行删除操作。"); + return false; + } + // 获取定义信息 + Map definitionMap = defService.getByIds( + StreamUtils.toList(instances, Instance::getDefinitionId) + ).stream().collect(Collectors.toMap(Definition::getId, definition -> definition)); + + // 逐一触发删除事件 + instances.forEach(instance -> { + Definition definition = definitionMap.get(instance.getDefinitionId()); + if (ObjectUtil.isNull(definition)) { + log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); + return; + } + flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); + }); + + // 删除实例 + return insService.remove(instanceIds); + } + + /** + * 撤销流程 + * + * @param bo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean cancelProcessApply(FlowCancelBo bo) { + try { + Instance instance = selectInstByBusinessId(bo.getBusinessId()); + if (instance == null) { + throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); + } + Definition definition = defService.getById(instance.getDefinitionId()); + if (definition == null) { + throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF); + } + String message = bo.getMessage(); + String userIdStr = LoginHelper.getUserIdStr(); + BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); + FlowParams flowParams = FlowParams.build() + .message(message) + .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) + .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) + .handler(userIdStr) + .ignore(true); + taskService.revoke(instance.getId(), flowParams); + } catch (Exception e) { + log.error("撤销失败: {}", e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + return true; + } + + /** + * 获取当前登陆人发起的流程实例 + * + * @param instanceBo 流程实例 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(instanceBo); + queryWrapper.eq("fi.create_by", LoginHelper.getUserIdStr()); + Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 获取流程图,流程记录 + * + * @param businessId 业务id + */ + @Override + public Map flowHisTaskList(String businessId) { + FlowInstance flowInstance = this.selectInstByBusinessId(businessId); + if (ObjectUtil.isNull(flowInstance)) { + throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); + } + Long instanceId = flowInstance.getId(); + //运行中的任务 + List list = new ArrayList<>(); + List flowTaskList = flwTaskService.selectByInstId(instanceId); + if (CollUtil.isNotEmpty(flowTaskList)) { + List flowHisTaskVos = BeanUtil.copyToList(flowTaskList, FlowHisTaskVo.class); + for (FlowHisTaskVo flowHisTaskVo : flowHisTaskVos) { + flowHisTaskVo.setFlowStatus(TaskStatusEnum.WAITING.getStatus()); + flowHisTaskVo.setUpdateTime(null); + flowHisTaskVo.setRunDuration(null); + List allUser = flwTaskService.currentTaskAllUser(flowHisTaskVo.getId()); + if (CollUtil.isNotEmpty(allUser)) { + String join = StreamUtils.join(allUser, e -> String.valueOf(e.getUserId())); + flowHisTaskVo.setApprover(join); + } + if (BusinessStatusEnum.isDraftOrCancelOrBack(flowInstance.getFlowStatus())) { + flowHisTaskVo.setApprover(LoginHelper.getUserIdStr()); + flowHisTaskVo.setApproveName(LoginHelper.getLoginUser().getNickname()); + } + } + list.addAll(flowHisTaskVos); + } + //历史任务 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(FlowHisTask::getInstanceId, instanceId); + wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()); + wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime); + List flowHisTasks = flowHisTaskMapper.selectList(wrapper); + if (CollUtil.isNotEmpty(flowHisTasks)) { + list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); + } + return Map.of("list", list,"instanceId",instanceId); + } + + /** + * 按照实例id更新状态 + * + * @param instanceId 实例id + * @param status 状态 + */ + @Override + public void updateStatus(Long instanceId, String status) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.set(FlowInstance::getFlowStatus, status); + wrapper.eq(FlowInstance::getId, instanceId); + flowInstanceMapper.update(wrapper); + } + + /** + * 获取流程变量 + * + * @param instanceId 实例id + */ + @Override + public Map instanceVariable(Long instanceId) { + FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); + Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()); + List> variableList = variableMap.entrySet().stream() + .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) + .toList(); + return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); + } + + /** + * 设置流程变量 + * + * @param instanceId 实例id + * @param variable 流程变量 + */ + @Override + public void setVariable(Long instanceId, Map variable) { + Instance instance = insService.getById(instanceId); + if (instance != null) { + taskService.mergeVariable(instance, variable); + insService.updateById(instance); + } + } + + /** + * 按任务id查询实例 + * + * @param taskId 任务id + */ + @Override + public FlowInstance selectByTaskId(Long taskId) { + Task task = taskService.getById(taskId); + if (task == null) { + FlowHisTask flowHisTask = flwTaskService.selectHisTaskById(taskId); + if (flowHisTask != null) { + return this.selectInstById(flowHisTask.getInstanceId()); + } + } else { + return this.selectInstById(task.getInstanceId()); + } + return null; + } + + /** + * 按任务id查询实例 + * + * @param taskIdList 任务id + */ + @Override + public List selectByTaskIdList(List taskIdList) { + if (CollUtil.isEmpty(taskIdList)) { + return Collections.emptyList(); + } + Set instanceIds = new HashSet<>(); + List flowTaskList = flwTaskService.selectByIdList(taskIdList); + for (FlowTask flowTask : flowTaskList) { + instanceIds.add(flowTask.getInstanceId()); + } + List flowHisTaskList = flwTaskService.selectHisTaskByIdList(taskIdList); + for (FlowHisTask flowHisTask : flowHisTaskList) { + instanceIds.add(flowHisTask.getInstanceId()); + } + if (!instanceIds.isEmpty()) { + return this.selectInstListByIdList(new ArrayList<>(instanceIds)); + } + return Collections.emptyList(); + } + + /** + * 作废流程 + * + * @param bo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean processInvalid(FlowInvalidBo bo) { + try { + Instance instance = insService.getById(bo.getId()); + if (instance != null) { + BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); + } + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.INVALID.getStatus()) + .hisStatus(TaskStatusEnum.INVALID.getStatus()) + .ignore(true); + taskService.terminationByInsId(bo.getId(), flowParams); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + } +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwNodeExtServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwNodeExtServiceImpl.java new file mode 100644 index 0000000..f7e5e27 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -0,0 +1,243 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.dto.DictTypeDTO; +import com.fuyuanshen.common.core.service.DictService; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.json.utils.JsonUtils; +import org.dromara.warm.flow.ui.service.NodeExtService; +import org.dromara.warm.flow.ui.vo.NodeExt; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.enums.ButtonPermissionEnum; +import com.fuyuanshen.workflow.common.enums.NodeExtEnum; +import com.fuyuanshen.workflow.domain.vo.ButtonPermissionVo; +import com.fuyuanshen.workflow.service.IFlwNodeExtService; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 流程设计器-节点扩展属性 + * + * @author AprilWind + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService { + + /** + * 存储不同 dictType 对应的配置信息 + */ + private static final Map CHILD_NODE_MAP = new HashMap<>(); + + record ButtonPermission(String label, Integer type, Boolean must, Boolean multiple) { + } + + static { + CHILD_NODE_MAP.put(ButtonPermissionEnum.class.getSimpleName(), + new ButtonPermission("权限按钮", 4, false, true)); + } + + private final DictService dictService; + + /** + * 获取节点扩展属性 + * + * @return 节点扩展属性列表 + */ + @Override + public List getNodeExt() { + List nodeExtList = new ArrayList<>(); + // 构建按钮权限页面 + nodeExtList.add(buildNodeExt("wf_button_tab", "权限", 2, + List.of(ButtonPermissionEnum.class))); + // 自定义构建 规则参考 NodeExt 与 warm-flow文档说明 + // nodeExtList.add(buildNodeExt("xxx_xxx", "xxx", 1, List); + return nodeExtList; + } + + /** + * 构建一个 `NodeExt` 对象 + * + * @param code 唯一编码 + * @param name 名称(新页签时,作为页签名称) + * @param type 节点类型(1: 基础设置,2: 新页签) + * @param sources 数据来源(枚举类或字典类型) + * @return 构建的 `NodeExt` 对象 + */ + @SuppressWarnings("unchecked cast") + private NodeExt buildNodeExt(String code, String name, int type, List sources) { + NodeExt nodeExt = new NodeExt(); + nodeExt.setCode(code); + nodeExt.setType(type); + nodeExt.setName(name); + nodeExt.setChilds(sources.stream() + .map(source -> { + if (source instanceof Class clazz && NodeExtEnum.class.isAssignableFrom(clazz)) { + return buildChildNode((Class) clazz); + } else if (source instanceof String dictType) { + return buildChildNode(dictType); + } + return null; + }) + .filter(ObjectUtil::isNotNull) + .toList() + ); + return nodeExt; + } + + /** + * 根据枚举类型构建一个 `ChildNode` 对象 + * + * @param enumClass 枚举类,必须实现 `NodeExtEnum` 接口 + * @return 构建的 `ChildNode` 对象 + */ + private NodeExt.ChildNode buildChildNode(Class enumClass) { + if (!enumClass.isEnum()) { + return null; + } + String simpleName = enumClass.getSimpleName(); + NodeExt.ChildNode childNode = buildChildNodeMap(simpleName); + // 编码,此json中唯 + childNode.setCode(simpleName); + // 字典,下拉框和复选框时用到 + childNode.setDict(Arrays.stream(enumClass.getEnumConstants()) + .map(NodeExtEnum.class::cast) + .map(x -> + new NodeExt.DictItem(x.getLabel(), x.getValue(), x.isSelected()) + ).toList()); + return childNode; + } + + /** + * 根据字典类型构建 `ChildNode` 对象 + * + * @param dictType 字典类型 + * @return 构建的 `ChildNode` 对象 + */ + private NodeExt.ChildNode buildChildNode(String dictType) { + DictTypeDTO dictTypeDTO = dictService.getDictType(dictType); + if (ObjectUtil.isNull(dictTypeDTO)) { + return null; + } + NodeExt.ChildNode childNode = buildChildNodeMap(dictType); + // 编码,此json中唯一 + childNode.setCode(dictType); + // label名称 + childNode.setLabel(dictTypeDTO.getDictName()); + // 描述 + childNode.setDesc(dictTypeDTO.getRemark()); + // 字典,下拉框和复选框时用到 + childNode.setDict(dictService.getDictData(dictType) + .stream().map(x -> + new NodeExt.DictItem(x.getDictLabel(), x.getDictValue(), Convert.toBool(x.getIsDefault(), false)) + ).toList()); + return childNode; + } + + /** + * 根据 CHILD_NODE_MAP 中的配置信息,构建一个基本的 ChildNode 对象 + * 该方法用于设置 ChildNode 的常规属性,例如 label、type、是否必填、是否多选等 + * + * @param key CHILD_NODE_MAP 的 key + * @return 返回构建好的 ChildNode 对象 + */ + private NodeExt.ChildNode buildChildNodeMap(String key) { + NodeExt.ChildNode childNode = new NodeExt.ChildNode(); + ButtonPermission bp = CHILD_NODE_MAP.get(key); + if (bp == null) { + childNode.setType(1); + childNode.setMust(false); + childNode.setMultiple(true); + return childNode; + } + // label名称 + childNode.setLabel(bp.label()); + // 1:输入框 2:输入框 3:下拉框 4:选择框 + childNode.setType(bp.type()); + // 是否必填 + childNode.setMust(bp.must()); + // 是否多选 + childNode.setMultiple(bp.multiple()); + return childNode; + } + + /** + * 从扩展属性构建按钮权限列表:根据 ext 中记录的权限值,标记每个按钮是否勾选 + * + * @param ext 扩展属性 JSON 字符串 + * @return 按钮权限 VO 列表 + */ + @Override + public List buildButtonPermissionsFromExt(String ext) { + // 解析 ext 为 Map>,用于标记权限 + Map> permissionMap = JsonUtils.parseArray(ext, ButtonPermissionVo.class) + .stream() + .collect(Collectors.toMap( + ButtonPermissionVo::getCode, + item -> StringUtils.splitList(item.getValue()).stream() + .map(String::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()), + (a, b) -> b, + HashMap::new + )); + + // 构建按钮权限列表,标记哪些按钮在 permissionMap 中出现(表示已勾选) + return buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class)); + } + + /** + * 将权限映射与按钮权限来源(枚举类或字典类型)进行匹配,生成权限视图列表 + *

+ * 使用说明: + * - sources 支持传入多个来源类型,支持 NodeExtEnum 枚举类 或 字典类型字符串(dictType) + * - 若需要扩展更多按钮权限,只需在 sources 中新增对应的枚举类或字典类型 + *

+ * 示例: + * buildPermissionsFromSources(permissionMap, List.of(ButtonPermissionEnum.class, "custom_button_dict")); + * + * @param permissionMap 权限映射 + * @param sources 枚举类或字典类型列表 + * @return 按钮权限视图对象列表 + */ + @SuppressWarnings("unchecked cast") + private List buildPermissionsFromSources(Map> permissionMap, List sources) { + return sources.stream() + .flatMap(source -> { + if (source instanceof Class clazz && NodeExtEnum.class.isAssignableFrom(clazz)) { + Set selectedSet = permissionMap.getOrDefault(clazz.getSimpleName(), Collections.emptySet()); + return extractDictItems(this.buildChildNode((Class) clazz), selectedSet).stream(); + } else if (source instanceof String dictType) { + Set selectedSet = permissionMap.getOrDefault(dictType, Collections.emptySet()); + return extractDictItems(this.buildChildNode(dictType), selectedSet).stream(); + } + return Stream.empty(); + }).toList(); + } + + /** + * 从节点子项中提取字典项,并构建按钮权限视图对象列表 + * + * @param childNode 子节点 + * @param selectedSet 已选中的值集 + * @return 按钮权限视图对象列表 + */ + private List extractDictItems(NodeExt.ChildNode childNode, Set selectedSet) { + return Optional.ofNullable(childNode) + .map(NodeExt.ChildNode::getDict) + .orElse(List.of()) + .stream() + .map(dict -> new ButtonPermissionVo(dict.getValue(), selectedSet.contains(dict.getValue()))) + .toList(); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskAssigneeServiceImpl.java new file mode 100644 index 0000000..025cf0d --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -0,0 +1,250 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.dto.DeptDTO; +import com.fuyuanshen.common.core.domain.dto.TaskAssigneeDTO; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.domain.model.TaskAssigneeBody; +import com.fuyuanshen.common.core.enums.FormatsType; +import com.fuyuanshen.common.core.service.DeptService; +import com.fuyuanshen.common.core.service.TaskAssigneeService; +import com.fuyuanshen.common.core.service.UserService; +import com.fuyuanshen.common.core.utils.DateUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.enums.TaskAssigneeEnum; +import com.fuyuanshen.workflow.service.IFlwTaskAssigneeService; +import org.dromara.warm.flow.ui.dto.HandlerFunDto; +import org.dromara.warm.flow.ui.dto.HandlerQuery; +import org.dromara.warm.flow.ui.dto.TreeFunDto; +import org.dromara.warm.flow.ui.service.HandlerSelectService; +import org.dromara.warm.flow.ui.vo.HandlerFeedBackVo; +import org.dromara.warm.flow.ui.vo.HandlerSelectVo; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 流程设计器-获取办理人权限设置列表 + * + * @author AprilWind + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, HandlerSelectService { + + private static final String DEFAULT_GROUP_NAME = "默认分组"; + private final TaskAssigneeService taskAssigneeService; + private final UserService userService; + private final DeptService deptService; + + /** + * 获取办理人权限设置列表tabs页签 + * + * @return tabs页签 + */ + @Override + public List getHandlerType() { + return TaskAssigneeEnum.getAssigneeTypeList(); + } + + /** + * 获取办理列表, 同时构建左侧部门树状结构 + * + * @param query 查询条件 + * @return HandlerSelectVo + */ + @Override + public HandlerSelectVo getHandlerSelect(HandlerQuery query) { + // 获取任务办理类型 + TaskAssigneeEnum type = TaskAssigneeEnum.fromDesc(query.getHandlerType()); + // 转换查询条件为 TaskAssigneeBody + TaskAssigneeBody taskQuery = BeanUtil.toBean(query, TaskAssigneeBody.class); + + // 统一查询并构建业务数据 + TaskAssigneeDTO dto = fetchTaskAssigneeData(type, taskQuery); + List depts = fetchDeptData(type); + + return getHandlerSelectVo(buildHandlerData(dto, type), buildDeptTree(depts)); + } + + /** + * 办理人权限名称回显 + * + * @param storageIds 入库主键集合 + * @return 结果 + */ + @Override + public List handlerFeedback(List storageIds) { + if (CollUtil.isEmpty(storageIds)) { + return Collections.emptyList(); + } + // 解析并归类 ID,同时记录原始顺序和对应解析结果 + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + Map> parsedMap = new LinkedHashMap<>(); + for (String storageId : storageIds) { + Pair parsed = this.parseStorageId(storageId); + parsedMap.put(storageId, parsed); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + + // 查询所有类型对应的 ID 名称映射 + Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); + typeIdMap.forEach((type, ids) -> nameMap.put(type, this.getNamesByType(type, ids))); + + // 组装返回结果,保持原始顺序 + return parsedMap.entrySet().stream() + .map(entry -> { + String storageId = entry.getKey(); + Pair parsed = entry.getValue(); + String handlerName = "格式错误"; + if (parsed != null) { + Map nameMapping = nameMap.getOrDefault(parsed.getKey(), Collections.emptyMap()); + handlerName = nameMapping.getOrDefault(parsed.getValue(), "未知名称"); + } + HandlerFeedBackVo backVo = new HandlerFeedBackVo(); + backVo.setStorageId(storageId); + backVo.setHandlerName(handlerName); + return backVo; + }).toList(); + } + + /** + * 根据任务办理类型查询对应的数据 + */ + private TaskAssigneeDTO fetchTaskAssigneeData(TaskAssigneeEnum type, TaskAssigneeBody taskQuery) { + return switch (type) { + case USER -> taskAssigneeService.selectUsersByTaskAssigneeList(taskQuery); + case ROLE -> taskAssigneeService.selectRolesByTaskAssigneeList(taskQuery); + case DEPT -> taskAssigneeService.selectDeptsByTaskAssigneeList(taskQuery); + case POST -> taskAssigneeService.selectPostsByTaskAssigneeList(taskQuery); + }; + } + + /** + * 根据任务办理类型获取部门数据 + */ + private List fetchDeptData(TaskAssigneeEnum type) { + if (type == TaskAssigneeEnum.USER || type == TaskAssigneeEnum.DEPT || type == TaskAssigneeEnum.POST) { + return deptService.selectDeptsByList(); + } + return new ArrayList<>(); + } + + /** + * 构建部门树状结构 + */ + private TreeFunDto buildDeptTree(List depts) { + return new TreeFunDto<>(depts) + .setId(dept -> String.valueOf(dept.getDeptId())) + .setName(DeptDTO::getDeptName) + .setParentId(dept -> String.valueOf(dept.getParentId())); + } + + /** + * 构建任务办理人数据 + */ + private HandlerFunDto buildHandlerData(TaskAssigneeDTO dto, TaskAssigneeEnum type) { + return new HandlerFunDto<>(dto.getList(), dto.getTotal()) + .setStorageId(assignee -> type.getCode() + assignee.getStorageId()) + .setHandlerCode(assignee -> StringUtils.blankToDefault(assignee.getHandlerCode(), "无")) + .setHandlerName(assignee -> StringUtils.blankToDefault(assignee.getHandlerName(), "无")) + .setGroupName(assignee -> StringUtils.defaultIfBlank( + Optional.ofNullable(assignee.getGroupName()) + .map(deptService::selectDeptNameByIds) + .orElse(DEFAULT_GROUP_NAME), DEFAULT_GROUP_NAME)) + .setCreateTime(assignee -> DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, assignee.getCreateTime())); + } + + /** + * 批量解析多个存储标识符(storageIds),按类型分类并合并查询用户列表 + * 输入格式支持多个以逗号分隔的标识(如 "user:123,role:456,789") + * 会自动去重返回结果,非法格式的标识将被忽略 + * + * @param storageIds 多个存储标识符字符串(逗号分隔) + * @return 合并后的用户列表,去重后返回,非法格式的标识将被跳过 + */ + @Override + public List fetchUsersByStorageIds(String storageIds) { + if (StringUtils.isEmpty(storageIds)) { + return List.of(); + } + Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); + for (String storageId : storageIds.split(StringUtils.SEPARATOR)) { + Pair parsed = this.parseStorageId(storageId); + if (parsed != null) { + typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); + } + } + return typeIdMap.entrySet().stream() + .flatMap(entry -> this.getUsersByType(entry.getKey(), entry.getValue()).stream()) + .distinct() + .toList(); + } + + /** + * 根据指定的任务分配类型(TaskAssigneeEnum)和 ID 列表,获取对应的用户信息列表 + * + * @param type 任务分配类型,表示用户、角色、部门或其他(TaskAssigneeEnum 枚举值) + * @param ids 与指定分配类型关联的 ID 列表(例如用户ID、角色ID、部门ID等) + * @return 返回包含用户信息的列表。如果类型为用户(USER),则通过用户ID列表查询; + * 如果类型为角色(ROLE),则通过角色ID列表查询; + * 如果类型为部门(DEPT),则通过部门ID列表查询; + * 如果类型为岗位(POST)或无法识别的类型,则返回空列表 + */ + private List getUsersByType(TaskAssigneeEnum type, List ids) { + return switch (type) { + case USER -> userService.selectListByIds(ids); + case ROLE -> userService.selectUsersByRoleIds(ids); + case DEPT -> userService.selectUsersByDeptIds(ids); + case POST -> userService.selectUsersByPostIds(ids); + }; + } + + /** + * 根据任务分配类型和对应 ID 列表,批量查询名称映射关系 + * + * @param type 分配类型(用户、角色、部门、岗位) + * @param ids ID 列表(如用户ID、角色ID等) + * @return 返回 Map,其中 key 为 ID,value 为对应的名称 + */ + private Map getNamesByType(TaskAssigneeEnum type, List ids) { + return switch (type) { + case USER -> userService.selectUserNamesByIds(ids); + case ROLE -> userService.selectRoleNamesByIds(ids); + case DEPT -> userService.selectDeptNamesByIds(ids); + case POST -> userService.selectPostNamesByIds(ids); + }; + } + + /** + * 解析 storageId 字符串,返回类型和ID的组合 + * + * @param storageId 例如 "user:123" 或 "456" + * @return Pair(TaskAssigneeEnum, Long),如果格式非法返回 null + */ + private Pair parseStorageId(String storageId) { + try { + String[] parts = storageId.split(StrUtil.COLON, 2); + if (parts.length < 2) { + return Pair.of(TaskAssigneeEnum.USER, Long.valueOf(parts[0])); + } else { + TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); + return Pair.of(type, Long.valueOf(parts[1])); + } + } catch (Exception e) { + log.warn("解析 storageId 失败,格式非法:{},错误信息:{}", storageId, e.getMessage()); + return null; + } + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskServiceImpl.java new file mode 100644 index 0000000..adf423d --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/FlwTaskServiceImpl.java @@ -0,0 +1,752 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.dto.StartProcessReturnDTO; +import com.fuyuanshen.common.core.domain.dto.UserDTO; +import com.fuyuanshen.common.core.enums.BusinessStatusEnum; +import com.fuyuanshen.common.core.exception.ServiceException; +import com.fuyuanshen.common.core.service.UserService; +import com.fuyuanshen.common.core.utils.StreamUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.core.utils.ValidatorUtils; +import com.fuyuanshen.common.core.validate.AddGroup; +import com.fuyuanshen.common.core.validate.EditGroup; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.common.satoken.utils.LoginHelper; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.common.constant.FlowConstant; +import com.fuyuanshen.workflow.common.enums.TaskAssigneeType; +import com.fuyuanshen.workflow.common.enums.TaskStatusEnum; +import com.fuyuanshen.workflow.domain.bo.*; +import com.fuyuanshen.workflow.domain.vo.FlowHisTaskVo; +import com.fuyuanshen.workflow.domain.vo.FlowTaskVo; +import com.fuyuanshen.workflow.mapper.FlwCategoryMapper; +import com.fuyuanshen.workflow.mapper.FlwTaskMapper; +import com.fuyuanshen.workflow.service.IFlwCommonService; +import com.fuyuanshen.workflow.service.IFlwNodeExtService; +import com.fuyuanshen.workflow.service.IFlwTaskAssigneeService; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.dromara.warm.flow.core.FlowEngine; +import org.dromara.warm.flow.core.dto.FlowParams; +import org.dromara.warm.flow.core.entity.*; +import org.dromara.warm.flow.core.enums.NodeType; +import org.dromara.warm.flow.core.enums.SkipType; +import org.dromara.warm.flow.core.service.*; +import org.dromara.warm.flow.core.utils.ExpressionUtil; +import org.dromara.warm.flow.core.utils.MapUtil; +import org.dromara.warm.flow.orm.entity.*; +import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper; +import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; +import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; +import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +import static com.fuyuanshen.workflow.common.constant.FlowConstant.*; + +/** + * 任务 服务层实现 + * + * @author may + */ +@ConditionalOnEnable +@Slf4j +@RequiredArgsConstructor +@Service +public class FlwTaskServiceImpl implements IFlwTaskService { + + private final TaskService taskService; + private final InsService insService; + private final DefService defService; + private final HisTaskService hisTaskService; + private final NodeService nodeService; + private final FlowInstanceMapper flowInstanceMapper; + private final FlowTaskMapper flowTaskMapper; + private final FlowHisTaskMapper flowHisTaskMapper; + private final IdentifierGenerator identifierGenerator; + private final UserService userService; + private final FlwTaskMapper flwTaskMapper; + private final FlwCategoryMapper flwCategoryMapper; + private final FlowNodeMapper flowNodeMapper; + private final IFlwTaskAssigneeService flwTaskAssigneeService; + private final IFlwCommonService flwCommonService; + private final IFlwNodeExtService flwNodeExtService; + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public StartProcessReturnDTO startWorkFlow(StartProcessBo startProcessBo) { + String businessId = startProcessBo.getBusinessId(); + if (StringUtils.isBlank(businessId)) { + throw new ServiceException("启动工作流时必须包含业务ID"); + } + // 启动流程实例(提交申请) + Map variables = startProcessBo.getVariables(); + // 流程发起人 + variables.put(INITIATOR, LoginHelper.getUserIdStr()); + // 业务id + variables.put(BUSINESS_ID, businessId); + FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<>(FlowInstance.class) + .eq(FlowInstance::getBusinessId, businessId)); + if (ObjectUtil.isNotNull(flowInstance)) { + BusinessStatusEnum.checkStartStatus(flowInstance.getFlowStatus()); + List taskList = taskService.list(new FlowTask().setInstanceId(flowInstance.getId())); + taskService.mergeVariable(flowInstance, variables); + insService.updateById(flowInstance); + StartProcessReturnDTO dto = new StartProcessReturnDTO(); + dto.setProcessInstanceId(taskList.get(0).getInstanceId()); + dto.setTaskId(taskList.get(0).getId()); + return dto; + } + FlowParams flowParams = FlowParams.build() + .flowCode(startProcessBo.getFlowCode()) + .variable(startProcessBo.getVariables()) + .flowStatus(BusinessStatusEnum.DRAFT.getStatus()); + Instance instance; + try { + instance = insService.start(businessId, flowParams); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + // 申请人执行流程 + List taskList = taskService.list(new FlowTask().setInstanceId(instance.getId())); + if (taskList.size() > 1) { + throw new ServiceException("请检查流程第一个环节是否为申请人!"); + } + StartProcessReturnDTO dto = new StartProcessReturnDTO(); + dto.setProcessInstanceId(instance.getId()); + dto.setTaskId(taskList.get(0).getId()); + return dto; + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean completeTask(CompleteTaskBo completeTaskBo) { + try { + // 获取任务ID并查询对应的流程任务和实例信息 + Long taskId = completeTaskBo.getTaskId(); + List messageType = completeTaskBo.getMessageType(); + String notice = completeTaskBo.getNotice(); + // 获取抄送人 + List flowCopyList = completeTaskBo.getFlowCopyList(); + // 设置抄送人 + completeTaskBo.getVariables().put(FlowConstant.FLOW_COPY_LIST, flowCopyList); + // 消息类型 + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_TYPE, messageType); + // 消息通知 + completeTaskBo.getVariables().put(FlowConstant.MESSAGE_NOTICE, notice); + + + FlowTask flowTask = flowTaskMapper.selectById(taskId); + if (ObjectUtil.isNull(flowTask)) { + throw new ServiceException("流程任务不存在或任务已审批!"); + } + Instance ins = insService.getById(flowTask.getInstanceId()); + // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 + if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { + completeTaskBo.getVariables().put(FlowConstant.SUBMIT, true); + } + // 设置弹窗处理人 + Map assigneeMap = setPopAssigneeMap(completeTaskBo.getAssigneeMap(), ins.getVariableMap()); + if (CollUtil.isNotEmpty(assigneeMap)) { + completeTaskBo.getVariables().putAll(assigneeMap); + } + // 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息 + FlowParams flowParams = FlowParams.build() + .variable(completeTaskBo.getVariables()) + .skipType(SkipType.PASS.getKey()) + .message(completeTaskBo.getMessage()) + .flowStatus(BusinessStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.PASS.getStatus()) + .hisTaskExt(completeTaskBo.getFileId()); + // 执行任务跳转,并根据返回的处理人设置下一步处理人 + taskService.skip(taskId, flowParams); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 设置弹窗处理人 + * + * @param assigneeMap 处理人 + * @param variablesMap 变量 + */ + private Map setPopAssigneeMap(Map assigneeMap, Map variablesMap) { + Map map = new HashMap<>(); + if (CollUtil.isEmpty(assigneeMap)) { + return map; + } + for (Map.Entry entry : assigneeMap.entrySet()) { + if (variablesMap.containsKey(entry.getKey())) { + String userIds = variablesMap.get(entry.getKey()).toString(); + if (StringUtils.isNotBlank(userIds)) { + Set hashSet = new HashSet<>(); + //弹窗传入的选人 + List popUserIds = Arrays.asList(entry.getValue().toString().split(StringUtils.SEPARATOR)); + //已有的选人 + List variableUserIds = Arrays.asList(userIds.split(StringUtils.SEPARATOR)); + hashSet.addAll(popUserIds); + hashSet.addAll(variableUserIds); + map.put(entry.getKey(), String.join(StringUtils.SEPARATOR, hashSet)); + } + } else { + map.put(entry.getKey(), entry.getValue()); + } + } + return map; + } + + /** + * 添加抄送人 + * + * @param task 任务信息 + * @param flowCopyList 抄送人 + */ + @Override + public void setCopy(Task task, List flowCopyList) { + if (CollUtil.isEmpty(flowCopyList)) { + return; + } + // 添加抄送人记录 + FlowHisTask flowHisTask = flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getTaskId, task.getId())).get(0); + FlowNode flowNode = new FlowNode(); + flowNode.setNodeCode(flowHisTask.getTargetNodeCode()); + flowNode.setNodeName(flowHisTask.getTargetNodeName()); + //生成新的任务id + long taskId = identifierGenerator.nextId(null).longValue(); + task.setId(taskId); + task.setNodeName("【抄送】" + task.getNodeName()); + Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000); + FlowParams flowParams = FlowParams.build() + .skipType(SkipType.NONE.getKey()) + .hisStatus(TaskStatusEnum.COPY.getStatus()) + .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); + HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams); + hisTask.setCreateTime(updateTime); + hisTask.setUpdateTime(updateTime); + hisTaskService.save(hisTask); + List userList = flowCopyList.stream() + .map(flowCopy -> { + FlowUser flowUser = new FlowUser(); + flowUser.setType(TaskAssigneeType.COPY.getCode()); + flowUser.setProcessedBy(String.valueOf(flowCopy.getUserId())); + flowUser.setAssociated(taskId); + return flowUser; + }).collect(Collectors.toList()); + // 批量保存抄送人员 + FlowEngine.userService().saveBatch(userList); + } + + /** + * 查询当前用户的待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo pageByTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); + queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); + queryWrapper.in("t.flow_status", BusinessStatusEnum.WAITING.getStatus()); + Page page = this.getFlowTaskVoPage(pageQuery, queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 查询当前用户的已办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo pageByTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); + queryWrapper.in("t.approver", LoginHelper.getUserIdStr()); + queryWrapper.orderByDesc("t.create_time").orderByDesc("t.update_time"); + Page page = flwTaskMapper.getListFinishTask(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 查询待办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo pageByAllTaskWait(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + queryWrapper.eq("t.node_type", NodeType.BETWEEN.getKey()); + Page page = getFlowTaskVoPage(pageQuery, queryWrapper); + return TableDataInfo.build(page); + } + + private Page getFlowTaskVoPage(PageQuery pageQuery, QueryWrapper queryWrapper) { + Page page = flwTaskMapper.getListRunTask(pageQuery.build(), queryWrapper); + List records = page.getRecords(); + if (CollUtil.isNotEmpty(records)) { + List taskIds = StreamUtils.toList(records, FlowTaskVo::getId); + Map> listMap = currentTaskAllUser(taskIds); + records.forEach(t -> { + List userList = listMap.getOrDefault(t.getId(), Collections.emptyList()); + if (CollUtil.isNotEmpty(userList)) { + t.setAssigneeIds(StreamUtils.join(userList, e -> String.valueOf(e.getUserId()))); + t.setAssigneeNames(StreamUtils.join(userList, UserDTO::getNickName)); + } + }); + } + return page; + } + + /** + * 查询已办任务 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo pageByAllTaskFinish(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + Page page = flwTaskMapper.getListFinishTask(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + /** + * 查询当前用户的抄送 + * + * @param flowTaskBo 参数 + * @param pageQuery 分页 + */ + @Override + public TableDataInfo pageByTaskCopy(FlowTaskBo flowTaskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = buildQueryWrapper(flowTaskBo); + queryWrapper.in("t.processed_by", LoginHelper.getUserIdStr()); + Page page = flwTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper); + return TableDataInfo.build(page); + } + + private QueryWrapper buildQueryWrapper(FlowTaskBo flowTaskBo) { + QueryWrapper wrapper = Wrappers.query(); + wrapper.like(StringUtils.isNotBlank(flowTaskBo.getNodeName()), "t.node_name", flowTaskBo.getNodeName()); + wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowName()), "t.flow_name", flowTaskBo.getFlowName()); + wrapper.like(StringUtils.isNotBlank(flowTaskBo.getFlowCode()), "t.flow_code", flowTaskBo.getFlowCode()); + wrapper.in(CollUtil.isNotEmpty(flowTaskBo.getCreateByIds()), "t.create_by", flowTaskBo.getCreateByIds()); + if (StringUtils.isNotBlank(flowTaskBo.getCategory())) { + List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowTaskBo.getCategory())); + wrapper.in("t.category", StreamUtils.toList(categoryIds, Convert::toStr)); + } + wrapper.orderByDesc("t.create_time"); + return wrapper; + } + + /** + * 驳回任务 + * + * @param bo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean backProcess(BackProcessBo bo) { + try { + Long taskId = bo.getTaskId(); + String notice = bo.getNotice(); + List messageType = bo.getMessageType(); + String message = bo.getMessage(); + FlowTask task = flowTaskMapper.selectById(taskId); + if (ObjectUtil.isNull(task)) { + throw new ServiceException("任务不存在!"); + } + Instance inst = insService.getById(task.getInstanceId()); + BusinessStatusEnum.checkBackStatus(inst.getFlowStatus()); + Long definitionId = task.getDefinitionId(); + String applyNodeCode = flwCommonService.applyNodeCode(definitionId); + + Map variable = new HashMap<>(); + // 消息类型 + variable.put("messageType", messageType); + // 消息通知 + variable.put("notice", notice); + + FlowParams flowParams = FlowParams.build() + .nodeCode(bo.getNodeCode()) + .variable(variable) + .message(message) + .skipType(SkipType.REJECT.getKey()) + .flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) + .hisStatus(TaskStatusEnum.BACK.getStatus()) + .hisTaskExt(bo.getFileId()); + taskService.skip(task.getId(), flowParams); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 获取可驳回的前置节点 + * + * @param definitionId 流程定义id + * @param nowNodeCode 当前节点 + */ + @Override + public List getBackTaskNode(Long definitionId, String nowNodeCode) { + List nodeCodes = nodeService.getByNodeCodes(Collections.singletonList(nowNodeCode), definitionId); + if (!CollUtil.isNotEmpty(nodeCodes)) { + return nodeCodes; + } + //判断是否配置了固定驳回节点 + Node node = nodeCodes.get(0); + if (StringUtils.isNotBlank(node.getAnyNodeSkip())) { + return nodeService.getByNodeCodes(Collections.singletonList(node.getAnyNodeSkip()), definitionId); + } + //获取可驳回的前置节点 + List nodes = nodeService.previousNodeList(definitionId, nowNodeCode); + if (CollUtil.isNotEmpty(nodes)) { + return StreamUtils.filter(nodes, e -> NodeType.BETWEEN.getKey().equals(e.getNodeType())); + } + return nodes; + } + + /** + * 终止任务 + * + * @param bo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean terminationTask(FlowTerminationBo bo) { + try { + Long taskId = bo.getTaskId(); + Task task = taskService.getById(taskId); + if (task == null) { + throw new ServiceException("任务不存在!"); + } + Instance instance = insService.getById(task.getInstanceId()); + if (ObjectUtil.isNotNull(instance)) { + BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); + } + FlowParams flowParams = FlowParams.build() + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) + .hisStatus(TaskStatusEnum.TERMINATION.getStatus()); + taskService.termination(taskId, flowParams); + return true; + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 按照任务id查询任务 + * + * @param taskIdList 任务id + */ + @Override + public List selectByIdList(List taskIdList) { + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) + .in(FlowTask::getId, taskIdList)); + } + + /** + * 按照任务id查询任务 + * + * @param taskId 任务id + */ + @Override + public FlowTaskVo selectById(Long taskId) { + Task task = taskService.getById(taskId); + if (ObjectUtil.isNull(task)) { + return null; + } + FlowTaskVo flowTaskVo = BeanUtil.toBean(task, FlowTaskVo.class); + Instance instance = insService.getById(task.getInstanceId()); + Definition definition = defService.getById(task.getDefinitionId()); + flowTaskVo.setFlowStatus(instance.getFlowStatus()); + flowTaskVo.setVersion(definition.getVersion()); + flowTaskVo.setFlowCode(definition.getFlowCode()); + flowTaskVo.setFlowName(definition.getFlowName()); + flowTaskVo.setBusinessId(instance.getBusinessId()); + FlowNode flowNode = this.getByNodeCode(flowTaskVo.getNodeCode(), instance.getDefinitionId()); + if (ObjectUtil.isNull(flowNode)) { + throw new NullPointerException("当前【" + flowTaskVo.getNodeCode() + "】节点编码不存在"); + } + //设置按钮权限 + flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt())); + flowTaskVo.setNodeRatio(flowNode.getNodeRatio()); + flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId()))); + return flowTaskVo; + } + + /** + * 获取下一节点信息 + * + * @param bo 参数 + */ + @Override + public List getNextNodeList(FlowNextNodeBo bo) { + Long taskId = bo.getTaskId(); + Map variables = bo.getVariables(); + Task task = taskService.getById(taskId); + Instance instance = insService.getById(task.getInstanceId()); + Definition definition = defService.getById(task.getDefinitionId()); + Map mergeVariable = MapUtil.mergeAll(instance.getVariableMap(), variables); + // 获取下一节点列表 + List nextNodeList = nodeService.getNextNodeList(task.getDefinitionId(), task.getNodeCode(), null, SkipType.PASS.getKey(), mergeVariable); + List nextFlowNodes = BeanUtil.copyToList(nextNodeList, FlowNode.class); + // 只获取中间节点 + nextFlowNodes = StreamUtils.filter(nextFlowNodes, node -> NodeType.BETWEEN.getKey().equals(node.getNodeType())); + if (CollUtil.isNotEmpty(nextNodeList)) { + // 构建以下节点数据 + List buildNextTaskList = StreamUtils.toList(nextNodeList, node -> taskService.addTask(node, instance, definition, FlowParams.build())); + // 办理人变量替换 + ExpressionUtil.evalVariable(buildNextTaskList, mergeVariable); + for (FlowNode flowNode : nextFlowNodes) { + buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst().ifPresent(t -> { + if (CollUtil.isNotEmpty(t.getPermissionList())) { + List users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList())); + if (CollUtil.isNotEmpty(users)) { + flowNode.setPermissionFlag(StreamUtils.join(users, e -> String.valueOf(e.getUserId()))); + } + } + }); + } + } + return nextFlowNodes; + } + + /** + * 按照任务id查询任务 + * + * @param taskIdList 任务id + * @return 结果 + */ + @Override + public List selectHisTaskByIdList(List taskIdList) { + return flowHisTaskMapper.selectList(new LambdaQueryWrapper<>(FlowHisTask.class) + .in(FlowHisTask::getId, taskIdList)); + } + + /** + * 按照任务id查询任务 + * + * @param taskId 任务id + * @return 结果 + */ + @Override + public FlowHisTask selectHisTaskById(Long taskId) { + return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class) + .eq(FlowHisTask::getId, taskId)); + } + + /** + * 按照实例id查询任务 + * + * @param instanceIdList 流程实例id + */ + @Override + public List selectByInstIdList(List instanceIdList) { + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) + .in(FlowTask::getInstanceId, instanceIdList)); + } + + /** + * 按照实例id查询任务 + * + * @param instanceId 流程实例id + */ + @Override + public List selectByInstId(Long instanceId) { + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) + .eq(FlowTask::getInstanceId, instanceId)); + } + + /** + * 任务操作 + * + * @param bo 参数 + * @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean taskOperation(TaskOperationBo bo, String taskOperation) { + FlowParams flowParams = FlowParams.build() + .message(bo.getMessage()); + if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { + flowParams.ignore(true); + } + + // 根据操作类型构建 FlowParams + switch (taskOperation) { + case DELEGATE_TASK, TRANSFER_TASK -> { + ValidatorUtils.validate(bo, AddGroup.class); + flowParams.addHandlers(Collections.singletonList(bo.getUserId())); + } + case ADD_SIGNATURE -> { + ValidatorUtils.validate(bo, EditGroup.class); + flowParams.addHandlers(bo.getUserIds()); + } + case REDUCTION_SIGNATURE -> { + ValidatorUtils.validate(bo, EditGroup.class); + flowParams.reductionHandlers(bo.getUserIds()); + } + default -> { + log.error("Invalid operation type:{} ", taskOperation); + throw new ServiceException("Invalid operation type " + taskOperation); + } + } + + Long taskId = bo.getTaskId(); + Task task = taskService.getById(taskId); + FlowNode flowNode = getByNodeCode(task.getNodeCode(), task.getDefinitionId()); + if ("addSignature".equals(taskOperation) || "reductionSignature".equals(taskOperation)) { + if (flowNode.getNodeRatio().compareTo(BigDecimal.ZERO) == 0) { + throw new ServiceException(task.getNodeName() + "不是会签节点!"); + } + } + // 设置任务状态并执行对应的任务操作 + switch (taskOperation) { + //委派任务 + case DELEGATE_TASK -> { + flowParams.hisStatus(TaskStatusEnum.DEPUTE.getStatus()); + return taskService.depute(taskId, flowParams); + } + //转办任务 + case TRANSFER_TASK -> { + flowParams.hisStatus(TaskStatusEnum.TRANSFER.getStatus()); + return taskService.transfer(taskId, flowParams); + } + //加签,增加办理人 + case ADD_SIGNATURE -> { + flowParams.hisStatus(TaskStatusEnum.SIGN.getStatus()); + return taskService.addSignature(taskId, flowParams); + } + //减签,减少办理人 + case REDUCTION_SIGNATURE -> { + flowParams.hisStatus(TaskStatusEnum.SIGN_OFF.getStatus()); + return taskService.reductionSignature(taskId, flowParams); + } + default -> { + log.error("Invalid operation type:{} ", taskOperation); + throw new ServiceException("Invalid operation type " + taskOperation); + } + } + } + + /** + * 修改任务办理人(此方法将会批量修改所有任务的办理人) + * + * @param taskIdList 任务id + * @param userId 用户id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateAssignee(List taskIdList, String userId) { + if (CollUtil.isEmpty(taskIdList)) { + return false; + } + try { + List flowTasks = this.selectByIdList(taskIdList); + // 批量删除现有任务的办理人记录 + if (CollUtil.isNotEmpty(flowTasks)) { + FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); + List userList = flowTasks.stream() + .map(flowTask -> { + FlowUser flowUser = new FlowUser(); + flowUser.setType(TaskAssigneeType.APPROVER.getCode()); + flowUser.setProcessedBy(userId); + flowUser.setAssociated(flowTask.getId()); + return flowUser; + }) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(userList)) { + FlowEngine.userService().saveBatch(userList); + } + } + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ServiceException(e.getMessage()); + } + return true; + } + + /** + * 获取任务所有办理人 + * + * @param taskIdList 任务id + */ + @Override + public Map> currentTaskAllUser(List taskIdList) { + Map> map = new HashMap<>(); + // 获取与当前任务关联的用户列表 + List associatedUsers = FlowEngine.userService().getByAssociateds(taskIdList); + Map> listMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); + for (Map.Entry> entry : listMap.entrySet()) { + List value = entry.getValue(); + if (CollUtil.isNotEmpty(value)) { + List userDtoList = userService.selectListByIds(StreamUtils.toList(value, e -> Long.valueOf(e.getProcessedBy()))); + map.put(entry.getKey(), userDtoList); + } + } + return map; + } + + /** + * 获取当前任务的所有办理人 + * + * @param taskId 任务id + */ + @Override + public List currentTaskAllUser(Long taskId) { + // 获取与当前任务关联的用户列表 + List userList = FlowEngine.userService().getByAssociateds(Collections.singletonList(taskId)); + if (CollUtil.isEmpty(userList)) { + return Collections.emptyList(); + } + return userService.selectListByIds(StreamUtils.toList(userList, e -> Long.valueOf(e.getProcessedBy()))); + } + + /** + * 按照节点编码查询节点 + * + * @param nodeCode 节点编码 + * @param definitionId 流程定义id + */ + @Override + public FlowNode getByNodeCode(String nodeCode, Long definitionId) { + return flowNodeMapper.selectOne(new LambdaQueryWrapper() + .eq(FlowNode::getNodeCode, nodeCode) + .eq(FlowNode::getDefinitionId, definitionId)); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/TestLeaveServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/TestLeaveServiceImpl.java new file mode 100644 index 0000000..5d44952 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/TestLeaveServiceImpl.java @@ -0,0 +1,198 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.fuyuanshen.common.core.domain.event.ProcessTaskEvent; +import com.fuyuanshen.common.core.domain.event.ProcessDeleteEvent; +import com.fuyuanshen.common.core.domain.event.ProcessEvent; +import com.fuyuanshen.common.core.enums.BusinessStatusEnum; +import com.fuyuanshen.common.core.service.WorkflowService; +import com.fuyuanshen.common.core.utils.MapstructUtils; +import com.fuyuanshen.common.core.utils.StringUtils; +import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; +import com.fuyuanshen.common.mybatis.core.page.PageQuery; +import com.fuyuanshen.common.mybatis.core.page.TableDataInfo; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.TestLeave; +import com.fuyuanshen.workflow.domain.bo.TestLeaveBo; +import com.fuyuanshen.workflow.domain.vo.TestLeaveVo; +import com.fuyuanshen.workflow.mapper.TestLeaveMapper; +import com.fuyuanshen.workflow.service.ITestLeaveService; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 请假Service业务层处理 + * + * @author may + * @date 2023-07-21 + */ +@ConditionalOnEnable +@RequiredArgsConstructor +@Service +@Slf4j +public class TestLeaveServiceImpl implements ITestLeaveService { + + private final TestLeaveMapper baseMapper; + private final WorkflowService workflowService; + + /** + * spel条件表达:判断小于2 + * + * @param leaveDays 待判断的变量(可不传自行返回true或false) + * @return boolean + */ + public boolean eval(Integer leaveDays) { + if (leaveDays <= 2) { + return true; + } + return false; + } + + /** + * 查询请假 + */ + @Override + public TestLeaveVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 查询请假列表 + */ + @Override + public TableDataInfo queryPageList(TestLeaveBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询请假列表 + */ + @Override + public List queryList(TestLeaveBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(TestLeaveBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getLeaveType()), TestLeave::getLeaveType, bo.getLeaveType()); + lqw.ge(bo.getStartLeaveDays() != null, TestLeave::getLeaveDays, bo.getStartLeaveDays()); + lqw.le(bo.getEndLeaveDays() != null, TestLeave::getLeaveDays, bo.getEndLeaveDays()); + lqw.orderByDesc(BaseEntity::getCreateTime); + return lqw; + } + + /** + * 新增请假 + */ + @Override + public TestLeaveVo insertByBo(TestLeaveBo bo) { + long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true); + // 截止日期也算一天 + bo.setLeaveDays((int) day + 1); + TestLeave add = MapstructUtils.convert(bo, TestLeave.class); + if (StringUtils.isBlank(add.getStatus())) { + add.setStatus(BusinessStatusEnum.DRAFT.getStatus()); + } + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return MapstructUtils.convert(add, TestLeaveVo.class); + } + + /** + * 修改请假 + */ + @Override + public TestLeaveVo updateByBo(TestLeaveBo bo) { + TestLeave update = MapstructUtils.convert(bo, TestLeave.class); + baseMapper.updateById(update); + return MapstructUtils.convert(update, TestLeaveVo.class); + } + + /** + * 批量删除请假 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(List ids) { + workflowService.deleteInstance(ids); + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等) + * 正常使用只需#processEvent.flowCode=='leave1' + * 示例为了方便则使用startsWith匹配了全部示例key + * + * @param processEvent 参数 + */ + @EventListener(condition = "#processEvent.flowCode.startsWith('leave')") + public void processHandler(ProcessEvent processEvent) { + log.info("当前任务执行了{}", processEvent.toString()); + TestLeave testLeave = baseMapper.selectById(Long.valueOf(processEvent.getBusinessId())); + testLeave.setStatus(processEvent.getStatus()); + // 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程 + Map params = processEvent.getParams(); + if (MapUtil.isNotEmpty(params)) { + // 历史任务扩展(通常为附件) + String hisTaskExt = Convert.toStr(params.get("hisTaskExt")); + // 办理人 + String handler = Convert.toStr(params.get("handler")); + // 办理意见 + String message = Convert.toStr(params.get("message")); + } + if (processEvent.getSubmit()) { + testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); + } + baseMapper.updateById(testLeave); + } + + /** + * 执行任务创建监听 + * 示例:也可通过 @EventListener(condition = "#processTaskEvent.flowCode=='leave1'")进行判断 + * 在方法中判断流程节点key + * if ("xxx".equals(processTaskEvent.getNodeCode())) { + * //执行业务逻辑 + * } + * + * @param processTaskEvent 参数 + */ + @EventListener(condition = "#processTaskEvent.flowCode.startsWith('leave')") + public void processTaskHandler(ProcessTaskEvent processTaskEvent) { + log.info("当前任务创建了{}", processTaskEvent.toString()); + } + + /** + * 监听删除流程事件 + * 正常使用只需#processDeleteEvent.flowCode=='leave1' + * 示例为了方便则使用startsWith匹配了全部示例key + * + * @param processDeleteEvent 参数 + */ + @EventListener(condition = "#processDeleteEvent.flowCode.startsWith('leave')") + public void processDeleteHandler(ProcessDeleteEvent processDeleteEvent) { + log.info("监听删除流程事件,当前任务执行了{}", processDeleteEvent.toString()); + TestLeave testLeave = baseMapper.selectById(Long.valueOf(processDeleteEvent.getBusinessId())); + if (ObjectUtil.isNull(testLeave)) { + return; + } + baseMapper.deleteById(testLeave.getId()); + } + +} diff --git a/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/WorkflowServiceImpl.java b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/WorkflowServiceImpl.java new file mode 100644 index 0000000..77ac667 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/java/com/fuyuanshen/workflow/service/impl/WorkflowServiceImpl.java @@ -0,0 +1,151 @@ +package com.fuyuanshen.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import com.fuyuanshen.common.core.domain.dto.CompleteTaskDTO; +import com.fuyuanshen.common.core.domain.dto.StartProcessDTO; +import com.fuyuanshen.common.core.domain.dto.StartProcessReturnDTO; +import com.fuyuanshen.common.core.service.WorkflowService; +import com.fuyuanshen.common.core.utils.StringUtils; +import org.dromara.warm.flow.orm.entity.FlowInstance; +import com.fuyuanshen.workflow.common.ConditionalOnEnable; +import com.fuyuanshen.workflow.domain.bo.CompleteTaskBo; +import com.fuyuanshen.workflow.domain.bo.StartProcessBo; +import com.fuyuanshen.workflow.service.IFlwDefinitionService; +import com.fuyuanshen.workflow.service.IFlwInstanceService; +import com.fuyuanshen.workflow.service.IFlwTaskService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务实现 + * + * @author may + */ +@ConditionalOnEnable +@RequiredArgsConstructor +@Service +public class WorkflowServiceImpl implements WorkflowService { + + private final IFlwInstanceService flwInstanceService; + private final IFlwDefinitionService flwDefinitionService; + private final IFlwTaskService flwTaskService; + + /** + * 删除流程实例 + * + * @param businessIds 业务id + * @return 结果 + */ + @Override + public boolean deleteInstance(List businessIds) { + return flwInstanceService.deleteByBusinessIds(businessIds); + } + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + */ + @Override + public String getBusinessStatusByTaskId(Long taskId) { + FlowInstance flowInstance = flwInstanceService.selectByTaskId(taskId); + return ObjectUtil.isNotNull(flowInstance) ? flowInstance.getFlowStatus() : StringUtils.EMPTY; + } + + /** + * 获取当前流程状态 + * + * @param businessId 业务id + */ + @Override + public String getBusinessStatus(String businessId) { + FlowInstance flowInstance = flwInstanceService.selectInstByBusinessId(businessId); + return ObjectUtil.isNotNull(flowInstance) ? flowInstance.getFlowStatus() : StringUtils.EMPTY; + } + + /** + * 设置流程变量 + * + * @param instanceId 流程实例id + * @param variables 流程变量 + */ + @Override + public void setVariable(Long instanceId, Map variables) { + flwInstanceService.setVariable(instanceId, variables); + } + + /** + * 获取流程变量 + * + * @param instanceId 流程实例id + */ + @Override + public Map instanceVariable(Long instanceId) { + return flwInstanceService.instanceVariable(instanceId); + } + + /** + * 按照业务id查询流程实例id + * + * @param businessId 业务id + * @return 结果 + */ + @Override + public Long getInstanceIdByBusinessId(String businessId) { + FlowInstance flowInstance = flwInstanceService.selectInstByBusinessId(businessId); + return ObjectUtil.isNotNull(flowInstance) ? flowInstance.getId() : null; + } + + /** + * 新增租户流程定义 + * + * @param tenantId 租户id + */ + @Override + public void syncDef(String tenantId) { + flwDefinitionService.syncDef(tenantId); + } + + /** + * 启动流程 + * + * @param startProcess 参数 + */ + @Override + public StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess) { + return flwTaskService.startWorkFlow(BeanUtil.toBean(startProcess, StartProcessBo.class)); + } + + /** + * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); + * + * @param completeTask 参数 + */ + @Override + public boolean completeTask(CompleteTaskDTO completeTask) { + return flwTaskService.completeTask(BeanUtil.toBean(completeTask, CompleteTaskBo.class)); + } + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + */ + @Override + public boolean completeTask(Long taskId, String message) { + CompleteTaskBo completeTask = new CompleteTaskBo(); + completeTask.setTaskId(taskId); + completeTask.setMessage(message); + // 忽略权限(系统后台发起审批 无用户信息 需要忽略权限) + completeTask.getVariables().put("ignore", true); + return flwTaskService.completeTask(completeTask); + } + +} diff --git a/fys-modules/fys-workflow/src/main/resources/mapper/package-info.md b/fys-modules/fys-workflow/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/fys-modules/fys-workflow/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwCategoryMapper.xml b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwCategoryMapper.xml new file mode 100644 index 0000000..3fae21c --- /dev/null +++ b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwCategoryMapper.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml new file mode 100644 index 0000000..9b9d48d --- /dev/null +++ b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwInstanceMapper.xml @@ -0,0 +1,36 @@ + + + + + + + + + diff --git a/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml new file mode 100644 index 0000000..103d280 --- /dev/null +++ b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + diff --git a/fys-modules/fys-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml new file mode 100644 index 0000000..7edc60f --- /dev/null +++ b/fys-modules/fys-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/fys-modules/pom.xml b/fys-modules/pom.xml new file mode 100644 index 0000000..18d4a90 --- /dev/null +++ b/fys-modules/pom.xml @@ -0,0 +1,30 @@ + + + + fys-vue-plus + com.fuyuanshen + ${revision} + + 4.0.0 + + + fys-demo + fys-generator + fys-job + fys-system + fys-workflow + fys-equipment + fys-customer + fys-app + + + fys-modules + pom + + + fys-modules 业务模块 + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8cb9b27 --- /dev/null +++ b/pom.xml @@ -0,0 +1,538 @@ + + + 4.0.0 + + com.fuyuanshen + fys-vue-plus + ${revision} + + FYS-Vue-Plus + http://47.107.152.87:3000/dyf/fys-Multi-tenant + 富源晟 FYS-Vue-Plus多租户管理系统 + + + 5.4.0 + 3.4.6 + UTF-8 + UTF-8 + 17 + 3.5.16 + 2.8.8 + 0.15.0 + 1.2.0 + 2.3 + 1.42.0 + 3.5.12 + 3.9.1 + 5.8.35 + 3.4.7 + 3.45.1 + 2.2.7 + 4.3.1 + 1.5.0 + 1.4.8 + 0.2.0 + 1.18.36 + 1.80 + 1.16.7 + + 2.7.0 + + + 2.28.22 + + 3.3.4 + + 1.2.83 + + 8.7.2-20250101 + + 1.7.3 + + + 3.2.2 + 3.2.2 + 3.11.0 + 3.1.2 + 1.3.0 + + true + + + + + local + + + local + info + fys + 123456 + + + + dev + + + dev + info + fys + 123456 + + + + true + + + + prod + + prod + warn + fys + 123456 + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + cn.hutool + hutool-bom + ${hutool.version} + pom + import + + + + + org.dromara.warm + warm-flow-mybatis-plus-sb3-starter + ${warm-flow.version} + + + org.dromara.warm + warm-flow-plugin-ui-sb-web + ${warm-flow.version} + + + + + me.zhyd.oauth + JustAuth + ${justauth.version} + + + + + com.fuyuanshen + fys-common-bom + ${revision} + pom + import + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + ${springdoc.version} + + + + + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.8 + + + + com.github.therapi + therapi-runtime-javadoc + ${therapi-javadoc.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + cn.idev.excel + fastexcel + ${fastexcel.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + cn.dev33 + sa-token-spring-boot3-starter + ${satoken.version} + + + + cn.dev33 + sa-token-jwt + ${satoken.version} + + + cn.hutool + hutool-all + + + + + cn.dev33 + sa-token-core + ${satoken.version} + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-ds.version} + + + + org.mybatis + mybatis + ${mybatis.version} + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-jsqlparser + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-annotation + ${mybatis-plus.version} + + + + + p6spy + p6spy + ${p6spy.version} + + + + + software.amazon.awssdk + s3 + ${aws.sdk.version} + + + + software.amazon.awssdk + s3-transfer-manager + ${aws.sdk.version} + + + + software.amazon.awssdk + netty-nio-client + ${aws.sdk.version} + + + + org.dromara.sms4j + sms4j-spring-boot-starter + ${sms4j.version} + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-starter-client + ${spring-boot-admin.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + + + + + com.aizuda + snail-job-client-starter + ${snailjob.version} + + + com.aizuda + snail-job-client-job-core + ${snailjob.version} + + + + + org.bouncycastle + bcprov-jdk15to18 + ${bouncycastle.version} + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + ${mapstruct-plus.version} + + + + + org.lionsoul + ip2region + ${ip2region.version} + + + + com.alibaba + fastjson + ${fastjson.version} + + + + com.fuyuanshen + fys-system + ${revision} + + + + + com.fuyuanshen + fys-equipment + ${revision} + + + + + com.fuyuanshen + fys-customer + ${revision} + + + + com.fuyuanshen + fys-job + ${revision} + + + + com.fuyuanshen + fys-generator + ${revision} + + + + com.fuyuanshen + fys-app + ${revision} + + + + com.fuyuanshen + fys-demo + ${revision} + + + + + + + com.fuyuanshen + fys-workflow + ${revision} + + + + + + + fys-admin + fys-common + fys-extend + fys-modules + + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + com.github.therapi + therapi-runtime-javadoc-scribe + ${therapi-javadoc.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + + + io.github.linpeilie + mapstruct-plus-processor + ${mapstruct-plus.version} + + + org.projectlombok + lombok-mapstruct-binding + ${mapstruct-plus.lombok.version} + + + + -parameters + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + -Dfile.encoding=UTF-8 + + ${profiles.active} + + exclude + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + + + src/main/resources + + false + + + src/main/resources + + + application* + bootstrap* + banner* + + + true + + + + + + + public + huawei nexus + https://mirrors.huaweicloud.com/repository/maven/ + + true + + + + + + + public + huawei nexus + https://mirrors.huaweicloud.com/repository/maven/ + + true + + + false + + + + + + + diff --git a/script/bin/ry.bat b/script/bin/ry.bat new file mode 100644 index 0000000..aad851b --- /dev/null +++ b/script/bin/ry.bat @@ -0,0 +1,68 @@ +rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 +@echo off + +rem jar平级目录 +set AppName=fys-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC" + + +ECHO. + ECHO. [1] 启动%AppName% + ECHO. [2] 关闭%AppName% + ECHO. [3] 重启%AppName% + ECHO. [4] 启动状态 %AppName% + ECHO. [5] 退 出 +ECHO. + +ECHO.请输入选择项目的序号: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting…… +echo Start %AppName% success... +goto:eof + +rem 函数stop通过jps命令查找pid并结束进程 +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem 根据进程ID,kill进程 + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/script/bin/ry.sh b/script/bin/ry.sh new file mode 100644 index 0000000..272ed50 --- /dev/null +++ b/script/bin/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=fys-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/script/docker/database.yml b/script/docker/database.yml new file mode 100644 index 0000000..4221e40 --- /dev/null +++ b/script/docker/database.yml @@ -0,0 +1,59 @@ +services: + # 此镜像仅用于测试 正式环境需自行安装数据库 + # SID: XE user: system password: oracle + oracle: + image: tekintian/oracle12c:latest + container_name: oracle + environment: + # 时区上海 + TZ: Asia/Shanghai + DBCA_TOTAL_MEMORY: 16192 + ports: + - "18080:8080" + - "1521:1521" + volumes: + # 数据挂载 + - "/docker/oracle/data:/u01/app/oracle" + network_mode: "host" + + # 此镜像仅用于测试 正式环境需自行安装数据库 + sqlserver: + image: mcr.microsoft.com/mssql/server:2017-latest + container_name: sqlserver + environment: + # 时区上海 + TZ: Asia/Shanghai + ACCEPT_EULA: "Y" + SA_PASSWORD: "fys@123" + ports: + - "1433:1433" + volumes: + # 数据挂载 + - "/docker/sqlserver/data:/var/opt/mssql" + network_mode: "host" + + postgres: + image: postgres:14.2 + container_name: postgres + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5432:5432" + volumes: + - /docker/postgres/data:/var/lib/postgresql/data + network_mode: "host" + + postgres13: + image: postgres:13.6 + container_name: postgres13 + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: root + POSTGRES_DB: postgres + ports: + - "5433:5432" + volumes: + - /docker/postgres13/data:/var/lib/postgresql/data + network_mode: "host" diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml new file mode 100644 index 0000000..3862d6b --- /dev/null +++ b/script/docker/docker-compose.yml @@ -0,0 +1,157 @@ +services: + mysql: + image: mysql:8.0.42 + container_name: mysql + environment: + # 时区上海 + TZ: Asia/Shanghai + # root 密码 + MYSQL_ROOT_PASSWORD: root + # 初始化数据库(后续的初始化sql会在这个库执行) + MYSQL_DATABASE: ry-vue + ports: + - "3306:3306" + volumes: + # 数据挂载 + - /docker/mysql/data/:/var/lib/mysql/ + # 配置挂载 + - /docker/mysql/conf/:/etc/mysql/conf.d/ + command: + # 将mysql8.0默认密码策略 修改为 原先 策略 (mysql8.0对其默认策略做了更改 会导致密码无法匹配) + --default-authentication-plugin=mysql_native_password + --character-set-server=utf8mb4 + --collation-server=utf8mb4_general_ci + --explicit_defaults_for_timestamp=true + --lower_case_table_names=1 + privileged: true + network_mode: "host" + + nginx-web: + image: nginx:1.23.4 + container_name: nginx-web + environment: + # 时区上海 + TZ: Asia/Shanghai + ports: + - "80:80" + - "443:443" + volumes: + # 证书映射 + - /docker/nginx/cert:/etc/nginx/cert + # 配置文件映射 + - /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf + # 页面目录 + - /docker/nginx/html:/usr/share/nginx/html + # 日志目录 + - /docker/nginx/log:/var/log/nginx + privileged: true + network_mode: "host" + + redis: + image: redis:7.2.8 + container_name: redis + ports: + - "6379:6379" + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/redis/conf:/redis/config:rw + # 数据文件 + - /docker/redis/data/:/redis/data/:rw + command: "redis-server /redis/config/redis.conf" + privileged: true + network_mode: "host" + + minio: + # minio 最后一个未阉割版本 不能再进行升级 在往上的版本功能被阉割 + image: minio/minio:RELEASE.2025-04-22T22-12-26Z + container_name: minio + ports: + # api 端口 + - "9000:9000" + # 控制台端口 + - "9001:9001" + environment: + # 时区上海 + TZ: Asia/Shanghai + # 管理后台用户名 + MINIO_ROOT_USER: fys + # 管理后台密码,最小8个字符 + MINIO_ROOT_PASSWORD: fys123 + # https需要指定域名 + #MINIO_SERVER_URL: "https://xxx.com:9000" + #MINIO_BROWSER_REDIRECT_URL: "https://xxx.com:9001" + # 开启压缩 on 开启 off 关闭 + MINIO_COMPRESS: "off" + # 扩展名 .pdf,.doc 为空 所有类型均压缩 + MINIO_COMPRESS_EXTENSIONS: "" + # mime 类型 application/pdf 为空 所有类型均压缩 + MINIO_COMPRESS_MIME_TYPES: "" + volumes: + # 映射当前目录下的data目录至容器内/data目录 + - /docker/minio/data:/data + # 映射配置目录 + - /docker/minio/config:/root/.minio/ + command: server --address ':9000' --console-address ':9001' /data # 指定容器中的目录 /data + privileged: true + network_mode: "host" + + fys-server1: + image: fys/fys-server:5.4.0 + container_name: fys-server1 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8080 + SNAIL_PORT: 28080 + volumes: + # 配置文件 + - /docker/server1/logs/:/fys/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/fys/skywalking/agent + privileged: true + network_mode: "host" + + fys-server2: + image: fys/fys-server:5.4.0 + container_name: fys-server2 + environment: + # 时区上海 + TZ: Asia/Shanghai + SERVER_PORT: 8081 + SNAIL_PORT: 28081 + volumes: + # 配置文件 + - /docker/server2/logs/:/fys/server/logs/ + # skywalking 探针 +# - /docker/skywalking/agent/:/fys/skywalking/agent + privileged: true + network_mode: "host" + + fys-monitor-admin: + image: fys/fys-monitor-admin:5.4.0 + container_name: fys-monitor-admin + environment: + # 时区上海 + TZ: Asia/Shanghai + volumes: + # 配置文件 + - /docker/monitor/logs/:/fys/monitor/logs + privileged: true + network_mode: "host" + + fys-snailjob-server: + image: fys/fys-snailjob-server:5.4.0 + container_name: fys-snailjob-server + environment: + # 时区上海 + TZ: Asia/Shanghai + ports: + - "8800:8800" + - "17888:17888" + volumes: + - /docker/snailjob/logs/:/fys/snailjob/logs + privileged: true + network_mode: "host" diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf new file mode 100644 index 0000000..4b9b179 --- /dev/null +++ b/script/docker/nginx/conf/nginx.conf @@ -0,0 +1,123 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + # 限制body大小 + client_max_body_size 100m; + # 开启静态资源压缩 + gzip_static on; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + upstream server { + ip_hash; + server 127.0.0.1:8080; + server 127.0.0.1:8081; + } + + upstream monitor-admin { + server 127.0.0.1:9090; + } + + upstream snailjob-server { + server 127.0.0.1:8800; + } + + server { + listen 80; + server_name localhost; + + # https配置参考 start + #listen 443 ssl; + + # 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径 + #ssl on; + #ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改 + #ssl_session_timeout 5m; + #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + #ssl_prefer_server_ciphers on; + # https配置参考 end + + # 演示环境配置 拦截除 GET POST 之外的所有请求 + # if ($request_method !~* GET|POST) { + # rewrite ^/(.*)$ /403; + # } + + # location = /403 { + # default_type application/json; + # return 200 '{"msg":"演示模式,不允许操作","code":500}'; + # } + + # 限制外网访问内网 actuator 相关路径 + location ~ ^(/[^/]*)?/actuator.*(/.*)?$ { + return 403; + } + + location / { + root /usr/share/nginx/html; # docker映射路径 不允许更改 + try_files $uri $uri/ /index.html; + index index.html index.htm; + } + + location /prod-api/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 86400s; + # sse 与 websocket参数 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; + proxy_pass http://server/; + } + + # https 会拦截内链所有的 http 请求 造成功能无法使用 + # 解决方案1 将 admin 服务 也配置成 https + # 解决方案2 将菜单配置为外链访问 走独立页面 http 访问 + location /admin/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://monitor-admin/admin/; + } + + location /snail-job/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # sse 与 websocket参数 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_buffering off; + proxy_cache off; + proxy_pass http://snailjob-server/snail-job/; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/script/docker/redis/conf/redis.conf b/script/docker/redis/conf/redis.conf new file mode 100644 index 0000000..beced3c --- /dev/null +++ b/script/docker/redis/conf/redis.conf @@ -0,0 +1,28 @@ +# redis 密码 +requirepass fys123 + +# key 监听器配置 +# notify-keyspace-events Ex + +# 配置持久化文件存储路径 +dir /redis/data +# 配置rdb +# 15分钟内有至少1个key被更改则进行快照 +save 900 1 +# 5分钟内有至少10个key被更改则进行快照 +save 300 10 +# 1分钟内有至少10000个key被更改则进行快照 +save 60 10000 +# 开启压缩 +rdbcompression yes +# rdb文件名 用默认的即可 +dbfilename dump.rdb + +# 开启aof +appendonly yes +# 文件名 +appendfilename "appendonly.aof" +# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢 +# appendfsync always +appendfsync everysec +# appendfsync no diff --git a/script/docker/redis/data/README.md b/script/docker/redis/data/README.md new file mode 100644 index 0000000..fbc5474 --- /dev/null +++ b/script/docker/redis/data/README.md @@ -0,0 +1 @@ +数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据 \ No newline at end of file diff --git a/script/leave/leave1.json b/script/leave/leave1.json new file mode 100644 index 0000000..0ffdeeb --- /dev/null +++ b/script/leave/leave1.json @@ -0,0 +1,75 @@ +{ + "flowCode" : "leave1", + "flowName" : "请假申请-普通", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "d5ee3ddf-3968-4379-a86f-9ceabde5faac", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "200,200|200,200", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "d5ee3ddf-3968-4379-a86f-9ceabde5faac", + "nextNodeCode" : "dd515cdd-59f6-446f-94ca-25ca062afb42", + "skipType" : "PASS", + "coordinate" : "220,200;310,200" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "dd515cdd-59f6-446f-94ca-25ca062afb42", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "360,200|360,200", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "dd515cdd-59f6-446f-94ca-25ca062afb42", + "nextNodeCode" : "78fa8e5b-e809-44ed-978a-41092409ebcf", + "skipType" : "PASS", + "coordinate" : "410,200;490,200" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "78fa8e5b-e809-44ed-978a-41092409ebcf", + "nodeName" : "组长", + "permissionFlag" : "role:1", + "nodeRatio" : 0.000, + "coordinate" : "540,200|540,200", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "78fa8e5b-e809-44ed-978a-41092409ebcf", + "nextNodeCode" : "a8abf15f-b83e-428a-86cc-033555ea9bbe", + "skipType" : "PASS", + "coordinate" : "590,200;670,200" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "a8abf15f-b83e-428a-86cc-033555ea9bbe", + "nodeName" : "部门主管", + "permissionFlag" : "role:3@@role:4", + "nodeRatio" : 0.000, + "coordinate" : "720,200|720,200", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "a8abf15f-b83e-428a-86cc-033555ea9bbe", + "nextNodeCode" : "8b82b7d7-8660-455e-b880-d6d22ea3eb6d", + "skipType" : "PASS", + "coordinate" : "770,200;880,200" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "8b82b7d7-8660-455e-b880-d6d22ea3eb6d", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "900,200|900,200", + "formCustom" : "N", + "ext" : "[]" + } ] +} diff --git a/script/leave/leave2.json b/script/leave/leave2.json new file mode 100644 index 0000000..7bbdbaa --- /dev/null +++ b/script/leave/leave2.json @@ -0,0 +1,111 @@ +{ + "flowCode" : "leave2", + "flowName" : "请假申请-排他网关", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "300,240|300,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a", + "nextNodeCode" : "fdcae93b-b69c-498a-b231-09255e74bcbd", + "skipType" : "PASS", + "coordinate" : "320,240;390,240" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "fdcae93b-b69c-498a-b231-09255e74bcbd", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "440,240|440,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "fdcae93b-b69c-498a-b231-09255e74bcbd", + "nextNodeCode" : "7b8c7ead-7dc8-4951-a7f3-f0c41995909e", + "skipType" : "PASS", + "coordinate" : "490,240;535,240" + } ] + }, { + "nodeType" : 3, + "nodeCode" : "7b8c7ead-7dc8-4951-a7f3-f0c41995909e", + "nodeRatio" : 0.000, + "coordinate" : "560,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "7b8c7ead-7dc8-4951-a7f3-f0c41995909e", + "nextNodeCode" : "b3528155-dcb7-4445-bbdf-3d00e3499e86", + "skipType" : "PASS", + "skipCondition" : "le@@leaveDays|2", + "coordinate" : "560,265;560,320;670,320" + }, { + "nowNodeCode" : "7b8c7ead-7dc8-4951-a7f3-f0c41995909e", + "nextNodeCode" : "5ed2362b-fc0c-4d52-831f-95208b830605", + "skipName" : "大于两天", + "skipType" : "PASS", + "skipCondition" : "gt@@leaveDays|2", + "coordinate" : "560,215;560,160;670,160|560,187" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "b3528155-dcb7-4445-bbdf-3d00e3499e86", + "nodeName" : "组长", + "permissionFlag" : "3@@4", + "nodeRatio" : 0.000, + "coordinate" : "720,320|720,320", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "b3528155-dcb7-4445-bbdf-3d00e3499e86", + "nextNodeCode" : "c9fa6d7d-2a74-4e78-b947-0cad8a6af869", + "skipType" : "PASS", + "coordinate" : "770,320;860,320;860,280" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "c9fa6d7d-2a74-4e78-b947-0cad8a6af869", + "nodeName" : "总经理", + "permissionFlag" : "role:1", + "nodeRatio" : 0.000, + "coordinate" : "860,240|860,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "c9fa6d7d-2a74-4e78-b947-0cad8a6af869", + "nextNodeCode" : "40aa65fd-0712-4d23-b6f7-d0432b920fd1", + "skipType" : "PASS", + "coordinate" : "910,240;980,240" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "40aa65fd-0712-4d23-b6f7-d0432b920fd1", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "1000,240|1000,240", + "formCustom" : "N", + "ext" : "[]" + }, { + "nodeType" : 1, + "nodeCode" : "5ed2362b-fc0c-4d52-831f-95208b830605", + "nodeName" : "部门领导", + "permissionFlag" : "role:1", + "nodeRatio" : 0.000, + "coordinate" : "720,160|720,160", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "5ed2362b-fc0c-4d52-831f-95208b830605", + "nextNodeCode" : "c9fa6d7d-2a74-4e78-b947-0cad8a6af869", + "skipType" : "PASS", + "coordinate" : "770,160;860,160;860,200" + } ] + } ] +} diff --git a/script/leave/leave3.json b/script/leave/leave3.json new file mode 100644 index 0000000..bb22d42 --- /dev/null +++ b/script/leave/leave3.json @@ -0,0 +1,121 @@ +{ + "flowCode" : "leave3", + "flowName" : "请假申请-并行网关", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "a80ecf9f-f465-4ae5-a429-e30ec5d0f957", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "380,220|380,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "a80ecf9f-f465-4ae5-a429-e30ec5d0f957", + "nextNodeCode" : "b7bbb571-06de-455c-8083-f83c07bf0b99", + "skipType" : "PASS", + "coordinate" : "400,220;470,220" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "b7bbb571-06de-455c-8083-f83c07bf0b99", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "520,220|520,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "b7bbb571-06de-455c-8083-f83c07bf0b99", + "nextNodeCode" : "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a", + "skipType" : "PASS", + "coordinate" : "570,220;655,220" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a", + "nodeRatio" : 0.000, + "coordinate" : "680,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a", + "nextNodeCode" : "4b7743cd-940c-431b-926f-e7b614fbf1fe", + "skipType" : "PASS", + "coordinate" : "680,195;680,140;750,140" + }, { + "nowNodeCode" : "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a", + "nextNodeCode" : "762cb975-37d8-4276-b6db-79a4c3606394", + "skipType" : "PASS", + "coordinate" : "680,245;680,300;750,300" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "4b7743cd-940c-431b-926f-e7b614fbf1fe", + "nodeName" : "市场部", + "permissionFlag" : "role:1", + "nodeRatio" : 0.000, + "coordinate" : "800,140|800,140", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "4b7743cd-940c-431b-926f-e7b614fbf1fe", + "nextNodeCode" : "b66b6563-f9fe-41cc-a782-f7837bb6f3d2", + "skipType" : "PASS", + "coordinate" : "850,140;920,140;920,195" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "b66b6563-f9fe-41cc-a782-f7837bb6f3d2", + "nodeRatio" : 0.000, + "coordinate" : "920,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "b66b6563-f9fe-41cc-a782-f7837bb6f3d2", + "nextNodeCode" : "23e7429e-2b47-4431-b93e-40db7c431ce6", + "skipType" : "PASS", + "coordinate" : "945,220;975,220;975,220;960,220;960,220;990,220" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "23e7429e-2b47-4431-b93e-40db7c431ce6", + "nodeName" : "CEO", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "1040,220|1040,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "23e7429e-2b47-4431-b93e-40db7c431ce6", + "nextNodeCode" : "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1", + "skipType" : "PASS", + "coordinate" : "1090,220;1140,220" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "1160,220|1160,220", + "formCustom" : "N", + "ext" : "[]" + }, { + "nodeType" : 1, + "nodeCode" : "762cb975-37d8-4276-b6db-79a4c3606394", + "nodeName" : "综合部", + "permissionFlag" : "role:3@@role:4", + "nodeRatio" : 0.000, + "coordinate" : "800,300|800,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "762cb975-37d8-4276-b6db-79a4c3606394", + "nextNodeCode" : "b66b6563-f9fe-41cc-a782-f7837bb6f3d2", + "skipType" : "PASS", + "coordinate" : "850,300;920,300;920,245" + } ] + } ] +} diff --git a/script/leave/leave4.json b/script/leave/leave4.json new file mode 100644 index 0000000..50968f8 --- /dev/null +++ b/script/leave/leave4.json @@ -0,0 +1,90 @@ +{ + "flowCode" : "leave4", + "flowName" : "请假申请-会签", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "9ce8bf00-f25b-4fc6-91b8-827082fc4876", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "320,240|320,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "9ce8bf00-f25b-4fc6-91b8-827082fc4876", + "nextNodeCode" : "e90b98ef-35b4-410c-a663-bae8b7624b9f", + "skipType" : "PASS", + "coordinate" : "340,240;410,240" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "e90b98ef-35b4-410c-a663-bae8b7624b9f", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "460,240|460,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "e90b98ef-35b4-410c-a663-bae8b7624b9f", + "nextNodeCode" : "768b5b1a-6726-4d67-8853-4cc70d5b1045", + "skipType" : "PASS", + "coordinate" : "510,240;590,240" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "768b5b1a-6726-4d67-8853-4cc70d5b1045", + "nodeName" : "百分之60通过", + "permissionFlag" : "${userList}", + "nodeRatio" : 60.000, + "coordinate" : "640,240|640,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "768b5b1a-6726-4d67-8853-4cc70d5b1045", + "nextNodeCode" : "2f9f2e21-9bcf-42a3-a07c-13037aad22d1", + "skipType" : "PASS", + "coordinate" : "690,240;770,240" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "2f9f2e21-9bcf-42a3-a07c-13037aad22d1", + "nodeName" : "全部审批通过", + "permissionFlag" : "role:1@@role:3", + "nodeRatio" : 100.000, + "coordinate" : "820,240|820,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "2f9f2e21-9bcf-42a3-a07c-13037aad22d1", + "nextNodeCode" : "27461e01-3d9f-4530-8fe3-bd5ec7f9571f", + "skipType" : "PASS", + "coordinate" : "870,240;950,240" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "27461e01-3d9f-4530-8fe3-bd5ec7f9571f", + "nodeName" : "CEO", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "1000,240|1000,240", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "27461e01-3d9f-4530-8fe3-bd5ec7f9571f", + "nextNodeCode" : "b62b88c3-8d8d-4969-911e-2aaea219e7fc", + "skipType" : "PASS", + "coordinate" : "1050,240;1080,240;1080,240;1070,240;1070,240;1100,240" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "b62b88c3-8d8d-4969-911e-2aaea219e7fc", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "1120,240|1120,240", + "formCustom" : "N", + "ext" : "[]" + } ] +} diff --git a/script/leave/leave5.json b/script/leave/leave5.json new file mode 100644 index 0000000..a27b1de --- /dev/null +++ b/script/leave/leave5.json @@ -0,0 +1,121 @@ +{ + "flowCode" : "leave5", + "flowName" : "请假申请-并行会签网关", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "ebebaf26-9cb6-497e-8119-4c9fed4c597c", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "300,220|300,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "ebebaf26-9cb6-497e-8119-4c9fed4c597c", + "nextNodeCode" : "e1b04e96-dc81-4858-a309-2fe945d2f374", + "skipType" : "PASS", + "coordinate" : "320,220;350,220;350,220;340,220;340,220;370,220" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "e1b04e96-dc81-4858-a309-2fe945d2f374", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "420,220|420,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "e1b04e96-dc81-4858-a309-2fe945d2f374", + "nextNodeCode" : "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9", + "skipType" : "PASS", + "coordinate" : "470,220;535,220" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9", + "nodeRatio" : 0.000, + "coordinate" : "560,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9", + "nextNodeCode" : "c80f273e-1f17-4bd8-9ad1-04a4a94ea862", + "skipType" : "PASS", + "coordinate" : "560,245;560,320;650,320" + }, { + "nowNodeCode" : "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9", + "nextNodeCode" : "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4", + "skipType" : "PASS", + "coordinate" : "560,195;560,120;650,120" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "c80f273e-1f17-4bd8-9ad1-04a4a94ea862", + "nodeName" : "会签", + "permissionFlag" : "role:1@@role:3", + "nodeRatio" : 100.000, + "coordinate" : "700,320|700,320", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "c80f273e-1f17-4bd8-9ad1-04a4a94ea862", + "nextNodeCode" : "1a20169e-3d82-4926-a151-e2daad28de1b", + "skipType" : "PASS", + "coordinate" : "750,320;860,320;860,245" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "1a20169e-3d82-4926-a151-e2daad28de1b", + "nodeRatio" : 0.000, + "coordinate" : "860,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "1a20169e-3d82-4926-a151-e2daad28de1b", + "nextNodeCode" : "7a8f0473-e409-442e-a843-5c2b813d00e9", + "skipType" : "PASS", + "coordinate" : "885,220;950,220" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "7a8f0473-e409-442e-a843-5c2b813d00e9", + "nodeName" : "CEO", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "1000,220|1000,220", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "7a8f0473-e409-442e-a843-5c2b813d00e9", + "nextNodeCode" : "03c4d2bc-58b5-4408-a2e4-65afb046f169", + "skipType" : "PASS", + "coordinate" : "1050,220;1120,220" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "03c4d2bc-58b5-4408-a2e4-65afb046f169", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "1140,220|1140,220", + "formCustom" : "N", + "ext" : "[]" + }, { + "nodeType" : 1, + "nodeCode" : "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4", + "nodeName" : "百分之60票签", + "permissionFlag" : "${userList}", + "nodeRatio" : 60.000, + "coordinate" : "700,120|700,120", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4", + "nextNodeCode" : "1a20169e-3d82-4926-a151-e2daad28de1b", + "skipType" : "PASS", + "coordinate" : "750,120;860,120;860,195" + } ] + } ] +} diff --git a/script/leave/leave6.json b/script/leave/leave6.json new file mode 100644 index 0000000..d21d9d2 --- /dev/null +++ b/script/leave/leave6.json @@ -0,0 +1,215 @@ +{ + "flowCode" : "leave6", + "flowName" : "请假申请-排他并行会签", + "category" : "100", + "version" : "1", + "formCustom" : "N", + "formPath" : "/workflow/leaveEdit/index", + "nodeList" : [ { + "nodeType" : 0, + "nodeCode" : "122b89a5-7c6f-40a3-aa09-7a263f902054", + "nodeName" : "开始", + "nodeRatio" : 0.000, + "coordinate" : "240,300|240,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "122b89a5-7c6f-40a3-aa09-7a263f902054", + "nextNodeCode" : "c25a0e86-fdd1-4f03-8e22-14db70389dbd", + "skipType" : "PASS", + "coordinate" : "260,300;350,300" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "c25a0e86-fdd1-4f03-8e22-14db70389dbd", + "nodeName" : "申请人", + "nodeRatio" : 0.000, + "coordinate" : "400,300|400,300", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "c25a0e86-fdd1-4f03-8e22-14db70389dbd", + "nextNodeCode" : "07ecda1d-7a0a-47b5-8a91-6186c9473742", + "skipType" : "PASS", + "coordinate" : "450,300;510,300" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "2bfa3919-78cf-4bc1-b59b-df463a4546f9", + "nodeName" : "副经理", + "permissionFlag" : "role:1@@role:3@@role:4", + "nodeRatio" : 0.000, + "coordinate" : "860,200|860,200", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "2bfa3919-78cf-4bc1-b59b-df463a4546f9", + "nextNodeCode" : "394e1cc8-b8b2-4189-9f81-44448e88ac32", + "skipType" : "PASS", + "coordinate" : "910,200;1000,200;1000,275" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "ec17f60e-94e0-4d96-a3ce-3417e9d32d60", + "nodeName" : "组长", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "860,400|860,400", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "ec17f60e-94e0-4d96-a3ce-3417e9d32d60", + "nextNodeCode" : "394e1cc8-b8b2-4189-9f81-44448e88ac32", + "skipType" : "PASS", + "coordinate" : "910,400;1000,400;1000,325" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "07ecda1d-7a0a-47b5-8a91-6186c9473742", + "nodeName" : "副组长", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "560,300|560,300", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,transfer,copy,pop\"}]", + "skipList" : [ { + "nowNodeCode" : "07ecda1d-7a0a-47b5-8a91-6186c9473742", + "nextNodeCode" : "48117e2c-6328-406b-b102-c4a9d115bb13", + "skipType" : "PASS", + "coordinate" : "610,300;675,300" + } ] + }, { + "nodeType" : 3, + "nodeCode" : "48117e2c-6328-406b-b102-c4a9d115bb13", + "nodeRatio" : 0.000, + "coordinate" : "700,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "48117e2c-6328-406b-b102-c4a9d115bb13", + "nextNodeCode" : "2bfa3919-78cf-4bc1-b59b-df463a4546f9", + "skipName" : "大于两天", + "skipType" : "PASS", + "skipCondition" : "default@@${leaveDays > 2}", + "coordinate" : "700,275;700,200;810,200|700,237" + }, { + "nowNodeCode" : "48117e2c-6328-406b-b102-c4a9d115bb13", + "nextNodeCode" : "ec17f60e-94e0-4d96-a3ce-3417e9d32d60", + "skipType" : "PASS", + "skipCondition" : "spel@@#{@testLeaveServiceImpl.eval(#leaveDays)}", + "coordinate" : "700,325;700,400;810,400" + } ] + }, { + "nodeType" : 3, + "nodeCode" : "394e1cc8-b8b2-4189-9f81-44448e88ac32", + "nodeRatio" : 0.000, + "coordinate" : "1000,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "394e1cc8-b8b2-4189-9f81-44448e88ac32", + "nextNodeCode" : "9c93a195-cff2-4e17-ab0a-a4f264191496", + "skipType" : "PASS", + "coordinate" : "1025,300;1130,300" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "9c93a195-cff2-4e17-ab0a-a4f264191496", + "nodeName" : "经理会签", + "permissionFlag" : "1@@3", + "nodeRatio" : 100.000, + "coordinate" : "1180,300|1180,300", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,pop,addSign,subSign\"}]", + "skipList" : [ { + "nowNodeCode" : "9c93a195-cff2-4e17-ab0a-a4f264191496", + "nextNodeCode" : "a1a42056-afd1-4e90-88bc-36cbf5a66992", + "skipType" : "PASS", + "coordinate" : "1230,300;1315,300" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "a1a42056-afd1-4e90-88bc-36cbf5a66992", + "nodeRatio" : 0.000, + "coordinate" : "1340,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "a1a42056-afd1-4e90-88bc-36cbf5a66992", + "nextNodeCode" : "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5", + "skipType" : "PASS", + "coordinate" : "1340,325;1340,400;1430,400" + }, { + "nowNodeCode" : "a1a42056-afd1-4e90-88bc-36cbf5a66992", + "nextNodeCode" : "350dfa0c-a77c-4efa-8527-10efa02d8be4", + "skipType" : "PASS", + "coordinate" : "1340,275;1340,200;1430,200" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "350dfa0c-a77c-4efa-8527-10efa02d8be4", + "nodeName" : "总经理", + "permissionFlag" : "3@@1", + "nodeRatio" : 0.000, + "coordinate" : "1480,200|1480,200", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "350dfa0c-a77c-4efa-8527-10efa02d8be4", + "nextNodeCode" : "c36a46ef-04f9-463f-bad7-4b395c818519", + "skipType" : "PASS", + "coordinate" : "1530,200;1640,200;1640,275" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5", + "nodeName" : "副总经理", + "permissionFlag" : "1@@3", + "nodeRatio" : 0.000, + "coordinate" : "1480,400|1480,400", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5", + "nextNodeCode" : "c36a46ef-04f9-463f-bad7-4b395c818519", + "skipType" : "PASS", + "coordinate" : "1530,400;1640,400;1640,325" + } ] + }, { + "nodeType" : 4, + "nodeCode" : "c36a46ef-04f9-463f-bad7-4b395c818519", + "nodeRatio" : 0.000, + "coordinate" : "1640,300", + "formCustom" : "N", + "ext" : "[]", + "skipList" : [ { + "nowNodeCode" : "c36a46ef-04f9-463f-bad7-4b395c818519", + "nextNodeCode" : "3fcea762-b53a-4ae1-8365-7bec90444828", + "skipType" : "PASS", + "coordinate" : "1665,300;1770,300" + } ] + }, { + "nodeType" : 1, + "nodeCode" : "3fcea762-b53a-4ae1-8365-7bec90444828", + "nodeName" : "董事", + "permissionFlag" : "1", + "nodeRatio" : 0.000, + "coordinate" : "1820,300|1820,300", + "formCustom" : "N", + "ext" : "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]", + "skipList" : [ { + "nowNodeCode" : "3fcea762-b53a-4ae1-8365-7bec90444828", + "nextNodeCode" : "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31", + "skipType" : "PASS", + "coordinate" : "1870,300;1960,300" + } ] + }, { + "nodeType" : 2, + "nodeCode" : "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31", + "nodeName" : "结束", + "nodeRatio" : 0.000, + "coordinate" : "1980,300|1980,300", + "formCustom" : "N", + "ext" : "[]" + } ] +} diff --git a/script/sql/oracle/oracle_ry_job.sql b/script/sql/oracle/oracle_ry_job.sql new file mode 100644 index 0000000..95ad535 --- /dev/null +++ b/script/sql/oracle/oracle_ry_job.sql @@ -0,0 +1,924 @@ + +/* + SnailJob Database Transfer Tool + Source Server Type : MySQL + Target Server Type : Oracle + Date: 2025-04-26 10:01:54 +*/ + + +-- sj_namespace +CREATE TABLE sj_namespace +( + id number GENERATED ALWAYS AS IDENTITY, + name varchar2(64) NULL, + unique_id varchar2(64) NULL, + description varchar2(256) DEFAULT '' NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_namespace + ADD CONSTRAINT pk_sj_namespace PRIMARY KEY (id); + +CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name); + +COMMENT ON COLUMN sj_namespace.id IS '主键'; +COMMENT ON COLUMN sj_namespace.name IS '名称'; +COMMENT ON COLUMN sj_namespace.unique_id IS '唯一id'; +COMMENT ON COLUMN sj_namespace.description IS '描述'; +COMMENT ON COLUMN sj_namespace.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_namespace.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_namespace.update_dt IS '修改时间'; +COMMENT ON TABLE sj_namespace IS '命名空间'; + +INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES ('Development', 'dev', '', 0, sysdate, sysdate); +INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES ('Production', 'prod', '', 0, sysdate, sysdate); + +-- sj_group_config +CREATE TABLE sj_group_config +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) DEFAULT '' NULL, + description varchar2(256) DEFAULT '' NULL, + token varchar2(64) DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT' NULL, + group_status smallint DEFAULT 0 NOT NULL, + version number NOT NULL, + group_partition number NOT NULL, + id_generator_mode smallint DEFAULT 1 NOT NULL, + init_scene smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_group_config + ADD CONSTRAINT pk_sj_group_config PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name); + +COMMENT ON COLUMN sj_group_config.id IS '主键'; +COMMENT ON COLUMN sj_group_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_group_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_group_config.description IS '组描述'; +COMMENT ON COLUMN sj_group_config.token IS 'token'; +COMMENT ON COLUMN sj_group_config.group_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_group_config.version IS '版本号'; +COMMENT ON COLUMN sj_group_config.group_partition IS '分区'; +COMMENT ON COLUMN sj_group_config.id_generator_mode IS '唯一id生成模式 默认号段模式'; +COMMENT ON COLUMN sj_group_config.init_scene IS '是否初始化场景 0:否 1:是'; +COMMENT ON COLUMN sj_group_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_group_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_group_config IS '组配置'; + +INSERT INTO sj_group_config (namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, create_dt, update_dt) VALUES ('dev', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, sysdate, sysdate); +INSERT INTO sj_group_config (namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, create_dt, update_dt) VALUES ('prod', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, sysdate, sysdate); + +-- sj_notify_config +CREATE TABLE sj_notify_config +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + notify_name varchar2(64) DEFAULT '' NULL, + system_task_type smallint DEFAULT 3 NOT NULL, + notify_status smallint DEFAULT 0 NOT NULL, + recipient_ids varchar2(128) NULL, + notify_threshold number DEFAULT 0 NOT NULL, + notify_scene smallint DEFAULT 0 NOT NULL, + rate_limiter_status smallint DEFAULT 0 NOT NULL, + rate_limiter_threshold number DEFAULT 0 NOT NULL, + description varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_notify_config + ADD CONSTRAINT pk_sj_notify_config PRIMARY KEY (id); + +CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name); + +COMMENT ON COLUMN sj_notify_config.id IS '主键'; +COMMENT ON COLUMN sj_notify_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_notify_config.notify_name IS '通知名称'; +COMMENT ON COLUMN sj_notify_config.system_task_type IS '任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_notify_config.notify_status IS '通知状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.recipient_ids IS '接收人id列表'; +COMMENT ON COLUMN sj_notify_config.notify_threshold IS '通知阈值'; +COMMENT ON COLUMN sj_notify_config.notify_scene IS '通知场景'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_status IS '限流状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_threshold IS '每秒限流阈值'; +COMMENT ON COLUMN sj_notify_config.description IS '描述'; +COMMENT ON COLUMN sj_notify_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_config IS '通知配置'; + +-- sj_notify_recipient +CREATE TABLE sj_notify_recipient +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + recipient_name varchar2(64) NULL, + notify_type smallint DEFAULT 0 NOT NULL, + notify_attribute varchar2(512) NULL, + description varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_notify_recipient + ADD CONSTRAINT pk_sj_notify_recipient PRIMARY KEY (id); + +CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id); + +COMMENT ON COLUMN sj_notify_recipient.id IS '主键'; +COMMENT ON COLUMN sj_notify_recipient.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_recipient.recipient_name IS '接收人名称'; +COMMENT ON COLUMN sj_notify_recipient.notify_type IS '通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook'; +COMMENT ON COLUMN sj_notify_recipient.notify_attribute IS '配置属性'; +COMMENT ON COLUMN sj_notify_recipient.description IS '描述'; +COMMENT ON COLUMN sj_notify_recipient.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_recipient.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_recipient IS '告警通知接收人'; + +-- sj_retry_dead_letter +CREATE TABLE sj_retry_dead_letter +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + group_id number NOT NULL, + scene_name varchar2(64) NULL, + scene_id number NOT NULL, + idempotent_id varchar2(64) NULL, + biz_no varchar2(64) DEFAULT '' NULL, + executor_name varchar2(512) DEFAULT '' NULL, + args_str clob NULL, + ext_attrs clob NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry_dead_letter + ADD CONSTRAINT pk_sj_retry_dead_letter PRIMARY KEY (id); + +CREATE INDEX idx_sj_retry_dead_letter_01 ON sj_retry_dead_letter (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_dead_letter_02 ON sj_retry_dead_letter (idempotent_id); +CREATE INDEX idx_sj_retry_dead_letter_03 ON sj_retry_dead_letter (biz_no); +CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); + +COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; +COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; +COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry_dead_letter.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry_dead_letter.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_dead_letter.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_dead_letter IS '死信队列表'; + +-- sj_retry +CREATE TABLE sj_retry +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + group_id number NOT NULL, + scene_name varchar2(64) NULL, + scene_id number NOT NULL, + idempotent_id varchar2(64) NULL, + biz_no varchar2(64) DEFAULT '' NULL, + executor_name varchar2(512) DEFAULT '' NULL, + args_str clob NULL, + ext_attrs clob NULL, + next_trigger_at number NOT NULL, + retry_count number DEFAULT 0 NOT NULL, + retry_status smallint DEFAULT 0 NOT NULL, + task_type smallint DEFAULT 1 NOT NULL, + bucket_index number DEFAULT 0 NOT NULL, + parent_id number DEFAULT 0 NOT NULL, + deleted number DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry + ADD CONSTRAINT pk_sj_retry PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); + +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); + +COMMENT ON COLUMN sj_retry.id IS '主键'; +COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; +COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; +COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_retry.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_retry.retry_status IS '重试状态 0、重试中 1、成功 2、最大重试次数'; +COMMENT ON COLUMN sj_retry.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_retry.parent_id IS '父节点id'; +COMMENT ON COLUMN sj_retry.deleted IS '逻辑删除'; +COMMENT ON COLUMN sj_retry.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry IS '重试信息表'; + +-- sj_retry_task +CREATE TABLE sj_retry_task +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + scene_name varchar2(64) NULL, + retry_id number NOT NULL, + ext_attrs clob NULL, + task_status smallint DEFAULT 1 NOT NULL, + task_type smallint DEFAULT 1 NOT NULL, + operation_reason smallint DEFAULT 0 NOT NULL, + client_info varchar2(128) DEFAULT NULL NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry_task + ADD CONSTRAINT pk_sj_retry_task PRIMARY KEY (id); + +CREATE INDEX idx_sj_retry_task_01 ON sj_retry_task (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_task_02 ON sj_retry_task (task_status); +CREATE INDEX idx_sj_retry_task_03 ON sj_retry_task (create_dt); +CREATE INDEX idx_sj_retry_task_04 ON sj_retry_task (retry_id); + +COMMENT ON COLUMN sj_retry_task.id IS '主键'; +COMMENT ON COLUMN sj_retry_task.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_task.retry_id IS '重试信息Id'; +COMMENT ON COLUMN sj_retry_task.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_task.task_status IS '重试状态'; +COMMENT ON COLUMN sj_retry_task.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry_task.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_retry_task.client_info IS '客户端地址 clientId#ip:port'; +COMMENT ON COLUMN sj_retry_task.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_task.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_task IS '重试任务表'; + +-- sj_retry_task_log_message +CREATE TABLE sj_retry_task_log_message +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + retry_id number NOT NULL, + retry_task_id number NOT NULL, + message clob NULL, + log_num number DEFAULT 1 NOT NULL, + real_time number DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry_task_log_message + ADD CONSTRAINT pk_sj_retry_task_log_message PRIMARY KEY (id); + +CREATE INDEX idx_sj_rt_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, retry_task_id); +CREATE INDEX idx_sj_rt_log_message_02 ON sj_retry_task_log_message (create_dt); + +COMMENT ON COLUMN sj_retry_task_log_message.id IS '主键'; +COMMENT ON COLUMN sj_retry_task_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task_log_message.retry_id IS '重试信息Id'; +COMMENT ON COLUMN sj_retry_task_log_message.retry_task_id IS '重试任务Id'; +COMMENT ON COLUMN sj_retry_task_log_message.message IS '异常信息'; +COMMENT ON COLUMN sj_retry_task_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_retry_task_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_retry_task_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_task_log_message IS '任务调度日志信息记录表'; + +-- sj_retry_scene_config +CREATE TABLE sj_retry_scene_config +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + scene_name varchar2(64) NULL, + group_name varchar2(64) NULL, + scene_status smallint DEFAULT 0 NOT NULL, + max_retry_count number DEFAULT 5 NOT NULL, + back_off smallint DEFAULT 1 NOT NULL, + trigger_interval varchar2(16) DEFAULT '' NULL, + notify_ids varchar2(128) DEFAULT '' NULL, + deadline_request number DEFAULT 60000 NOT NULL, + executor_timeout number DEFAULT 5 NOT NULL, + route_key smallint DEFAULT 4 NOT NULL, + block_strategy smallint DEFAULT 1 NOT NULL, + cb_status smallint DEFAULT 0 NOT NULL, + cb_trigger_type smallint DEFAULT 1 NOT NULL, + cb_max_count number DEFAULT 16 NOT NULL, + cb_trigger_interval varchar2(16) DEFAULT '' NULL, + description varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry_scene_config + ADD CONSTRAINT pk_sj_retry_scene_config PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name); + +COMMENT ON COLUMN sj_retry_scene_config.id IS '主键'; +COMMENT ON COLUMN sj_retry_scene_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_scene_config.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_scene_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_scene_config.scene_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_retry_scene_config.max_retry_count IS '最大重试次数'; +COMMENT ON COLUMN sj_retry_scene_config.back_off IS '1、默认等级 2、固定间隔时间 3、CRON 表达式'; +COMMENT ON COLUMN sj_retry_scene_config.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_retry_scene_config.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_retry_scene_config.deadline_request IS 'Deadline Request 调用链超时 单位毫秒'; +COMMENT ON COLUMN sj_retry_scene_config.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_retry_scene_config.route_key IS '路由策略'; +COMMENT ON COLUMN sj_retry_scene_config.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_retry_scene_config.cb_status IS '回调状态 0、不开启 1、开启'; +COMMENT ON COLUMN sj_retry_scene_config.cb_trigger_type IS '1、默认等级 2、固定间隔时间 3、CRON 表达式'; +COMMENT ON COLUMN sj_retry_scene_config.cb_max_count IS '回调的最大执行次数'; +COMMENT ON COLUMN sj_retry_scene_config.cb_trigger_interval IS '回调的最大执行次数'; +COMMENT ON COLUMN sj_retry_scene_config.description IS '描述'; +COMMENT ON COLUMN sj_retry_scene_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_scene_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_scene_config IS '场景配置'; + +-- sj_server_node +CREATE TABLE sj_server_node +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + host_id varchar2(64) NULL, + host_ip varchar2(64) NULL, + host_port number NOT NULL, + expire_at date NOT NULL, + node_type smallint NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_server_node + ADD CONSTRAINT pk_sj_server_node PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip); + +CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name); +CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type); + +COMMENT ON COLUMN sj_server_node.id IS '主键'; +COMMENT ON COLUMN sj_server_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_server_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_server_node.host_id IS '主机id'; +COMMENT ON COLUMN sj_server_node.host_ip IS '机器ip'; +COMMENT ON COLUMN sj_server_node.host_port IS '机器端口'; +COMMENT ON COLUMN sj_server_node.expire_at IS '过期时间'; +COMMENT ON COLUMN sj_server_node.node_type IS '节点类型 1、客户端 2、是服务端'; +COMMENT ON COLUMN sj_server_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_server_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_server_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_server_node IS '服务器节点'; + +-- sj_distributed_lock +CREATE TABLE sj_distributed_lock +( + name varchar2(64) NULL, + lock_until timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, + locked_at timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL, + locked_by varchar2(255) NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_distributed_lock + ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (id); + +COMMENT ON COLUMN sj_distributed_lock.name IS '锁名称'; +COMMENT ON COLUMN sj_distributed_lock.lock_until IS '锁定时长'; +COMMENT ON COLUMN sj_distributed_lock.locked_at IS '锁定时间'; +COMMENT ON COLUMN sj_distributed_lock.locked_by IS '锁定者'; +COMMENT ON COLUMN sj_distributed_lock.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_distributed_lock.update_dt IS '修改时间'; +COMMENT ON TABLE sj_distributed_lock IS '锁定表'; + +-- sj_system_user +CREATE TABLE sj_system_user +( + id number GENERATED ALWAYS AS IDENTITY, + username varchar2(64) NULL, + password varchar2(128) NULL, + role smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_system_user + ADD CONSTRAINT pk_sj_system_user PRIMARY KEY (id); + +COMMENT ON COLUMN sj_system_user.id IS '主键'; +COMMENT ON COLUMN sj_system_user.username IS '账号'; +COMMENT ON COLUMN sj_system_user.password IS '密码'; +COMMENT ON COLUMN sj_system_user.role IS '角色:1-普通用户、2-管理员'; +COMMENT ON COLUMN sj_system_user.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user IS '系统用户表'; + +-- pwd: admin +INSERT INTO sj_system_user(username, password, role, create_dt, update_dt) VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2, sysdate, sysdate); + +-- sj_system_user_permission +CREATE TABLE sj_system_user_permission +( + id number GENERATED ALWAYS AS IDENTITY, + group_name varchar2(64) NULL, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + system_user_id number NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_system_user_permission + ADD CONSTRAINT pk_sj_system_user_permission PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_su_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id); + +COMMENT ON COLUMN sj_system_user_permission.id IS '主键'; +COMMENT ON COLUMN sj_system_user_permission.group_name IS '组名称'; +COMMENT ON COLUMN sj_system_user_permission.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_system_user_permission.system_user_id IS '系统用户id'; +COMMENT ON COLUMN sj_system_user_permission.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user_permission.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user_permission IS '系统用户权限表'; + +-- sj_sequence_alloc +CREATE TABLE sj_sequence_alloc +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) DEFAULT '' NULL, + max_id number DEFAULT 1 NOT NULL, + step number DEFAULT 100 NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_sequence_alloc + ADD CONSTRAINT pk_sj_sequence_alloc PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name); + +COMMENT ON COLUMN sj_sequence_alloc.id IS '主键'; +COMMENT ON COLUMN sj_sequence_alloc.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_sequence_alloc.group_name IS '组名称'; +COMMENT ON COLUMN sj_sequence_alloc.max_id IS '最大id'; +COMMENT ON COLUMN sj_sequence_alloc.step IS '步长'; +COMMENT ON COLUMN sj_sequence_alloc.update_dt IS '更新时间'; +COMMENT ON TABLE sj_sequence_alloc IS '号段模式序号ID分配表'; + +-- sj_job +CREATE TABLE sj_job +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + job_name varchar2(64) NULL, + args_str clob DEFAULT NULL NULL, + args_type smallint DEFAULT 1 NOT NULL, + next_trigger_at number NOT NULL, + job_status smallint DEFAULT 1 NOT NULL, + task_type smallint DEFAULT 1 NOT NULL, + route_key smallint DEFAULT 4 NOT NULL, + executor_type smallint DEFAULT 1 NOT NULL, + executor_info varchar2(255) DEFAULT NULL NULL, + trigger_type smallint NOT NULL, + trigger_interval varchar2(255) NULL, + block_strategy smallint DEFAULT 1 NOT NULL, + executor_timeout number DEFAULT 0 NOT NULL, + max_retry_times number DEFAULT 0 NOT NULL, + parallel_num number DEFAULT 1 NOT NULL, + retry_interval number DEFAULT 0 NOT NULL, + bucket_index number DEFAULT 0 NOT NULL, + resident smallint DEFAULT 0 NOT NULL, + notify_ids varchar2(128) DEFAULT '' NULL, + owner_id number NULL, + description varchar2(256) DEFAULT '' NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_job + ADD CONSTRAINT pk_sj_job PRIMARY KEY (id); + +CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); +CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); +CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); + +COMMENT ON COLUMN sj_job.id IS '主键'; +COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.group_name IS '组名称'; +COMMENT ON COLUMN sj_job.job_name IS '名称'; +COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_job.job_status IS '任务状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_job.task_type IS '任务类型 1、集群 2、广播 3、切片'; +COMMENT ON COLUMN sj_job.route_key IS '路由策略'; +COMMENT ON COLUMN sj_job.executor_type IS '执行器类型'; +COMMENT ON COLUMN sj_job.executor_info IS '执行器名称'; +COMMENT ON COLUMN sj_job.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_job.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_job.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行 4、恢复'; +COMMENT ON COLUMN sj_job.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_job.max_retry_times IS '最大重试次数'; +COMMENT ON COLUMN sj_job.parallel_num IS '并行数'; +COMMENT ON COLUMN sj_job.retry_interval IS '重试间隔 ( s)'; +COMMENT ON COLUMN sj_job.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_job.resident IS '是否是常驻任务'; +COMMENT ON COLUMN sj_job.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_job.owner_id IS '负责人id'; +COMMENT ON COLUMN sj_job.description IS '描述'; +COMMENT ON COLUMN sj_job.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job IS '任务信息'; + +INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'fys_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1,'', '', 0, sysdate, sysdate); + +-- sj_job_log_message +CREATE TABLE sj_job_log_message +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + job_id number NOT NULL, + task_batch_id number NOT NULL, + task_id number NOT NULL, + message clob NULL, + log_num number DEFAULT 1 NOT NULL, + real_time number DEFAULT 0 NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_job_log_message + ADD CONSTRAINT pk_sj_job_log_message PRIMARY KEY (id); + +CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id); +CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt); +CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_log_message.id IS '主键'; +COMMENT ON COLUMN sj_job_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_log_message.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_log_message.task_batch_id IS '任务批次id'; +COMMENT ON COLUMN sj_job_log_message.task_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_log_message.message IS '调度信息'; +COMMENT ON COLUMN sj_job_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_job_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_job_log_message.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_job_log_message IS '调度日志'; + +-- sj_job_task +CREATE TABLE sj_job_task +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + job_id number NOT NULL, + task_batch_id number NOT NULL, + parent_id number DEFAULT 0 NOT NULL, + task_status smallint DEFAULT 0 NOT NULL, + retry_count number DEFAULT 0 NOT NULL, + mr_stage smallint DEFAULT NULL NULL, + leaf smallint DEFAULT '1' NOT NULL, + task_name varchar2(255) DEFAULT '' NULL, + client_info varchar2(128) DEFAULT NULL NULL, + wf_context clob DEFAULT NULL NULL, + result_message clob NULL, + args_str clob DEFAULT NULL NULL, + args_type smallint DEFAULT 1 NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_job_task + ADD CONSTRAINT pk_sj_job_task PRIMARY KEY (id); + +CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status); +CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt); +CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_task.id IS '主键'; +COMMENT ON COLUMN sj_job_task.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_task.task_batch_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_task.parent_id IS '父执行器id'; +COMMENT ON COLUMN sj_job_task.task_status IS '执行的状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_job_task.mr_stage IS '动态分片所处阶段 1:map 2:reduce 3:mergeReduce'; +COMMENT ON COLUMN sj_job_task.leaf IS '叶子节点'; +COMMENT ON COLUMN sj_job_task.task_name IS '任务名称'; +COMMENT ON COLUMN sj_job_task.client_info IS '客户端地址 clientId#ip:port'; +COMMENT ON COLUMN sj_job_task.wf_context IS '工作流全局上下文'; +COMMENT ON COLUMN sj_job_task.result_message IS '执行结果'; +COMMENT ON COLUMN sj_job_task.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job_task.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job_task.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task IS '任务实例'; + +-- sj_job_task_batch +CREATE TABLE sj_job_task_batch +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + job_id number NOT NULL, + workflow_node_id number DEFAULT 0 NOT NULL, + parent_workflow_node_id number DEFAULT 0 NOT NULL, + workflow_task_batch_id number DEFAULT 0 NOT NULL, + task_batch_status smallint DEFAULT 0 NOT NULL, + operation_reason smallint DEFAULT 0 NOT NULL, + execution_at number DEFAULT 0 NOT NULL, + system_task_type smallint DEFAULT 3 NOT NULL, + parent_id varchar2(64) DEFAULT '' NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_job_task_batch + ADD CONSTRAINT pk_sj_job_task_batch PRIMARY KEY (id); + +CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status); +CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt); +CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name); +CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id); + +COMMENT ON COLUMN sj_job_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_job_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task_batch.job_id IS '任务id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_node_id IS '工作流节点id'; +COMMENT ON COLUMN sj_job_task_batch.parent_workflow_node_id IS '工作流任务父批次id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_task_batch_id IS '工作流任务批次id'; +COMMENT ON COLUMN sj_job_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_job_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_job_task_batch.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_task_batch.parent_id IS '父节点'; +COMMENT ON COLUMN sj_job_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task_batch IS '任务批次'; + +-- sj_job_summary +CREATE TABLE sj_job_summary +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) DEFAULT '' NULL, + business_id number NOT NULL, + system_task_type smallint DEFAULT 3 NOT NULL, + trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL, + success_num number DEFAULT 0 NOT NULL, + fail_num number DEFAULT 0 NOT NULL, + fail_reason varchar2(512) DEFAULT '' NULL, + stop_num number DEFAULT 0 NOT NULL, + stop_reason varchar2(512) DEFAULT '' NULL, + cancel_num number DEFAULT 0 NOT NULL, + cancel_reason varchar2(512) DEFAULT '' NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_job_summary + ADD CONSTRAINT pk_sj_job_summary PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id); + +CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id); + +COMMENT ON COLUMN sj_job_summary.id IS '主键'; +COMMENT ON COLUMN sj_job_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_summary.business_id IS '业务id ( job_id或workflow_id)'; +COMMENT ON COLUMN sj_job_summary.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_job_summary.success_num IS '执行成功-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.stop_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.stop_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.cancel_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.cancel_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_summary IS 'DashBoard_Job'; + +-- sj_retry_summary +CREATE TABLE sj_retry_summary +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) DEFAULT '' NULL, + scene_name varchar2(50) DEFAULT '' NULL, + trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL, + running_num number DEFAULT 0 NOT NULL, + finish_num number DEFAULT 0 NOT NULL, + max_count_num number DEFAULT 0 NOT NULL, + suspend_num number DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_retry_summary + ADD CONSTRAINT pk_sj_retry_summary PRIMARY KEY (id); + +CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at); + +CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at); + +COMMENT ON COLUMN sj_retry_summary.id IS '主键'; +COMMENT ON COLUMN sj_retry_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_summary.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_retry_summary.running_num IS '重试中-日志数量'; +COMMENT ON COLUMN sj_retry_summary.finish_num IS '重试完成-日志数量'; +COMMENT ON COLUMN sj_retry_summary.max_count_num IS '重试到达最大次数-日志数量'; +COMMENT ON COLUMN sj_retry_summary.suspend_num IS '暂停重试-日志数量'; +COMMENT ON COLUMN sj_retry_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_summary IS 'DashBoard_Retry'; + +-- sj_workflow +CREATE TABLE sj_workflow +( + id number GENERATED ALWAYS AS IDENTITY, + workflow_name varchar2(64) NULL, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + workflow_status smallint DEFAULT 1 NOT NULL, + trigger_type smallint NOT NULL, + trigger_interval varchar2(255) NULL, + next_trigger_at number NOT NULL, + block_strategy smallint DEFAULT 1 NOT NULL, + executor_timeout number DEFAULT 0 NOT NULL, + description varchar2(256) DEFAULT '' NULL, + flow_info clob DEFAULT NULL NULL, + wf_context clob DEFAULT NULL NULL, + notify_ids varchar2(128) DEFAULT '' NULL, + bucket_index number DEFAULT 0 NOT NULL, + version number NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_workflow + ADD CONSTRAINT pk_sj_workflow PRIMARY KEY (id); + +CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); +CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow.id IS '主键'; +COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; +COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_workflow.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_workflow.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_workflow.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_workflow.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_workflow.description IS '描述'; +COMMENT ON COLUMN sj_workflow.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow.wf_context IS '上下文'; +COMMENT ON COLUMN sj_workflow.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_workflow.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_workflow.version IS '版本号'; +COMMENT ON COLUMN sj_workflow.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow IS '工作流'; + +-- sj_workflow_node +CREATE TABLE sj_workflow_node +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + node_name varchar2(64) NULL, + group_name varchar2(64) NULL, + job_id number NOT NULL, + workflow_id number NOT NULL, + node_type smallint DEFAULT 1 NOT NULL, + expression_type smallint DEFAULT 0 NOT NULL, + fail_strategy smallint DEFAULT 1 NOT NULL, + workflow_node_status smallint DEFAULT 1 NOT NULL, + priority_level number DEFAULT 1 NOT NULL, + node_info clob DEFAULT NULL NULL, + version number NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_workflow_node + ADD CONSTRAINT pk_sj_workflow_node PRIMARY KEY (id); + +CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt); +CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_node.id IS '主键'; +COMMENT ON COLUMN sj_workflow_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_node.node_name IS '节点名称'; +COMMENT ON COLUMN sj_workflow_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_node.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_workflow_node.workflow_id IS '工作流ID'; +COMMENT ON COLUMN sj_workflow_node.node_type IS '1、任务节点 2、条件节点'; +COMMENT ON COLUMN sj_workflow_node.expression_type IS '1、SpEl、2、Aviator 3、QL'; +COMMENT ON COLUMN sj_workflow_node.fail_strategy IS '失败策略 1、跳过 2、阻塞'; +COMMENT ON COLUMN sj_workflow_node.workflow_node_status IS '工作流节点状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow_node.priority_level IS '优先级'; +COMMENT ON COLUMN sj_workflow_node.node_info IS '节点信息 '; +COMMENT ON COLUMN sj_workflow_node.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_node.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_node IS '工作流节点'; + +-- sj_workflow_task_batch +CREATE TABLE sj_workflow_task_batch +( + id number GENERATED ALWAYS AS IDENTITY, + namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + group_name varchar2(64) NULL, + workflow_id number NOT NULL, + task_batch_status smallint DEFAULT 0 NOT NULL, + operation_reason smallint DEFAULT 0 NOT NULL, + flow_info clob DEFAULT NULL NULL, + wf_context clob DEFAULT NULL NULL, + execution_at number DEFAULT 0 NOT NULL, + ext_attrs varchar2(256) DEFAULT '' NULL, + version number DEFAULT 1 NOT NULL, + deleted smallint DEFAULT 0 NOT NULL, + create_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL, + update_dt date DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +ALTER TABLE sj_workflow_task_batch + ADD CONSTRAINT pk_sj_workflow_task_batch PRIMARY KEY (id); + +CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status); +CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt); +CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_workflow_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_task_batch.workflow_id IS '工作流任务id'; +COMMENT ON COLUMN sj_workflow_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_workflow_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_workflow_task_batch.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow_task_batch.wf_context IS '全局上下文'; +COMMENT ON COLUMN sj_workflow_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_workflow_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_task_batch.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_task_batch IS '工作流批次'; diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql new file mode 100644 index 0000000..662a7d1 --- /dev/null +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -0,0 +1,1407 @@ +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +create table sys_social +( + id number(20) not null, + user_id number(20) not null, + tenant_id varchar2(20) default '000000', + auth_id varchar2(255) not null, + source varchar2(255) not null, + open_id varchar2(255) default null, + user_name varchar2(30) not null, + nick_name varchar2(30) default '', + email varchar2(255) default '', + avatar varchar2(500) default '', + access_token varchar2(2000) not null, + expire_in number(20) default null, + refresh_token varchar2(2000) default null, + access_code varchar2(255) default null, + union_id varchar2(255) default null, + scope varchar2(255) default null, + token_type varchar2(255) default null, + id_token varchar2(2000) default null, + mac_algorithm varchar2(255) default null, + mac_key varchar2(255) default null, + code varchar2(255) default null, + oauth_token varchar2(255) default null, + oauth_token_secret varchar2(255) default null, + create_dept number(20), + create_by number(20), + create_time date, + update_by number(20), + update_time date, + del_flag char(1) default '0' +); + +alter table sys_social add constraint pk_sys_social primary key (id); + +comment on table sys_social is '社会化关系表'; +comment on column sys_social.id is '主键'; +comment on column sys_social.user_id is '用户ID'; +comment on column sys_social.tenant_id is '租户id'; +comment on column sys_social.auth_id is '平台+平台唯一id'; +comment on column sys_social.source is '用户来源'; +comment on column sys_social.open_id is '平台编号唯一id'; +comment on column sys_social.user_name is '登录账号'; +comment on column sys_social.nick_name is '用户昵称'; +comment on column sys_social.email is '用户邮箱'; +comment on column sys_social.avatar is '头像地址'; +comment on column sys_social.access_token is '用户的授权令牌'; +comment on column sys_social.expire_in is '用户的授权令牌的有效期,部分平台可能没有'; +comment on column sys_social.refresh_token is '刷新令牌,部分平台可能没有'; +comment on column sys_social.access_code is '平台的授权信息,部分平台可能没有'; +comment on column sys_social.union_id is '用户的 unionid'; +comment on column sys_social.scope is '授予的权限,部分平台可能没有'; +comment on column sys_social.token_type is '个别平台的授权信息,部分平台可能没有'; +comment on column sys_social.id_token is 'id token,部分平台可能没有'; +comment on column sys_social.mac_algorithm is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.mac_key is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.code is '用户的授权code,部分平台可能没有'; +comment on column sys_social.oauth_token is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.oauth_token_secret is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.create_dept is '创建部门'; +comment on column sys_social.create_by is '创建者'; +comment on column sys_social.create_time is '创建时间'; +comment on column sys_social.update_by is '更新者'; +comment on column sys_social.update_time is '更新时间'; +comment on column sys_social.del_flag is '删除标志(0代表存在 1代表删除)'; + +-- ---------------------------- +-- 租户表 +-- ---------------------------- +create table sys_tenant ( + id number(20) not null, + tenant_id varchar2(20) not null, + contact_user_name varchar2(20) default '', + contact_phone varchar2(20) default '', + company_name varchar2(30) default '', + license_number varchar2(30) default '', + address varchar2(200) default '', + intro varchar2(200) default '', + domain varchar2(200) default '', + remark varchar2(200) default '', + package_id number(20) default null, + expire_time date default null, + account_count number(4) default -1, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_tenant add constraint pk_sys_tenant primary key (id); + +comment on table sys_tenant is '租户表'; +comment on column sys_tenant.tenant_id is '租户编号'; +comment on column sys_tenant.contact_phone is '联系电话'; +comment on column sys_tenant.company_name is '企业名称'; +comment on column sys_tenant.company_name is '联系人'; +comment on column sys_tenant.license_number is '统一社会信用代码'; +comment on column sys_tenant.address is '地址'; +comment on column sys_tenant.intro is '企业简介'; +comment on column sys_tenant.remark is '备注'; +comment on column sys_tenant.package_id is '租户套餐编号'; +comment on column sys_tenant.expire_time is '过期时间'; +comment on column sys_tenant.account_count is '用户数量(-1不限制)'; +comment on column sys_tenant.status is '租户状态(0正常 1停用)'; +comment on column sys_tenant.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_tenant.create_dept is '创建部门'; +comment on column sys_tenant.create_by is '创建者'; +comment on column sys_tenant.create_time is '创建时间'; +comment on column sys_tenant.update_by is '更新者'; +comment on column sys_tenant.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-租户表数据 +-- ---------------------------- + +insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, sysdate, null, null); + + +-- ---------------------------- +-- 租户套餐表 +-- ---------------------------- +create table sys_tenant_package ( + package_id number(20) not null, + package_name varchar2(20) default '', + menu_ids varchar2(3000) default '', + remark varchar2(200) default '', + menu_check_strictly number(1) default 1, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_tenant_package add constraint pk_sys_tenant_package primary key (package_id); + +comment on table sys_tenant_package is '租户套餐表'; +comment on column sys_tenant_package.package_id is '租户套餐id'; +comment on column sys_tenant_package.package_name is '套餐名称'; +comment on column sys_tenant_package.menu_ids is '关联菜单id'; +comment on column sys_tenant_package.remark is '备注'; +comment on column sys_tenant_package.status is '状态(0正常 1停用)'; +comment on column sys_tenant_package.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_tenant_package.create_dept is '创建部门'; +comment on column sys_tenant_package.create_by is '创建者'; +comment on column sys_tenant_package.create_time is '创建时间'; +comment on column sys_tenant_package.update_by is '更新者'; +comment on column sys_tenant_package.update_time is '更新时间'; + + +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +create table sys_dept ( + dept_id number(20) not null, + tenant_id varchar2(20) default '000000', + parent_id number(20) default 0, + ancestors varchar2(500) default '', + dept_name varchar2(30) default '', + dept_category varchar2(100) default null, + order_num number(4) default 0, + leader number(20) default null, + phone varchar2(11) default null, + email varchar2(50) default null, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_dept add constraint pk_sys_dept primary key (dept_id); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门id'; +comment on column sys_dept.tenant_id is '租户编号'; +comment on column sys_dept.parent_id is '父部门id'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.dept_category is '部门类别编码'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_dept.create_dept is '创建部门'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- + +insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); +insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +create table sys_user ( + user_id number(20) not null, + tenant_id varchar2(20) default '000000', + dept_id number(20) default null, + user_name varchar2(40) not null, + nick_name varchar2(40) not null, + user_type varchar2(10) default 'sys_user', + email varchar2(50) default '', + phonenumber varchar2(11) default '', + sex char(1) default '0', + avatar number(20) default null, + password varchar2(100) default '', + status char(1) default '0', + del_flag char(1) default '0', + login_ip varchar2(128) default '', + login_date date, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default '' +); + +alter table sys_user add constraint pk_sys_user primary key (user_id); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.tenant_id is '租户编号'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像路径'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_user.login_ip is '最后登录IP'; +comment on column sys_user.login_date is '最后登录时间'; +comment on column sys_user.create_dept is '创建部门'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '管理员'); +insert into sys_user values(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, ''); +insert into sys_user values(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, ''); + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +create table sys_post ( + post_id number(20) not null, + tenant_id varchar2(20) default '000000', + dept_id number(20) not null, + post_code varchar2(64) not null, + post_category varchar2(64) default null, + post_name varchar2(50) not null, + post_sort number(4) not null, + status char(1) not null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) +); + +alter table sys_post add constraint pk_sys_post primary key (post_id); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.tenant_id is '租户编号'; +comment on column sys_post.dept_id is '部门id'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_category is '岗位类别编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_dept is '创建部门'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, '000000', 103, 'ceo', null, '董事长', 1, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(2, '000000', 100, 'se', null, '项目经理', 2, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(3, '000000', 100, 'hr', null, '人力资源', 3, '0', 103, 1, sysdate, null, null, ''); +insert into sys_post values(4, '000000', 100, 'user', null, '普通员工', 4, '0', 103, 1, sysdate, null, null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +create table sys_role ( + role_id number(20) not null, + tenant_id varchar2(20) default '000000', + role_name varchar2(30) not null, + role_key varchar2(100) not null, + role_sort number(4) not null, + data_scope char(1) default '1', + menu_check_strictly number(1) default 1, + dept_check_strictly number(1) default 1, + status char(1) not null, + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_role add constraint pk_sys_role primary key (role_id); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.tenant_id is '租户编号'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_role.create_dept is '创建部门'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '000000', '超级管理员', 'superadmin', 1, 1, 1, 1, '0', '0', 103, 1, sysdate, null, null, '超级管理员'); +insert into sys_role values('3', '000000', '本部门及以下', 'test1', 3, 4, 1, 1, '0', '0', 103, 1, sysdate, null, null, null); +insert into sys_role values('4', '000000', '仅本人', 'test2', 4, 5, 1, 1, '0', '0', 103, 1, sysdate, null, null, null); + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +create table sys_menu ( + menu_id number(20) not null, + menu_name varchar2(50) not null, + parent_id number(20) default 0, + order_num number(4) default 0, + path varchar2(200) default '', + component varchar2(255) default null, + query_param varchar2(255) default null, + is_frame number(1) default 1, + is_cache number(1) default 0, + menu_type char(1) default '', + visible char(1) default 0, + status char(1) default 0, + perms varchar2(100) default null, + icon varchar2(100) default '#', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date , + remark varchar2(500) default '' +); + +alter table sys_menu add constraint pk_sys_menu primary key (menu_id); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '请求地址'; +comment on column sys_menu.component is '路由地址'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_dept is '创建部门'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, sysdate, null, null, '系统管理目录'); +insert into sys_menu values('6', '租户管理', '0', '2', 'tenant', null, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, sysdate, null, null, '租户管理目录'); +insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, sysdate, null, null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, sysdate, null, null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/fys-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 103, 1, sysdate, null, null, 'fys-Vue-Plus官网地址'); +insert into sys_menu values('5', '测试菜单', '0', '5', 'demo', null, '', 1, 0, 'M', '0', '0', null, 'star', 103, 1, sysdate, null, null, ''); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, sysdate, null, null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, sysdate, null, null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, sysdate, null, null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, sysdate, null, null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, sysdate, null, null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, sysdate, null, null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, sysdate, null, null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, sysdate, null, null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, sysdate, null, null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, sysdate, null, null, '在线用户菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, sysdate, null, null, '缓存监控菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, sysdate, null, null, '代码生成菜单'); +insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate, null, null, '租户管理菜单'); +insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate, null, null, '租户套餐管理菜单'); +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate, null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); + +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate, null, null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, sysdate, null, null, '文件管理菜单'); +-- snail-job server控制台 +insert into sys_menu values('120', '任务调度中心', '2', '5', 'snailjob', 'monitor/snailjob/index', '', 1, 0, 'C', '0', '0', 'monitor:snailjob:list', 'job', 103, 1, sysdate, null, null, 'snailjob控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, sysdate, null, null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, sysdate, null, null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, sysdate, null, null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, sysdate, null, null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, sysdate, null, null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, sysdate, null, null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, sysdate, null, null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, sysdate, null, null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, sysdate, null, null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, sysdate, null, null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, sysdate, null, null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate, null, null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, sysdate, null, null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, sysdate, null, null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate, null, null, ''); +-- 租户管理相关按钮 +insert into sys_menu values('1606', '租户查询', '121', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1607', '租户新增', '121', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1608', '租户修改', '121', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1609', '租户删除', '121', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1610', '租户导出', '121', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, sysdate, null, null, ''); +-- 租户套餐管理相关按钮 +insert into sys_menu values('1611', '租户套餐查询', '122', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1612', '租户套餐新增', '122', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1613', '租户套餐修改', '122', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1614', '租户套餐删除', '122', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1615', '租户套餐导出', '122', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, sysdate, null, null, ''); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, sysdate, null, null, ''); +-- 测试菜单 +insert into sys_menu values('1500', '测试单表', '5', '1', 'demo', 'demo/demo/index', '', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate, null, null, '测试单表菜单'); +insert into sys_menu values('1501', '测试单表查询', '1500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1502', '测试单表新增', '1500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1503', '测试单表修改', '1500', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1504', '测试单表删除', '1500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1505', '测试单表导出', '1500', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1506', '测试树表', '5', '1', 'tree', 'demo/tree/index', '', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate, null, null, '测试树表菜单'); +insert into sys_menu values('1507', '测试树表查询', '1506', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1508', '测试树表新增', '1506', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1509', '测试树表修改', '1506', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1510', '测试树表删除', '1506', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1511', '测试树表导出', '1506', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate, null, null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +create table sys_user_role ( + user_id number(20) not null, + role_id number(20) not null +); + +alter table sys_user_role add constraint pk_sys_user_role primary key (user_id, role_id); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('3', '3'); +insert into sys_user_role values ('4', '4'); + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +create table sys_role_menu ( + role_id number(20) not null, + menu_id number(20) not null +); + +alter table sys_role_menu add constraint pk_sys_role_menu primary key (role_id, menu_id); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('3', '1'); +insert into sys_role_menu values ('3', '5'); +insert into sys_role_menu values ('3', '100'); +insert into sys_role_menu values ('3', '101'); +insert into sys_role_menu values ('3', '102'); +insert into sys_role_menu values ('3', '103'); +insert into sys_role_menu values ('3', '104'); +insert into sys_role_menu values ('3', '105'); +insert into sys_role_menu values ('3', '106'); +insert into sys_role_menu values ('3', '107'); +insert into sys_role_menu values ('3', '108'); +insert into sys_role_menu values ('3', '118'); +insert into sys_role_menu values ('3', '123'); +insert into sys_role_menu values ('3', '130'); +insert into sys_role_menu values ('3', '131'); +insert into sys_role_menu values ('3', '132'); +insert into sys_role_menu values ('3', '133'); +insert into sys_role_menu values ('3', '500'); +insert into sys_role_menu values ('3', '501'); +insert into sys_role_menu values ('3', '1001'); +insert into sys_role_menu values ('3', '1002'); +insert into sys_role_menu values ('3', '1003'); +insert into sys_role_menu values ('3', '1004'); +insert into sys_role_menu values ('3', '1005'); +insert into sys_role_menu values ('3', '1006'); +insert into sys_role_menu values ('3', '1007'); +insert into sys_role_menu values ('3', '1008'); +insert into sys_role_menu values ('3', '1009'); +insert into sys_role_menu values ('3', '1010'); +insert into sys_role_menu values ('3', '1011'); +insert into sys_role_menu values ('3', '1012'); +insert into sys_role_menu values ('3', '1013'); +insert into sys_role_menu values ('3', '1014'); +insert into sys_role_menu values ('3', '1015'); +insert into sys_role_menu values ('3', '1016'); +insert into sys_role_menu values ('3', '1017'); +insert into sys_role_menu values ('3', '1018'); +insert into sys_role_menu values ('3', '1019'); +insert into sys_role_menu values ('3', '1020'); +insert into sys_role_menu values ('3', '1021'); +insert into sys_role_menu values ('3', '1022'); +insert into sys_role_menu values ('3', '1023'); +insert into sys_role_menu values ('3', '1024'); +insert into sys_role_menu values ('3', '1025'); +insert into sys_role_menu values ('3', '1026'); +insert into sys_role_menu values ('3', '1027'); +insert into sys_role_menu values ('3', '1028'); +insert into sys_role_menu values ('3', '1029'); +insert into sys_role_menu values ('3', '1030'); +insert into sys_role_menu values ('3', '1031'); +insert into sys_role_menu values ('3', '1032'); +insert into sys_role_menu values ('3', '1033'); +insert into sys_role_menu values ('3', '1034'); +insert into sys_role_menu values ('3', '1035'); +insert into sys_role_menu values ('3', '1036'); +insert into sys_role_menu values ('3', '1037'); +insert into sys_role_menu values ('3', '1038'); +insert into sys_role_menu values ('3', '1039'); +insert into sys_role_menu values ('3', '1040'); +insert into sys_role_menu values ('3', '1041'); +insert into sys_role_menu values ('3', '1042'); +insert into sys_role_menu values ('3', '1043'); +insert into sys_role_menu values ('3', '1044'); +insert into sys_role_menu values ('3', '1045'); +insert into sys_role_menu values ('3', '1050'); +insert into sys_role_menu values ('3', '1061'); +insert into sys_role_menu values ('3', '1062'); +insert into sys_role_menu values ('3', '1063'); +insert into sys_role_menu values ('3', '1064'); +insert into sys_role_menu values ('3', '1065'); +insert into sys_role_menu values ('3', '1500'); +insert into sys_role_menu values ('3', '1501'); +insert into sys_role_menu values ('3', '1502'); +insert into sys_role_menu values ('3', '1503'); +insert into sys_role_menu values ('3', '1504'); +insert into sys_role_menu values ('3', '1505'); +insert into sys_role_menu values ('3', '1506'); +insert into sys_role_menu values ('3', '1507'); +insert into sys_role_menu values ('3', '1508'); +insert into sys_role_menu values ('3', '1509'); +insert into sys_role_menu values ('3', '1510'); +insert into sys_role_menu values ('3', '1511'); +insert into sys_role_menu values ('3', '1600'); +insert into sys_role_menu values ('3', '1601'); +insert into sys_role_menu values ('3', '1602'); +insert into sys_role_menu values ('3', '1603'); +insert into sys_role_menu values ('3', '1620'); +insert into sys_role_menu values ('3', '1621'); +insert into sys_role_menu values ('3', '1622'); +insert into sys_role_menu values ('3', '1623'); +insert into sys_role_menu values ('3', '11616'); +insert into sys_role_menu values ('3', '11618'); +insert into sys_role_menu values ('3', '11619'); +insert into sys_role_menu values ('3', '11622'); +insert into sys_role_menu values ('3', '11623'); +insert into sys_role_menu values ('3', '11629'); +insert into sys_role_menu values ('3', '11632'); +insert into sys_role_menu values ('3', '11633'); +insert into sys_role_menu values ('3', '11638'); +insert into sys_role_menu values ('3', '11639'); +insert into sys_role_menu values ('3', '11640'); +insert into sys_role_menu values ('3', '11641'); +insert into sys_role_menu values ('3', '11642'); +insert into sys_role_menu values ('3', '11643'); +insert into sys_role_menu values ('3', '11701'); +insert into sys_role_menu values ('4', '5'); +insert into sys_role_menu values ('4', '1500'); +insert into sys_role_menu values ('4', '1501'); +insert into sys_role_menu values ('4', '1502'); +insert into sys_role_menu values ('4', '1503'); +insert into sys_role_menu values ('4', '1504'); +insert into sys_role_menu values ('4', '1505'); +insert into sys_role_menu values ('4', '1506'); +insert into sys_role_menu values ('4', '1507'); +insert into sys_role_menu values ('4', '1508'); +insert into sys_role_menu values ('4', '1509'); +insert into sys_role_menu values ('4', '1510'); +insert into sys_role_menu values ('4', '1511'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +create table sys_role_dept ( + role_id number(20) not null, + dept_id number(20) not null +); + +alter table sys_role_dept add constraint pk_sys_role_dept primary key (role_id, dept_id); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +create table sys_user_post ( + user_id number(20) not null, + post_id number(20) not null +); + +alter table sys_user_post add constraint pk_sys_user_post primary key (user_id, post_id); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +create table sys_oper_log ( + oper_id number(20) not null, + tenant_id varchar2(20) default '000000', + title varchar2(50) default '', + business_type number(2) default 0, + method varchar2(100) default '', + request_method varchar2(10) default '', + operator_type number(1) default 0, + oper_name varchar2(50) default '', + dept_name varchar2(50) default '', + oper_url varchar2(255) default '', + oper_ip varchar2(128) default '', + oper_location varchar2(255) default '', + oper_param varchar2(4000) default '', + json_result varchar2(4000) default '', + status number(1) default 0, + error_msg varchar2(4000) default '', + oper_time date, + cost_time number(20) default 0 +); + +alter table sys_oper_log add constraint pk_sys_oper_log primary key (oper_id); +create index idx_sys_oper_log_bt on sys_oper_log (business_type); +create index idx_sys_oper_log_s on sys_oper_log (status); +create index idx_sys_oper_log_ot on sys_oper_log (oper_time); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.tenant_id is '租户编号'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; +comment on column sys_oper_log.cost_time is '消耗时间'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +create table sys_dict_type ( + dict_id number(20) not null, + tenant_id varchar2(20) default '000000', + dict_name varchar2(100) default '', + dict_type varchar2(100) default '', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_type add constraint pk_sys_dict_type primary key (dict_id); +create unique index sys_dict_type_index1 on sys_dict_type (tenant_id, dict_type); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.create_dept is '创建部门'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '000000', '用户性别', 'sys_user_sex', 103, 1, sysdate, null, null, '用户性别列表'); +insert into sys_dict_type values(2, '000000', '菜单状态', 'sys_show_hide', 103, 1, sysdate, null, null, '菜单状态列表'); +insert into sys_dict_type values(3, '000000', '系统开关', 'sys_normal_disable', 103, 1, sysdate, null, null, '系统开关列表'); +insert into sys_dict_type values(6, '000000', '系统是否', 'sys_yes_no', 103, 1, sysdate, null, null, '系统是否列表'); +insert into sys_dict_type values(7, '000000', '通知类型', 'sys_notice_type', 103, 1, sysdate, null, null, '通知类型列表'); +insert into sys_dict_type values(8, '000000', '通知状态', 'sys_notice_status', 103, 1, sysdate, null, null, '通知状态列表'); +insert into sys_dict_type values(9, '000000', '操作类型', 'sys_oper_type', 103, 1, sysdate, null, null, '操作类型列表'); +insert into sys_dict_type values(10, '000000', '系统状态', 'sys_common_status', 103, 1, sysdate, null, null, '登录状态列表'); +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', 103, 1, sysdate, null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, sysdate, null, null, '客户端设备类型'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +create table sys_dict_data ( + dict_code number(20) not null, + tenant_id varchar2(20) default '000000', + dict_sort number(4) default 0, + dict_label varchar2(100) default '', + dict_value varchar2(100) default '', + dict_type varchar2(100) default '', + css_class varchar2(100) default null, + list_class varchar2(100) default null, + is_default char(1) default 'N', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table sys_dict_data add constraint pk_sys_dict_data primary key (dict_code); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典主键'; +comment on column sys_dict_data.tenant_id is '租户编号'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.create_dept is '创建部门'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', 103, 1, sysdate, null, null, '性别男'); +insert into sys_dict_data values(2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', 103, 1, sysdate, null, null, '性别女'); +insert into sys_dict_data values(3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', 103, 1, sysdate, null, null, '性别未知'); +insert into sys_dict_data values(4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', 103, 1, sysdate, null, null, '显示菜单'); +insert into sys_dict_data values(5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', 103, 1, sysdate, null, null, '隐藏菜单'); +insert into sys_dict_data values(6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', 103, 1, sysdate, null, null, '停用状态'); +insert into sys_dict_data values(12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', 103, 1, sysdate, null, null, '系统默认是'); +insert into sys_dict_data values(13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', 103, 1, sysdate, null, null, '系统默认否'); +insert into sys_dict_data values(14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', 103, 1, sysdate, null, null, '通知'); +insert into sys_dict_data values(15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', 103, 1, sysdate, null, null, '公告'); +insert into sys_dict_data values(16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', 103, 1, sysdate, null, null, '关闭状态'); +insert into sys_dict_data values(29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate, null, null, '其他操作'); +insert into sys_dict_data values(18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate, null, null, '新增操作'); +insert into sys_dict_data values(19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate, null, null, '修改操作'); +insert into sys_dict_data values(20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate, null, null, '删除操作'); +insert into sys_dict_data values(21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', 103, 1, sysdate, null, null, '授权操作'); +insert into sys_dict_data values(22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate, null, null, '导出操作'); +insert into sys_dict_data values(23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate, null, null, '导入操作'); +insert into sys_dict_data values(24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate, null, null, '强退操作'); +insert into sys_dict_data values(25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate, null, null, '生成操作'); +insert into sys_dict_data values(26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate, null, null, '清空操作'); +insert into sys_dict_data values(27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', 103, 1, sysdate, null, null, '正常状态'); +insert into sys_dict_data values(28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', 103, 1, sysdate, null, null, '停用状态'); +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate, null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate, null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate, null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate, null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate, null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate, null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate, null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate, null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate, null, null, '小程序'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +create table sys_config ( + config_id number(20) not null, + tenant_id varchar2(20) default '000000', + config_name varchar2(100) default '', + config_key varchar2(100) default '', + config_value varchar2(100) default '', + config_type char(1) default 'N', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); +alter table sys_config add constraint pk_sys_config primary key (config_id); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.tenant_id is '租户编号'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_dept is '创建部门'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, sysdate, null, null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, sysdate, null, null, '初始化密码 123456' ); +insert into sys_config values(3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, sysdate, null, null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, sysdate, null, null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, sysdate, null, null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +create table sys_logininfor ( + info_id number(20) not null, + tenant_id varchar2(20) default '000000', + user_name varchar2(50) default '', + client_key varchar2(32) default '', + device_type varchar2(32) default '', + ipaddr varchar2(128) default '', + login_location varchar2(255) default '', + browser varchar2(50) default '', + os varchar2(50) default '', + status char(1) default '0', + msg varchar2(255) default '', + login_time date +); + +alter table sys_logininfor add constraint pk_sys_logininfor primary key (info_id); +create index idx_sys_logininfor_s on sys_logininfor (status); +create index idx_sys_logininfor_lt on sys_logininfor (login_time); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.tenant_id is '租户编号'; +comment on column sys_logininfor.user_name is '登录账号'; +comment on column sys_logininfor.client_key is '客户端'; +comment on column sys_logininfor.device_type is '设备类型'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +create table sys_notice ( + notice_id number(20) not null, + tenant_id varchar2(20) default '000000', + notice_title varchar2(50) not null, + notice_type char(1) not null, + notice_content clob default null, + status char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(255) default null +); + +alter table sys_notice add constraint pk_sys_notice primary key (notice_id); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告主键'; +comment on column sys_notice.tenant_id is '租户编号'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_dept is '创建部门'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, sysdate, null, null, '管理员'); +insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, sysdate, null, null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +create table gen_table ( + table_id number(20) not null, + data_name varchar2(200) default '', + table_name varchar2(200) default '', + table_comment varchar2(500) default '', + sub_table_name varchar2(64) default null, + sub_table_fk_name varchar2(64) default null, + class_name varchar2(100) default '', + tpl_category varchar2(200) default 'crud', + package_name varchar2(100), + module_name varchar2(30), + business_name varchar2(30), + function_name varchar2(50), + function_author varchar2(50), + gen_type char(1) default '0', + gen_path varchar2(200) default '/', + options varchar2(1000), + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date, + remark varchar2(500) default null +); + +alter table gen_table add constraint pk_gen_table primary key (table_id); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.data_name is '数据源名称'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(crud单表操作 tree树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_dept is '创建部门'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +create table gen_table_column ( + column_id number(20) not null, + table_id number(20), + column_name varchar2(200), + column_comment varchar2(500), + column_type varchar2(100), + java_type varchar2(500), + java_field varchar2(200), + is_pk char(1), + is_increment char(1), + is_required char(1), + is_insert char(1), + is_edit char(1), + is_list char(1), + is_query char(1), + query_type varchar2(200) default 'EQ', + html_type varchar2(200), + dict_type varchar2(200) default '', + sort number(4), + create_dept number(20) default null, + create_by number(20) default null, + create_time date , + update_by number(20) default null, + update_time date +); + +alter table gen_table_column add constraint pk_gen_table_column primary key (column_id); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_dept is '创建部门'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +create table sys_oss ( + oss_id number(20) not null, + tenant_id varchar2(20) default '000000', + file_name varchar2(255) not null, + original_name varchar2(255) not null, + file_suffix varchar2(10) not null, + url varchar2(500) not null, + service varchar2(20) default 'minio' not null, + ext1 varchar2(500) default '', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_oss add constraint pk_sys_oss primary key (oss_id); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.tenant_id is '租户编码'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.service is '服务商'; +comment on column sys_oss.ext1 is '扩展字段'; +comment on column sys_oss.create_dept is '创建部门'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.create_by is '上传者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.update_by is '更新者'; + + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +create table sys_oss_config ( + oss_config_id number(20) not null, + tenant_id varchar2(20) default '000000', + config_key varchar2(20) not null, + access_key varchar2(255) default '', + secret_key varchar2(255) default '', + bucket_name varchar2(255) default '', + prefix varchar2(255) default '', + endpoint varchar2(255) default '', + domain varchar2(255) default '', + is_https char(1) default 'N', + region varchar2(255) default '', + access_policy char(1) default '1' not null, + status char(1) default '1', + ext1 varchar2(255) default '', + remark varchar2(500) default null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主键'; +comment on column sys_oss_config.tenant_id is '租户编码'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accesskey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.remark is '备注'; +comment on column sys_oss_config.create_dept is '创建部门'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; + +insert into sys_oss_config values (1, '000000', 'minio', 'fys', 'fys123', 'fys', '', '127.0.0.1:9000', '','N', '', '1', '0', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys-1250000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); +insert into sys_oss_config values (5, '000000', 'image', 'fys', 'fys123', 'fys', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', NULL, 103, 1, sysdate, 1, sysdate); + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +create table sys_client ( + id number(20) not null, + client_id varchar2(64) default null, + client_key varchar2(32) default null, + client_secret varchar2(255) default null, + grant_type varchar2(255) default null, + device_type varchar2(32) default null, + active_timeout number(11) default 1800, + timeout number(11) default 604800, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_client add constraint pk_sys_client primary key (id); + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主键'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.device_type is '设备类型'; +comment on column sys_client.active_timeout is 'token活跃超时时间'; +comment on column sys_client.timeout is 'token固定超时'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate); + +create table test_demo ( + id number(20) not null, + tenant_id varchar2(20) default '000000', + dept_id number(20) default null, + user_id number(20) default null, + order_num number(10) default 0, + test_key varchar2(255) default null, + value varchar2(255) default null, + version number(10) default 0, + create_dept number(20) default null, + create_time date, + create_by number(20) default null, + update_time date, + update_by number(20) default null, + del_flag number(2) default 0 +); + +alter table test_demo add constraint pk_test_demo primary key (id); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.tenant_id is '租户编号'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_dept is '创建部门'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +create table test_tree ( + id number(20) not null, + tenant_id varchar2(20) default '000000', + parent_id number(20) default 0, + dept_id number(20) default null, + user_id number(20) default null, + tree_name varchar2(255) default null, + version number(10) default 0, + create_dept number(20) default null, + create_time date, + create_by number(20) default null, + update_time date, + update_by number(20) default null, + del_flag number(2) default 0 +); + +alter table test_tree add constraint pk_test_tree primary key (id); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.tenant_id is '租户编号'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_dept is '创建部门'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +insert into test_demo values (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, sysdate, 1, null, null, 0); +insert into test_demo values (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, sysdate, 1, null, null, 0); + +insert into test_tree values (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (2, '000000', 1, 102, 3, '子节点1', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (3, '000000', 2, 102, 3, '子节点2', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (4, '000000', 0, 108, 4, '测试树1', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (5, '000000', 4, 108, 3, '子节点11', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (6, '000000', 4, 108, 3, '子节点22', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (7, '000000', 4, 108, 3, '子节点33', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (8, '000000', 5, 108, 3, '子节点44', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (9, '000000', 6, 108, 3, '子节点55', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (10, '000000', 7, 108, 3, '子节点66', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (11, '000000', 7, 108, 3, '子节点77', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (12, '000000', 10, 108, 3, '子节点88', 0, 103, sysdate, 1, null, null, 0); +insert into test_tree values (13, '000000', 10, 108, 3, '子节点99', 0, 103, sysdate, 1, null, null, 0); + + +-- ---------------------------- +-- 钩子 ,用于session连接之后 自动设置默认的date类型格式化 简化时间查询 +-- 如需设置其它配置 可在此钩子内任意增加处理语句 +-- 例如: SELECT * FROM sys_user WHERE create_time BETWEEN '2022-03-01 00:00:00' AND '2022-04-01 00:00:00' +-- ---------------------------- +create or replace trigger login_trg +after logon on database +begin +execute immediate 'alter session set nls_date_format=''YYYY-MM-DD HH24:MI:SS'''; +end; diff --git a/script/sql/oracle/oracle_ry_workflow.sql b/script/sql/oracle/oracle_ry_workflow.sql new file mode 100644 index 0000000..4c6f80e --- /dev/null +++ b/script/sql/oracle/oracle_ry_workflow.sql @@ -0,0 +1,429 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/oracle/oracle-wram-flow-all.sql +-- ---------------------------- +create table FLOW_DEFINITION +( + ID NUMBER(20) not null, + FLOW_CODE VARCHAR2(40) not null, + FLOW_NAME VARCHAR2(100) not null, + CATEGORY VARCHAR2(100), + VERSION VARCHAR2(20) not null, + IS_PUBLISH NUMBER(1) default 0 not null, + FORM_CUSTOM VARCHAR2(1) default 'N', + FORM_PATH VARCHAR2(100), + ACTIVITY_STATUS NUMBER(1) default 1, + LISTENER_TYPE VARCHAR2(100), + LISTENER_PATH VARCHAR2(500), + EXT VARCHAR2(500), + CREATE_TIME DATE, + UPDATE_TIME DATE, + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) +); + +alter table FLOW_DEFINITION + add constraint PK_FLOW_DEFINITION primary key (ID); + +comment on table FLOW_DEFINITION is '流程定义表'; +comment on column FLOW_DEFINITION.ID is '主键id'; +comment on column FLOW_DEFINITION.FLOW_CODE is '流程编码'; +comment on column FLOW_DEFINITION.FLOW_NAME is '流程名称'; +comment on column FLOW_DEFINITION.CATEGORY is '流程类别'; +comment on column FLOW_DEFINITION.VERSION is '流程版本'; +comment on column FLOW_DEFINITION.IS_PUBLISH is '是否发布 (0未发布 1已发布 9失效)'; +comment on column FLOW_DEFINITION.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; +comment on column FLOW_DEFINITION.FORM_PATH is '审批表单路径'; +comment on column FLOW_DEFINITION.ACTIVITY_STATUS is '流程激活状态(0挂起 1激活)'; +comment on column FLOW_DEFINITION.LISTENER_TYPE is '监听器类型'; +comment on column FLOW_DEFINITION.LISTENER_PATH is '监听器路径'; +comment on column FLOW_DEFINITION.EXT is '扩展字段,预留给业务系统使用'; +comment on column FLOW_DEFINITION.CREATE_TIME is '创建时间'; +comment on column FLOW_DEFINITION.UPDATE_TIME is '更新时间'; +comment on column FLOW_DEFINITION.DEL_FLAG is '删除标志'; +comment on column FLOW_DEFINITION.TENANT_ID is '租户id'; + +create table FLOW_NODE +( + ID NUMBER(20) not null, + NODE_TYPE NUMBER(1) not null, + DEFINITION_ID NUMBER(20) not null, + NODE_CODE VARCHAR2(100) not null, + NODE_NAME VARCHAR2(100), + NODE_RATIO NUMBER(6, 3), + COORDINATE VARCHAR2(100), + ANY_NODE_SKIP VARCHAR2(100), + LISTENER_TYPE VARCHAR2(100), + LISTENER_PATH VARCHAR2(500), + HANDLER_TYPE VARCHAR2(100), + HANDLER_PATH VARCHAR2(400), + FORM_CUSTOM VARCHAR2(1) default 'N', + FORM_PATH VARCHAR2(100), + VERSION VARCHAR2(20), + CREATE_TIME DATE, + UPDATE_TIME DATE, + EXT VARCHAR2(500), + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40), + PERMISSION_FLAG VARCHAR2(200) +); + +alter table FLOW_NODE + add constraint PK_FLOW_NODE primary key (ID); + +comment on table FLOW_NODE is '流程节点表'; +comment on column FLOW_NODE.ID is '主键id'; +comment on column FLOW_NODE.NODE_TYPE is '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_NODE.DEFINITION_ID is '对应flow_definition表的id'; +comment on column FLOW_NODE.NODE_CODE is '流程节点编码'; +comment on column FLOW_NODE.NODE_NAME is '流程节点名称'; +comment on column FLOW_NODE.NODE_RATIO is '流程签署比例值'; +comment on column FLOW_NODE.COORDINATE is '坐标'; +comment on column FLOW_NODE.ANY_NODE_SKIP is '任意结点跳转'; +comment on column FLOW_NODE.LISTENER_TYPE is '监听器类型'; +comment on column FLOW_NODE.LISTENER_PATH is '监听器路径'; +comment on column FLOW_NODE.HANDLER_TYPE is '处理器类型'; +comment on column FLOW_NODE.HANDLER_PATH is '处理器路径'; +comment on column FLOW_NODE.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; +comment on column FLOW_NODE.FORM_PATH is '审批表单路径'; +comment on column FLOW_NODE.VERSION is '版本'; +comment on column FLOW_NODE.CREATE_TIME is '创建时间'; +comment on column FLOW_NODE.UPDATE_TIME is '更新时间'; +comment on column FLOW_NODE.EXT is '扩展属性'; +comment on column FLOW_NODE.DEL_FLAG is '删除标志'; +comment on column FLOW_NODE.TENANT_ID is '租户id'; +comment on column FLOW_NODE.PERMISSION_FLAG is '权限标识(权限类型:权限标识,可以多个,用逗号隔开)'; + +create table FLOW_SKIP +( + ID NUMBER(20) not null, + DEFINITION_ID NUMBER(20) not null, + NOW_NODE_CODE VARCHAR2(100) not null, + NOW_NODE_TYPE NUMBER(1), + NEXT_NODE_CODE VARCHAR2(100) not null, + NEXT_NODE_TYPE NUMBER(1), + SKIP_NAME VARCHAR2(100), + SKIP_TYPE VARCHAR2(40), + SKIP_CONDITION VARCHAR2(200), + COORDINATE VARCHAR2(100), + CREATE_TIME DATE, + UPDATE_TIME DATE, + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) +); + +alter table FLOW_SKIP + add constraint PK_FLOW_SKIP primary key (ID); + +comment on table FLOW_SKIP is '节点跳转关联表'; +comment on column FLOW_SKIP.ID is '主键id'; +comment on column FLOW_SKIP.DEFINITION_ID is '流程定义id'; +comment on column FLOW_SKIP.NOW_NODE_CODE is '当前流程节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_SKIP.NOW_NODE_TYPE is '下一个流程节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_SKIP.NEXT_NODE_CODE is '下一个流程节点编码'; +comment on column FLOW_SKIP.NEXT_NODE_TYPE is '下一个流程节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_SKIP.SKIP_NAME is '跳转名称'; +comment on column FLOW_SKIP.SKIP_TYPE is '跳转类型 (PASS审批通过 REJECT退回)'; +comment on column FLOW_SKIP.SKIP_CONDITION is '跳转条件'; +comment on column FLOW_SKIP.COORDINATE is '坐标'; +comment on column FLOW_SKIP.CREATE_TIME is '创建时间'; +comment on column FLOW_SKIP.UPDATE_TIME is '更新时间'; +comment on column FLOW_SKIP.DEL_FLAG is '删除标志'; +comment on column FLOW_SKIP.TENANT_ID is '租户id'; + +create table FLOW_INSTANCE +( + ID NUMBER not null, + DEFINITION_ID NUMBER not null, + BUSINESS_ID VARCHAR2(40) not null, + NODE_TYPE NUMBER(1), + NODE_CODE VARCHAR2(100), + NODE_NAME VARCHAR2(100), + VARIABLE CLOB, + FLOW_STATUS VARCHAR2(20), + ACTIVITY_STATUS NUMBER(1) default 1, + DEF_JSON CLOB, + CREATE_BY VARCHAR2(64) default '', + CREATE_TIME DATE, + UPDATE_TIME DATE, + EXT VARCHAR2(500), + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) +); + +alter table FLOW_INSTANCE + add constraint PK_FLOW_INSTANCE primary key (ID); + +comment on table FLOW_INSTANCE is '流程实例表'; +comment on column FLOW_INSTANCE.ID is '主键id'; +comment on column FLOW_INSTANCE.DEFINITION_ID is '对应flow_definition表的id'; +comment on column FLOW_INSTANCE.BUSINESS_ID is '业务id'; +comment on column FLOW_INSTANCE.NODE_TYPE is '开始节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_INSTANCE.NODE_CODE is '开始节点编码'; +comment on column FLOW_INSTANCE.NODE_NAME is '开始节点名称'; +comment on column FLOW_INSTANCE.VARIABLE is '任务变量'; +comment on column FLOW_INSTANCE.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +comment on column FLOW_INSTANCE.ACTIVITY_STATUS is '流程激活状态(0挂起 1激活)'; +comment on column FLOW_INSTANCE.DEF_JSON is '流程定义json'; +comment on column FLOW_INSTANCE.CREATE_BY is '创建者'; +comment on column FLOW_INSTANCE.CREATE_TIME is '创建时间'; +comment on column FLOW_INSTANCE.UPDATE_TIME is '更新时间'; +comment on column FLOW_INSTANCE.EXT is '扩展字段,预留给业务系统使用'; +comment on column FLOW_INSTANCE.DEL_FLAG is '删除标志'; +comment on column FLOW_INSTANCE.TENANT_ID is '租户id'; + +create table FLOW_TASK +( + ID NUMBER(20) not null, + DEFINITION_ID NUMBER(20) not null, + INSTANCE_ID NUMBER(20) not null, + NODE_CODE VARCHAR2(100), + NODE_NAME VARCHAR2(100), + NODE_TYPE NUMBER(1), + FLOW_STATUS VARCHAR2(20), + FORM_CUSTOM VARCHAR2(1) default 'N', + FORM_PATH VARCHAR2(100), + CREATE_TIME DATE, + UPDATE_TIME DATE, + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) +); + +alter table FLOW_TASK + add constraint PK_FLOW_TASK primary key (ID); + +comment on table FLOW_TASK is '待办任务表'; +comment on column FLOW_TASK.ID is '主键id'; +comment on column FLOW_TASK.DEFINITION_ID is '对应flow_definition表的id'; +comment on column FLOW_TASK.INSTANCE_ID is '对应flow_instance表的id'; +comment on column FLOW_TASK.NODE_CODE is '节点编码'; +comment on column FLOW_TASK.NODE_NAME is '节点名称'; +comment on column FLOW_TASK.NODE_TYPE is '节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +comment on column FLOW_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; +comment on column FLOW_TASK.FORM_PATH is '审批表单路径'; +comment on column FLOW_TASK.CREATE_TIME is '创建时间'; +comment on column FLOW_TASK.UPDATE_TIME is '更新时间'; +comment on column FLOW_TASK.DEL_FLAG is '删除标志'; +comment on column FLOW_TASK.TENANT_ID is '租户id'; + +create table FLOW_HIS_TASK +( + ID NUMBER(20) not null, + DEFINITION_ID NUMBER(20) not null, + INSTANCE_ID NUMBER(20) not null, + TASK_ID NUMBER(20) not null, + NODE_CODE VARCHAR2(100), + NODE_NAME VARCHAR2(100), + NODE_TYPE NUMBER(1), + TARGET_NODE_CODE VARCHAR2(200), + TARGET_NODE_NAME VARCHAR2(200), + APPROVER VARCHAR2(40), + COOPERATE_TYPE NUMBER(1) default 0, + COLLABORATOR VARCHAR2(40), + SKIP_TYPE VARCHAR2(10), + FLOW_STATUS VARCHAR2(20), + FORM_CUSTOM VARCHAR2(1) default 'N', + FORM_PATH VARCHAR2(100), + MESSAGE VARCHAR2(500), + VARIABLE CLOB, + EXT VARCHAR2(500), + CREATE_TIME DATE, + UPDATE_TIME DATE, + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) + +); + +alter table FLOW_HIS_TASK + add constraint PK_FLOW_HIS_TASK primary key (ID); + +comment on table FLOW_HIS_TASK is '历史任务记录表'; +comment on column FLOW_HIS_TASK.ID is '主键id'; +comment on column FLOW_HIS_TASK.DEFINITION_ID is '对应flow_definition表的id'; +comment on column FLOW_HIS_TASK.INSTANCE_ID is '对应flow_instance表的id'; +comment on column FLOW_HIS_TASK.TASK_ID is '对应flow_task表的id'; +comment on column FLOW_HIS_TASK.NODE_CODE is '开始节点编码'; +comment on column FLOW_HIS_TASK.NODE_NAME is '开始节点名称'; +comment on column FLOW_HIS_TASK.NODE_TYPE is '开始节点类型 (0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +comment on column FLOW_HIS_TASK.TARGET_NODE_CODE is '目标节点编码'; +comment on column FLOW_HIS_TASK.TARGET_NODE_NAME is '目标节点名称'; +comment on column FLOW_HIS_TASK.SKIP_TYPE is '流转类型(PASS通过 REJECT退回 NONE无动作)'; +comment on column FLOW_HIS_TASK.FLOW_STATUS is '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +comment on column FLOW_HIS_TASK.FORM_CUSTOM is '审批表单是否自定义 (Y是 N否)'; +comment on column FLOW_HIS_TASK.FORM_PATH is '审批表单路径'; +comment on column FLOW_HIS_TASK.MESSAGE is '审批意见'; +comment on column FLOW_HIS_TASK.VARIABLE is '任务变量'; +comment on column FLOW_HIS_TASK.EXT is '扩展字段,预留给业务系统使用'; +comment on column FLOW_HIS_TASK.CREATE_TIME is '任务开始时间'; +comment on column FLOW_HIS_TASK.UPDATE_TIME is '审批完成时间'; +comment on column FLOW_HIS_TASK.DEL_FLAG is '删除标志'; +comment on column FLOW_HIS_TASK.TENANT_ID is '租户id'; +comment on column FLOW_HIS_TASK.APPROVER is '审批者'; +comment on column FLOW_HIS_TASK.COOPERATE_TYPE is '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)'; +comment on column FLOW_HIS_TASK.COLLABORATOR is '协作人'; + +create table FLOW_USER +( + ID NUMBER(20) not null, + TYPE VARCHAR2(1) not null, + PROCESSED_BY VARCHAR2(80), + ASSOCIATED NUMBER(20) not null, + CREATE_TIME DATE, + CREATE_BY VARCHAR2(80), + UPDATE_TIME DATE, + DEL_FLAG VARCHAR2(1) default '0', + TENANT_ID VARCHAR2(40) +); + +alter table FLOW_USER + add constraint PK_FLOW_USER primary key (ID); + +comment on table FLOW_USER is '待办任务表'; +comment on column FLOW_USER.ID is '主键id'; +comment on column FLOW_USER.TYPE is '人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)'; +comment on column FLOW_USER.PROCESSED_BY is '权限人)'; +comment on column FLOW_USER.ASSOCIATED is '任务表id'; +comment on column FLOW_USER.CREATE_TIME is '创建时间'; +comment on column FLOW_USER.CREATE_BY is '节点名称'; +comment on column FLOW_USER.UPDATE_TIME is '更新时间'; +comment on column FLOW_USER.DEL_FLAG is '删除标志'; +comment on column FLOW_USER.TENANT_ID is '租户id'; + +create index USER_PROCESSED_TYPE on FLOW_USER (PROCESSED_BY, TYPE); +create index USER_ASSOCIATED_IDX on FLOW_USER (ASSOCIATED); + +-- ---------------------------- +-- 流程分类表 +-- ---------------------------- +CREATE TABLE flow_category +( + category_id NUMBER (20) NOT NULL, + tenant_id VARCHAR2 (20) DEFAULT '000000', + parent_id NUMBER (20) DEFAULT 0, + ancestors VARCHAR2 (500) DEFAULT '', + category_name VARCHAR2 (30) NOT NULL, + order_num NUMBER (4) DEFAULT 0, + del_flag CHAR(1) DEFAULT '0', + create_dept NUMBER (20), + create_by NUMBER (20), + create_time DATE, + update_by NUMBER (20), + update_time DATE +); + +alter table flow_category add constraint pk_flow_category primary key (category_id); + +COMMENT ON TABLE flow_category IS '流程分类'; +COMMENT ON COLUMN flow_category.category_id IS '流程分类ID'; +COMMENT ON COLUMN flow_category.tenant_id IS '租户编号'; +COMMENT ON COLUMN flow_category.parent_id IS '父流程分类id'; +COMMENT ON COLUMN flow_category.ancestors IS '祖级列表'; +COMMENT ON COLUMN flow_category.category_name IS '流程分类名称'; +COMMENT ON COLUMN flow_category.order_num IS '显示顺序'; +COMMENT ON COLUMN flow_category.del_flag IS '删除标志(0代表存在 1代表删除)'; +COMMENT ON COLUMN flow_category.create_dept IS '创建部门'; +COMMENT ON COLUMN flow_category.create_by IS '创建者'; +COMMENT ON COLUMN flow_category.create_time IS '创建时间'; +COMMENT ON COLUMN flow_category.update_by IS '更新者'; +COMMENT ON COLUMN flow_category.update_time IS '更新时间'; + +INSERT INTO flow_category VALUES (100, '000000', 0, '0', 'OA审批', 0, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (101, '000000', 100, '0,100', '假勤管理', 0, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (102, '000000', 100, '0,100', '人事管理', 1, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (103, '000000', 101, '0,100,101', '请假', 0, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (104, '000000', 101, '0,100,101', '出差', 1, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (105, '000000', 101, '0,100,101', '加班', 2, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (106, '000000', 101, '0,100,101', '换班', 3, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (107, '000000', 101, '0,100,101', '外出', 4, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (108, '000000', 102, '0,100,102', '转正', 1, '0', 103, 1, SYSDATE, NULL, NULL); +INSERT INTO flow_category VALUES (109, '000000', 102, '0,100,102', '离职', 2, '0', 103, 1, SYSDATE, NULL, NULL); + + +-- ---------------------------- +-- 请假单信息 +-- ---------------------------- +CREATE TABLE test_leave +( + id NUMBER (20) NOT NULL, + tenant_id VARCHAR2 (20) DEFAULT '000000', + leave_type VARCHAR2 (255) NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + leave_days NUMBER (10) NOT NULL, + remark VARCHAR2 (255), + status VARCHAR2 (255), + create_dept NUMBER (20), + create_by NUMBER (20), + create_time DATE, + update_by NUMBER (20), + update_time DATE +); + +alter table test_leave add constraint pk_test_leave primary key (id); + +COMMENT ON TABLE test_leave IS '请假申请表'; +COMMENT ON COLUMN test_leave.id IS 'ID'; +COMMENT ON COLUMN test_leave.tenant_id IS '租户编号'; +COMMENT ON COLUMN test_leave.leave_type IS '请假类型'; +COMMENT ON COLUMN test_leave.start_date IS '开始时间'; +COMMENT ON COLUMN test_leave.end_date IS '结束时间'; +COMMENT ON COLUMN test_leave.leave_days IS '请假天数'; +COMMENT ON COLUMN test_leave.remark IS '请假原因'; +COMMENT ON COLUMN test_leave.status IS '状态'; +COMMENT ON COLUMN test_leave.create_dept IS '创建部门'; +COMMENT ON COLUMN test_leave.create_by IS '创建者'; +COMMENT ON COLUMN test_leave.create_time IS '创建时间'; +COMMENT ON COLUMN test_leave.update_by IS '更新者'; +COMMENT ON COLUMN test_leave.update_time IS '更新时间'; + +INSERT INTO sys_menu VALUES ('11616', '工作流', '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); + +INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, SYSDATE, NULL, NULL, ''); + +INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, SYSDATE, NULL, NULL, '请假申请菜单'); +INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11640', '请假申请新增', '11638', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11641', '请假申请修改', '11638', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11642', '请假申请删除', '11638', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11643', '请假申请导出', '11638', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, SYSDATE, NULL, NULL, ''); + +INSERT INTO sys_dict_type VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, SYSDATE, NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, SYSDATE, NULL, NULL, '表单类型列表'); +INSERT INTO sys_dict_type VALUES (15, '000000', '任务状态', 'wf_task_status', 103, 1, SYSDATE, NULL, NULL, '任务状态'); +INSERT INTO sys_dict_data VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '已撤销'); +INSERT INTO sys_dict_data VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, SYSDATE, NULL, NULL, '草稿'); +INSERT INTO sys_dict_data VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '待审核'); +INSERT INTO sys_dict_data VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, SYSDATE, NULL, NULL, '已完成'); +INSERT INTO sys_dict_data VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '已作废'); +INSERT INTO sys_dict_data VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '已退回'); +INSERT INTO sys_dict_data VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '已终止'); +INSERT INTO sys_dict_data VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, SYSDATE, NULL, NULL, '自定义表单'); +INSERT INTO sys_dict_data VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '动态表单'); +INSERT INTO sys_dict_data VALUES (48, '000000', 1, '撤销', 'cancel', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '撤销'); +INSERT INTO sys_dict_data VALUES (49, '000000', 2, '通过', 'pass', 'wf_task_status', '', 'success', 'N', 103, 1, SYSDATE, NULL, NULL, '通过'); +INSERT INTO sys_dict_data VALUES (50, '000000', 3, '待审核', 'waiting', 'wf_task_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '待审核'); +INSERT INTO sys_dict_data VALUES (51, '000000', 4, '作废', 'invalid', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '作废'); +INSERT INTO sys_dict_data VALUES (52, '000000', 5, '退回', 'back', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '退回'); +INSERT INTO sys_dict_data VALUES (53, '000000', 6, '终止', 'termination', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '终止'); +INSERT INTO sys_dict_data VALUES (54, '000000', 7, '转办', 'transfer', 'wf_task_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '转办'); +INSERT INTO sys_dict_data VALUES (55, '000000', 8, '委托', 'depute', 'wf_task_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '委托'); +INSERT INTO sys_dict_data VALUES (56, '000000', 9, '抄送', 'copy', 'wf_task_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '抄送'); +INSERT INTO sys_dict_data VALUES (57, '000000', 10, '加签', 'sign', 'wf_task_status', '', 'primary', 'N', 103, 1, SYSDATE, NULL, NULL, '加签'); +INSERT INTO sys_dict_data VALUES (58, '000000', 11, '减签', 'sign_off', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '减签'); +INSERT INTO sys_dict_data VALUES (59, '000000', 11, '超时', 'timeout', 'wf_task_status', '', 'danger', 'N', 103, 1, SYSDATE, NULL, NULL, '超时'); diff --git a/script/sql/postgres/postgres_ry_job.sql b/script/sql/postgres/postgres_ry_job.sql new file mode 100644 index 0000000..84ebe01 --- /dev/null +++ b/script/sql/postgres/postgres_ry_job.sql @@ -0,0 +1,855 @@ +/* + SnailJob Database Transfer Tool + Source Server Type : MySQL + Target Server Type : PostgreSQL + Date: 2025-04-26 09:56:45 +*/ + + +-- sj_namespace +CREATE TABLE sj_namespace +( + id bigserial PRIMARY KEY, + name varchar(64) NOT NULL, + unique_id varchar(64) NOT NULL, + description varchar(256) NOT NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name); + +COMMENT ON COLUMN sj_namespace.id IS '主键'; +COMMENT ON COLUMN sj_namespace.name IS '名称'; +COMMENT ON COLUMN sj_namespace.unique_id IS '唯一id'; +COMMENT ON COLUMN sj_namespace.description IS '描述'; +COMMENT ON COLUMN sj_namespace.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_namespace.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_namespace.update_dt IS '修改时间'; +COMMENT ON TABLE sj_namespace IS '命名空间'; + +INSERT INTO sj_namespace VALUES (1, 'Development', 'dev', '', 0, now(), now()); +INSERT INTO sj_namespace VALUES (2, 'Production', 'prod', '', 0, now(), now()); + +-- sj_group_config +CREATE TABLE sj_group_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + description varchar(256) NOT NULL DEFAULT '', + token varchar(64) NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', + group_status smallint NOT NULL DEFAULT 0, + version int NOT NULL, + group_partition int NOT NULL, + id_generator_mode smallint NOT NULL DEFAULT 1, + init_scene smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name); + +COMMENT ON COLUMN sj_group_config.id IS '主键'; +COMMENT ON COLUMN sj_group_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_group_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_group_config.description IS '组描述'; +COMMENT ON COLUMN sj_group_config.token IS 'token'; +COMMENT ON COLUMN sj_group_config.group_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_group_config.version IS '版本号'; +COMMENT ON COLUMN sj_group_config.group_partition IS '分区'; +COMMENT ON COLUMN sj_group_config.id_generator_mode IS '唯一id生成模式 默认号段模式'; +COMMENT ON COLUMN sj_group_config.init_scene IS '是否初始化场景 0:否 1:是'; +COMMENT ON COLUMN sj_group_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_group_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_group_config IS '组配置'; + +INSERT INTO sj_group_config VALUES (1, 'dev', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now()); +INSERT INTO sj_group_config VALUES (2, 'prod', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now()); + +-- sj_notify_config +CREATE TABLE sj_notify_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + notify_name varchar(64) NOT NULL DEFAULT '', + system_task_type smallint NOT NULL DEFAULT 3, + notify_status smallint NOT NULL DEFAULT 0, + recipient_ids varchar(128) NOT NULL, + notify_threshold int NOT NULL DEFAULT 0, + notify_scene smallint NOT NULL DEFAULT 0, + rate_limiter_status smallint NOT NULL DEFAULT 0, + rate_limiter_threshold int NOT NULL DEFAULT 0, + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name); + +COMMENT ON COLUMN sj_notify_config.id IS '主键'; +COMMENT ON COLUMN sj_notify_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_notify_config.notify_name IS '通知名称'; +COMMENT ON COLUMN sj_notify_config.system_task_type IS '任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_notify_config.notify_status IS '通知状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.recipient_ids IS '接收人id列表'; +COMMENT ON COLUMN sj_notify_config.notify_threshold IS '通知阈值'; +COMMENT ON COLUMN sj_notify_config.notify_scene IS '通知场景'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_status IS '限流状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_notify_config.rate_limiter_threshold IS '每秒限流阈值'; +COMMENT ON COLUMN sj_notify_config.description IS '描述'; +COMMENT ON COLUMN sj_notify_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_config IS '通知配置'; + +-- sj_notify_recipient +CREATE TABLE sj_notify_recipient +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + recipient_name varchar(64) NOT NULL, + notify_type smallint NOT NULL DEFAULT 0, + notify_attribute varchar(512) NOT NULL, + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id); + +COMMENT ON COLUMN sj_notify_recipient.id IS '主键'; +COMMENT ON COLUMN sj_notify_recipient.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_notify_recipient.recipient_name IS '接收人名称'; +COMMENT ON COLUMN sj_notify_recipient.notify_type IS '通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook'; +COMMENT ON COLUMN sj_notify_recipient.notify_attribute IS '配置属性'; +COMMENT ON COLUMN sj_notify_recipient.description IS '描述'; +COMMENT ON COLUMN sj_notify_recipient.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_notify_recipient.update_dt IS '修改时间'; +COMMENT ON TABLE sj_notify_recipient IS '告警通知接收人'; + +-- sj_retry_dead_letter +CREATE TABLE sj_retry_dead_letter +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, + scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, + idempotent_id varchar(64) NOT NULL, + biz_no varchar(64) NOT NULL DEFAULT '', + executor_name varchar(512) NOT NULL DEFAULT '', + args_str text NOT NULL, + ext_attrs text NOT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_retry_dead_letter_01 ON sj_retry_dead_letter (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_dead_letter_02 ON sj_retry_dead_letter (idempotent_id); +CREATE INDEX idx_sj_retry_dead_letter_03 ON sj_retry_dead_letter (biz_no); +CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt); + +COMMENT ON COLUMN sj_retry_dead_letter.id IS '主键'; +COMMENT ON COLUMN sj_retry_dead_letter.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_dead_letter.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_dead_letter.group_id IS '组Id'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_dead_letter.scene_id IS '场景ID'; +COMMENT ON COLUMN sj_retry_dead_letter.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry_dead_letter.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry_dead_letter.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry_dead_letter.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry_dead_letter.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_dead_letter.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_dead_letter IS '死信队列表'; + +-- sj_retry +CREATE TABLE sj_retry +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + group_id bigint NOT NULL, + scene_name varchar(64) NOT NULL, + scene_id bigint NOT NULL, + idempotent_id varchar(64) NOT NULL, + biz_no varchar(64) NOT NULL DEFAULT '', + executor_name varchar(512) NOT NULL DEFAULT '', + args_str text NOT NULL, + ext_attrs text NOT NULL, + next_trigger_at bigint NOT NULL, + retry_count int NOT NULL DEFAULT 0, + retry_status smallint NOT NULL DEFAULT 0, + task_type smallint NOT NULL DEFAULT 1, + bucket_index int NOT NULL DEFAULT 0, + parent_id bigint NOT NULL DEFAULT 0, + deleted bigint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted); + +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no); +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index); +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id); +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt); +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id); + + +COMMENT ON COLUMN sj_retry.id IS '主键'; +COMMENT ON COLUMN sj_retry.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry.group_id IS '组Id'; +COMMENT ON COLUMN sj_retry.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry.scene_id IS '场景ID'; +COMMENT ON COLUMN sj_retry.idempotent_id IS '幂等id'; +COMMENT ON COLUMN sj_retry.biz_no IS '业务编号'; +COMMENT ON COLUMN sj_retry.executor_name IS '执行器名称'; +COMMENT ON COLUMN sj_retry.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_retry.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_retry.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_retry.retry_status IS '重试状态 0、重试中 1、成功 2、最大重试次数'; +COMMENT ON COLUMN sj_retry.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_retry.parent_id IS '父节点id'; +COMMENT ON COLUMN sj_retry.deleted IS '逻辑删除'; +COMMENT ON COLUMN sj_retry.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry IS '重试信息表'; + +-- sj_retry_task +CREATE TABLE sj_retry_task +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + scene_name varchar(64) NOT NULL, + retry_id bigint NOT NULL, + ext_attrs text NOT NULL, + task_status smallint NOT NULL DEFAULT 1, + task_type smallint NOT NULL DEFAULT 1, + operation_reason smallint NOT NULL DEFAULT 0, + client_info varchar(128) NULL DEFAULT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_retry_task_01 ON sj_retry_task (namespace_id, group_name, scene_name); +CREATE INDEX idx_sj_retry_task_02 ON sj_retry_task (task_status); +CREATE INDEX idx_sj_retry_task_03 ON sj_retry_task (create_dt); +CREATE INDEX idx_sj_retry_task_04 ON sj_retry_task (retry_id); + +COMMENT ON COLUMN sj_retry_task.id IS '主键'; +COMMENT ON COLUMN sj_retry_task.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_task.retry_id IS '重试信息Id'; +COMMENT ON COLUMN sj_retry_task.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_retry_task.task_status IS '重试状态'; +COMMENT ON COLUMN sj_retry_task.task_type IS '任务类型 1、重试数据 2、回调数据'; +COMMENT ON COLUMN sj_retry_task.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_retry_task.client_info IS '客户端地址 clientId#ip:port'; +COMMENT ON COLUMN sj_retry_task.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_task.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_task IS '重试任务表'; + +-- sj_retry_task_log_message +CREATE TABLE sj_retry_task_log_message +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + retry_id bigint NOT NULL, + retry_task_id bigint NOT NULL, + message text NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, retry_task_id); +CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt); + +COMMENT ON COLUMN sj_retry_task_log_message.id IS '主键'; +COMMENT ON COLUMN sj_retry_task_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_task_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_task_log_message.retry_id IS '重试信息Id'; +COMMENT ON COLUMN sj_retry_task_log_message.retry_task_id IS '重试任务Id'; +COMMENT ON COLUMN sj_retry_task_log_message.message IS '异常信息'; +COMMENT ON COLUMN sj_retry_task_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_retry_task_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_retry_task_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_retry_task_log_message IS '任务调度日志信息记录表'; + +-- sj_retry_scene_config +CREATE TABLE sj_retry_scene_config +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + scene_name varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + scene_status smallint NOT NULL DEFAULT 0, + max_retry_count int NOT NULL DEFAULT 5, + back_off smallint NOT NULL DEFAULT 1, + trigger_interval varchar(16) NOT NULL DEFAULT '', + notify_ids varchar(128) NOT NULL DEFAULT '', + deadline_request bigint NOT NULL DEFAULT 60000, + executor_timeout int NOT NULL DEFAULT 5, + route_key smallint NOT NULL DEFAULT 4, + block_strategy smallint NOT NULL DEFAULT 1, + cb_status smallint NOT NULL DEFAULT 0, + cb_trigger_type smallint NOT NULL DEFAULT 1, + cb_max_count int NOT NULL DEFAULT 16, + cb_trigger_interval varchar(16) NOT NULL DEFAULT '', + description varchar(256) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name); + +COMMENT ON COLUMN sj_retry_scene_config.id IS '主键'; +COMMENT ON COLUMN sj_retry_scene_config.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_scene_config.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_scene_config.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_scene_config.scene_status IS '组状态 0、未启用 1、启用'; +COMMENT ON COLUMN sj_retry_scene_config.max_retry_count IS '最大重试次数'; +COMMENT ON COLUMN sj_retry_scene_config.back_off IS '1、默认等级 2、固定间隔时间 3、CRON 表达式'; +COMMENT ON COLUMN sj_retry_scene_config.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_retry_scene_config.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_retry_scene_config.deadline_request IS 'Deadline Request 调用链超时 单位毫秒'; +COMMENT ON COLUMN sj_retry_scene_config.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_retry_scene_config.route_key IS '路由策略'; +COMMENT ON COLUMN sj_retry_scene_config.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_retry_scene_config.cb_status IS '回调状态 0、不开启 1、开启'; +COMMENT ON COLUMN sj_retry_scene_config.cb_trigger_type IS '1、默认等级 2、固定间隔时间 3、CRON 表达式'; +COMMENT ON COLUMN sj_retry_scene_config.cb_max_count IS '回调的最大执行次数'; +COMMENT ON COLUMN sj_retry_scene_config.cb_trigger_interval IS '回调的最大执行次数'; +COMMENT ON COLUMN sj_retry_scene_config.description IS '描述'; +COMMENT ON COLUMN sj_retry_scene_config.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_scene_config.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_scene_config IS '场景配置'; + +-- sj_server_node +CREATE TABLE sj_server_node +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + host_id varchar(64) NOT NULL, + host_ip varchar(64) NOT NULL, + host_port int NOT NULL, + expire_at timestamp NOT NULL, + node_type smallint NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip); + +CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name); +CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type); + +COMMENT ON COLUMN sj_server_node.id IS '主键'; +COMMENT ON COLUMN sj_server_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_server_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_server_node.host_id IS '主机id'; +COMMENT ON COLUMN sj_server_node.host_ip IS '机器ip'; +COMMENT ON COLUMN sj_server_node.host_port IS '机器端口'; +COMMENT ON COLUMN sj_server_node.expire_at IS '过期时间'; +COMMENT ON COLUMN sj_server_node.node_type IS '节点类型 1、客户端 2、是服务端'; +COMMENT ON COLUMN sj_server_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_server_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_server_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_server_node IS '服务器节点'; + +-- sj_distributed_lock +CREATE TABLE sj_distributed_lock +( + name varchar(64) NOT NULL, + lock_until timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + locked_at timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + locked_by varchar(255) NOT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +COMMENT ON COLUMN sj_distributed_lock.name IS '锁名称'; +COMMENT ON COLUMN sj_distributed_lock.lock_until IS '锁定时长'; +COMMENT ON COLUMN sj_distributed_lock.locked_at IS '锁定时间'; +COMMENT ON COLUMN sj_distributed_lock.locked_by IS '锁定者'; +COMMENT ON COLUMN sj_distributed_lock.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_distributed_lock.update_dt IS '修改时间'; +COMMENT ON TABLE sj_distributed_lock IS '锁定表'; + +-- sj_system_user +CREATE TABLE sj_system_user +( + id bigserial PRIMARY KEY, + username varchar(64) NOT NULL, + password varchar(128) NOT NULL, + role smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +COMMENT ON COLUMN sj_system_user.id IS '主键'; +COMMENT ON COLUMN sj_system_user.username IS '账号'; +COMMENT ON COLUMN sj_system_user.password IS '密码'; +COMMENT ON COLUMN sj_system_user.role IS '角色:1-普通用户、2-管理员'; +COMMENT ON COLUMN sj_system_user.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user IS '系统用户表'; + +-- pwd: admin +INSERT INTO sj_system_user VALUES (1, 'admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2, now(), now()); + +-- sj_system_user_permission +CREATE TABLE sj_system_user_permission +( + id bigserial PRIMARY KEY, + group_name varchar(64) NOT NULL, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + system_user_id bigint NOT NULL, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id); + +COMMENT ON COLUMN sj_system_user_permission.id IS '主键'; +COMMENT ON COLUMN sj_system_user_permission.group_name IS '组名称'; +COMMENT ON COLUMN sj_system_user_permission.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_system_user_permission.system_user_id IS '系统用户id'; +COMMENT ON COLUMN sj_system_user_permission.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_system_user_permission.update_dt IS '修改时间'; +COMMENT ON TABLE sj_system_user_permission IS '系统用户权限表'; + +-- sj_sequence_alloc +CREATE TABLE sj_sequence_alloc +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + max_id bigint NOT NULL DEFAULT 1, + step int NOT NULL DEFAULT 100, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name); + +COMMENT ON COLUMN sj_sequence_alloc.id IS '主键'; +COMMENT ON COLUMN sj_sequence_alloc.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_sequence_alloc.group_name IS '组名称'; +COMMENT ON COLUMN sj_sequence_alloc.max_id IS '最大id'; +COMMENT ON COLUMN sj_sequence_alloc.step IS '步长'; +COMMENT ON COLUMN sj_sequence_alloc.update_dt IS '更新时间'; +COMMENT ON TABLE sj_sequence_alloc IS '号段模式序号ID分配表'; + +-- sj_job +CREATE TABLE sj_job +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_name varchar(64) NOT NULL, + args_str text NULL DEFAULT NULL, + args_type smallint NOT NULL DEFAULT 1, + next_trigger_at bigint NOT NULL, + job_status smallint NOT NULL DEFAULT 1, + task_type smallint NOT NULL DEFAULT 1, + route_key smallint NOT NULL DEFAULT 4, + executor_type smallint NOT NULL DEFAULT 1, + executor_info varchar(255) NULL DEFAULT NULL, + trigger_type smallint NOT NULL, + trigger_interval varchar(255) NOT NULL, + block_strategy smallint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + max_retry_times int NOT NULL DEFAULT 0, + parallel_num int NOT NULL DEFAULT 1, + retry_interval int NOT NULL DEFAULT 0, + bucket_index int NOT NULL DEFAULT 0, + resident smallint NOT NULL DEFAULT 0, + notify_ids varchar(128) NOT NULL DEFAULT '', + owner_id bigint NULL, + description varchar(256) NOT NULL DEFAULT '', + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); +CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); +CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); + +COMMENT ON COLUMN sj_job.id IS '主键'; +COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.group_name IS '组名称'; +COMMENT ON COLUMN sj_job.job_name IS '名称'; +COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_job.job_status IS '任务状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_job.task_type IS '任务类型 1、集群 2、广播 3、切片'; +COMMENT ON COLUMN sj_job.route_key IS '路由策略'; +COMMENT ON COLUMN sj_job.executor_type IS '执行器类型'; +COMMENT ON COLUMN sj_job.executor_info IS '执行器名称'; +COMMENT ON COLUMN sj_job.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_job.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_job.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行 4、恢复'; +COMMENT ON COLUMN sj_job.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_job.max_retry_times IS '最大重试次数'; +COMMENT ON COLUMN sj_job.parallel_num IS '并行数'; +COMMENT ON COLUMN sj_job.retry_interval IS '重试间隔 ( s)'; +COMMENT ON COLUMN sj_job.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_job.resident IS '是否是常驻任务'; +COMMENT ON COLUMN sj_job.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_job.owner_id IS '负责人id'; +COMMENT ON COLUMN sj_job.description IS '描述'; +COMMENT ON COLUMN sj_job.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job IS '任务信息'; + +INSERT INTO sj_job VALUES (1, 'dev', 'fys_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', 0, now(), now()); + +-- sj_job_log_message +CREATE TABLE sj_job_log_message +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + task_id bigint NOT NULL, + message text NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id); +CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt); +CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_log_message.id IS '主键'; +COMMENT ON COLUMN sj_job_log_message.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_log_message.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_log_message.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_log_message.task_batch_id IS '任务批次id'; +COMMENT ON COLUMN sj_job_log_message.task_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_log_message.message IS '调度信息'; +COMMENT ON COLUMN sj_job_log_message.log_num IS '日志数量'; +COMMENT ON COLUMN sj_job_log_message.real_time IS '上报时间'; +COMMENT ON COLUMN sj_job_log_message.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_log_message.create_dt IS '创建时间'; +COMMENT ON TABLE sj_job_log_message IS '调度日志'; + +-- sj_job_task +CREATE TABLE sj_job_task +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + parent_id bigint NOT NULL DEFAULT 0, + task_status smallint NOT NULL DEFAULT 0, + retry_count int NOT NULL DEFAULT 0, + mr_stage smallint NULL DEFAULT NULL, + leaf smallint NOT NULL DEFAULT '1', + task_name varchar(255) NOT NULL DEFAULT '', + client_info varchar(128) NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + result_message text NOT NULL, + args_str text NULL DEFAULT NULL, + args_type smallint NOT NULL DEFAULT 1, + ext_attrs varchar(256) NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status); +CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt); +CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name); + +COMMENT ON COLUMN sj_job_task.id IS '主键'; +COMMENT ON COLUMN sj_job_task.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_job_task.task_batch_id IS '调度任务id'; +COMMENT ON COLUMN sj_job_task.parent_id IS '父执行器id'; +COMMENT ON COLUMN sj_job_task.task_status IS '执行的状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task.retry_count IS '重试次数'; +COMMENT ON COLUMN sj_job_task.mr_stage IS '动态分片所处阶段 1:map 2:reduce 3:mergeReduce'; +COMMENT ON COLUMN sj_job_task.leaf IS '叶子节点'; +COMMENT ON COLUMN sj_job_task.task_name IS '任务名称'; +COMMENT ON COLUMN sj_job_task.client_info IS '客户端地址 clientId#ip:port'; +COMMENT ON COLUMN sj_job_task.wf_context IS '工作流全局上下文'; +COMMENT ON COLUMN sj_job_task.result_message IS '执行结果'; +COMMENT ON COLUMN sj_job_task.args_str IS '执行方法参数'; +COMMENT ON COLUMN sj_job_task.args_type IS '参数类型 '; +COMMENT ON COLUMN sj_job_task.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task IS '任务实例'; + +-- sj_job_task_batch +CREATE TABLE sj_job_task_batch +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_node_id bigint NOT NULL DEFAULT 0, + parent_workflow_node_id bigint NOT NULL DEFAULT 0, + workflow_task_batch_id bigint NOT NULL DEFAULT 0, + task_batch_status smallint NOT NULL DEFAULT 0, + operation_reason smallint NOT NULL DEFAULT 0, + execution_at bigint NOT NULL DEFAULT 0, + system_task_type smallint NOT NULL DEFAULT 3, + parent_id varchar(64) NOT NULL DEFAULT '', + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status); +CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt); +CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name); +CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id); + +COMMENT ON COLUMN sj_job_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_job_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_task_batch.job_id IS '任务id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_node_id IS '工作流节点id'; +COMMENT ON COLUMN sj_job_task_batch.parent_workflow_node_id IS '工作流任务父批次id'; +COMMENT ON COLUMN sj_job_task_batch.workflow_task_batch_id IS '工作流任务批次id'; +COMMENT ON COLUMN sj_job_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_job_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_job_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_job_task_batch.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_task_batch.parent_id IS '父节点'; +COMMENT ON COLUMN sj_job_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_job_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_job_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_task_batch IS '任务批次'; + +-- sj_job_summary +CREATE TABLE sj_job_summary +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + business_id bigint NOT NULL, + system_task_type smallint NOT NULL DEFAULT 3, + trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + success_num int NOT NULL DEFAULT 0, + fail_num int NOT NULL DEFAULT 0, + fail_reason varchar(512) NOT NULL DEFAULT '', + stop_num int NOT NULL DEFAULT 0, + stop_reason varchar(512) NOT NULL DEFAULT '', + cancel_num int NOT NULL DEFAULT 0, + cancel_reason varchar(512) NOT NULL DEFAULT '', + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id); + +CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id); + +COMMENT ON COLUMN sj_job_summary.id IS '主键'; +COMMENT ON COLUMN sj_job_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_job_summary.business_id IS '业务id ( job_id或workflow_id)'; +COMMENT ON COLUMN sj_job_summary.system_task_type IS '任务类型 3、JOB任务 4、WORKFLOW任务'; +COMMENT ON COLUMN sj_job_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_job_summary.success_num IS '执行成功-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.fail_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.stop_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.stop_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.cancel_num IS '执行失败-日志数量'; +COMMENT ON COLUMN sj_job_summary.cancel_reason IS '失败原因'; +COMMENT ON COLUMN sj_job_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_job_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_job_summary IS 'DashBoard_Job'; + +-- sj_retry_summary +CREATE TABLE sj_retry_summary +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL DEFAULT '', + scene_name varchar(50) NOT NULL DEFAULT '', + trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + running_num int NOT NULL DEFAULT 0, + finish_num int NOT NULL DEFAULT 0, + max_count_num int NOT NULL DEFAULT 0, + suspend_num int NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at); + +CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at); + +COMMENT ON COLUMN sj_retry_summary.id IS '主键'; +COMMENT ON COLUMN sj_retry_summary.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_retry_summary.group_name IS '组名称'; +COMMENT ON COLUMN sj_retry_summary.scene_name IS '场景名称'; +COMMENT ON COLUMN sj_retry_summary.trigger_at IS '统计时间'; +COMMENT ON COLUMN sj_retry_summary.running_num IS '重试中-日志数量'; +COMMENT ON COLUMN sj_retry_summary.finish_num IS '重试完成-日志数量'; +COMMENT ON COLUMN sj_retry_summary.max_count_num IS '重试到达最大次数-日志数量'; +COMMENT ON COLUMN sj_retry_summary.suspend_num IS '暂停重试-日志数量'; +COMMENT ON COLUMN sj_retry_summary.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_retry_summary.update_dt IS '修改时间'; +COMMENT ON TABLE sj_retry_summary IS 'DashBoard_Retry'; + +-- sj_workflow +CREATE TABLE sj_workflow +( + id bigserial PRIMARY KEY, + workflow_name varchar(64) NOT NULL, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + workflow_status smallint NOT NULL DEFAULT 1, + trigger_type smallint NOT NULL, + trigger_interval varchar(255) NOT NULL, + next_trigger_at bigint NOT NULL, + block_strategy smallint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + description varchar(256) NOT NULL DEFAULT '', + flow_info text NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + notify_ids varchar(128) NOT NULL DEFAULT '', + bucket_index int NOT NULL DEFAULT 0, + version int NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); +CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow.id IS '主键'; +COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; +COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; +COMMENT ON COLUMN sj_workflow.trigger_interval IS '间隔时长'; +COMMENT ON COLUMN sj_workflow.next_trigger_at IS '下次触发时间'; +COMMENT ON COLUMN sj_workflow.block_strategy IS '阻塞策略 1、丢弃 2、覆盖 3、并行'; +COMMENT ON COLUMN sj_workflow.executor_timeout IS '任务执行超时时间,单位秒'; +COMMENT ON COLUMN sj_workflow.description IS '描述'; +COMMENT ON COLUMN sj_workflow.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow.wf_context IS '上下文'; +COMMENT ON COLUMN sj_workflow.notify_ids IS '通知告警场景配置id列表'; +COMMENT ON COLUMN sj_workflow.bucket_index IS 'bucket'; +COMMENT ON COLUMN sj_workflow.version IS '版本号'; +COMMENT ON COLUMN sj_workflow.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow IS '工作流'; + +-- sj_workflow_node +CREATE TABLE sj_workflow_node +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + node_name varchar(64) NOT NULL, + group_name varchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_id bigint NOT NULL, + node_type smallint NOT NULL DEFAULT 1, + expression_type smallint NOT NULL DEFAULT 0, + fail_strategy smallint NOT NULL DEFAULT 1, + workflow_node_status smallint NOT NULL DEFAULT 1, + priority_level int NOT NULL DEFAULT 1, + node_info text NULL DEFAULT NULL, + version int NOT NULL, + ext_attrs varchar(256) NULL DEFAULT '', + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt); +CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_node.id IS '主键'; +COMMENT ON COLUMN sj_workflow_node.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_node.node_name IS '节点名称'; +COMMENT ON COLUMN sj_workflow_node.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_node.job_id IS '任务信息id'; +COMMENT ON COLUMN sj_workflow_node.workflow_id IS '工作流ID'; +COMMENT ON COLUMN sj_workflow_node.node_type IS '1、任务节点 2、条件节点'; +COMMENT ON COLUMN sj_workflow_node.expression_type IS '1、SpEl、2、Aviator 3、QL'; +COMMENT ON COLUMN sj_workflow_node.fail_strategy IS '失败策略 1、跳过 2、阻塞'; +COMMENT ON COLUMN sj_workflow_node.workflow_node_status IS '工作流节点状态 0、关闭、1、开启'; +COMMENT ON COLUMN sj_workflow_node.priority_level IS '优先级'; +COMMENT ON COLUMN sj_workflow_node.node_info IS '节点信息 '; +COMMENT ON COLUMN sj_workflow_node.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_node.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_node.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_node.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_node.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_node IS '工作流节点'; + +-- sj_workflow_task_batch +CREATE TABLE sj_workflow_task_batch +( + id bigserial PRIMARY KEY, + namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name varchar(64) NOT NULL, + workflow_id bigint NOT NULL, + task_batch_status smallint NOT NULL DEFAULT 0, + operation_reason smallint NOT NULL DEFAULT 0, + flow_info text NULL DEFAULT NULL, + wf_context text NULL DEFAULT NULL, + execution_at bigint NOT NULL DEFAULT 0, + ext_attrs varchar(256) NULL DEFAULT '', + version int NOT NULL DEFAULT 1, + deleted smallint NOT NULL DEFAULT 0, + create_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status); +CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt); +CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name); + +COMMENT ON COLUMN sj_workflow_task_batch.id IS '主键'; +COMMENT ON COLUMN sj_workflow_task_batch.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow_task_batch.group_name IS '组名称'; +COMMENT ON COLUMN sj_workflow_task_batch.workflow_id IS '工作流任务id'; +COMMENT ON COLUMN sj_workflow_task_batch.task_batch_status IS '任务批次状态 0、失败 1、成功'; +COMMENT ON COLUMN sj_workflow_task_batch.operation_reason IS '操作原因'; +COMMENT ON COLUMN sj_workflow_task_batch.flow_info IS '流程信息'; +COMMENT ON COLUMN sj_workflow_task_batch.wf_context IS '全局上下文'; +COMMENT ON COLUMN sj_workflow_task_batch.execution_at IS '任务执行时间'; +COMMENT ON COLUMN sj_workflow_task_batch.ext_attrs IS '扩展字段'; +COMMENT ON COLUMN sj_workflow_task_batch.version IS '版本号'; +COMMENT ON COLUMN sj_workflow_task_batch.deleted IS '逻辑删除 1、删除'; +COMMENT ON COLUMN sj_workflow_task_batch.create_dt IS '创建时间'; +COMMENT ON COLUMN sj_workflow_task_batch.update_dt IS '修改时间'; +COMMENT ON TABLE sj_workflow_task_batch IS '工作流批次'; diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql new file mode 100644 index 0000000..543b6bf --- /dev/null +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -0,0 +1,1398 @@ +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +create table sys_social +( + id int8 not null, + user_id int8 not null, + tenant_id varchar(20) default '000000'::varchar, + auth_id varchar(255) not null, + source varchar(255) not null, + open_id varchar(255) default null::varchar, + user_name varchar(30) not null, + nick_name varchar(30) default ''::varchar, + email varchar(255) default ''::varchar, + avatar varchar(500) default ''::varchar, + access_token varchar(2000) not null, + expire_in int8 default null, + refresh_token varchar(2000) default null::varchar, + access_code varchar(255) default null::varchar, + union_id varchar(255) default null::varchar, + scope varchar(255) default null::varchar, + token_type varchar(255) default null::varchar, + id_token varchar(2000) default null::varchar, + mac_algorithm varchar(255) default null::varchar, + mac_key varchar(255) default null::varchar, + code varchar(255) default null::varchar, + oauth_token varchar(255) default null::varchar, + oauth_token_secret varchar(255) default null::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + del_flag char default '0'::bpchar, + constraint "pk_sys_social" primary key (id) +); + +comment on table sys_social is '社会化关系表'; +comment on column sys_social.id is '主键'; +comment on column sys_social.user_id is '用户ID'; +comment on column sys_social.tenant_id is '租户id'; +comment on column sys_social.auth_id is '平台+平台唯一id'; +comment on column sys_social.source is '用户来源'; +comment on column sys_social.open_id is '平台编号唯一id'; +comment on column sys_social.user_name is '登录账号'; +comment on column sys_social.nick_name is '用户昵称'; +comment on column sys_social.email is '用户邮箱'; +comment on column sys_social.avatar is '头像地址'; +comment on column sys_social.access_token is '用户的授权令牌'; +comment on column sys_social.expire_in is '用户的授权令牌的有效期,部分平台可能没有'; +comment on column sys_social.refresh_token is '刷新令牌,部分平台可能没有'; +comment on column sys_social.access_code is '平台的授权信息,部分平台可能没有'; +comment on column sys_social.union_id is '用户的 unionid'; +comment on column sys_social.scope is '授予的权限,部分平台可能没有'; +comment on column sys_social.token_type is '个别平台的授权信息,部分平台可能没有'; +comment on column sys_social.id_token is 'id token,部分平台可能没有'; +comment on column sys_social.mac_algorithm is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.mac_key is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.code is '用户的授权code,部分平台可能没有'; +comment on column sys_social.oauth_token is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.oauth_token_secret is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.create_dept is '创建部门'; +comment on column sys_social.create_by is '创建者'; +comment on column sys_social.create_time is '创建时间'; +comment on column sys_social.update_by is '更新者'; +comment on column sys_social.update_time is '更新时间'; +comment on column sys_social.del_flag is '删除标志(0代表存在 1代表删除)'; + +-- ---------------------------- +-- 租户表 +-- ---------------------------- +create table if not exists sys_tenant +( + id int8, + tenant_id varchar(20) not null, + contact_user_name varchar(20) default null::varchar, + contact_phone varchar(20) default null::varchar, + company_name varchar(30) default null::varchar, + license_number varchar(30) default null::varchar, + address varchar(200) default null::varchar, + intro varchar(200) default null::varchar, + domain varchar(200) default null::varchar, + remark varchar(200) default null::varchar, + package_id int8, + expire_time timestamp, + account_count int4 default -1, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "pk_sys_tenant" primary key (id) +); + + +comment on table sys_tenant is '租户表'; +comment on column sys_tenant.tenant_id is '租户编号'; +comment on column sys_tenant.contact_phone is '联系电话'; +comment on column sys_tenant.company_name is '企业名称'; +comment on column sys_tenant.company_name is '联系人'; +comment on column sys_tenant.license_number is '统一社会信用代码'; +comment on column sys_tenant.address is '地址'; +comment on column sys_tenant.intro is '企业简介'; +comment on column sys_tenant.domain is '域名'; +comment on column sys_tenant.remark is '备注'; +comment on column sys_tenant.package_id is '租户套餐编号'; +comment on column sys_tenant.expire_time is '过期时间'; +comment on column sys_tenant.account_count is '用户数量(-1不限制)'; +comment on column sys_tenant.status is '租户状态(0正常 1停用)'; +comment on column sys_tenant.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_tenant.create_dept is '创建部门'; +comment on column sys_tenant.create_by is '创建者'; +comment on column sys_tenant.create_time is '创建时间'; +comment on column sys_tenant.update_by is '更新者'; +comment on column sys_tenant.update_time is '更新时间'; + + +-- ---------------------------- +-- 初始化-租户表数据 +-- ---------------------------- + +insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, now(), null, null); + + +-- ---------------------------- +-- 租户套餐表 +-- ---------------------------- +create table if not exists sys_tenant_package +( + package_id int8, + package_name varchar(20) default ''::varchar, + menu_ids varchar(3000) default ''::varchar, + remark varchar(200) default ''::varchar, + menu_check_strictly bool default true, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "pk_sys_tenant_package" primary key (package_id) +); + + +comment on table sys_tenant_package is '租户套餐表'; +comment on column sys_tenant_package.package_id is '租户套餐id'; +comment on column sys_tenant_package.package_name is '套餐名称'; +comment on column sys_tenant_package.menu_ids is '关联菜单id'; +comment on column sys_tenant_package.remark is '备注'; +comment on column sys_tenant_package.status is '状态(0正常 1停用)'; +comment on column sys_tenant_package.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_tenant_package.create_dept is '创建部门'; +comment on column sys_tenant_package.create_by is '创建者'; +comment on column sys_tenant_package.create_time is '创建时间'; +comment on column sys_tenant_package.update_by is '更新者'; +comment on column sys_tenant_package.update_time is '更新时间'; + + +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +create table if not exists sys_dept +( + dept_id int8, + tenant_id varchar(20) default '000000'::varchar, + parent_id int8 default 0, + ancestors varchar(500)default ''::varchar, + dept_name varchar(30) default ''::varchar, + dept_category varchar(100) default null::varchar, + order_num int4 default 0, + leader int8 default null, + phone varchar(11) default null::varchar, + email varchar(50) default null::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint "sys_dept_pk" primary key (dept_id) +); + +comment on table sys_dept is '部门表'; +comment on column sys_dept.dept_id is '部门ID'; +comment on column sys_dept.tenant_id is '租户编号'; +comment on column sys_dept.parent_id is '父部门ID'; +comment on column sys_dept.ancestors is '祖级列表'; +comment on column sys_dept.dept_name is '部门名称'; +comment on column sys_dept.dept_category is '部门类别编码'; +comment on column sys_dept.order_num is '显示顺序'; +comment on column sys_dept.leader is '负责人'; +comment on column sys_dept.phone is '联系电话'; +comment on column sys_dept.email is '邮箱'; +comment on column sys_dept.status is '部门状态(0正常 1停用)'; +comment on column sys_dept.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_dept.create_dept is '创建部门'; +comment on column sys_dept.create_by is '创建者'; +comment on column sys_dept.create_time is '创建时间'; +comment on column sys_dept.update_by is '更新者'; +comment on column sys_dept.update_time is '更新时间'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); +insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null); + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +create table if not exists sys_user +( + user_id int8, + tenant_id varchar(20) default '000000'::varchar, + dept_id int8, + user_name varchar(30) not null, + nick_name varchar(30) not null, + user_type varchar(10) default 'sys_user'::varchar, + email varchar(50) default ''::varchar, + phonenumber varchar(11) default ''::varchar, + sex char default '0'::bpchar, + avatar int8, + password varchar(100) default ''::varchar, + status char default '0'::bpchar, + del_flag char default '0'::bpchar, + login_ip varchar(128) default ''::varchar, + login_date timestamp, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_user_pk" primary key (user_id) +); + +comment on table sys_user is '用户信息表'; +comment on column sys_user.user_id is '用户ID'; +comment on column sys_user.tenant_id is '租户编号'; +comment on column sys_user.dept_id is '部门ID'; +comment on column sys_user.user_name is '用户账号'; +comment on column sys_user.nick_name is '用户昵称'; +comment on column sys_user.user_type is '用户类型(sys_user系统用户)'; +comment on column sys_user.email is '用户邮箱'; +comment on column sys_user.phonenumber is '手机号码'; +comment on column sys_user.sex is '用户性别(0男 1女 2未知)'; +comment on column sys_user.avatar is '头像地址'; +comment on column sys_user.password is '密码'; +comment on column sys_user.status is '帐号状态(0正常 1停用)'; +comment on column sys_user.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_user.login_ip is '最后登陆IP'; +comment on column sys_user.login_date is '最后登陆时间'; +comment on column sys_user.create_dept is '创建部门'; +comment on column sys_user.create_by is '创建者'; +comment on column sys_user.create_time is '创建时间'; +comment on column sys_user.update_by is '更新者'; +comment on column sys_user.update_time is '更新时间'; +comment on column sys_user.remark is '备注'; + +-- ---------------------------- + +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 103, 1, now(), null, null, '管理员'); +insert into sys_user VALUES(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 3, now(), NULL); +insert into sys_user VALUES(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 4, now(), NULL); + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +create table if not exists sys_post +( + post_id int8, + tenant_id varchar(20) default '000000'::varchar, + dept_id int8, + post_code varchar(64) not null, + post_category varchar(100) default null, + post_name varchar(50) not null, + post_sort int4 not null, + status char not null, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_post_pk" primary key (post_id) +); + +comment on table sys_post is '岗位信息表'; +comment on column sys_post.post_id is '岗位ID'; +comment on column sys_post.tenant_id is '租户编号'; +comment on column sys_post.dept_id is '部门id'; +comment on column sys_post.post_code is '岗位编码'; +comment on column sys_post.post_category is '岗位类别编码'; +comment on column sys_post.post_name is '岗位名称'; +comment on column sys_post.post_sort is '显示顺序'; +comment on column sys_post.status is '状态(0正常 1停用)'; +comment on column sys_post.create_dept is '创建部门'; +comment on column sys_post.create_by is '创建者'; +comment on column sys_post.create_time is '创建时间'; +comment on column sys_post.update_by is '更新者'; +comment on column sys_post.update_time is '更新时间'; +comment on column sys_post.remark is '备注'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, '000000', 103, 'ceo', null, '董事长', 1, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(2, '000000', 100, 'se', null, '项目经理', 2, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(3, '000000', 100, 'hr', null, '人力资源', 3, '0', 103, 1, now(), null, null, ''); +insert into sys_post values(4, '000000', 100, 'user', null, '普通员工', 4, '0', 103, 1, now(), null, null, ''); + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +create table if not exists sys_role +( + role_id int8, + tenant_id varchar(20) default '000000'::varchar, + role_name varchar(30) not null, + role_key varchar(100) not null, + role_sort int4 not null, + data_scope char default '1'::bpchar, + menu_check_strictly bool default true, + dept_check_strictly bool default true, + status char not null, + del_flag char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint "sys_role_pk" primary key (role_id) +); + +comment on table sys_role is '角色信息表'; +comment on column sys_role.role_id is '角色ID'; +comment on column sys_role.tenant_id is '租户编号'; +comment on column sys_role.role_name is '角色名称'; +comment on column sys_role.role_key is '角色权限字符串'; +comment on column sys_role.role_sort is '显示顺序'; +comment on column sys_role.data_scope is '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)'; +comment on column sys_role.menu_check_strictly is '菜单树选择项是否关联显示'; +comment on column sys_role.dept_check_strictly is '部门树选择项是否关联显示'; +comment on column sys_role.status is '角色状态(0正常 1停用)'; +comment on column sys_role.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_role.create_dept is '创建部门'; +comment on column sys_role.create_by is '创建者'; +comment on column sys_role.create_time is '创建时间'; +comment on column sys_role.update_by is '更新者'; +comment on column sys_role.update_time is '更新时间'; +comment on column sys_role.remark is '备注'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '000000', '超级管理员', 'superadmin', 1, '1', 't', 't', '0', '0', 103, 1, now(), null, null, '超级管理员'); +insert into sys_role values('3', '000000', '本部门及以下', 'test1', 3, '4', 't', 't', '0', '0', 103, 1, now(), NULL, NULL, ''); +insert into sys_role values('4', '000000', '仅本人', 'test2', 4, '5', 't', 't', '0', '0', 103, 1, now(), NULL, NULL, ''); + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +create table if not exists sys_menu +( + menu_id int8, + menu_name varchar(50) not null, + parent_id int8 default 0, + order_num int4 default 0, + path varchar(200) default ''::varchar, + component varchar(255) default null::varchar, + query_param varchar(255) default null::varchar, + is_frame char default '1'::bpchar, + is_cache char default '0'::bpchar, + menu_type char default ''::bpchar, + visible char default '0'::bpchar, + status char default '0'::bpchar, + perms varchar(100) default null::varchar, + icon varchar(100) default '#'::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint "sys_menu_pk" primary key (menu_id) +); + +comment on table sys_menu is '菜单权限表'; +comment on column sys_menu.menu_id is '菜单ID'; +comment on column sys_menu.menu_name is '菜单名称'; +comment on column sys_menu.parent_id is '父菜单ID'; +comment on column sys_menu.order_num is '显示顺序'; +comment on column sys_menu.path is '路由地址'; +comment on column sys_menu.component is '组件路径'; +comment on column sys_menu.query_param is '路由参数'; +comment on column sys_menu.is_frame is '是否为外链(0是 1否)'; +comment on column sys_menu.is_cache is '是否缓存(0缓存 1不缓存)'; +comment on column sys_menu.menu_type is '菜单类型(M目录 C菜单 F按钮)'; +comment on column sys_menu.visible is '显示状态(0显示 1隐藏)'; +comment on column sys_menu.status is '菜单状态(0正常 1停用)'; +comment on column sys_menu.perms is '权限标识'; +comment on column sys_menu.icon is '菜单图标'; +comment on column sys_menu.create_dept is '创建部门'; +comment on column sys_menu.create_by is '创建者'; +comment on column sys_menu.create_time is '创建时间'; +comment on column sys_menu.update_by is '更新者'; +comment on column sys_menu.update_time is '更新时间'; +comment on column sys_menu.remark is '备注'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '1', '0', 'M', '0', '0', '', 'system', 103, 1, now(), null, null, '系统管理目录'); +insert into sys_menu values('6', '租户管理', '0', '2', 'tenant', null, '', '1', '0', 'M', '0', '0', '', 'chart', 103, 1, now(), null, null, '租户管理目录'); +insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), null, null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', '1', '0', 'M', '0', '0', '', 'tool', 103, 1, now(), null, null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/fys-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide', 103, 1, now(), null, null, 'fys-Vue-Plus官网地址'); +insert into sys_menu VALUES('5', '测试菜单', '0', '5', 'demo', null, '', '1', '0', 'M', '0', '0', null, 'star', 103, 1, now(), null, null, '测试菜单'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '1', '0', 'C', '0', '0', 'system:user:list', 'user', 103, 1, now(), null, null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '1', '0', 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, now(), null, null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '1', '0', 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, now(), null, null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '1', '0', 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, now(), null, null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', '1', '0', 'C', '0', '0', 'system:post:list', 'post', 103, 1, now(), null, null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', '1', '0', 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, now(), null, null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', '1', '0', 'C', '0', '0', 'system:config:list', 'edit', 103, 1, now(), null, null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '1', '0', 'C', '0', '0', 'system:notice:list', 'message', 103, 1, now(), null, null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', '1', '0', 'M', '0', '0', '', 'log', 103, 1, now(), null, null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '1', '0', 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, now(), null, null, '在线用户菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '1', '0', 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, now(), null, null, '缓存监控菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '1', '0', 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, now(), null, null, '代码生成菜单'); +insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', '1', '0', 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, now(), null, null, '租户管理菜单'); +insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', '1', '0', 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, now(), null, null, '租户套餐管理菜单'); +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', '1', '0', 'C', '0', '0', 'system:client:list', 'international', 103, 1, now(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); + +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', '1', '0', 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, now(), null, null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', '1', '0', 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, now(), null, null, '文件管理菜单'); +-- snail-job server控制台 +insert into sys_menu values('120', '任务调度中心', '2', '6', 'snailjob', 'monitor/snailjob/index', '', '1', '0', 'C', '0', '0', 'monitor:snailjob:list', 'job', 103, 1, now(), null, null, 'SnailJob控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', '1', '0', 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, now(), null, null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '1', '0', 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, now(), null, null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:user:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:user:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:user:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:user:export', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', '1', '0', 'F', '0', '0', 'system:user:import', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', '1', '0', 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, now(), null, null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:role:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:role:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:role:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:role:export', '#', 103, 1, now(), null, null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:menu:remove', '#', 103, 1, now(), null, null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:dept:remove', '#', 103, 1, now(), null, null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', '1', '0', 'F', '0', '0', 'system:post:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', '1', '0', 'F', '0', '0', 'system:post:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', '1', '0', 'F', '0', '0', 'system:post:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', '1', '0', 'F', '0', '0', 'system:post:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', '1', '0', 'F', '0', '0', 'system:post:export', '#', 103, 1, now(), null, null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:dict:export', '#', 103, 1, now(), null, null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:config:export', '#', 103, 1, now(), null, null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:notice:remove', '#', 103, 1, now(), null, null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, now(), null, null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, now(), null, null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '1', '0', 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, now(), null, null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:import', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', '1', '0', 'F', '0', '0', 'tool:gen:code', '#', 103, 1, now(), null, null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:upload', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:download', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:oss:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, now(), null, null, ''); +-- 租户管理相关按钮 +insert into sys_menu values('1606', '租户查询', '121', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1607', '租户新增', '121', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1608', '租户修改', '121', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1609', '租户删除', '121', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1610', '租户导出', '121', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenant:export', '#', 103, 1, now(), null, null, ''); +-- 租户套餐管理相关按钮 +insert into sys_menu values('1611', '租户套餐查询', '122', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1612', '租户套餐新增', '122', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1613', '租户套餐修改', '122', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1614', '租户套餐删除', '122', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1615', '租户套餐导出', '122', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, now(), null, null, ''); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:export', '#', 103, 1, now(), null, null, ''); +-- 测试菜单 +INSERT INTO sys_menu VALUES('1500', '测试单表', '5', '1', 'demo', 'demo/demo/index', '', '1', '0', 'C', '0', '0', 'demo:demo:list', '#', 103, 1, now(), NULL, NULL, '测试单表菜单'); +INSERT INTO sys_menu VALUES('1501', '测试单表查询', '1500', '1', '#', '', '', '1', '0', 'F', '0', '0', 'demo:demo:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1502', '测试单表新增', '1500', '2', '#', '', '', '1', '0', 'F', '0', '0', 'demo:demo:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1503', '测试单表修改', '1500', '3', '#', '', '', '1', '0', 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1504', '测试单表删除', '1500', '4', '#', '', '', '1', '0', 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1505', '测试单表导出', '1500', '5', '#', '', '', '1', '0', 'F', '0', '0', 'demo:demo:export', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1506', '测试树表', '5', '1', 'tree', 'demo/tree/index', '', '1', '0', 'C', '0', '0', 'demo:tree:list', '#', 103, 1, now(), NULL, NULL, '测试树表菜单'); +INSERT INTO sys_menu VALUES('1507', '测试树表查询', '1506', '1', '#', '', '', '1', '0', 'F', '0', '0', 'demo:tree:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1508', '测试树表新增', '1506', '2', '#', '', '', '1', '0', 'F', '0', '0', 'demo:tree:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1509', '测试树表修改', '1506', '3', '#', '', '', '1', '0', 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1510', '测试树表删除', '1506', '4', '#', '', '', '1', '0', 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES('1511', '测试树表导出', '1506', '5', '#', '', '', '1', '0', 'F', '0', '0', 'demo:tree:export', '#', 103, 1, now(), NULL, NULL, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +create table if not exists sys_user_role +( + user_id int8 not null, + role_id int8 not null, + constraint sys_user_role_pk primary key (user_id, role_id) +); + +comment on table sys_user_role is '用户和角色关联表'; +comment on column sys_user_role.user_id is '用户ID'; +comment on column sys_user_role.role_id is '角色ID'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('3', '3'); +insert into sys_user_role values ('4', '4'); + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +create table if not exists sys_role_menu +( + role_id int8 not null, + menu_id int8 not null, + constraint sys_role_menu_pk primary key (role_id, menu_id) +); + +comment on table sys_role_menu is '角色和菜单关联表'; +comment on column sys_role_menu.role_id is '角色ID'; +comment on column sys_role_menu.menu_id is '菜单ID'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('3', '1'); +insert into sys_role_menu values ('3', '5'); +insert into sys_role_menu values ('3', '100'); +insert into sys_role_menu values ('3', '101'); +insert into sys_role_menu values ('3', '102'); +insert into sys_role_menu values ('3', '103'); +insert into sys_role_menu values ('3', '104'); +insert into sys_role_menu values ('3', '105'); +insert into sys_role_menu values ('3', '106'); +insert into sys_role_menu values ('3', '107'); +insert into sys_role_menu values ('3', '108'); +insert into sys_role_menu values ('3', '118'); +insert into sys_role_menu values ('3', '123'); +insert into sys_role_menu values ('3', '130'); +insert into sys_role_menu values ('3', '131'); +insert into sys_role_menu values ('3', '132'); +insert into sys_role_menu values ('3', '133'); +insert into sys_role_menu values ('3', '500'); +insert into sys_role_menu values ('3', '501'); +insert into sys_role_menu values ('3', '1001'); +insert into sys_role_menu values ('3', '1002'); +insert into sys_role_menu values ('3', '1003'); +insert into sys_role_menu values ('3', '1004'); +insert into sys_role_menu values ('3', '1005'); +insert into sys_role_menu values ('3', '1006'); +insert into sys_role_menu values ('3', '1007'); +insert into sys_role_menu values ('3', '1008'); +insert into sys_role_menu values ('3', '1009'); +insert into sys_role_menu values ('3', '1010'); +insert into sys_role_menu values ('3', '1011'); +insert into sys_role_menu values ('3', '1012'); +insert into sys_role_menu values ('3', '1013'); +insert into sys_role_menu values ('3', '1014'); +insert into sys_role_menu values ('3', '1015'); +insert into sys_role_menu values ('3', '1016'); +insert into sys_role_menu values ('3', '1017'); +insert into sys_role_menu values ('3', '1018'); +insert into sys_role_menu values ('3', '1019'); +insert into sys_role_menu values ('3', '1020'); +insert into sys_role_menu values ('3', '1021'); +insert into sys_role_menu values ('3', '1022'); +insert into sys_role_menu values ('3', '1023'); +insert into sys_role_menu values ('3', '1024'); +insert into sys_role_menu values ('3', '1025'); +insert into sys_role_menu values ('3', '1026'); +insert into sys_role_menu values ('3', '1027'); +insert into sys_role_menu values ('3', '1028'); +insert into sys_role_menu values ('3', '1029'); +insert into sys_role_menu values ('3', '1030'); +insert into sys_role_menu values ('3', '1031'); +insert into sys_role_menu values ('3', '1032'); +insert into sys_role_menu values ('3', '1033'); +insert into sys_role_menu values ('3', '1034'); +insert into sys_role_menu values ('3', '1035'); +insert into sys_role_menu values ('3', '1036'); +insert into sys_role_menu values ('3', '1037'); +insert into sys_role_menu values ('3', '1038'); +insert into sys_role_menu values ('3', '1039'); +insert into sys_role_menu values ('3', '1040'); +insert into sys_role_menu values ('3', '1041'); +insert into sys_role_menu values ('3', '1042'); +insert into sys_role_menu values ('3', '1043'); +insert into sys_role_menu values ('3', '1044'); +insert into sys_role_menu values ('3', '1045'); +insert into sys_role_menu values ('3', '1050'); +insert into sys_role_menu values ('3', '1061'); +insert into sys_role_menu values ('3', '1062'); +insert into sys_role_menu values ('3', '1063'); +insert into sys_role_menu values ('3', '1064'); +insert into sys_role_menu values ('3', '1065'); +insert into sys_role_menu values ('3', '1500'); +insert into sys_role_menu values ('3', '1501'); +insert into sys_role_menu values ('3', '1502'); +insert into sys_role_menu values ('3', '1503'); +insert into sys_role_menu values ('3', '1504'); +insert into sys_role_menu values ('3', '1505'); +insert into sys_role_menu values ('3', '1506'); +insert into sys_role_menu values ('3', '1507'); +insert into sys_role_menu values ('3', '1508'); +insert into sys_role_menu values ('3', '1509'); +insert into sys_role_menu values ('3', '1510'); +insert into sys_role_menu values ('3', '1511'); +insert into sys_role_menu values ('3', '1600'); +insert into sys_role_menu values ('3', '1601'); +insert into sys_role_menu values ('3', '1602'); +insert into sys_role_menu values ('3', '1603'); +insert into sys_role_menu values ('3', '1620'); +insert into sys_role_menu values ('3', '1621'); +insert into sys_role_menu values ('3', '1622'); +insert into sys_role_menu values ('3', '1623'); +insert into sys_role_menu values ('3', '11616'); +insert into sys_role_menu values ('3', '11618'); +insert into sys_role_menu values ('3', '11619'); +insert into sys_role_menu values ('3', '11622'); +insert into sys_role_menu values ('3', '11623'); +insert into sys_role_menu values ('3', '11629'); +insert into sys_role_menu values ('3', '11632'); +insert into sys_role_menu values ('3', '11633'); +insert into sys_role_menu values ('3', '11638'); +insert into sys_role_menu values ('3', '11639'); +insert into sys_role_menu values ('3', '11640'); +insert into sys_role_menu values ('3', '11641'); +insert into sys_role_menu values ('3', '11642'); +insert into sys_role_menu values ('3', '11643'); +insert into sys_role_menu values ('3', '11701'); +insert into sys_role_menu values ('4', '5'); +insert into sys_role_menu values ('4', '1500'); +insert into sys_role_menu values ('4', '1501'); +insert into sys_role_menu values ('4', '1502'); +insert into sys_role_menu values ('4', '1503'); +insert into sys_role_menu values ('4', '1504'); +insert into sys_role_menu values ('4', '1505'); +insert into sys_role_menu values ('4', '1506'); +insert into sys_role_menu values ('4', '1507'); +insert into sys_role_menu values ('4', '1508'); +insert into sys_role_menu values ('4', '1509'); +insert into sys_role_menu values ('4', '1510'); +insert into sys_role_menu values ('4', '1511'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +create table if not exists sys_role_dept +( + role_id int8 not null, + dept_id int8 not null, + constraint sys_role_dept_pk primary key (role_id, dept_id) +); + +comment on table sys_role_dept is '角色和部门关联表'; +comment on column sys_role_dept.role_id is '角色ID'; +comment on column sys_role_dept.dept_id is '部门ID'; + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +create table if not exists sys_user_post +( + user_id int8 not null, + post_id int8 not null, + constraint sys_user_post_pk primary key (user_id, post_id) +); + +comment on table sys_user_post is '用户与岗位关联表'; +comment on column sys_user_post.user_id is '用户ID'; +comment on column sys_user_post.post_id is '岗位ID'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +create table if not exists sys_oper_log +( + oper_id int8, + tenant_id varchar(20) default '000000'::varchar, + title varchar(50) default ''::varchar, + business_type int4 default 0, + method varchar(100) default ''::varchar, + request_method varchar(10) default ''::varchar, + operator_type int4 default 0, + oper_name varchar(50) default ''::varchar, + dept_name varchar(50) default ''::varchar, + oper_url varchar(255) default ''::varchar, + oper_ip varchar(128) default ''::varchar, + oper_location varchar(255) default ''::varchar, + oper_param varchar(4000) default ''::varchar, + json_result varchar(4000) default ''::varchar, + status int4 default 0, + error_msg varchar(4000) default ''::varchar, + oper_time timestamp, + cost_time int8 default 0, + constraint sys_oper_log_pk primary key (oper_id) +); + +create index idx_sys_oper_log_bt ON sys_oper_log (business_type); +create index idx_sys_oper_log_s ON sys_oper_log (status); +create index idx_sys_oper_log_ot ON sys_oper_log (oper_time); + +comment on table sys_oper_log is '操作日志记录'; +comment on column sys_oper_log.oper_id is '日志主键'; +comment on column sys_oper_log.tenant_id is '租户编号'; +comment on column sys_oper_log.title is '模块标题'; +comment on column sys_oper_log.business_type is '业务类型(0其它 1新增 2修改 3删除)'; +comment on column sys_oper_log.method is '方法名称'; +comment on column sys_oper_log.request_method is '请求方式'; +comment on column sys_oper_log.operator_type is '操作类别(0其它 1后台用户 2手机端用户)'; +comment on column sys_oper_log.oper_name is '操作人员'; +comment on column sys_oper_log.dept_name is '部门名称'; +comment on column sys_oper_log.oper_url is '请求URL'; +comment on column sys_oper_log.oper_ip is '主机地址'; +comment on column sys_oper_log.oper_location is '操作地点'; +comment on column sys_oper_log.oper_param is '请求参数'; +comment on column sys_oper_log.json_result is '返回参数'; +comment on column sys_oper_log.status is '操作状态(0正常 1异常)'; +comment on column sys_oper_log.error_msg is '错误消息'; +comment on column sys_oper_log.oper_time is '操作时间'; +comment on column sys_oper_log.cost_time is '消耗时间'; + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +create table if not exists sys_dict_type +( + dict_id int8, + tenant_id varchar(20) default '000000'::varchar, + dict_name varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_type_pk primary key (dict_id) +); + +create unique index sys_dict_type_index1 ON sys_dict_type (tenant_id, dict_type); + +comment on table sys_dict_type is '字典类型表'; +comment on column sys_dict_type.dict_id is '字典主键'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_type.dict_name is '字典名称'; +comment on column sys_dict_type.dict_type is '字典类型'; +comment on column sys_dict_type.create_dept is '创建部门'; +comment on column sys_dict_type.create_by is '创建者'; +comment on column sys_dict_type.create_time is '创建时间'; +comment on column sys_dict_type.update_by is '更新者'; +comment on column sys_dict_type.update_time is '更新时间'; +comment on column sys_dict_type.remark is '备注'; + +insert into sys_dict_type values(1, '000000', '用户性别', 'sys_user_sex', 103, 1, now(), null, null, '用户性别列表'); +insert into sys_dict_type values(2, '000000', '菜单状态', 'sys_show_hide', 103, 1, now(), null, null, '菜单状态列表'); +insert into sys_dict_type values(3, '000000', '系统开关', 'sys_normal_disable', 103, 1, now(), null, null, '系统开关列表'); +insert into sys_dict_type values(6, '000000', '系统是否', 'sys_yes_no', 103, 1, now(), null, null, '系统是否列表'); +insert into sys_dict_type values(7, '000000', '通知类型', 'sys_notice_type', 103, 1, now(), null, null, '通知类型列表'); +insert into sys_dict_type values(8, '000000', '通知状态', 'sys_notice_status', 103, 1, now(), null, null, '通知状态列表'); +insert into sys_dict_type values(9, '000000', '操作类型', 'sys_oper_type', 103, 1, now(), null, null, '操作类型列表'); +insert into sys_dict_type values(10, '000000', '系统状态', 'sys_common_status', 103, 1, now(), null, null, '登录状态列表'); +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', 103, 1, now(), null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, now(), null, null, '客户端设备类型'); + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +create table if not exists sys_dict_data +( + dict_code int8, + tenant_id varchar(20) default '000000'::varchar, + dict_sort int4 default 0, + dict_label varchar(100) default ''::varchar, + dict_value varchar(100) default ''::varchar, + dict_type varchar(100) default ''::varchar, + css_class varchar(100) default null::varchar, + list_class varchar(100) default null::varchar, + is_default char default 'N'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_dict_data_pk primary key (dict_code) +); + +comment on table sys_dict_data is '字典数据表'; +comment on column sys_dict_data.dict_code is '字典编码'; +comment on column sys_dict_type.tenant_id is '租户编号'; +comment on column sys_dict_data.dict_sort is '字典排序'; +comment on column sys_dict_data.dict_label is '字典标签'; +comment on column sys_dict_data.dict_value is '字典键值'; +comment on column sys_dict_data.dict_type is '字典类型'; +comment on column sys_dict_data.css_class is '样式属性(其他样式扩展)'; +comment on column sys_dict_data.list_class is '表格回显样式'; +comment on column sys_dict_data.is_default is '是否默认(Y是 N否)'; +comment on column sys_dict_data.create_dept is '创建部门'; +comment on column sys_dict_data.create_by is '创建者'; +comment on column sys_dict_data.create_time is '创建时间'; +comment on column sys_dict_data.update_by is '更新者'; +comment on column sys_dict_data.update_time is '更新时间'; +comment on column sys_dict_data.remark is '备注'; + +insert into sys_dict_data values(1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', 103, 1, now(), null, null, '性别男'); +insert into sys_dict_data values(2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', 103, 1, now(), null, null, '性别女'); +insert into sys_dict_data values(3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', 103, 1, now(), null, null, '性别未知'); +insert into sys_dict_data values(4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', 103, 1, now(), null, null, '显示菜单'); +insert into sys_dict_data values(5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', 103, 1, now(), null, null, '隐藏菜单'); +insert into sys_dict_data values(6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', 103, 1, now(), null, null, '停用状态'); +insert into sys_dict_data values(12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', 103, 1, now(), null, null, '系统默认是'); +insert into sys_dict_data values(13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', 103, 1, now(), null, null, '系统默认否'); +insert into sys_dict_data values(14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', 103, 1, now(), null, null, '通知'); +insert into sys_dict_data values(15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', 103, 1, now(), null, null, '公告'); +insert into sys_dict_data values(16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', 103, 1, now(), null, null, '关闭状态'); +insert into sys_dict_data values(29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', 103, 1, now(), null, null, '其他操作'); +insert into sys_dict_data values(18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', 103, 1, now(), null, null, '新增操作'); +insert into sys_dict_data values(19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', 103, 1, now(), null, null, '修改操作'); +insert into sys_dict_data values(20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', 103, 1, now(), null, null, '删除操作'); +insert into sys_dict_data values(21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', 103, 1, now(), null, null, '授权操作'); +insert into sys_dict_data values(22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', 103, 1, now(), null, null, '导出操作'); +insert into sys_dict_data values(23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', 103, 1, now(), null, null, '导入操作'); +insert into sys_dict_data values(24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', 103, 1, now(), null, null, '强退操作'); +insert into sys_dict_data values(25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', 103, 1, now(), null, null, '生成操作'); +insert into sys_dict_data values(26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', 103, 1, now(), null, null, '清空操作'); +insert into sys_dict_data values(27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', 103, 1, now(), null, null, '正常状态'); +insert into sys_dict_data values(28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', 103, 1, now(), null, null, '停用状态'); +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', '', 'default', 'N', 103, 1, now(), null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', '', 'default', 'N', 103, 1, now(), null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', '', 'default', 'N', 103, 1, now(), null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', '', 'default', 'N', 103, 1, now(), null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', '', 'default', 'N', 103, 1, now(), null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', 103, 1, now(), null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, now(), null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, now(), null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, now(), null, null, '小程序'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +create table if not exists sys_config +( + config_id int8, + tenant_id varchar(20) default '000000'::varchar, + config_name varchar(100) default ''::varchar, + config_key varchar(100) default ''::varchar, + config_value varchar(500) default ''::varchar, + config_type char default 'N'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint sys_config_pk primary key (config_id) +); + +comment on table sys_config is '参数配置表'; +comment on column sys_config.config_id is '参数主键'; +comment on column sys_config.tenant_id is '租户编号'; +comment on column sys_config.config_name is '参数名称'; +comment on column sys_config.config_key is '参数键名'; +comment on column sys_config.config_value is '参数键值'; +comment on column sys_config.config_type is '系统内置(Y是 N否)'; +comment on column sys_config.create_dept is '创建部门'; +comment on column sys_config.create_by is '创建者'; +comment on column sys_config.create_time is '创建时间'; +comment on column sys_config.update_by is '更新者'; +comment on column sys_config.update_time is '更新时间'; +comment on column sys_config.remark is '备注'; + +insert into sys_config values(1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, now(), null, null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, now(), null, null, '初始化密码 123456' ); +insert into sys_config values(3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, now(), null, null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, now(), null, null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, now(), null, null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +create table if not exists sys_logininfor +( + info_id int8, + tenant_id varchar(20) default '000000'::varchar, + user_name varchar(50) default ''::varchar, + client_key varchar(32) default ''::varchar, + device_type varchar(32) default ''::varchar, + ipaddr varchar(128) default ''::varchar, + login_location varchar(255) default ''::varchar, + browser varchar(50) default ''::varchar, + os varchar(50) default ''::varchar, + status char default '0'::bpchar, + msg varchar(255) default ''::varchar, + login_time timestamp, + constraint sys_logininfor_pk primary key (info_id) +); + +create index idx_sys_logininfor_s ON sys_logininfor (status); +create index idx_sys_logininfor_lt ON sys_logininfor (login_time); + +comment on table sys_logininfor is '系统访问记录'; +comment on column sys_logininfor.info_id is '访问ID'; +comment on column sys_logininfor.tenant_id is '租户编号'; +comment on column sys_logininfor.user_name is '用户账号'; +comment on column sys_logininfor.client_key is '客户端'; +comment on column sys_logininfor.device_type is '设备类型'; +comment on column sys_logininfor.ipaddr is '登录IP地址'; +comment on column sys_logininfor.login_location is '登录地点'; +comment on column sys_logininfor.browser is '浏览器类型'; +comment on column sys_logininfor.os is '操作系统'; +comment on column sys_logininfor.status is '登录状态(0成功 1失败)'; +comment on column sys_logininfor.msg is '提示消息'; +comment on column sys_logininfor.login_time is '访问时间'; + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +create table if not exists sys_notice +( + notice_id int8, + tenant_id varchar(20) default '000000'::varchar, + notice_title varchar(50) not null, + notice_type char not null, + notice_content text, + status char default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(255) default null::varchar, + constraint sys_notice_pk primary key (notice_id) +); + +comment on table sys_notice is '通知公告表'; +comment on column sys_notice.notice_id is '公告ID'; +comment on column sys_notice.tenant_id is '租户编号'; +comment on column sys_notice.notice_title is '公告标题'; +comment on column sys_notice.notice_type is '公告类型(1通知 2公告)'; +comment on column sys_notice.notice_content is '公告内容'; +comment on column sys_notice.status is '公告状态(0正常 1关闭)'; +comment on column sys_notice.create_dept is '创建部门'; +comment on column sys_notice.create_by is '创建者'; +comment on column sys_notice.create_time is '创建时间'; +comment on column sys_notice.update_by is '更新者'; +comment on column sys_notice.update_time is '更新时间'; +comment on column sys_notice.remark is '备注'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, now(), null, null, '管理员'); +insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, now(), null, null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +create table if not exists gen_table +( + table_id int8, + data_name varchar(200) default ''::varchar, + table_name varchar(200) default ''::varchar, + table_comment varchar(500) default ''::varchar, + sub_table_name varchar(64) default ''::varchar, + sub_table_fk_name varchar(64) default ''::varchar, + class_name varchar(100) default ''::varchar, + tpl_category varchar(200) default 'crud'::varchar, + package_name varchar(100) default null::varchar, + module_name varchar(30) default null::varchar, + business_name varchar(30) default null::varchar, + function_name varchar(50) default null::varchar, + function_author varchar(50) default null::varchar, + gen_type char default '0'::bpchar not null, + gen_path varchar(200) default '/'::varchar, + options varchar(1000) default null::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default null::varchar, + constraint gen_table_pk primary key (table_id) +); + +comment on table gen_table is '代码生成业务表'; +comment on column gen_table.table_id is '编号'; +comment on column gen_table.data_name is '数据源名称'; +comment on column gen_table.table_name is '表名称'; +comment on column gen_table.table_comment is '表描述'; +comment on column gen_table.sub_table_name is '关联子表的表名'; +comment on column gen_table.sub_table_fk_name is '子表关联的外键名'; +comment on column gen_table.class_name is '实体类名称'; +comment on column gen_table.tpl_category is '使用的模板(CRUD单表操作 TREE树表操作)'; +comment on column gen_table.package_name is '生成包路径'; +comment on column gen_table.module_name is '生成模块名'; +comment on column gen_table.business_name is '生成业务名'; +comment on column gen_table.function_name is '生成功能名'; +comment on column gen_table.function_author is '生成功能作者'; +comment on column gen_table.gen_type is '生成代码方式(0zip压缩包 1自定义路径)'; +comment on column gen_table.gen_path is '生成路径(不填默认项目路径)'; +comment on column gen_table.options is '其它生成选项'; +comment on column gen_table.create_dept is '创建部门'; +comment on column gen_table.create_by is '创建者'; +comment on column gen_table.create_time is '创建时间'; +comment on column gen_table.update_by is '更新者'; +comment on column gen_table.update_time is '更新时间'; +comment on column gen_table.remark is '备注'; + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +create table if not exists gen_table_column +( + column_id int8, + table_id int8, + column_name varchar(200) default null::varchar, + column_comment varchar(500) default null::varchar, + column_type varchar(100) default null::varchar, + java_type varchar(500) default null::varchar, + java_field varchar(200) default null::varchar, + is_pk char default null::bpchar, + is_increment char default null::bpchar, + is_required char default null::bpchar, + is_insert char default null::bpchar, + is_edit char default null::bpchar, + is_list char default null::bpchar, + is_query char default null::bpchar, + query_type varchar(200) default 'EQ'::varchar, + html_type varchar(200) default null::varchar, + dict_type varchar(200) default ''::varchar, + sort int4, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint gen_table_column_pk primary key (column_id) +); + +comment on table gen_table_column is '代码生成业务表字段'; +comment on column gen_table_column.column_id is '编号'; +comment on column gen_table_column.table_id is '归属表编号'; +comment on column gen_table_column.column_name is '列名称'; +comment on column gen_table_column.column_comment is '列描述'; +comment on column gen_table_column.column_type is '列类型'; +comment on column gen_table_column.java_type is 'JAVA类型'; +comment on column gen_table_column.java_field is 'JAVA字段名'; +comment on column gen_table_column.is_pk is '是否主键(1是)'; +comment on column gen_table_column.is_increment is '是否自增(1是)'; +comment on column gen_table_column.is_required is '是否必填(1是)'; +comment on column gen_table_column.is_insert is '是否为插入字段(1是)'; +comment on column gen_table_column.is_edit is '是否编辑字段(1是)'; +comment on column gen_table_column.is_list is '是否列表字段(1是)'; +comment on column gen_table_column.is_query is '是否查询字段(1是)'; +comment on column gen_table_column.query_type is '查询方式(等于、不等于、大于、小于、范围)'; +comment on column gen_table_column.html_type is '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)'; +comment on column gen_table_column.dict_type is '字典类型'; +comment on column gen_table_column.sort is '排序'; +comment on column gen_table_column.create_dept is '创建部门'; +comment on column gen_table_column.create_by is '创建者'; +comment on column gen_table_column.create_time is '创建时间'; +comment on column gen_table_column.update_by is '更新者'; +comment on column gen_table_column.update_time is '更新时间'; + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +create table if not exists sys_oss +( + oss_id int8, + tenant_id varchar(20) default '000000'::varchar, + file_name varchar(255) default ''::varchar not null, + original_name varchar(255) default ''::varchar not null, + file_suffix varchar(10) default ''::varchar not null, + url varchar(500) default ''::varchar not null, + ext1 varchar(500) default ''::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + service varchar(20) default 'minio'::varchar, + constraint sys_oss_pk primary key (oss_id) +); + +comment on table sys_oss is 'OSS对象存储表'; +comment on column sys_oss.oss_id is '对象存储主键'; +comment on column sys_oss.tenant_id is '租户编码'; +comment on column sys_oss.file_name is '文件名'; +comment on column sys_oss.original_name is '原名'; +comment on column sys_oss.file_suffix is '文件后缀名'; +comment on column sys_oss.url is 'URL地址'; +comment on column sys_oss.ext1 is '扩展字段'; +comment on column sys_oss.create_by is '上传人'; +comment on column sys_oss.create_dept is '创建部门'; +comment on column sys_oss.create_time is '创建时间'; +comment on column sys_oss.update_by is '更新者'; +comment on column sys_oss.update_time is '更新时间'; +comment on column sys_oss.service is '服务商'; + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +create table if not exists sys_oss_config +( + oss_config_id int8, + tenant_id varchar(20) default '000000'::varchar, + config_key varchar(20) default ''::varchar not null, + access_key varchar(255) default ''::varchar, + secret_key varchar(255) default ''::varchar, + bucket_name varchar(255) default ''::varchar, + prefix varchar(255) default ''::varchar, + endpoint varchar(255) default ''::varchar, + domain varchar(255) default ''::varchar, + is_https char default 'N'::bpchar, + region varchar(255) default ''::varchar, + access_policy char(1) default '1'::bpchar not null, + status char default '1'::bpchar, + ext1 varchar(255) default ''::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + remark varchar(500) default ''::varchar, + constraint sys_oss_config_pk primary key (oss_config_id) +); + +comment on table sys_oss_config is '对象存储配置表'; +comment on column sys_oss_config.oss_config_id is '主键'; +comment on column sys_oss_config.tenant_id is '租户编码'; +comment on column sys_oss_config.config_key is '配置key'; +comment on column sys_oss_config.access_key is 'accessKey'; +comment on column sys_oss_config.secret_key is '秘钥'; +comment on column sys_oss_config.bucket_name is '桶名称'; +comment on column sys_oss_config.prefix is '前缀'; +comment on column sys_oss_config.endpoint is '访问站点'; +comment on column sys_oss_config.domain is '自定义域名'; +comment on column sys_oss_config.is_https is '是否https(Y=是,N=否)'; +comment on column sys_oss_config.region is '域'; +comment on column sys_oss_config.access_policy is '桶权限类型(0=private 1=public 2=custom)'; +comment on column sys_oss_config.status is '是否默认(0=是,1=否)'; +comment on column sys_oss_config.ext1 is '扩展字段'; +comment on column sys_oss_config.create_dept is '创建部门'; +comment on column sys_oss_config.create_by is '创建者'; +comment on column sys_oss_config.create_time is '创建时间'; +comment on column sys_oss_config.update_by is '更新者'; +comment on column sys_oss_config.update_time is '更新时间'; +comment on column sys_oss_config.remark is '备注'; + +insert into sys_oss_config values (1, '000000', 'minio', 'fys', 'fys123', 'fys', '', '127.0.0.1:9000', '','N', '', '1', '0', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys-1240000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1', '1', '', 103, 1, now(), 1, now(), null); +insert into sys_oss_config values (5, '000000', 'image', 'fys', 'fys123', 'fys', 'image', '127.0.0.1:9000', '','N', '', '1', '1', '', 103, 1, now(), 1, now(), NULL); + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +create table sys_client ( + id int8, + client_id varchar(64) default ''::varchar, + client_key varchar(32) default ''::varchar, + client_secret varchar(255) default ''::varchar, + grant_type varchar(255) default ''::varchar, + device_type varchar(32) default ''::varchar, + active_timeout int4 default 1800, + timeout int4 default 604800, + status char(1) default '0'::bpchar, + del_flag char(1) default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint sys_client_pk primary key (id) +); + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主键'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.device_type is '设备类型'; +comment on column sys_client.active_timeout is 'token活跃超时时间'; +comment on column sys_client.timeout is 'token固定超时'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 1代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, now(), 1, now()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, now(), 1, now()); + +create table if not exists test_demo +( + id int8, + tenant_id varchar(20) default '000000', + dept_id int8, + user_id int8, + order_num int4 default 0, + test_key varchar(255), + value varchar(255), + version int4 default 0, + create_dept int8, + create_time timestamp, + create_by int8, + update_time timestamp, + update_by int8, + del_flag int4 default 0 +); + +comment on table test_demo is '测试单表'; +comment on column test_demo.id is '主键'; +comment on column test_demo.tenant_id is '租户编号'; +comment on column test_demo.dept_id is '部门id'; +comment on column test_demo.user_id is '用户id'; +comment on column test_demo.order_num is '排序号'; +comment on column test_demo.test_key is 'key键'; +comment on column test_demo.value is '值'; +comment on column test_demo.version is '版本'; +comment on column test_demo.create_dept is '创建部门'; +comment on column test_demo.create_time is '创建时间'; +comment on column test_demo.create_by is '创建人'; +comment on column test_demo.update_time is '更新时间'; +comment on column test_demo.update_by is '更新人'; +comment on column test_demo.del_flag is '删除标志'; + +create table if not exists test_tree +( + id int8, + tenant_id varchar(20) default '000000', + parent_id int8 default 0, + dept_id int8, + user_id int8, + tree_name varchar(255), + version int4 default 0, + create_dept int8, + create_time timestamp, + create_by int8, + update_time timestamp, + update_by int8, + del_flag integer default 0 +); + +comment on table test_tree is '测试树表'; +comment on column test_tree.id is '主键'; +comment on column test_tree.tenant_id is '租户编号'; +comment on column test_tree.parent_id is '父id'; +comment on column test_tree.dept_id is '部门id'; +comment on column test_tree.user_id is '用户id'; +comment on column test_tree.tree_name is '值'; +comment on column test_tree.version is '版本'; +comment on column test_tree.create_dept is '创建部门'; +comment on column test_tree.create_time is '创建时间'; +comment on column test_tree.create_by is '创建人'; +comment on column test_tree.update_time is '更新时间'; +comment on column test_tree.update_by is '更新人'; +comment on column test_tree.del_flag is '删除标志'; + +INSERT INTO test_demo VALUES (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, now(), 1, NULL, NULL, 0); + +INSERT INTO test_tree VALUES (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (2, '000000', 1, 102, 3, '子节点1', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (3, '000000', 2, 102, 3, '子节点2', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (4, '000000', 0, 108, 4, '测试树1', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (5, '000000', 4, 108, 3, '子节点11', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (6, '000000', 4, 108, 3, '子节点22', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (7, '000000', 4, 108, 3, '子节点33', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (8, '000000', 5, 108, 3, '子节点44', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (9, '000000', 6, 108, 3, '子节点55', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (10, '000000', 7, 108, 3, '子节点66', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (11, '000000', 7, 108, 3, '子节点77', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (12, '000000', 10, 108, 3, '子节点88', 0, 103, now(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (13, '000000', 10, 108, 3, '子节点99', 0, 103, now(), 1, NULL, NULL, 0); + +-- 字符串自动转时间 避免框架时间查询报错问题 +create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$ +select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss'); +$$ language sql strict ; + +create cast (varchar as timestamptz) with function cast_varchar_to_timestamp as IMPLICIT; diff --git a/script/sql/postgres/postgres_ry_workflow.sql b/script/sql/postgres/postgres_ry_workflow.sql new file mode 100644 index 0000000..8db29c7 --- /dev/null +++ b/script/sql/postgres/postgres_ry_workflow.sql @@ -0,0 +1,412 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/postgresql/postgresql-warm-flow-all.sql +-- ---------------------------- +CREATE TABLE flow_definition +( + id int8 NOT NULL, -- 主键id + flow_code varchar(40) NOT NULL, -- 流程编码 + flow_name varchar(100) NOT NULL, -- 流程名称 + category varchar(100) NULL, -- 流程类别 + "version" varchar(20) NOT NULL, -- 流程版本 + is_publish int2 NOT NULL DEFAULT 0, -- 是否发布(0未发布 1已发布 9失效) + form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) + form_path varchar(100) NULL, -- 审批表单路径 + activity_status int2 NOT NULL DEFAULT 1, -- 流程激活状态(0挂起 1激活) + listener_type varchar(100) NULL, -- 监听器类型 + listener_path varchar(400) NULL, -- 监听器路径 + ext varchar(500) NULL, -- 扩展字段,预留给业务系统使用 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_definition_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_definition IS '流程定义表'; + +COMMENT ON COLUMN flow_definition.id IS '主键id'; +COMMENT ON COLUMN flow_definition.flow_code IS '流程编码'; +COMMENT ON COLUMN flow_definition.flow_name IS '流程名称'; +COMMENT ON COLUMN flow_definition.category IS '流程类别'; +COMMENT ON COLUMN flow_definition."version" IS '流程版本'; +COMMENT ON COLUMN flow_definition.is_publish IS '是否发布(0未发布 1已发布 9失效)'; +COMMENT ON COLUMN flow_definition.form_custom IS '审批表单是否自定义(Y是 N否)'; +COMMENT ON COLUMN flow_definition.form_path IS '审批表单路径'; +COMMENT ON COLUMN flow_definition.activity_status IS '流程激活状态(0挂起 1激活)'; +COMMENT ON COLUMN flow_definition.listener_type IS '监听器类型'; +COMMENT ON COLUMN flow_definition.listener_path IS '监听器路径'; +COMMENT ON COLUMN flow_definition.ext IS '扩展字段,预留给业务系统使用'; +COMMENT ON COLUMN flow_definition.create_time IS '创建时间'; +COMMENT ON COLUMN flow_definition.update_time IS '更新时间'; +COMMENT ON COLUMN flow_definition.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_definition.tenant_id IS '租户id'; + +CREATE TABLE flow_node +( + id int8 NOT NULL, -- 主键id + node_type int2 NOT NULL, -- 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + definition_id int8 NOT NULL, -- 流程定义id + node_code varchar(100) NOT NULL, -- 流程节点编码 + node_name varchar(100) NULL, -- 流程节点名称 + permission_flag varchar(200) NULL, -- 权限标识(权限类型:权限标识,可以多个,用逗号隔开) + node_ratio numeric(6, 3) NULL, -- 流程签署比例值 + coordinate varchar(100) NULL, -- 坐标 + any_node_skip varchar(100) NULL, -- 任意结点跳转 + listener_type varchar(100) NULL, -- 监听器类型 + listener_path varchar(400) NULL, -- 监听器路径 + handler_type varchar(100) NULL, -- 处理器类型 + handler_path varchar(400) NULL, -- 处理器路径 + form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) + form_path varchar(100) NULL, -- 审批表单路径 + "version" varchar(20) NOT NULL, -- 版本 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + ext varchar(500) NULL, -- 扩展属性 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_node_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_node IS '流程节点表'; + +COMMENT ON COLUMN flow_node.id IS '主键id'; +COMMENT ON COLUMN flow_node.node_type IS '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_node.definition_id IS '流程定义id'; +COMMENT ON COLUMN flow_node.node_code IS '流程节点编码'; +COMMENT ON COLUMN flow_node.node_name IS '流程节点名称'; +COMMENT ON COLUMN flow_node.permission_flag IS '权限标识(权限类型:权限标识,可以多个,用逗号隔开)'; +COMMENT ON COLUMN flow_node.node_ratio IS '流程签署比例值'; +COMMENT ON COLUMN flow_node.coordinate IS '坐标'; +COMMENT ON COLUMN flow_node.any_node_skip IS '任意结点跳转'; +COMMENT ON COLUMN flow_node.listener_type IS '监听器类型'; +COMMENT ON COLUMN flow_node.listener_path IS '监听器路径'; +COMMENT ON COLUMN flow_node.handler_type IS '处理器类型'; +COMMENT ON COLUMN flow_node.handler_path IS '处理器路径'; +COMMENT ON COLUMN flow_node.form_custom IS '审批表单是否自定义(Y是 N否)'; +COMMENT ON COLUMN flow_node.form_path IS '审批表单路径'; +COMMENT ON COLUMN flow_node."version" IS '版本'; +COMMENT ON COLUMN flow_node.create_time IS '创建时间'; +COMMENT ON COLUMN flow_node.update_time IS '更新时间'; +COMMENT ON COLUMN flow_node.ext IS '扩展属性'; +COMMENT ON COLUMN flow_node.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_node.tenant_id IS '租户id'; + + +CREATE TABLE flow_skip +( + id int8 NOT NULL, -- 主键id + definition_id int8 NOT NULL, -- 流程定义id + now_node_code varchar(100) NOT NULL, -- 当前流程节点的编码 + now_node_type int2 NULL, -- 当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + next_node_code varchar(100) NOT NULL, -- 下一个流程节点的编码 + next_node_type int2 NULL, -- 下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + skip_name varchar(100) NULL, -- 跳转名称 + skip_type varchar(40) NULL, -- 跳转类型(PASS审批通过 REJECT退回) + skip_condition varchar(200) NULL, -- 跳转条件 + coordinate varchar(100) NULL, -- 坐标 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_skip_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_skip IS '节点跳转关联表'; + +COMMENT ON COLUMN flow_skip.id IS '主键id'; +COMMENT ON COLUMN flow_skip.definition_id IS '流程定义id'; +COMMENT ON COLUMN flow_skip.now_node_code IS '当前流程节点的编码'; +COMMENT ON COLUMN flow_skip.now_node_type IS '当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_skip.next_node_code IS '下一个流程节点的编码'; +COMMENT ON COLUMN flow_skip.next_node_type IS '下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_skip.skip_name IS '跳转名称'; +COMMENT ON COLUMN flow_skip.skip_type IS '跳转类型(PASS审批通过 REJECT退回)'; +COMMENT ON COLUMN flow_skip.skip_condition IS '跳转条件'; +COMMENT ON COLUMN flow_skip.coordinate IS '坐标'; +COMMENT ON COLUMN flow_skip.create_time IS '创建时间'; +COMMENT ON COLUMN flow_skip.update_time IS '更新时间'; +COMMENT ON COLUMN flow_skip.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_skip.tenant_id IS '租户id'; + +CREATE TABLE flow_instance +( + id int8 NOT NULL, -- 主键id + definition_id int8 NOT NULL, -- 对应flow_definition表的id + business_id varchar(40) NOT NULL, -- 业务id + node_type int2 NOT NULL, -- 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + node_code varchar(40) NOT NULL, -- 流程节点编码 + node_name varchar(100) NULL, -- 流程节点名称 + variable text NULL, -- 任务变量 + flow_status varchar(20) NOT NULL, -- 流程状态(0待提交 1审批中 2 审批通过 8已完成 9已退回 10失效) + activity_status int2 NOT NULL DEFAULT 1, -- 流程激活状态(0挂起 1激活) + def_json text NULL, -- 流程定义json + create_by varchar(64) NULL DEFAULT '':: character varying, -- 创建者 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + ext varchar(500) NULL, -- 扩展字段,预留给业务系统使用 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_instance_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_instance IS '流程实例表'; + +COMMENT ON COLUMN flow_instance.id IS '主键id'; +COMMENT ON COLUMN flow_instance.definition_id IS '对应flow_definition表的id'; +COMMENT ON COLUMN flow_instance.business_id IS '业务id'; +COMMENT ON COLUMN flow_instance.node_type IS '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_instance.node_code IS '流程节点编码'; +COMMENT ON COLUMN flow_instance.node_name IS '流程节点名称'; +COMMENT ON COLUMN flow_instance.variable IS '任务变量'; +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +COMMENT ON COLUMN flow_instance.activity_status IS '流程激活状态(0挂起 1激活)'; +COMMENT ON COLUMN flow_instance.def_json IS '流程定义json'; +COMMENT ON COLUMN flow_instance.create_by IS '创建者'; +COMMENT ON COLUMN flow_instance.create_time IS '创建时间'; +COMMENT ON COLUMN flow_instance.update_time IS '更新时间'; +COMMENT ON COLUMN flow_instance.ext IS '扩展字段,预留给业务系统使用'; +COMMENT ON COLUMN flow_instance.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_instance.tenant_id IS '租户id'; + +CREATE TABLE flow_task +( + id int8 NOT NULL, -- 主键id + definition_id int8 NOT NULL, -- 对应flow_definition表的id + instance_id int8 NOT NULL, -- 对应flow_instance表的id + node_code varchar(100) NOT NULL, -- 节点编码 + node_name varchar(100) NULL, -- 节点名称 + node_type int2 NOT NULL, -- 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + flow_status varchar(20) NOT NULL, -- 流程状态(0待提交 1审批中 2 审批通过 8已完成 9已退回 10失效) + form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) + form_path varchar(100) NULL, -- 审批表单路径 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_task_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_task IS '待办任务表'; + +COMMENT ON COLUMN flow_task.id IS '主键id'; +COMMENT ON COLUMN flow_task.definition_id IS '对应flow_definition表的id'; +COMMENT ON COLUMN flow_task.instance_id IS '对应flow_instance表的id'; +COMMENT ON COLUMN flow_task.node_code IS '节点编码'; +COMMENT ON COLUMN flow_task.node_name IS '节点名称'; +COMMENT ON COLUMN flow_task.node_type IS '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +COMMENT ON COLUMN flow_task.form_custom IS '审批表单是否自定义(Y是 N否)'; +COMMENT ON COLUMN flow_task.form_path IS '审批表单路径'; +COMMENT ON COLUMN flow_task.create_time IS '创建时间'; +COMMENT ON COLUMN flow_task.update_time IS '更新时间'; +COMMENT ON COLUMN flow_task.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_task.tenant_id IS '租户id'; + +CREATE TABLE flow_his_task +( + id int8 NOT NULL, -- 主键id + definition_id int8 NOT NULL, -- 对应flow_definition表的id + instance_id int8 NOT NULL, -- 对应flow_instance表的id + task_id int8 NOT NULL, -- 对应flow_task表的id + node_code varchar(100) NULL, -- 开始节点编码 + node_name varchar(100) NULL, -- 开始节点名称 + node_type int2 NULL, -- 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + target_node_code varchar(200) NULL, -- 目标节点编码 + target_node_name varchar(200) NULL, -- 结束节点名称 + approver varchar(40) NULL, -- 审批者 + cooperate_type int2 NOT NULL DEFAULT 0, -- 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签) + collaborator varchar(40) NULL, -- 协作人(只有转办、会签、票签、委派) + skip_type varchar(10) NULL, -- 流转类型(PASS通过 REJECT退回 NONE无动作) + flow_status varchar(20) NOT NULL, -- 流程状态(0待提交 1审批中 2 审批通过 8已完成 9已退回 10失效) + form_custom bpchar(1) NULL DEFAULT 'N':: character varying, -- 审批表单是否自定义(Y是 N否) + form_path varchar(100) NULL, -- 审批表单路径 + ext varchar(500) NULL, -- 扩展字段,预留给业务系统使用 + message varchar(500) NULL, -- 审批意见 + variable text NULL, -- 任务变量 + create_time timestamp NULL, -- 创建时间 + update_time timestamp NULL, -- 更新时间 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_his_task_pkey PRIMARY KEY (id) +); +COMMENT ON TABLE flow_his_task IS '历史任务记录表'; + +COMMENT ON COLUMN flow_his_task.id IS '主键id'; +COMMENT ON COLUMN flow_his_task.definition_id IS '对应flow_definition表的id'; +COMMENT ON COLUMN flow_his_task.instance_id IS '对应flow_instance表的id'; +COMMENT ON COLUMN flow_his_task.task_id IS '对应flow_task表的id'; +COMMENT ON COLUMN flow_his_task.node_code IS '开始节点编码'; +COMMENT ON COLUMN flow_his_task.node_name IS '开始节点名称'; +COMMENT ON COLUMN flow_his_task.node_type IS '开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)'; +COMMENT ON COLUMN flow_his_task.target_node_code IS '目标节点编码'; +COMMENT ON COLUMN flow_his_task.target_node_name IS '结束节点名称'; +COMMENT ON COLUMN flow_his_task.approver IS '审批者'; +COMMENT ON COLUMN flow_his_task.cooperate_type IS '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)'; +COMMENT ON COLUMN flow_his_task.collaborator IS '协作人'; +COMMENT ON COLUMN flow_his_task.skip_type IS '流转类型(PASS通过 REJECT退回 NONE无动作)'; +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; +COMMENT ON COLUMN flow_his_task.form_custom IS '审批表单是否自定义(Y是 N否)'; +COMMENT ON COLUMN flow_his_task.form_path IS '审批表单路径'; +COMMENT ON COLUMN flow_his_task.message IS '审批意见'; +COMMENT ON COLUMN flow_his_task.variable IS '任务变量'; +COMMENT ON COLUMN flow_his_task.ext IS '扩展字段,预留给业务系统使用'; +COMMENT ON COLUMN flow_his_task.create_time IS '任务开始时间'; +COMMENT ON COLUMN flow_his_task.update_time IS '审批完成时间'; +COMMENT ON COLUMN flow_his_task.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_his_task.tenant_id IS '租户id'; + +CREATE TABLE flow_user +( + id int8 NOT NULL, -- 主键id + "type" bpchar(1) NOT NULL, -- 人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3流程实例的抄送人权限 4待办任务的委托人权限) + processed_by varchar(80) NULL, -- 权限人 + associated int8 NOT NULL, -- 任务表id + create_time timestamp NULL, -- 创建时间 + create_by varchar(80) NULL, -- 创建人 + update_time timestamp NULL, -- 更新时间 + del_flag bpchar(1) NULL DEFAULT '0':: character varying, -- 删除标志 + tenant_id varchar(40) NULL, -- 租户id + CONSTRAINT flow_user_pk PRIMARY KEY (id) +); +CREATE INDEX user_processed_type ON flow_user USING btree (processed_by, type); +CREATE INDEX user_associated_idx ON FLOW_USER USING btree (associated); + +COMMENT ON TABLE flow_user IS '流程用户表'; + +COMMENT ON COLUMN flow_user.id IS '主键id'; +COMMENT ON COLUMN flow_user."type" IS '人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)'; +COMMENT ON COLUMN flow_user.processed_by IS '权限人'; +COMMENT ON COLUMN flow_user.associated IS '任务表id'; +COMMENT ON COLUMN flow_user.create_time IS '创建时间'; +COMMENT ON COLUMN flow_user.create_by IS '创建人'; +COMMENT ON COLUMN flow_user.update_time IS '更新时间'; +COMMENT ON COLUMN flow_user.del_flag IS '删除标志'; +COMMENT ON COLUMN flow_user.tenant_id IS '租户id'; + +-- ---------------------------- +-- 流程分类表 +-- ---------------------------- +CREATE TABLE flow_category +( + category_id int8 NOT NULL, + tenant_id VARCHAR(20) DEFAULT '000000'::varchar, + parent_id int8 DEFAULT 0, + ancestors VARCHAR(500) DEFAULT ''::varchar, + category_name VARCHAR(30) NOT NULL, + order_num INT DEFAULT 0, + del_flag CHAR DEFAULT '0'::bpchar, + create_dept int8, + create_by int8, + create_time TIMESTAMP, + update_by int8, + update_time TIMESTAMP, + PRIMARY KEY (category_id) +); + +COMMENT ON TABLE flow_category IS '流程分类'; +COMMENT ON COLUMN flow_category.category_id IS '流程分类ID'; +COMMENT ON COLUMN flow_category.tenant_id IS '租户编号'; +COMMENT ON COLUMN flow_category.parent_id IS '父流程分类id'; +COMMENT ON COLUMN flow_category.ancestors IS '祖级列表'; +COMMENT ON COLUMN flow_category.category_name IS '流程分类名称'; +COMMENT ON COLUMN flow_category.order_num IS '显示顺序'; +COMMENT ON COLUMN flow_category.del_flag IS '删除标志(0代表存在 1代表删除)'; +COMMENT ON COLUMN flow_category.create_dept IS '创建部门'; +COMMENT ON COLUMN flow_category.create_by IS '创建者'; +COMMENT ON COLUMN flow_category.create_time IS '创建时间'; +COMMENT ON COLUMN flow_category.update_by IS '更新者'; +COMMENT ON COLUMN flow_category.update_time IS '更新时间'; + +INSERT INTO flow_category VALUES (100, '000000', 0, '0', 'OA审批', 0, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (101, '000000', 100, '0,100', '假勤管理', 0, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (102, '000000', 100, '0,100', '人事管理', 1, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (103, '000000', 101, '0,100,101', '请假', 0, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (104, '000000', 101, '0,100,101', '出差', 1, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (105, '000000', 101, '0,100,101', '加班', 2, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (106, '000000', 101, '0,100,101', '换班', 3, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (107, '000000', 101, '0,100,101', '外出', 4, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (108, '000000', 102, '0,100,102', '转正', 1, '0', 103, 1, now(), NULL, NULL); +INSERT INTO flow_category VALUES (109, '000000', 102, '0,100,102', '离职', 2, '0', 103, 1, now(), NULL, NULL); + +-- ---------------------------- +-- 请假单信息 +-- ---------------------------- +CREATE TABLE test_leave +( + id int8 NOT NULL, + tenant_id VARCHAR(20) DEFAULT '000000'::varchar, + leave_type VARCHAR(255) NOT NULL, + start_date TIMESTAMP NOT NULL, + end_date TIMESTAMP NOT NULL, + leave_days int2 NOT NULL, + remark VARCHAR(255), + status VARCHAR(255), + create_dept int8, + create_by int8, + create_time TIMESTAMP, + update_by int8, + update_time TIMESTAMP, + PRIMARY KEY (id) +); + +COMMENT ON TABLE test_leave IS '请假申请表'; +COMMENT ON COLUMN test_leave.id IS 'id'; +COMMENT ON COLUMN test_leave.tenant_id IS '租户编号'; +COMMENT ON COLUMN test_leave.leave_type IS '请假类型'; +COMMENT ON COLUMN test_leave.start_date IS '开始时间'; +COMMENT ON COLUMN test_leave.end_date IS '结束时间'; +COMMENT ON COLUMN test_leave.leave_days IS '请假天数'; +COMMENT ON COLUMN test_leave.remark IS '请假原因'; +COMMENT ON COLUMN test_leave.status IS '状态'; +COMMENT ON COLUMN test_leave.create_dept IS '创建部门'; +COMMENT ON COLUMN test_leave.create_by IS '创建者'; +COMMENT ON COLUMN test_leave.create_time IS '创建时间'; +COMMENT ON COLUMN test_leave.update_by IS '更新者'; +COMMENT ON COLUMN test_leave.update_time IS '更新时间'; + +INSERT INTO sys_menu VALUES ('11616', '工作流', '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_menu VALUES ('11623', '流程分类查询', '11622', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11624', '流程分类新增', '11622', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11625', '流程分类修改', '11622', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11626', '流程分类删除', '11622', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11627', '流程分类导出', '11622', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:category:export', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11638', '请假申请', '5', '1', 'leave', 'workflow/leave/index', '', '1', '0', 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单'); +INSERT INTO sys_menu VALUES ('11639', '请假申请查询', '11638', '1', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11640', '请假申请新增', '11638', '2', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11641', '请假申请修改', '11638', '3', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11642', '请假申请删除', '11638', '4', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11643', '请假申请导出', '11638', '5', '#', '', '', '1', '0', 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_dict_type VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, now(), NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, now(), NULL, NULL, '表单类型列表'); +INSERT INTO sys_dict_type VALUES (15, '000000', '任务状态', 'wf_task_status', 103, 1, now(), NULL, NULL, '任务状态'); +INSERT INTO sys_dict_data VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已撤销'); +INSERT INTO sys_dict_data VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, now(), NULL, NULL, '草稿'); +INSERT INTO sys_dict_data VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '待审核'); +INSERT INTO sys_dict_data VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, now(), NULL, NULL, '已完成'); +INSERT INTO sys_dict_data VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已作废'); +INSERT INTO sys_dict_data VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已退回'); +INSERT INTO sys_dict_data VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已终止'); +INSERT INTO sys_dict_data VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, now(), NULL, NULL, '自定义表单'); +INSERT INTO sys_dict_data VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '动态表单'); +INSERT INTO sys_dict_data VALUES (48, '000000', 1, '撤销', 'cancel', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '撤销'); +INSERT INTO sys_dict_data VALUES (49, '000000', 2, '通过', 'pass', 'wf_task_status', '', 'success', 'N', 103, 1, now(), NULL, NULL, '通过'); +INSERT INTO sys_dict_data VALUES (50, '000000', 3, '待审核', 'waiting', 'wf_task_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '待审核'); +INSERT INTO sys_dict_data VALUES (51, '000000', 4, '作废', 'invalid', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '作废'); +INSERT INTO sys_dict_data VALUES (52, '000000', 5, '退回', 'back', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '退回'); +INSERT INTO sys_dict_data VALUES (53, '000000', 6, '终止', 'termination', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '终止'); +INSERT INTO sys_dict_data VALUES (54, '000000', 7, '转办', 'transfer', 'wf_task_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '转办'); +INSERT INTO sys_dict_data VALUES (55, '000000', 8, '委托', 'depute', 'wf_task_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '委托'); +INSERT INTO sys_dict_data VALUES (56, '000000', 9, '抄送', 'copy', 'wf_task_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '抄送'); +INSERT INTO sys_dict_data VALUES (57, '000000', 10, '加签', 'sign', 'wf_task_status', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '加签'); +INSERT INTO sys_dict_data VALUES (58, '000000', 11, '减签', 'sign_off', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '减签'); +INSERT INTO sys_dict_data VALUES (59, '000000', 11, '超时', 'timeout', 'wf_task_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '超时'); + diff --git a/script/sql/ry_job.sql b/script/sql/ry_job.sql new file mode 100644 index 0000000..687bfa0 --- /dev/null +++ b/script/sql/ry_job.sql @@ -0,0 +1,523 @@ +SET NAMES utf8mb4; + +CREATE TABLE `sj_namespace` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(64) NOT NULL COMMENT '名称', + `unique_id` varchar(64) NOT NULL COMMENT '唯一id', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_name` (`name`), + UNIQUE KEY `uk_unique_id` (`unique_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='命名空间'; + +INSERT INTO `sj_namespace` VALUES (1, 'Development', 'dev', '', 0, now(), now()); +INSERT INTO `sj_namespace` VALUES (2, 'Production', 'prod', '', 0, now(), now()); + +CREATE TABLE `sj_group_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL DEFAULT '' COMMENT '组名称', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '组描述', + `token` varchar(64) NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT' COMMENT 'token', + `group_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '组状态 0、未启用 1、启用', + `version` int(11) NOT NULL COMMENT '版本号', + `group_partition` int(11) NOT NULL COMMENT '分区', + `id_generator_mode` tinyint(4) NOT NULL DEFAULT 1 COMMENT '唯一id生成模式 默认号段模式', + `init_scene` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否初始化场景 0:否 1:是', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='组配置'; + +INSERT INTO `sj_group_config` VALUES (1, 'dev', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now()); +INSERT INTO `sj_group_config` VALUES (2, 'prod', 'fys_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, now(), now()); + +CREATE TABLE `sj_notify_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `notify_name` varchar(64) NOT NULL DEFAULT '' COMMENT '通知名称', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务', + `notify_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知状态 0、未启用 1、启用', + `recipient_ids` varchar(128) NOT NULL COMMENT '接收人id列表', + `notify_threshold` int(11) NOT NULL DEFAULT 0 COMMENT '通知阈值', + `notify_scene` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知场景', + `rate_limiter_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '限流状态 0、未启用 1、启用', + `rate_limiter_threshold` int(11) NOT NULL DEFAULT 0 COMMENT '每秒限流阈值', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='通知配置'; + +CREATE TABLE `sj_notify_recipient` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `recipient_name` varchar(64) NOT NULL COMMENT '接收人名称', + `notify_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook', + `notify_attribute` varchar(512) NOT NULL COMMENT '配置属性', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id` (`namespace_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='告警通知接收人'; + +CREATE TABLE `sj_retry_dead_letter` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', + `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', + `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', + `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', + `args_str` text NOT NULL COMMENT '执行方法参数', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_biz_no` (`biz_no`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='死信队列表'; + +CREATE TABLE `sj_retry` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `group_id` bigint(20) NOT NULL COMMENT '组Id', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `scene_id` bigint(20) NOT NULL COMMENT '场景ID', + `idempotent_id` varchar(64) NOT NULL COMMENT '幂等id', + `biz_no` varchar(64) NOT NULL DEFAULT '' COMMENT '业务编号', + `executor_name` varchar(512) NOT NULL DEFAULT '' COMMENT '执行器名称', + `args_str` text NOT NULL COMMENT '执行方法参数', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `next_trigger_at` bigint(13) NOT NULL COMMENT '下次触发时间', + `retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '重试次数', + `retry_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '重试状态 0、重试中 1、成功 2、最大重试次数', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、重试数据 2、回调数据', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父节点id', + `deleted` bigint(20) NOT NULL DEFAULT 0 COMMENT '逻辑删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_biz_no` (`biz_no`), + KEY `idx_idempotent_id` (`idempotent_id`), + KEY `idx_retry_status_bucket_index` (`retry_status`, `bucket_index`), + KEY `idx_parent_id` (`parent_id`), + KEY `idx_create_dt` (`create_dt`), + UNIQUE KEY `uk_scene_tasktype_idempotentid_deleted` (`scene_id`, `task_type`, `idempotent_id`, `deleted`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='重试信息表'; + +CREATE TABLE `sj_retry_task` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `retry_id` bigint(20) NOT NULL COMMENT '重试信息Id', + `ext_attrs` text NOT NULL COMMENT '扩展字段', + `task_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '重试状态', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、重试数据 2、回调数据', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `client_info` varchar(128) DEFAULT NULL COMMENT '客户端地址 clientId#ip:port', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`), + KEY `task_status` (`task_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_retry_id` (`retry_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='重试任务表'; + +CREATE TABLE `sj_retry_task_log_message` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `retry_id` bigint(20) NOT NULL COMMENT '重试信息Id', + `retry_task_id` bigint(20) NOT NULL COMMENT '重试任务Id', + `message` longtext NOT NULL COMMENT '异常信息', + `log_num` int(11) NOT NULL DEFAULT 1 COMMENT '日志数量', + `real_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '上报时间', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_retry_task_id` (`namespace_id`, `group_name`, `retry_task_id`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务调度日志信息记录表'; + +CREATE TABLE `sj_retry_scene_config` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `scene_name` varchar(64) NOT NULL COMMENT '场景名称', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `scene_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '组状态 0、未启用 1、启用', + `max_retry_count` int(11) NOT NULL DEFAULT 5 COMMENT '最大重试次数', + `back_off` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、默认等级 2、固定间隔时间 3、CRON 表达式', + `trigger_interval` varchar(16) NOT NULL DEFAULT '' COMMENT '间隔时长', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `deadline_request` bigint(20) unsigned NOT NULL DEFAULT 60000 COMMENT 'Deadline Request 调用链超时 单位毫秒', + `executor_timeout` int(11) unsigned NOT NULL DEFAULT 5 COMMENT '任务执行超时时间,单位秒', + `route_key` tinyint(4) NOT NULL DEFAULT 4 COMMENT '路由策略', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行', + `cb_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '回调状态 0、不开启 1、开启', + `cb_trigger_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、默认等级 2、固定间隔时间 3、CRON 表达式', + `cb_max_count` int(11) NOT NULL DEFAULT 16 COMMENT '回调的最大执行次数', + `cb_trigger_interval` varchar(16) NOT NULL DEFAULT '' COMMENT '回调的最大执行次数', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='场景配置'; + +CREATE TABLE `sj_server_node` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `host_id` varchar(64) NOT NULL COMMENT '主机id', + `host_ip` varchar(64) NOT NULL COMMENT '机器ip', + `host_port` int(16) NOT NULL COMMENT '机器端口', + `expire_at` datetime NOT NULL COMMENT '过期时间', + `node_type` tinyint(4) NOT NULL COMMENT '节点类型 1、客户端 2、是服务端', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_expire_at_node_type` (`expire_at`, `node_type`), + UNIQUE KEY `uk_host_id_host_ip` (`host_id`, `host_ip`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='服务器节点'; + +CREATE TABLE `sj_distributed_lock` +( + `name` varchar(64) NOT NULL COMMENT '锁名称', + `lock_until` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '锁定时长', + `locked_at` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '锁定时间', + `locked_by` varchar(255) NOT NULL COMMENT '锁定者', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='锁定表'; + +CREATE TABLE `sj_system_user` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `username` varchar(64) NOT NULL COMMENT '账号', + `password` varchar(128) NOT NULL COMMENT '密码', + `role` tinyint(4) NOT NULL DEFAULT 0 COMMENT '角色:1-普通用户、2-管理员', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_username` (`username`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='系统用户表'; + +-- pwd: admin +INSERT INTO `sj_system_user` (username, password, role) +VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2); + +CREATE TABLE `sj_system_user_permission` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `system_user_id` bigint(20) NOT NULL COMMENT '系统用户id', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name_system_user_id` (`namespace_id`, `group_name`, `system_user_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='系统用户权限表'; + +CREATE TABLE `sj_sequence_alloc` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL DEFAULT '' COMMENT '组名称', + `max_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '最大id', + `step` int(11) NOT NULL DEFAULT 100 COMMENT '步长', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='号段模式序号ID分配表'; + +-- 分布式调度DDL +CREATE TABLE `sj_job` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_name` varchar(64) NOT NULL COMMENT '名称', + `args_str` text DEFAULT NULL COMMENT '执行方法参数', + `args_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '参数类型 ', + `next_trigger_at` bigint(13) NOT NULL COMMENT '下次触发时间', + `job_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务状态 0、关闭、1、开启', + `task_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '任务类型 1、集群 2、广播 3、切片', + `route_key` tinyint(4) NOT NULL DEFAULT 4 COMMENT '路由策略', + `executor_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '执行器类型', + `executor_info` varchar(255) DEFAULT NULL COMMENT '执行器名称', + `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', + `trigger_interval` varchar(255) NOT NULL COMMENT '间隔时长', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行 4、恢复', + `executor_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间,单位秒', + `max_retry_times` int(11) NOT NULL DEFAULT 0 COMMENT '最大重试次数', + `parallel_num` int(11) NOT NULL DEFAULT 1 COMMENT '并行数', + `retry_interval` int(11) NOT NULL DEFAULT 0 COMMENT '重试间隔(s)', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `resident` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否是常驻任务', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `owner_id` bigint(20) NULL COMMENT '负责人id', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`), + KEY `idx_create_dt` (`create_dt`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息'; + +INSERT INTO `sj_job` VALUES (1, 'dev', 'fys_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', 0 , now(), now()); + +CREATE TABLE `sj_job_log_message` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `task_batch_id` bigint(20) NOT NULL COMMENT '任务批次id', + `task_id` bigint(20) NOT NULL COMMENT '调度任务id', + `message` longtext NOT NULL COMMENT '调度信息', + `log_num` int(11) NOT NULL DEFAULT 1 COMMENT '日志数量', + `real_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '上报时间', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_task_batch_id_task_id` (`task_batch_id`, `task_id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='调度日志'; + +CREATE TABLE `sj_job_task` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `task_batch_id` bigint(20) NOT NULL COMMENT '调度任务id', + `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父执行器id', + `task_status` tinyint NOT NULL DEFAULT 0 COMMENT '执行的状态 0、失败 1、成功', + `retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '重试次数', + `mr_stage` tinyint DEFAULT NULL COMMENT '动态分片所处阶段 1:map 2:reduce 3:mergeReduce', + `leaf` tinyint NOT NULL DEFAULT '1' COMMENT '叶子节点', + `task_name` varchar(255) NOT NULL DEFAULT '' COMMENT '任务名称', + `client_info` varchar(128) DEFAULT NULL COMMENT '客户端地址 clientId#ip:port', + `wf_context` text DEFAULT NULL COMMENT '工作流全局上下文', + `result_message` text NOT NULL COMMENT '执行结果', + `args_str` text DEFAULT NULL COMMENT '执行方法参数', + `args_type` tinyint NOT NULL DEFAULT 1 COMMENT '参数类型 ', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_task_batch_id_task_status` (`task_batch_id`, `task_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务实例'; + +CREATE TABLE `sj_job_task_batch` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务id', + `workflow_node_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流节点id', + `parent_workflow_node_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流任务父批次id', + `workflow_task_batch_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '工作流任务批次id', + `task_batch_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '任务批次状态 0、失败 1、成功', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `execution_at` bigint(13) NOT NULL DEFAULT 0 COMMENT '任务执行时间', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 3、JOB任务 4、WORKFLOW任务', + `parent_id` varchar(64) NOT NULL DEFAULT '' COMMENT '父节点', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_job_id_task_batch_status` (`job_id`, `task_batch_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + KEY `idx_workflow_task_batch_id_workflow_node_id` (`workflow_task_batch_id`, `workflow_node_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='任务批次'; + +CREATE TABLE `sj_job_summary` +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称', + `business_id` bigint NOT NULL COMMENT '业务id (job_id或workflow_id)', + `system_task_type` tinyint(4) NOT NULL DEFAULT 3 COMMENT '任务类型 3、JOB任务 4、WORKFLOW任务', + `trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间', + `success_num` int NOT NULL DEFAULT 0 COMMENT '执行成功-日志数量', + `fail_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `fail_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `stop_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `stop_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `cancel_num` int NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量', + `cancel_reason` varchar(512) NOT NULL DEFAULT '' COMMENT '失败原因', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_namespace_id_group_name_business_id` (`namespace_id`, `group_name`, business_id), + UNIQUE KEY `uk_trigger_at_system_task_type_business_id` (`trigger_at`, `system_task_type`, `business_id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Job'; + +CREATE TABLE `sj_retry_summary` +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称', + `scene_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '场景名称', + `trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间', + `running_num` int NOT NULL DEFAULT 0 COMMENT '重试中-日志数量', + `finish_num` int NOT NULL DEFAULT 0 COMMENT '重试完成-日志数量', + `max_count_num` int NOT NULL DEFAULT 0 COMMENT '重试到达最大次数-日志数量', + `suspend_num` int NOT NULL DEFAULT 0 COMMENT '暂停重试-日志数量', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_trigger_at` (`trigger_at`), + UNIQUE KEY `uk_scene_name_trigger_at` (`namespace_id`, `group_name`, `scene_name`, `trigger_at`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Retry'; + +CREATE TABLE `sj_workflow` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `workflow_name` varchar(64) NOT NULL COMMENT '工作流名称', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启', + `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', + `trigger_interval` varchar(255) NOT NULL COMMENT '间隔时长', + `next_trigger_at` bigint NOT NULL COMMENT '下次触发时间', + `block_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '阻塞策略 1、丢弃 2、覆盖 3、并行', + `executor_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间,单位秒', + `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述', + `flow_info` text DEFAULT NULL COMMENT '流程信息', + `wf_context` text DEFAULT NULL COMMENT '上下文', + `notify_ids` varchar(128) NOT NULL DEFAULT '' COMMENT '通知告警场景配置id列表', + `bucket_index` int(11) NOT NULL DEFAULT 0 COMMENT 'bucket', + `version` int(11) NOT NULL COMMENT '版本号', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流'; + +CREATE TABLE `sj_workflow_node` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `node_name` varchar(64) NOT NULL COMMENT '节点名称', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `job_id` bigint(20) NOT NULL COMMENT '任务信息id', + `workflow_id` bigint(20) NOT NULL COMMENT '工作流ID', + `node_type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '1、任务节点 2、条件节点', + `expression_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '1、SpEl、2、Aviator 3、QL', + `fail_strategy` tinyint(4) NOT NULL DEFAULT 1 COMMENT '失败策略 1、跳过 2、阻塞', + `workflow_node_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流节点状态 0、关闭、1、开启', + `priority_level` int(11) NOT NULL DEFAULT 1 COMMENT '优先级', + `node_info` text DEFAULT NULL COMMENT '节点信息 ', + `version` int(11) NOT NULL COMMENT '版本号', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流节点'; + +CREATE TABLE `sj_workflow_task_batch` +( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `group_name` varchar(64) NOT NULL COMMENT '组名称', + `workflow_id` bigint(20) NOT NULL COMMENT '工作流任务id', + `task_batch_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '任务批次状态 0、失败 1、成功', + `operation_reason` tinyint(4) NOT NULL DEFAULT 0 COMMENT '操作原因', + `flow_info` text DEFAULT NULL COMMENT '流程信息', + `wf_context` text DEFAULT NULL COMMENT '全局上下文', + `execution_at` bigint(13) NOT NULL DEFAULT 0 COMMENT '任务执行时间', + `ext_attrs` varchar(256) NULL DEFAULT '' COMMENT '扩展字段', + `version` int(11) NOT NULL DEFAULT 1 COMMENT '版本号', + `deleted` tinyint(4) NOT NULL DEFAULT 0 COMMENT '逻辑删除 1、删除', + `create_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_job_id_task_batch_status` (`workflow_id`, `task_batch_status`), + KEY `idx_create_dt` (`create_dt`), + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) +) ENGINE = InnoDB + AUTO_INCREMENT = 0 + DEFAULT CHARSET = utf8mb4 COMMENT ='工作流批次'; diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql new file mode 100644 index 0000000..332cc1a --- /dev/null +++ b/script/sql/ry_vue_5.X.sql @@ -0,0 +1,976 @@ +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +create table sys_social +( + id bigint not null comment '主键', + user_id bigint not null comment '用户ID', + tenant_id varchar(20) default '000000' comment '租户id', + auth_id varchar(255) not null comment '平台+平台唯一id', + source varchar(255) not null comment '用户来源', + open_id varchar(255) default null comment '平台编号唯一id', + user_name varchar(30) not null comment '登录账号', + nick_name varchar(30) default '' comment '用户昵称', + email varchar(255) default '' comment '用户邮箱', + avatar varchar(500) default '' comment '头像地址', + access_token varchar(2000) not null comment '用户的授权令牌', + expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有', + refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有', + access_code varchar(2000) default null comment '平台的授权信息,部分平台可能没有', + union_id varchar(255) default null comment '用户的 unionid', + scope varchar(255) default null comment '授予的权限,部分平台可能没有', + token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有', + id_token varchar(2000) default null comment 'id token,部分平台可能没有', + mac_algorithm varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有', + mac_key varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有', + code varchar(255) default null comment '用户的授权code,部分平台可能没有', + oauth_token varchar(255) default null comment 'Twitter平台用户的附带属性,部分平台可能没有', + oauth_token_secret varchar(255) default null comment 'Twitter平台用户的附带属性,部分平台可能没有', + create_dept bigint(20) comment '创建部门', + create_by bigint(20) comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) comment '更新者', + update_time datetime comment '更新时间', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + PRIMARY KEY (id) +) engine=innodb comment = '社会化关系表'; + + +-- ---------------------------- +-- 租户表 +-- ---------------------------- +create table sys_tenant +( + id bigint(20) not null comment 'id', + tenant_id varchar(20) not null comment '租户编号', + contact_user_name varchar(20) comment '联系人', + contact_phone varchar(20) comment '联系电话', + company_name varchar(30) comment '企业名称', + license_number varchar(30) comment '统一社会信用代码', + address varchar(200) comment '地址', + intro varchar(200) comment '企业简介', + domain varchar(200) comment '域名', + remark varchar(200) comment '备注', + package_id bigint(20) comment '租户套餐编号', + expire_time datetime comment '过期时间', + account_count int default -1 comment '用户数量(-1不限制)', + status char(1) default '0' comment '租户状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) comment '创建部门', + create_by bigint(20) comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) comment '更新者', + update_time datetime comment '更新时间', + primary key (id) +) engine=innodb comment = '租户表'; + + +-- ---------------------------- +-- 初始化-租户表数据 +-- ---------------------------- + +insert into sys_tenant values(1, '000000', '管理组', '15888888888', 'XXX有限公司', null, null, '多租户通用后台管理管理系统', null, null, null, null, -1, '0', '0', 103, 1, sysdate(), null, null); + + +-- ---------------------------- +-- 租户套餐表 +-- ---------------------------- +create table sys_tenant_package ( + package_id bigint(20) not null comment '租户套餐id', + package_name varchar(20) comment '套餐名称', + menu_ids varchar(3000) comment '关联菜单id', + remark varchar(200) comment '备注', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + status char(1) default '0' comment '状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) comment '创建部门', + create_by bigint(20) comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) comment '更新者', + update_time datetime comment '更新时间', + primary key (package_id) +) engine=innodb comment = '租户套餐表'; + + +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +create table sys_dept ( + dept_id bigint(20) not null comment '部门id', + tenant_id varchar(20) default '000000' comment '租户编号', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(500) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + dept_category varchar(100) default null comment '部门类别编码', + order_num int(4) default 0 comment '显示顺序', + leader bigint(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- + + +insert into sys_dept values(100, '000000', 0, '0', 'XXX科技', null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(101, '000000', 100, '0,100', '深圳总公司', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(102, '000000', 100, '0,100', '长沙分公司', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(103, '000000', 101, '0,100,101', '研发部门', null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(104, '000000', 101, '0,100,101', '市场部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(105, '000000', 101, '0,100,101', '测试部门', null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(106, '000000', 101, '0,100,101', '财务部门', null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(107, '000000', 101, '0,100,101', '运维部门', null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(108, '000000', 102, '0,100,102', '市场部门', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); +insert into sys_dept values(109, '000000', 102, '0,100,102', '财务部门', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +create table sys_user ( + user_id bigint(20) not null comment '用户ID', + tenant_id varchar(20) default '000000' comment '租户编号', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(10) default 'sys_user' comment '用户类型(sys_user系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar bigint(20) comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '管理员'); +insert into sys_user values(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 3, sysdate(), null); +insert into sys_user values(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 4, sysdate(), null); + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +create table sys_post +( + post_id bigint(20) not null comment '岗位ID', + tenant_id varchar(20) default '000000' comment '租户编号', + dept_id bigint(20) not null comment '部门id', + post_code varchar(64) not null comment '岗位编码', + post_category varchar(100) default null comment '岗位类别编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, '000000', 103, 'ceo', null, '董事长', 1, '0', 103, 1, sysdate(), null, null, ''); +insert into sys_post values(2, '000000', 100, 'se', null, '项目经理', 2, '0', 103, 1, sysdate(), null, null, ''); +insert into sys_post values(3, '000000', 100, 'hr', null, '人力资源', 3, '0', 103, 1, sysdate(), null, null, ''); +insert into sys_post values(4, '000000', 100, 'user', null, '普通员工', 4, '0', 103, 1, sysdate(), null, null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +create table sys_role ( + role_id bigint(20) not null comment '角色ID', + tenant_id varchar(20) default '000000' comment '租户编号', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values(1, '000000', '超级管理员', 'superadmin', 1, 1, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '超级管理员'); +insert into sys_role values(3, '000000', '本部门及以下', 'test1', 3, 4, 1, 1, '0', '0', 103, 1, sysdate(), null, null, ''); +insert into sys_role values(4, '000000', '仅本人', 'test2', 4, 5, 1, 1, '0', '0', 103, 1, sysdate(), null, null, ''); + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +create table sys_menu ( + menu_id bigint(20) not null comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + query_param varchar(255) default null comment '路由参数', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '显示状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', 1, 0, 'M', '0', '0', '', 'system', 103, 1, sysdate(), null, null, '系统管理目录'); +insert into sys_menu values('6', '租户管理', '0', '2', 'tenant', null, '', 1, 0, 'M', '0', '0', '', 'chart', 103, 1, sysdate(), null, null, '租户管理目录'); +insert into sys_menu values('2', '系统监控', '0', '3', 'monitor', null, '', 1, 0, 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), null, null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '4', 'tool', null, '', 1, 0, 'M', '0', '0', '', 'tool', 103, 1, sysdate(), null, null, '系统工具目录'); +insert into sys_menu values('4', 'PLUS官网', '0', '5', 'https://gitee.com/dromara/fys-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide', 103, 1, sysdate(), null, null, 'fys-Vue-Plus官网地址'); +insert into sys_menu values('5', '测试菜单', '0', '5', 'demo', null, '', 1, 0, 'M', '0', '0', '', 'star', 103, 1, sysdate(), null, null, '测试菜单'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 103, 1, sysdate(), null, null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 103, 1, sysdate(), null, null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 103, 1, sysdate(), null, null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 103, 1, sysdate(), null, null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 103, 1, sysdate(), null, null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 103, 1, sysdate(), null, null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 103, 1, sysdate(), null, null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 103, 1, sysdate(), null, null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', 1, 0, 'M', '0', '0', '', 'log', 103, 1, sysdate(), null, null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 103, 1, sysdate(), null, null, '在线用户菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 103, 1, sysdate(), null, null, '缓存监控菜单'); +insert into sys_menu values('115', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 103, 1, sysdate(), null, null, '代码生成菜单'); +insert into sys_menu values('121', '租户管理', '6', '1', 'tenant', 'system/tenant/index', '', 1, 0, 'C', '0', '0', 'system:tenant:list', 'list', 103, 1, sysdate(), null, null, '租户管理菜单'); +insert into sys_menu values('122', '租户套餐管理', '6', '2', 'tenantPackage', 'system/tenantPackage/index', '', 1, 0, 'C', '0', '0', 'system:tenantPackage:list', 'form', 103, 1, sysdate(), null, null, '租户套餐管理菜单'); +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单'); +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); + +-- springboot-admin监控 +insert into sys_menu values('117', 'Admin监控', '2', '5', 'Admin', 'monitor/admin/index', '', 1, 0, 'C', '0', '0', 'monitor:admin:list', 'dashboard', 103, 1, sysdate(), null, null, 'Admin监控菜单'); +-- oss菜单 +insert into sys_menu values('118', '文件管理', '1', '10', 'oss', 'system/oss/index', '', 1, 0, 'C', '0', '0', 'system:oss:list', 'upload', 103, 1, sysdate(), null, null, '文件管理菜单'); +-- snail-job server控制台 +insert into sys_menu values('120', '任务调度中心', '2', '6', 'snailjob', 'monitor/snailjob/index', '', 1, 0, 'C', '0', '0', 'monitor:snailjob:list', 'job', 103, 1, sysdate(), null, null, 'SnailJob控制台菜单'); + +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 103, 1, sysdate(), null, null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 103, 1, sysdate(), null, null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1001', '用户查询', '100', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1002', '用户新增', '100', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1003', '用户修改', '100', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1004', '用户删除', '100', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1005', '用户导出', '100', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1006', '用户导入', '100', '6', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1007', '重置密码', '100', '7', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 103, 1, sysdate(), null, null, ''); +-- 角色管理按钮 +insert into sys_menu values('1008', '角色查询', '101', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1009', '角色新增', '101', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1010', '角色修改', '101', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1011', '角色删除', '101', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1012', '角色导出', '101', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 103, 1, sysdate(), null, null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1013', '菜单查询', '102', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1014', '菜单新增', '102', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1015', '菜单修改', '102', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1016', '菜单删除', '102', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 103, 1, sysdate(), null, null, ''); +-- 部门管理按钮 +insert into sys_menu values('1017', '部门查询', '103', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1018', '部门新增', '103', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1019', '部门修改', '103', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1020', '部门删除', '103', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 103, 1, sysdate(), null, null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1021', '岗位查询', '104', '1', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1022', '岗位新增', '104', '2', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1023', '岗位修改', '104', '3', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1024', '岗位删除', '104', '4', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1025', '岗位导出', '104', '5', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 103, 1, sysdate(), null, null, ''); +-- 字典管理按钮 +insert into sys_menu values('1026', '字典查询', '105', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1027', '字典新增', '105', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1028', '字典修改', '105', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1029', '字典删除', '105', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1030', '字典导出', '105', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 103, 1, sysdate(), null, null, ''); +-- 参数设置按钮 +insert into sys_menu values('1031', '参数查询', '106', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1032', '参数新增', '106', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1033', '参数修改', '106', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1034', '参数删除', '106', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1035', '参数导出', '106', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 103, 1, sysdate(), null, null, ''); +-- 通知公告按钮 +insert into sys_menu values('1036', '公告查询', '107', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1037', '公告新增', '107', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1038', '公告修改', '107', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1039', '公告删除', '107', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 103, 1, sysdate(), null, null, ''); +-- 操作日志按钮 +insert into sys_menu values('1040', '操作查询', '500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1041', '操作删除', '500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 103, 1, sysdate(), null, null, ''); +-- 登录日志按钮 +insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1050', '账户解锁', '501', '4', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 103, 1, sysdate(), null, null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 103, 1, sysdate(), null, null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '115', '1', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1056', '生成修改', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1057', '生成删除', '115', '3', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1058', '导入代码', '115', '2', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1059', '预览代码', '115', '4', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1060', '生成代码', '115', '5', '#', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 103, 1, sysdate(), null, null, ''); +-- oss相关按钮 +insert into sys_menu values('1600', '文件查询', '118', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1601', '文件上传', '118', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:upload', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1602', '文件下载', '118', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:download', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1603', '文件删除', '118', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:oss:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate(), null, null, ''); + +-- 租户管理相关按钮 +insert into sys_menu values ('1606', '租户查询', '121', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1607', '租户新增', '121', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1608', '租户修改', '121', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1609', '租户删除', '121', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1610', '租户导出', '121', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenant:export', '#', 103, 1, sysdate(), null, null, ''); +-- 租户套餐管理相关按钮 +insert into sys_menu values ('1611', '租户套餐查询', '122', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1612', '租户套餐新增', '122', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1613', '租户套餐修改', '122', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1614', '租户套餐删除', '122', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('1615', '租户套餐导出', '122', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:tenantPackage:export', '#', 103, 1, sysdate(), null, null, ''); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, sysdate(), null, null, ''); +-- 测试菜单 +insert into sys_menu values('1500', '测试单表', '5', '1', 'demo', 'demo/demo/index', '', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate(), null, null, '测试单表菜单'); +insert into sys_menu values('1501', '测试单表查询', '1500', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1502', '测试单表新增', '1500', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1503', '测试单表修改', '1500', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1504', '测试单表删除', '1500', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1505', '测试单表导出', '1500', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1506', '测试树表', '5', '1', 'tree', 'demo/tree/index', '', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate(), null, null, '测试树表菜单'); +insert into sys_menu values('1507', '测试树表查询', '1506', '1', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1508', '测试树表新增', '1506', '2', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1509', '测试树表修改', '1506', '3', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1510', '测试树表删除', '1506', '4', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1511', '测试树表导出', '1506', '5', '#', '', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate(), null, null, ''); + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('3', '3'); +insert into sys_user_role values ('4', '4'); + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('3', '1'); +insert into sys_role_menu values ('3', '5'); +insert into sys_role_menu values ('3', '100'); +insert into sys_role_menu values ('3', '101'); +insert into sys_role_menu values ('3', '102'); +insert into sys_role_menu values ('3', '103'); +insert into sys_role_menu values ('3', '104'); +insert into sys_role_menu values ('3', '105'); +insert into sys_role_menu values ('3', '106'); +insert into sys_role_menu values ('3', '107'); +insert into sys_role_menu values ('3', '108'); +insert into sys_role_menu values ('3', '118'); +insert into sys_role_menu values ('3', '123'); +insert into sys_role_menu values ('3', '130'); +insert into sys_role_menu values ('3', '131'); +insert into sys_role_menu values ('3', '132'); +insert into sys_role_menu values ('3', '133'); +insert into sys_role_menu values ('3', '500'); +insert into sys_role_menu values ('3', '501'); +insert into sys_role_menu values ('3', '1001'); +insert into sys_role_menu values ('3', '1002'); +insert into sys_role_menu values ('3', '1003'); +insert into sys_role_menu values ('3', '1004'); +insert into sys_role_menu values ('3', '1005'); +insert into sys_role_menu values ('3', '1006'); +insert into sys_role_menu values ('3', '1007'); +insert into sys_role_menu values ('3', '1008'); +insert into sys_role_menu values ('3', '1009'); +insert into sys_role_menu values ('3', '1010'); +insert into sys_role_menu values ('3', '1011'); +insert into sys_role_menu values ('3', '1012'); +insert into sys_role_menu values ('3', '1013'); +insert into sys_role_menu values ('3', '1014'); +insert into sys_role_menu values ('3', '1015'); +insert into sys_role_menu values ('3', '1016'); +insert into sys_role_menu values ('3', '1017'); +insert into sys_role_menu values ('3', '1018'); +insert into sys_role_menu values ('3', '1019'); +insert into sys_role_menu values ('3', '1020'); +insert into sys_role_menu values ('3', '1021'); +insert into sys_role_menu values ('3', '1022'); +insert into sys_role_menu values ('3', '1023'); +insert into sys_role_menu values ('3', '1024'); +insert into sys_role_menu values ('3', '1025'); +insert into sys_role_menu values ('3', '1026'); +insert into sys_role_menu values ('3', '1027'); +insert into sys_role_menu values ('3', '1028'); +insert into sys_role_menu values ('3', '1029'); +insert into sys_role_menu values ('3', '1030'); +insert into sys_role_menu values ('3', '1031'); +insert into sys_role_menu values ('3', '1032'); +insert into sys_role_menu values ('3', '1033'); +insert into sys_role_menu values ('3', '1034'); +insert into sys_role_menu values ('3', '1035'); +insert into sys_role_menu values ('3', '1036'); +insert into sys_role_menu values ('3', '1037'); +insert into sys_role_menu values ('3', '1038'); +insert into sys_role_menu values ('3', '1039'); +insert into sys_role_menu values ('3', '1040'); +insert into sys_role_menu values ('3', '1041'); +insert into sys_role_menu values ('3', '1042'); +insert into sys_role_menu values ('3', '1043'); +insert into sys_role_menu values ('3', '1044'); +insert into sys_role_menu values ('3', '1045'); +insert into sys_role_menu values ('3', '1050'); +insert into sys_role_menu values ('3', '1061'); +insert into sys_role_menu values ('3', '1062'); +insert into sys_role_menu values ('3', '1063'); +insert into sys_role_menu values ('3', '1064'); +insert into sys_role_menu values ('3', '1065'); +insert into sys_role_menu values ('3', '1500'); +insert into sys_role_menu values ('3', '1501'); +insert into sys_role_menu values ('3', '1502'); +insert into sys_role_menu values ('3', '1503'); +insert into sys_role_menu values ('3', '1504'); +insert into sys_role_menu values ('3', '1505'); +insert into sys_role_menu values ('3', '1506'); +insert into sys_role_menu values ('3', '1507'); +insert into sys_role_menu values ('3', '1508'); +insert into sys_role_menu values ('3', '1509'); +insert into sys_role_menu values ('3', '1510'); +insert into sys_role_menu values ('3', '1511'); +insert into sys_role_menu values ('3', '1600'); +insert into sys_role_menu values ('3', '1601'); +insert into sys_role_menu values ('3', '1602'); +insert into sys_role_menu values ('3', '1603'); +insert into sys_role_menu values ('3', '1620'); +insert into sys_role_menu values ('3', '1621'); +insert into sys_role_menu values ('3', '1622'); +insert into sys_role_menu values ('3', '1623'); +insert into sys_role_menu values ('3', '11616'); +insert into sys_role_menu values ('3', '11618'); +insert into sys_role_menu values ('3', '11619'); +insert into sys_role_menu values ('3', '11622'); +insert into sys_role_menu values ('3', '11623'); +insert into sys_role_menu values ('3', '11629'); +insert into sys_role_menu values ('3', '11632'); +insert into sys_role_menu values ('3', '11633'); +insert into sys_role_menu values ('3', '11638'); +insert into sys_role_menu values ('3', '11639'); +insert into sys_role_menu values ('3', '11640'); +insert into sys_role_menu values ('3', '11641'); +insert into sys_role_menu values ('3', '11642'); +insert into sys_role_menu values ('3', '11643'); +insert into sys_role_menu values ('3', '11701'); +insert into sys_role_menu values ('4', '5'); +insert into sys_role_menu values ('4', '1500'); +insert into sys_role_menu values ('4', '1501'); +insert into sys_role_menu values ('4', '1502'); +insert into sys_role_menu values ('4', '1503'); +insert into sys_role_menu values ('4', '1504'); +insert into sys_role_menu values ('4', '1505'); +insert into sys_role_menu values ('4', '1506'); +insert into sys_role_menu values ('4', '1507'); +insert into sys_role_menu values ('4', '1508'); +insert into sys_role_menu values ('4', '1509'); +insert into sys_role_menu values ('4', '1510'); +insert into sys_role_menu values ('4', '1511'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +create table sys_oper_log ( + oper_id bigint(20) not null comment '日志主键', + tenant_id varchar(20) default '000000' comment '租户编号', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(100) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(4000) default '' comment '请求参数', + json_result varchar(4000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(4000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + cost_time bigint(20) default 0 comment '消耗时间', + primary key (oper_id), + key idx_sys_oper_log_bt (business_type), + key idx_sys_oper_log_s (status), + key idx_sys_oper_log_ot (oper_time) +) engine=innodb comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +create table sys_dict_type +( + dict_id bigint(20) not null comment '字典主键', + tenant_id varchar(20) default '000000' comment '租户编号', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (tenant_id, dict_type) +) engine=innodb comment = '字典类型表'; + +insert into sys_dict_type values(1, '000000', '用户性别', 'sys_user_sex', 103, 1, sysdate(), null, null, '用户性别列表'); +insert into sys_dict_type values(2, '000000', '菜单状态', 'sys_show_hide', 103, 1, sysdate(), null, null, '菜单状态列表'); +insert into sys_dict_type values(3, '000000', '系统开关', 'sys_normal_disable', 103, 1, sysdate(), null, null, '系统开关列表'); +insert into sys_dict_type values(6, '000000', '系统是否', 'sys_yes_no', 103, 1, sysdate(), null, null, '系统是否列表'); +insert into sys_dict_type values(7, '000000', '通知类型', 'sys_notice_type', 103, 1, sysdate(), null, null, '通知类型列表'); +insert into sys_dict_type values(8, '000000', '通知状态', 'sys_notice_status', 103, 1, sysdate(), null, null, '通知状态列表'); +insert into sys_dict_type values(9, '000000', '操作类型', 'sys_oper_type', 103, 1, sysdate(), null, null, '操作类型列表'); +insert into sys_dict_type values(10, '000000', '系统状态', 'sys_common_status', 103, 1, sysdate(), null, null, '登录状态列表'); +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', 103, 1, sysdate(), null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, sysdate(), null, null, '客户端设备类型'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +create table sys_dict_data +( + dict_code bigint(20) not null comment '字典编码', + tenant_id varchar(20) default '000000' comment '租户编号', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb comment = '字典数据表'; + +insert into sys_dict_data values(1, '000000', 1, '男', '0', 'sys_user_sex', '', '', 'Y', 103, 1, sysdate(), null, null, '性别男'); +insert into sys_dict_data values(2, '000000', 2, '女', '1', 'sys_user_sex', '', '', 'N', 103, 1, sysdate(), null, null, '性别女'); +insert into sys_dict_data values(3, '000000', 3, '未知', '2', 'sys_user_sex', '', '', 'N', 103, 1, sysdate(), null, null, '性别未知'); +insert into sys_dict_data values(4, '000000', 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', 103, 1, sysdate(), null, null, '显示菜单'); +insert into sys_dict_data values(5, '000000', 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', 103, 1, sysdate(), null, null, '隐藏菜单'); +insert into sys_dict_data values(6, '000000', 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', 103, 1, sysdate(), null, null, '正常状态'); +insert into sys_dict_data values(7, '000000', 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', 103, 1, sysdate(), null, null, '停用状态'); +insert into sys_dict_data values(12, '000000', 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', 103, 1, sysdate(), null, null, '系统默认是'); +insert into sys_dict_data values(13, '000000', 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', 103, 1, sysdate(), null, null, '系统默认否'); +insert into sys_dict_data values(14, '000000', 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', 103, 1, sysdate(), null, null, '通知'); +insert into sys_dict_data values(15, '000000', 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', 103, 1, sysdate(), null, null, '公告'); +insert into sys_dict_data values(16, '000000', 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', 103, 1, sysdate(), null, null, '正常状态'); +insert into sys_dict_data values(17, '000000', 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', 103, 1, sysdate(), null, null, '关闭状态'); +insert into sys_dict_data values(29, '000000', 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate(), null, null, '其他操作'); +insert into sys_dict_data values(18, '000000', 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate(), null, null, '新增操作'); +insert into sys_dict_data values(19, '000000', 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', 103, 1, sysdate(), null, null, '修改操作'); +insert into sys_dict_data values(20, '000000', 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate(), null, null, '删除操作'); +insert into sys_dict_data values(21, '000000', 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '授权操作'); +insert into sys_dict_data values(22, '000000', 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate(), null, null, '导出操作'); +insert into sys_dict_data values(23, '000000', 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate(), null, null, '导入操作'); +insert into sys_dict_data values(24, '000000', 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate(), null, null, '强退操作'); +insert into sys_dict_data values(25, '000000', 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', 103, 1, sysdate(), null, null, '生成操作'); +insert into sys_dict_data values(26, '000000', 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', 103, 1, sysdate(), null, null, '清空操作'); +insert into sys_dict_data values(27, '000000', 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', 103, 1, sysdate(), null, null, '正常状态'); +insert into sys_dict_data values(28, '000000', 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', 103, 1, sysdate(), null, null, '停用状态'); +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, sysdate(), null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, sysdate(), null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, sysdate(), null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, sysdate(), null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', 'el-check-tag', 'default', 'N', 103, 1, sysdate(), null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '小程序'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +create table sys_config ( + config_id bigint(20) not null comment '参数主键', + tenant_id varchar(20) default '000000' comment '租户编号', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb comment = '参数配置表'; + +insert into sys_config values(1, '000000', '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 103, 1, sysdate(), null, null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '000000', '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 103, 1, sysdate(), null, null, '初始化密码 123456' ); +insert into sys_config values(3, '000000', '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 103, 1, sysdate(), null, null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(5, '000000', '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 103, 1, sysdate(), null, null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(11, '000000', 'OSS预览列表资源开关', 'sys.oss.previewListResource', 'true', 'Y', 103, 1, sysdate(), null, null, 'true:开启, false:关闭'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +create table sys_logininfor ( + info_id bigint(20) not null comment '访问ID', + tenant_id varchar(20) default '000000' comment '租户编号', + user_name varchar(50) default '' comment '用户账号', + client_key varchar(32) default '' comment '客户端', + device_type varchar(32) default '' comment '设备类型', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id), + key idx_sys_logininfor_s (status), + key idx_sys_logininfor_lt (login_time) +) engine=innodb comment = '系统访问记录'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +create table sys_notice ( + notice_id bigint(20) not null comment '公告ID', + tenant_id varchar(20) default '000000' comment '租户编号', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '000000', '温馨提醒:2018-07-01 新版本发布啦', '2', '新版本内容', '0', 103, 1, sysdate(), null, null, '管理员'); +insert into sys_notice values('2', '000000', '维护通知:2018-07-01 系统凌晨维护', '1', '维护内容', '0', 103, 1, sysdate(), null, null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +create table gen_table ( + table_id bigint(20) not null comment '编号', + data_name varchar(200) default '' comment '数据源名称', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +create table gen_table_column ( + column_id bigint(20) not null comment '编号', + table_id bigint(20) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb comment = '代码生成业务表字段'; + +-- ---------------------------- +-- OSS对象存储表 +-- ---------------------------- +create table sys_oss ( + oss_id bigint(20) not null comment '对象存储主键', + tenant_id varchar(20) default '000000' comment '租户编号', + file_name varchar(255) not null default '' comment '文件名', + original_name varchar(255) not null default '' comment '原名', + file_suffix varchar(10) not null default '' comment '文件后缀名', + url varchar(500) not null comment 'URL地址', + ext1 text default null comment '扩展字段', + create_dept bigint(20) default null comment '创建部门', + create_time datetime default null comment '创建时间', + create_by bigint(20) default null comment '上传人', + update_time datetime default null comment '更新时间', + update_by bigint(20) default null comment '更新人', + service varchar(20) not null default 'minio' comment '服务商', + primary key (oss_id) +) engine=innodb comment ='OSS对象存储表'; + +-- ---------------------------- +-- OSS对象存储动态配置表 +-- ---------------------------- +create table sys_oss_config ( + oss_config_id bigint(20) not null comment '主键', + tenant_id varchar(20) default '000000'comment '租户编号', + config_key varchar(20) not null default '' comment '配置key', + access_key varchar(255) default '' comment 'accessKey', + secret_key varchar(255) default '' comment '秘钥', + bucket_name varchar(255) default '' comment '桶名称', + prefix varchar(255) default '' comment '前缀', + endpoint varchar(255) default '' comment '访问站点', + domain varchar(255) default '' comment '自定义域名', + is_https char(1) default 'N' comment '是否https(Y=是,N=否)', + region varchar(255) default '' comment '域', + access_policy char(1) not null default '1' comment '桶权限类型(0=private 1=public 2=custom)', + status char(1) default '1' comment '是否默认(0=是,1=否)', + ext1 varchar(255) default '' comment '扩展字段', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime default null comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime default null comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (oss_config_id) +) engine=innodb comment='对象存储配置表'; + +insert into sys_oss_config values (1, '000000', 'minio', 'fys', 'fys123', 'fys', '', '127.0.0.1:9000', '','N', '', '1' ,'0', '', 103, 1, sysdate(), 1, sysdate(), null); +insert into sys_oss_config values (2, '000000', 'qiniu', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 's3-cn-north-1.qiniucs.com', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null); +insert into sys_oss_config values (3, '000000', 'aliyun', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys', '', 'oss-cn-beijing.aliyuncs.com', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null); +insert into sys_oss_config values (4, '000000', 'qcloud', 'XXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXX', 'fys-1240000000', '', 'cos.ap-beijing.myqcloud.com', '','N', 'ap-beijing', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null); +insert into sys_oss_config values (5, '000000', 'image', 'fys', 'fys123', 'fys', 'image', '127.0.0.1:9000', '','N', '', '1' ,'1', '', 103, 1, sysdate(), 1, sysdate(), null); + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +create table sys_client ( + id bigint(20) not null comment 'id', + client_id varchar(64) default null comment '客户端id', + client_key varchar(32) default null comment '客户端key', + client_secret varchar(255) default null comment '客户端秘钥', + grant_type varchar(255) default null comment '授权类型', + device_type varchar(32) default null comment '设备类型', + active_timeout int(11) default 1800 comment 'token活跃超时时间', + timeout int(11) default 604800 comment 'token固定超时', + status char(1) default '0' comment '状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime default null comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime default null comment '更新时间', + primary key (id) +) engine=innodb comment='系统授权表'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate()); + + +CREATE TABLE test_demo +( + id bigint(0) NOT NULL COMMENT '主键', + tenant_id varchar(20) NULL DEFAULT '000000' COMMENT '租户编号', + dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', + user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', + order_num int(0) NULL DEFAULT 0 COMMENT '排序号', + test_key varchar(255) NULL DEFAULT NULL COMMENT 'key键', + value varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_dept bigint(0) NULL DEFAULT NULL COMMENT '创建部门', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by bigint(0) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by bigint(0) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试单表'; + +CREATE TABLE test_tree +( + id bigint(0) NOT NULL COMMENT '主键', + tenant_id varchar(20) NULL DEFAULT '000000' COMMENT '租户编号', + parent_id bigint(0) NULL DEFAULT 0 COMMENT '父id', + dept_id bigint(0) NULL DEFAULT NULL COMMENT '部门id', + user_id bigint(0) NULL DEFAULT NULL COMMENT '用户id', + tree_name varchar(255) NULL DEFAULT NULL COMMENT '值', + version int(0) NULL DEFAULT 0 COMMENT '版本', + create_dept bigint(0) NULL DEFAULT NULL COMMENT '创建部门', + create_time datetime(0) NULL DEFAULT NULL COMMENT '创建时间', + create_by bigint(0) NULL DEFAULT NULL COMMENT '创建人', + update_time datetime(0) NULL DEFAULT NULL COMMENT '更新时间', + update_by bigint(0) NULL DEFAULT NULL COMMENT '更新人', + del_flag int(0) NULL DEFAULT 0 COMMENT '删除标志', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '测试树表'; + +INSERT INTO test_demo VALUES (1, '000000', 102, 4, 1, '测试数据权限', '测试', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (2, '000000', 102, 3, 2, '子节点1', '111', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (3, '000000', 102, 3, 3, '子节点2', '222', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (4, '000000', 108, 4, 4, '测试数据', 'demo', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (5, '000000', 108, 3, 13, '子节点11', '1111', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (6, '000000', 108, 3, 12, '子节点22', '2222', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (7, '000000', 108, 3, 11, '子节点33', '3333', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (8, '000000', 108, 3, 10, '子节点44', '4444', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (9, '000000', 108, 3, 9, '子节点55', '5555', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (10, '000000', 108, 3, 8, '子节点66', '6666', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (11, '000000', 108, 3, 7, '子节点77', '7777', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (12, '000000', 108, 3, 6, '子节点88', '8888', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_demo VALUES (13, '000000', 108, 3, 5, '子节点99', '9999', 0, 103, sysdate(), 1, NULL, NULL, 0); + +INSERT INTO test_tree VALUES (1, '000000', 0, 102, 4, '测试数据权限', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (2, '000000', 1, 102, 3, '子节点1', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (3, '000000', 2, 102, 3, '子节点2', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (4, '000000', 0, 108, 4, '测试树1', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (5, '000000', 4, 108, 3, '子节点11', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (6, '000000', 4, 108, 3, '子节点22', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (7, '000000', 4, 108, 3, '子节点33', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (8, '000000', 5, 108, 3, '子节点44', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (9, '000000', 6, 108, 3, '子节点55', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (10, '000000', 7, 108, 3, '子节点66', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (11, '000000', 7, 108, 3, '子节点77', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (12, '000000', 10, 108, 3, '子节点88', 0, 103, sysdate(), 1, NULL, NULL, 0); +INSERT INTO test_tree VALUES (13, '000000', 10, 108, 3, '子节点99', 0, 103, sysdate(), 1, NULL, NULL, 0); diff --git a/script/sql/ry_workflow.sql b/script/sql/ry_workflow.sql new file mode 100644 index 0000000..5e055d2 --- /dev/null +++ b/script/sql/ry_workflow.sql @@ -0,0 +1,257 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/mysql/warm-flow-all.sql +-- ---------------------------- +CREATE TABLE `flow_definition` +( + `id` bigint NOT NULL COMMENT '主键id', + `flow_code` varchar(40) NOT NULL COMMENT '流程编码', + `flow_name` varchar(100) NOT NULL COMMENT '流程名称', + `category` varchar(100) DEFAULT NULL COMMENT '流程类别', + `version` varchar(20) NOT NULL COMMENT '流程版本', + `is_publish` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否发布(0未发布 1已发布 9失效)', + `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', + `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', + `activity_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '流程激活状态(0挂起 1激活)', + `listener_type` varchar(100) DEFAULT NULL COMMENT '监听器类型', + `listener_path` varchar(400) DEFAULT NULL COMMENT '监听器路径', + `ext` varchar(500) DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='流程定义表'; + +CREATE TABLE `flow_node` +( + `id` bigint NOT NULL COMMENT '主键id', + `node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `definition_id` bigint NOT NULL COMMENT '流程定义id', + `node_code` varchar(100) NOT NULL COMMENT '流程节点编码', + `node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称', + `permission_flag` varchar(200) DEFAULT NULL COMMENT '权限标识(权限类型:权限标识,可以多个,用逗号隔开)', + `node_ratio` decimal(6, 3) DEFAULT NULL COMMENT '流程签署比例值', + `coordinate` varchar(100) DEFAULT NULL COMMENT '坐标', + `any_node_skip` varchar(100) DEFAULT NULL COMMENT '任意结点跳转', + `listener_type` varchar(100) DEFAULT NULL COMMENT '监听器类型', + `listener_path` varchar(400) DEFAULT NULL COMMENT '监听器路径', + `handler_type` varchar(100) DEFAULT NULL COMMENT '处理器类型', + `handler_path` varchar(400) DEFAULT NULL COMMENT '处理器路径', + `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', + `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', + `version` varchar(20) NOT NULL COMMENT '版本', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `ext` text COMMENT '扩展属性', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='流程节点表'; + +CREATE TABLE `flow_skip` +( + `id` bigint NOT NULL COMMENT '主键id', + `definition_id` bigint NOT NULL COMMENT '流程定义id', + `now_node_code` varchar(100) NOT NULL COMMENT '当前流程节点的编码', + `now_node_type` tinyint(1) DEFAULT NULL COMMENT '当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `next_node_code` varchar(100) NOT NULL COMMENT '下一个流程节点的编码', + `next_node_type` tinyint(1) DEFAULT NULL COMMENT '下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `skip_name` varchar(100) DEFAULT NULL COMMENT '跳转名称', + `skip_type` varchar(40) DEFAULT NULL COMMENT '跳转类型(PASS审批通过 REJECT退回)', + `skip_condition` varchar(200) DEFAULT NULL COMMENT '跳转条件', + `coordinate` varchar(100) DEFAULT NULL COMMENT '坐标', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='节点跳转关联表'; + +CREATE TABLE `flow_instance` +( + `id` bigint NOT NULL COMMENT '主键id', + `definition_id` bigint NOT NULL COMMENT '对应flow_definition表的id', + `business_id` varchar(40) NOT NULL COMMENT '业务id', + `node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `node_code` varchar(40) NOT NULL COMMENT '流程节点编码', + `node_name` varchar(100) DEFAULT NULL COMMENT '流程节点名称', + `variable` text COMMENT '任务变量', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', + `activity_status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '流程激活状态(0挂起 1激活)', + `def_json` text COMMENT '流程定义json', + `create_by` varchar(64) DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `ext` varchar(500) DEFAULT NULL COMMENT '扩展字段,预留给业务系统使用', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='流程实例表'; + +CREATE TABLE `flow_task` +( + `id` bigint NOT NULL COMMENT '主键id', + `definition_id` bigint NOT NULL COMMENT '对应flow_definition表的id', + `instance_id` bigint NOT NULL COMMENT '对应flow_instance表的id', + `node_code` varchar(100) NOT NULL COMMENT '节点编码', + `node_name` varchar(100) DEFAULT NULL COMMENT '节点名称', + `node_type` tinyint(1) NOT NULL COMMENT '节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', + `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', + `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='待办任务表'; + +CREATE TABLE `flow_his_task` +( + `id` bigint(20) NOT NULL COMMENT '主键id', + `definition_id` bigint(20) NOT NULL COMMENT '对应flow_definition表的id', + `instance_id` bigint(20) NOT NULL COMMENT '对应flow_instance表的id', + `task_id` bigint(20) NOT NULL COMMENT '对应flow_task表的id', + `node_code` varchar(100) DEFAULT NULL COMMENT '开始节点编码', + `node_name` varchar(100) DEFAULT NULL COMMENT '开始节点名称', + `node_type` tinyint(1) DEFAULT NULL COMMENT '开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', + `target_node_code` varchar(200) DEFAULT NULL COMMENT '目标节点编码', + `target_node_name` varchar(200) DEFAULT NULL COMMENT '结束节点名称', + `approver` varchar(40) DEFAULT NULL COMMENT '审批者', + `cooperate_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)', + `collaborator` varchar(40) DEFAULT NULL COMMENT '协作人', + `skip_type` varchar(10) NOT NULL COMMENT '流转类型(PASS通过 REJECT退回 NONE无动作)', + `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', + `form_custom` char(1) DEFAULT 'N' COMMENT '审批表单是否自定义(Y是 N否)', + `form_path` varchar(100) DEFAULT NULL COMMENT '审批表单路径', + `message` varchar(500) DEFAULT NULL COMMENT '审批意见', + `variable` TEXT DEFAULT NULL COMMENT '任务变量', + `ext` varchar(500) DEFAULT NULL COMMENT '业务详情 存业务表对象json字符串', + `create_time` datetime DEFAULT NULL COMMENT '任务开始时间', + `update_time` datetime DEFAULT NULL COMMENT '审批完成时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB COMMENT ='历史任务记录表'; + + +CREATE TABLE `flow_user` +( + `id` bigint NOT NULL COMMENT '主键id', + `type` char(1) NOT NULL COMMENT '人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)', + `processed_by` varchar(80) DEFAULT NULL COMMENT '权限人', + `associated` bigint NOT NULL COMMENT '任务表id', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `create_by` varchar(80) DEFAULT NULL COMMENT '创建人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标志', + `tenant_id` varchar(40) DEFAULT NULL COMMENT '租户id', + PRIMARY KEY (`id`) USING BTREE, + KEY `user_processed_type` (`processed_by`, `type`), + KEY `user_associated` (`associated`) USING BTREE +) ENGINE = InnoDB COMMENT ='流程用户表'; + +-- ---------------------------- +-- 流程分类表 +-- ---------------------------- +create table flow_category +( + category_id bigint(20) not null comment '流程分类ID', + tenant_id varchar(20) default '000000' comment '租户编号', + parent_id bigint(20) default 0 comment '父流程分类id', + ancestors varchar(500) default '' comment '祖级列表', + category_name varchar(30) not null comment '流程分类名称', + order_num int(4) default 0 comment '显示顺序', + del_flag char(1) default '0' comment '删除标志(0代表存在 1代表删除)', + create_dept bigint(20) null comment '创建部门', + create_by bigint(20) null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint(20) null comment '更新者', + update_time datetime null comment '更新时间', + primary key (category_id) +) engine = innodb comment = '流程分类'; + +INSERT INTO flow_category values (100, '000000', 0, '0', 'OA审批', 0, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (101, '000000', 100, '0,100', '假勤管理', 0, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (102, '000000', 100, '0,100', '人事管理', 1, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (103, '000000', 101, '0,100,101', '请假', 0, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (104, '000000', 101, '0,100,101', '出差', 1, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (105, '000000', 101, '0,100,101', '加班', 2, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (106, '000000', 101, '0,100,101', '换班', 3, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (107, '000000', 101, '0,100,101', '外出', 4, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (108, '000000', 102, '0,100,102', '转正', 1, '0', 103, 1, sysdate(), null, null); +INSERT INTO flow_category values (109, '000000', 102, '0,100,102', '离职', 2, '0', 103, 1, sysdate(), null, null); + +-- ---------------------------- +-- 请假单信息 +-- ---------------------------- +create table test_leave +( + id bigint(20) not null comment 'id', + tenant_id varchar(20) default '000000' comment '租户编号', + leave_type varchar(255) not null comment '请假类型', + start_date datetime not null comment '开始时间', + end_date datetime not null comment '结束时间', + leave_days int(10) not null comment '请假天数', + remark varchar(255) null comment '请假原因', + status varchar(255) null comment '状态', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '请假申请表'; + +insert into sys_menu values ('11616', '工作流', '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, sysdate(),NULL, NULL, ''); +insert into sys_menu values ('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +-- 流程分类管理相关按钮 +insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1,sysdate(), null, null, ''); +insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1,sysdate(), null, null, ''); +insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1,sysdate(), null, null, ''); +insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove', '#', 103,1, sysdate(), null, null, ''); +insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export', '#', 103,1, sysdate(), null, null, ''); +-- 请假测试相关按钮 +insert into sys_menu VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', '', 1, 0, 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, sysdate(), NULL, NULL, '请假申请菜单'); +insert into sys_menu VALUES (11639, '请假申请查询', 11638, 1, '#', '', '', 1, 0, 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu VALUES (11640, '请假申请新增', 11638, 2, '#', '', '', 1, 0, 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu VALUES (11641, '请假申请修改', 11638, 3, '#', '', '', 1, 0, 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu VALUES (11642, '请假申请删除', 11638, 4, '#', '', '', 1, 0, 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu VALUES (11643, '请假申请导出', 11638, 5, '#', '', '', 1, 0, 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, sysdate(), NULL, NULL, ''); + +INSERT INTO sys_dict_type VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, sysdate(), NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, sysdate(), NULL, NULL, '表单类型列表'); +INSERT INTO sys_dict_type VALUES (15, '000000', '任务状态', 'wf_task_status', 103, 1, sysdate(), NULL, NULL, '任务状态'); +INSERT INTO sys_dict_data VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL,'已撤销'); +INSERT INTO sys_dict_data VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '草稿'); +INSERT INTO sys_dict_data VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL,'待审核'); +INSERT INTO sys_dict_data VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL,'已完成'); +INSERT INTO sys_dict_data VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL,'已作废'); +INSERT INTO sys_dict_data VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL,'已退回'); +INSERT INTO sys_dict_data VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL,NULL, '已终止'); +INSERT INTO sys_dict_data VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL,'自定义表单'); +INSERT INTO sys_dict_data VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL,'动态表单'); +INSERT INTO sys_dict_data VALUES (48, '000000', 1, '撤销', 'cancel', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '撤销'); +INSERT INTO sys_dict_data VALUES (49, '000000', 2, '通过', 'pass', 'wf_task_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '通过'); +INSERT INTO sys_dict_data VALUES (50, '000000', 3, '待审核', 'waiting', 'wf_task_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '待审核'); +INSERT INTO sys_dict_data VALUES (51, '000000', 4, '作废', 'invalid', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '作废'); +INSERT INTO sys_dict_data VALUES (52, '000000', 5, '退回', 'back', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '退回'); +INSERT INTO sys_dict_data VALUES (53, '000000', 6, '终止', 'termination', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '终止'); +INSERT INTO sys_dict_data VALUES (54, '000000', 7, '转办', 'transfer', 'wf_task_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '转办'); +INSERT INTO sys_dict_data VALUES (55, '000000', 8, '委托', 'depute', 'wf_task_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '委托'); +INSERT INTO sys_dict_data VALUES (56, '000000', 9, '抄送', 'copy', 'wf_task_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '抄送'); +INSERT INTO sys_dict_data VALUES (57, '000000', 10, '加签', 'sign', 'wf_task_status', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '加签'); +INSERT INTO sys_dict_data VALUES (58, '000000', 11, '减签', 'sign_off', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '减签'); +INSERT INTO sys_dict_data VALUES (59, '000000', 11, '超时', 'timeout', 'wf_task_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '超时'); + diff --git a/script/sql/sqlserver/sqlserver_ry_job.sql b/script/sql/sqlserver/sqlserver_ry_job.sql new file mode 100644 index 0000000..b26fe67 --- /dev/null +++ b/script/sql/sqlserver/sqlserver_ry_job.sql @@ -0,0 +1,2825 @@ +/* + SnailJob Database Transfer Tool + Source Server Type : MySQL + Target Server Type : Microsoft SQL Server + Date: 2025-04-26 10:03:23 +*/ + + +-- sj_namespace +CREATE TABLE sj_namespace +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + name nvarchar(64) NOT NULL, + unique_id nvarchar(64) NOT NULL, + description nvarchar(256) NOT NULL DEFAULT '', + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'唯一id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'unique_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_namespace' +GO + +INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES (N'Development', N'dev', N'', 0, getdate(), getdate()) +GO +INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES (N'Production', N'prod', N'', 0, getdate(), getdate()) +GO + +-- sj_group_config +CREATE TABLE sj_group_config +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL DEFAULT '', + description nvarchar(256) NOT NULL DEFAULT '', + token nvarchar(64) NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', + group_status tinyint NOT NULL DEFAULT 0, + version int NOT NULL, + group_partition int NOT NULL, + id_generator_mode tinyint NOT NULL DEFAULT 1, + init_scene tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'token', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'token' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组状态 0、未启用 1、启用', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'group_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'分区', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'group_partition' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'唯一id生成模式 默认号段模式', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'id_generator_mode' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'是否初始化场景 0:否 1:是', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'init_scene' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组配置', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_group_config' +GO + +INSERT INTO sj_group_config(namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, create_dt, update_dt) VALUES (N'dev', N'fys_group', N'', N'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', N'1', N'1', N'0', N'1', N'1', getdate(), getdate()) +GO +INSERT INTO sj_group_config(namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, create_dt, update_dt) VALUES (N'prod', N'fys_group', N'', N'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', N'1', N'1', N'0', N'1', N'1', getdate(), getdate()) +GO + +-- sj_notify_config +CREATE TABLE sj_notify_config +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + notify_name nvarchar(64) NOT NULL DEFAULT '', + system_task_type tinyint NOT NULL DEFAULT 3, + notify_status tinyint NOT NULL DEFAULT 0, + recipient_ids nvarchar(128) NOT NULL, + notify_threshold int NOT NULL DEFAULT 0, + notify_scene tinyint NOT NULL DEFAULT 0, + rate_limiter_status tinyint NOT NULL DEFAULT 0, + rate_limiter_threshold int NOT NULL DEFAULT 0, + description nvarchar(256) NOT NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'notify_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 1. 重试任务 2. 重试回调 3、JOB任务 4、WORKFLOW任务', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'system_task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知状态 0、未启用 1、启用', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'notify_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'接收人id列表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'recipient_ids' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知阈值', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'notify_threshold' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知场景', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'notify_scene' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'限流状态 0、未启用 1、启用', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'rate_limiter_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'每秒限流阈值', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'rate_limiter_threshold' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知配置', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_config' +GO + +-- sj_notify_recipient +CREATE TABLE sj_notify_recipient +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + recipient_name nvarchar(64) NOT NULL, + notify_type tinyint NOT NULL DEFAULT 0, + notify_attribute nvarchar(512) NOT NULL, + description nvarchar(256) NOT NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'接收人名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'recipient_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知类型 1、钉钉 2、邮件 3、企业微信 4 飞书 5 webhook', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'notify_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'配置属性', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'notify_attribute' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'告警通知接收人', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_notify_recipient' +GO + +-- sj_retry_dead_letter +CREATE TABLE sj_retry_dead_letter +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, + scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, + idempotent_id nvarchar(64) NOT NULL, + biz_no nvarchar(64) NOT NULL DEFAULT '', + executor_name nvarchar(512) NOT NULL DEFAULT '', + args_str nvarchar(max) NOT NULL, + ext_attrs nvarchar(max) NOT NULL, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_retry_dead_letter_01 ON sj_retry_dead_letter (namespace_id, group_name, scene_name) +GO +CREATE INDEX idx_sj_retry_dead_letter_02 ON sj_retry_dead_letter (idempotent_id) +GO +CREATE INDEX idx_sj_retry_dead_letter_03 ON sj_retry_dead_letter (biz_no) +GO +CREATE INDEX idx_sj_retry_dead_letter_04 ON sj_retry_dead_letter (create_dt) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'group_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'scene_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'scene_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'幂等id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'idempotent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'业务编号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'biz_no' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行器名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'executor_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行方法参数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'args_str' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'死信队列表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_dead_letter' +GO + +-- sj_retry +CREATE TABLE sj_retry +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + group_id bigint NOT NULL, + scene_name nvarchar(64) NOT NULL, + scene_id bigint NOT NULL, + idempotent_id nvarchar(64) NOT NULL, + biz_no nvarchar(64) NOT NULL DEFAULT '', + executor_name nvarchar(512) NOT NULL DEFAULT '', + args_str nvarchar(max) NOT NULL, + ext_attrs nvarchar(max) NOT NULL, + next_trigger_at bigint NOT NULL, + retry_count int NOT NULL DEFAULT 0, + retry_status tinyint NOT NULL DEFAULT 0, + task_type tinyint NOT NULL DEFAULT 1, + bucket_index int NOT NULL DEFAULT 0, + parent_id bigint NOT NULL DEFAULT 0, + deleted bigint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_retry_01 ON sj_retry (scene_id, task_type, idempotent_id, deleted) +GO + +CREATE INDEX idx_sj_retry_01 ON sj_retry (biz_no) +GO +CREATE INDEX idx_sj_retry_02 ON sj_retry (retry_status, bucket_index) +GO +CREATE INDEX idx_sj_retry_03 ON sj_retry (parent_id) +GO +CREATE INDEX idx_sj_retry_04 ON sj_retry (create_dt) +GO +CREATE INDEX idx_sj_retry_05 ON sj_retry (idempotent_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'group_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'scene_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'scene_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'幂等id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'idempotent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'业务编号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'biz_no' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行器名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'executor_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行方法参数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'args_str' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'下次触发时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'next_trigger_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'retry_count' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试状态 0、重试中 1、成功 2、最大重试次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'retry_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 1、重试数据 2、回调数据', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'bucket', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'bucket_index' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父节点id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试信息表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry' +GO + +-- sj_retry_task +CREATE TABLE sj_retry_task +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + scene_name nvarchar(64) NOT NULL, + retry_id bigint NOT NULL, + ext_attrs nvarchar(max) NOT NULL, + task_status tinyint NOT NULL DEFAULT 1, + task_type tinyint NOT NULL DEFAULT 1, + operation_reason tinyint NOT NULL DEFAULT 0, + client_info nvarchar(128) NULL DEFAULT NULL, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_retry_task_01 ON sj_retry_task (namespace_id, group_name, scene_name) +GO +CREATE INDEX idx_sj_retry_task_02 ON sj_retry_task (task_status) +GO +CREATE INDEX idx_sj_retry_task_03 ON sj_retry_task (create_dt) +GO +CREATE INDEX idx_sj_retry_task_04 ON sj_retry_task (retry_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'scene_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试信息Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'retry_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试状态', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'task_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 1、重试数据 2、回调数据', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'操作原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'operation_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'客户端地址 clientId#ip:port', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'client_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试任务表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task' +GO + +-- sj_retry_task_log_message +CREATE TABLE sj_retry_task_log_message +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + retry_id bigint NOT NULL, + retry_task_id bigint NOT NULL, + message nvarchar(max) NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, retry_task_id) +GO +CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试信息Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'retry_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试任务Id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'retry_task_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'异常信息', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'message' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'log_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'上报时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'real_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务调度日志信息记录表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_task_log_message' +GO + +-- sj_retry_scene_config +CREATE TABLE sj_retry_scene_config +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + scene_name nvarchar(64) NOT NULL, + group_name nvarchar(64) NOT NULL, + scene_status tinyint NOT NULL DEFAULT 0, + max_retry_count int NOT NULL DEFAULT 5, + back_off tinyint NOT NULL DEFAULT 1, + trigger_interval nvarchar(16) NOT NULL DEFAULT '', + notify_ids nvarchar(128) NOT NULL DEFAULT '', + deadline_request bigint NOT NULL DEFAULT 60000, + executor_timeout int NOT NULL DEFAULT 5, + route_key tinyint NOT NULL DEFAULT 4, + block_strategy tinyint NOT NULL DEFAULT 1, + cb_status tinyint NOT NULL DEFAULT 0, + cb_trigger_type tinyint NOT NULL DEFAULT 1, + cb_max_count int NOT NULL DEFAULT 16, + cb_trigger_interval nvarchar(16) NOT NULL DEFAULT '', + description nvarchar(256) NOT NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'scene_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组状态 0、未启用 1、启用', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'scene_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'最大重试次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'max_retry_count' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'1、默认等级 2、固定间隔时间 3、CRON 表达式', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'back_off' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'间隔时长', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'trigger_interval' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知告警场景配置id列表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'notify_ids' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'Deadline Request 调用链超时 单位毫秒', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'deadline_request' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务执行超时时间,单位秒', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'executor_timeout' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'路由策略', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'route_key' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'阻塞策略 1、丢弃 2、覆盖 3、并行', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'block_strategy' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'回调状态 0、不开启 1、开启', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'cb_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'1、默认等级 2、固定间隔时间 3、CRON 表达式', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'cb_trigger_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'回调的最大执行次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'cb_max_count' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'回调的最大执行次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'cb_trigger_interval' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景配置', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_scene_config' +GO + +-- sj_server_node +CREATE TABLE sj_server_node +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + host_id nvarchar(64) NOT NULL, + host_ip nvarchar(64) NOT NULL, + host_port int NOT NULL, + expire_at datetime2 NOT NULL, + node_type tinyint NOT NULL, + ext_attrs nvarchar(256) NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip) +GO + +CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name) +GO +CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主机id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'host_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'机器ip', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'host_ip' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'机器端口', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'host_port' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'过期时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'expire_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'节点类型 1、客户端 2、是服务端', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'服务器节点', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_server_node' +GO + +-- sj_distributed_lock +CREATE TABLE sj_distributed_lock +( + name nvarchar(64) NOT NULL, + lock_until datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + locked_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + locked_by nvarchar(255) NOT NULL, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'锁名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'锁定时长', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'lock_until' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'锁定时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'locked_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'锁定者', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'locked_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'锁定表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_distributed_lock' +GO + +-- sj_system_user +CREATE TABLE sj_system_user +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + username nvarchar(64) NOT NULL, + password nvarchar(128) NOT NULL, + role tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'账号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'username' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'密码', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'password' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'角色:1-普通用户、2-管理员', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'role' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'系统用户表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user' +GO + +-- pwd: admin +INSERT INTO sj_system_user(username, password, role, create_dt, update_dt) VALUES (N'admin', N'465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', N'2', getdate(), getdate()) +GO + +-- sj_system_user_permission +CREATE TABLE sj_system_user_permission +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + group_name nvarchar(64) NOT NULL, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + system_user_id bigint NOT NULL, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'系统用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'system_user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'系统用户权限表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_system_user_permission' +GO + +-- sj_sequence_alloc +CREATE TABLE sj_sequence_alloc +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL DEFAULT '', + max_id bigint NOT NULL DEFAULT 1, + step int NOT NULL DEFAULT 100, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'最大id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'max_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'步长', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'step' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'号段模式序号ID分配表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_sequence_alloc' +GO + +-- sj_job +CREATE TABLE sj_job +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + job_name nvarchar(64) NOT NULL, + args_str nvarchar(max) NULL DEFAULT NULL, + args_type tinyint NOT NULL DEFAULT 1, + next_trigger_at bigint NOT NULL, + job_status tinyint NOT NULL DEFAULT 1, + task_type tinyint NOT NULL DEFAULT 1, + route_key tinyint NOT NULL DEFAULT 4, + executor_type tinyint NOT NULL DEFAULT 1, + executor_info nvarchar(255) NULL DEFAULT NULL, + trigger_type tinyint NOT NULL, + trigger_interval nvarchar(255) NOT NULL, + block_strategy tinyint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + max_retry_times int NOT NULL DEFAULT 0, + parallel_num int NOT NULL DEFAULT 1, + retry_interval int NOT NULL DEFAULT 0, + bucket_index int NOT NULL DEFAULT 0, + resident tinyint NOT NULL DEFAULT 0, + notify_ids nvarchar(128) NOT NULL DEFAULT '', + owner_id bigint NULL, + description nvarchar(256) NOT NULL DEFAULT '', + ext_attrs nvarchar(256) NULL DEFAULT '', + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name) +GO +CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index) +GO +CREATE INDEX idx_sj_job_03 ON sj_job (create_dt) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'job_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行方法参数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'args_str' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'参数类型 ', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'args_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'下次触发时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'next_trigger_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务状态 0、关闭、1、开启', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'job_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 1、集群 2、广播 3、切片', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'路由策略', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'route_key' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行器类型', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'executor_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行器名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'executor_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'触发类型 1.CRON 表达式 2. 固定时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'trigger_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'间隔时长', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'trigger_interval' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'阻塞策略 1、丢弃 2、覆盖 3、并行 4、恢复', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'block_strategy' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务执行超时时间,单位秒', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'executor_timeout' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'最大重试次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'max_retry_times' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'并行数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'parallel_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试间隔 ( s)', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'retry_interval' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'bucket', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'bucket_index' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'是否是常驻任务', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'resident' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知告警场景配置id列表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'notify_ids' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'负责人id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'owner_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务信息', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job' +GO + +INSERT INTO sj_job (namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'fys_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', 1, N'', N'', 0, getdate(), getdate()) +GO + +-- sj_job_log_message +CREATE TABLE sj_job_log_message +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + task_id bigint NOT NULL, + message nvarchar(max) NOT NULL, + log_num int NOT NULL DEFAULT 1, + real_time bigint NOT NULL DEFAULT 0, + ext_attrs nvarchar(256) NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id) +GO +CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt) +GO +CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务信息id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'job_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务批次id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'task_batch_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'调度任务id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'task_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'调度信息', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'message' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'log_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'上报时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'real_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'调度日志', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_log_message' +GO + +-- sj_job_task +CREATE TABLE sj_job_task +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + job_id bigint NOT NULL, + task_batch_id bigint NOT NULL, + parent_id bigint NOT NULL DEFAULT 0, + task_status tinyint NOT NULL DEFAULT 0, + retry_count int NOT NULL DEFAULT 0, + mr_stage tinyint NULL DEFAULT NULL, + leaf tinyint NOT NULL DEFAULT '1', + task_name nvarchar(255) NOT NULL DEFAULT '', + client_info nvarchar(128) NULL DEFAULT NULL, + wf_context nvarchar(max) NULL DEFAULT NULL, + result_message nvarchar(max) NOT NULL, + args_str nvarchar(max) NULL DEFAULT NULL, + args_type tinyint NOT NULL DEFAULT 1, + ext_attrs nvarchar(256) NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status) +GO +CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt) +GO +CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务信息id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'job_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'调度任务id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'task_batch_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父执行器id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行的状态 0、失败 1、成功', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'task_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试次数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'retry_count' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'动态分片所处阶段 1:map 2:reduce 3:mergeReduce', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'mr_stage' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'叶子节点', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'leaf' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'task_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'客户端地址 clientId#ip:port', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'client_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流全局上下文', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'wf_context' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行结果', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'result_message' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行方法参数', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'args_str' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'参数类型 ', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'args_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务实例', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task' +GO + +-- sj_job_task_batch +CREATE TABLE sj_job_task_batch +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_node_id bigint NOT NULL DEFAULT 0, + parent_workflow_node_id bigint NOT NULL DEFAULT 0, + workflow_task_batch_id bigint NOT NULL DEFAULT 0, + task_batch_status tinyint NOT NULL DEFAULT 0, + operation_reason tinyint NOT NULL DEFAULT 0, + execution_at bigint NOT NULL DEFAULT 0, + system_task_type tinyint NOT NULL DEFAULT 3, + parent_id nvarchar(64) NOT NULL DEFAULT '', + ext_attrs nvarchar(256) NULL DEFAULT '', + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status) +GO +CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt) +GO +CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name) +GO +CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'job_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流节点id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'workflow_node_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流任务父批次id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'parent_workflow_node_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流任务批次id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'workflow_task_batch_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务批次状态 0、失败 1、成功', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'task_batch_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'操作原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'operation_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务执行时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'execution_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 3、JOB任务 4、WORKFLOW任务', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'system_task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父节点', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务批次', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_task_batch' +GO + +-- sj_job_summary +CREATE TABLE sj_job_summary +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL DEFAULT '', + business_id bigint NOT NULL, + system_task_type tinyint NOT NULL DEFAULT 3, + trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + success_num int NOT NULL DEFAULT 0, + fail_num int NOT NULL DEFAULT 0, + fail_reason nvarchar(512) NOT NULL DEFAULT '', + stop_num int NOT NULL DEFAULT 0, + stop_reason nvarchar(512) NOT NULL DEFAULT '', + cancel_num int NOT NULL DEFAULT 0, + cancel_reason nvarchar(512) NOT NULL DEFAULT '', + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id) +GO + +CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'业务id ( job_id或workflow_id)', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'business_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务类型 3、JOB任务 4、WORKFLOW任务', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'system_task_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'统计时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'trigger_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行成功-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'success_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行失败-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'fail_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'失败原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'fail_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行失败-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'stop_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'失败原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'stop_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'执行失败-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'cancel_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'失败原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'cancel_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'DashBoard_Job', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job_summary' +GO + +-- sj_retry_summary +CREATE TABLE sj_retry_summary +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL DEFAULT '', + scene_name nvarchar(50) NOT NULL DEFAULT '', + trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + running_num int NOT NULL DEFAULT 0, + finish_num int NOT NULL DEFAULT 0, + max_count_num int NOT NULL DEFAULT 0, + suspend_num int NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at) +GO + +CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'场景名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'scene_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'统计时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'trigger_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试中-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'running_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试完成-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'finish_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'重试到达最大次数-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'max_count_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'暂停重试-日志数量', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'suspend_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'DashBoard_Retry', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_retry_summary' +GO + +-- sj_workflow +CREATE TABLE sj_workflow +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + workflow_name nvarchar(64) NOT NULL, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + workflow_status tinyint NOT NULL DEFAULT 1, + trigger_type tinyint NOT NULL, + trigger_interval nvarchar(255) NOT NULL, + next_trigger_at bigint NOT NULL, + block_strategy tinyint NOT NULL DEFAULT 1, + executor_timeout int NOT NULL DEFAULT 0, + description nvarchar(256) NOT NULL DEFAULT '', + flow_info nvarchar(max) NULL DEFAULT NULL, + wf_context nvarchar(max) NULL DEFAULT NULL, + notify_ids nvarchar(128) NOT NULL DEFAULT '', + bucket_index int NOT NULL DEFAULT 0, + version int NOT NULL, + ext_attrs nvarchar(256) NULL DEFAULT '', + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt) +GO +CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'workflow_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流状态 0、关闭、1、开启', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'workflow_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'触发类型 1.CRON 表达式 2. 固定时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'trigger_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'间隔时长', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'trigger_interval' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'下次触发时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'next_trigger_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'阻塞策略 1、丢弃 2、覆盖 3、并行', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'block_strategy' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务执行超时时间,单位秒', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'executor_timeout' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'描述', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'description' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'流程信息', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'flow_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'上下文', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'wf_context' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'通知告警场景配置id列表', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'notify_ids' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'bucket', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'bucket_index' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow' +GO + +-- sj_workflow_node +CREATE TABLE sj_workflow_node +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + node_name nvarchar(64) NOT NULL, + group_name nvarchar(64) NOT NULL, + job_id bigint NOT NULL, + workflow_id bigint NOT NULL, + node_type tinyint NOT NULL DEFAULT 1, + expression_type tinyint NOT NULL DEFAULT 0, + fail_strategy tinyint NOT NULL DEFAULT 1, + workflow_node_status tinyint NOT NULL DEFAULT 1, + priority_level int NOT NULL DEFAULT 1, + node_info nvarchar(max) NULL DEFAULT NULL, + version int NOT NULL, + ext_attrs nvarchar(256) NULL DEFAULT '', + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt) +GO +CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'节点名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'node_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务信息id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'job_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'workflow_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'1、任务节点 2、条件节点', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'1、SpEl、2、Aviator 3、QL', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'expression_type' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'失败策略 1、跳过 2、阻塞', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'fail_strategy' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流节点状态 0、关闭、1、开启', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'workflow_node_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'优先级', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'priority_level' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'节点信息 ', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'node_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流节点', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_node' +GO + +-- sj_workflow_task_batch +CREATE TABLE sj_workflow_task_batch +( + id bigint NOT NULL PRIMARY KEY IDENTITY, + namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + group_name nvarchar(64) NOT NULL, + workflow_id bigint NOT NULL, + task_batch_status tinyint NOT NULL DEFAULT 0, + operation_reason tinyint NOT NULL DEFAULT 0, + flow_info nvarchar(max) NULL DEFAULT NULL, + wf_context nvarchar(max) NULL DEFAULT NULL, + execution_at bigint NOT NULL DEFAULT 0, + ext_attrs nvarchar(256) NULL DEFAULT '', + version int NOT NULL DEFAULT 1, + deleted tinyint NOT NULL DEFAULT 0, + create_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP, + update_dt datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP +) +GO + +CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status) +GO +CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt) +GO +CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name) +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'命名空间id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'namespace_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'组名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'group_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流任务id', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'workflow_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务批次状态 0、失败 1、成功', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'task_batch_status' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'操作原因', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'operation_reason' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'流程信息', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'flow_info' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'全局上下文', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'wf_context' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'任务执行时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'execution_at' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'ext_attrs' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本号', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'逻辑删除 1、删除', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'deleted' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'create_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'修改时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch', + 'COLUMN', N'update_dt' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'工作流批次', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow_task_batch' +GO diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql new file mode 100644 index 0000000..88ee9a2 --- /dev/null +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -0,0 +1,3641 @@ +create table sys_social +( + id bigint NOT NULL, + user_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + auth_id nvarchar(255) NOT NULL, + source nvarchar(255) NOT NULL, + open_id nvarchar(255) NULL, + user_name nvarchar(30) NOT NULL, + nick_name nvarchar(30) DEFAULT ('') NULL, + email nvarchar(255) DEFAULT ('') NULL, + avatar nvarchar(500) DEFAULT ('') NULL, + access_token nvarchar(2000) NOT NULL, + expire_in bigint NULL, + refresh_token nvarchar(2000) NULL, + access_code nvarchar(255) NULL, + union_id nvarchar(255) NULL, + scope nvarchar(255) NULL, + token_type nvarchar(255) NULL, + id_token nvarchar(2000) NULL, + mac_algorithm nvarchar(255) NULL, + mac_key nvarchar(255) NULL, + code nvarchar(255) NULL, + oauth_token nvarchar(255) NULL, + oauth_token_secret nvarchar(255) NULL, + create_dept bigint, + create_by bigint, + create_time datetime2(7), + update_by bigint, + update_time datetime2(7), + del_flag nchar DEFAULT ('0') NULL, + CONSTRAINT PK__sys_social__B21E8F2427725F8A PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台+平台唯一id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'auth_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户来源' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'source' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台编号唯一id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'open_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户昵称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'nick_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'头像地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'avatar' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权令牌' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'access_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权令牌的有效期,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'expire_in' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'刷新令牌,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'refresh_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台的授权信息,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'access_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的 unionid' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'union_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'授予的权限,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'scope' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'个别平台的授权信息,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'token_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id token,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'id_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'小米平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'mac_algorithm' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'小米平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'mac_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权code,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'Twitter平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'oauth_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'Twitter平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'oauth_token_secret' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'社会化关系表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social' +GO + +CREATE TABLE sys_tenant +( + id bigint NOT NULL, + tenant_id nvarchar(20) NOT NULL, + contact_user_name nvarchar(20) NULL, + contact_phone nvarchar(20) NULL, + company_name nvarchar(30) NULL, + license_number nvarchar(30) NULL, + address nvarchar(200) NULL, + intro nvarchar(200) NULL, + domain nvarchar(200) NULL, + remark nvarchar(200) NULL, + package_id bigint NULL, + expire_time datetime2(7) NULL, + account_count int DEFAULT ((-1)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_tenant__B21E8F2427725F8A PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系人' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'contact_user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系电话' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'contact_phone' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'企业名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'company_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'统一社会信用代码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'license_number' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'address' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'企业简介' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'intro' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'域名' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'domain' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'package_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'过期时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'expire_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户数量(-1不限制)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'account_count' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant' +GO + +INSERT sys_tenant VALUES (1, N'000000', N'管理组', N'15888888888', N'XXX有限公司', NULL, NULL, N'多租户通用后台管理管理系统', NULL, NULL, NULL, NULL, -1, N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO + + +CREATE TABLE sys_tenant_package +( + package_id bigint NOT NULL, + package_name nvarchar(20) NOT NULL, + menu_ids nvarchar(20) NULL, + remark nvarchar(200) NULL, + menu_check_strictly tinyint DEFAULT ((1)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_tenant_package__B21E8F2427725F8A PRIMARY KEY CLUSTERED (package_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'package_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'套餐名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'package_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'关联菜单id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'menu_ids' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户套餐表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_tenant_package' +GO + + +CREATE TABLE gen_table +( + table_id bigint NOT NULL, + data_name nvarchar(200) DEFAULT '' NULL, + table_name nvarchar(200) DEFAULT '' NULL, + table_comment nvarchar(500) DEFAULT '' NULL, + sub_table_name nvarchar(64) NULL, + sub_table_fk_name nvarchar(64) NULL, + class_name nvarchar(100) DEFAULT '' NULL, + tpl_category nvarchar(200) DEFAULT ('crud') NULL, + package_name nvarchar(100) NULL, + module_name nvarchar(30) NULL, + business_name nvarchar(30) NULL, + function_name nvarchar(50) NULL, + function_author nvarchar(50) NULL, + gen_type nchar(1) DEFAULT ('0') NULL, + gen_path nvarchar(200) DEFAULT ('/') NULL, + options nvarchar(1000) NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__gen_tabl__B21E8F2427725F8A PRIMARY KEY CLUSTERED (table_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'数据源名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'data_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'table_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'关联子表的表名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'子表关联的外键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'sub_table_fk_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'实体类名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'class_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'使用的模板(crud单表操作 tree树表操作)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'tpl_category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成包路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'package_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成模块名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'module_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成业务名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'business_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成功能作者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'function_author' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成代码方式(0zip压缩包 1自定义路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'生成路径(不填默认项目路径)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'gen_path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'其它生成选项' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'options' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table' +GO + +CREATE TABLE gen_table_column +( + column_id bigint NOT NULL, + table_id bigint NULL, + column_name nvarchar(200) NULL, + column_comment nvarchar(500) NULL, + column_type nvarchar(100) NULL, + java_type nvarchar(500) NULL, + java_field nvarchar(200) NULL, + is_pk nchar(1) NULL, + is_increment nchar(1) NULL, + is_required nchar(1) NULL, + is_insert nchar(1) NULL, + is_edit nchar(1) NULL, + is_list nchar(1) NULL, + is_query nchar(1) NULL, + query_type nvarchar(200) DEFAULT ('EQ') NULL, + html_type nvarchar(200) NULL, + dict_type nvarchar(200) DEFAULT '' NULL, + sort int NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__gen_tabl__E301851F2E68B4E8 PRIMARY KEY CLUSTERED (column_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'归属表编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'table_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列描述' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_comment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'列类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'column_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'JAVA字段名' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'java_field' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否主键(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_pk' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否自增(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_increment' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否必填(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_required' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为插入字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_insert' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否编辑字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_edit' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否列表字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_list' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否查询字段(1是)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'is_query' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'查询方式(等于、不等于、大于、小于、范围)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'query_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'html_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'代码生成业务表字段' , + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table_column' +GO + +CREATE TABLE sys_config +( + config_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT '000000' NULL, + config_name nvarchar(100) DEFAULT '' NULL, + config_key nvarchar(100) DEFAULT '' NULL, + config_value nvarchar(500) DEFAULT '' NULL, + config_type nchar(1) DEFAULT ('N') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_conf__4AD1BFF182643682 PRIMARY KEY CLUSTERED (config_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键名' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统内置(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'config_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'参数配置表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_config' +GO + +INSERT sys_config VALUES (1, N'000000', N'主框架页-默认皮肤样式名称', N'sys.index.skinName', N'skin-blue', N'Y', 103, 1, getdate(), NULL, NULL, N'蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow') +GO +INSERT sys_config VALUES (2, N'000000', N'用户管理-账号初始密码', N'sys.user.initPassword', N'123456', N'Y', 103, 1, getdate(), NULL, NULL, N'初始化密码 123456') +GO +INSERT sys_config VALUES (3, N'000000', N'主框架页-侧边栏主题', N'sys.index.sideTheme', N'theme-dark', N'Y', 103, 1, getdate(), NULL, NULL, N'深色主题theme-dark,浅色主题theme-light') +GO +INSERT sys_config VALUES (5, N'000000', N'账号自助-是否开启用户注册功能', N'sys.account.registerUser', N'false', N'Y', 103, 1, getdate(), NULL, NULL, N'是否开启注册用户功能(true开启,false关闭)') +GO +INSERT sys_config VALUES (11, N'000000', N'OSS预览列表资源开关', N'sys.oss.previewListResource', N'true', N'Y', 103, 1, getdate(), NULL, NULL, N'true:开启, false:关闭'); +GO + +CREATE TABLE sys_dept +( + dept_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + parent_id bigint DEFAULT ((0)) NULL, + ancestors nvarchar(500)DEFAULT '' NULL, + dept_name nvarchar(30) NULL, + dept_category nvarchar(100) DEFAULT '' NULL, + order_num int DEFAULT ((0)) NULL, + leader bigint NULL, + phone nvarchar(11) NULL, + email nvarchar(50) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_dept__DCA659747DE13804 PRIMARY KEY CLUSTERED (dept_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'祖级列表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'ancestors' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门类别编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'负责人' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'leader' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'联系电话' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'phone' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept' +GO + +INSERT sys_dept VALUES (100, N'000000', 0, N'0', N'XXX科技', NULL, 0, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (101, N'000000', 100, N'0,100', N'深圳总公司', NULL, 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (102, N'000000', 100, N'0,100', N'长沙分公司', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (103, N'000000', 101, N'0,100,101', N'研发部门', NULL, 1, 1, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (104, N'000000', 101, N'0,100,101', N'市场部门', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (105, N'000000', 101, N'0,100,101', N'测试部门', NULL, 3, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (106, N'000000', 101, N'0,100,101', N'财务部门', NULL, 4, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (107, N'000000', 101, N'0,100,101', N'运维部门', NULL, 5, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (108, N'000000', 102, N'0,100,102', N'市场部门', NULL, 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO +INSERT sys_dept VALUES (109, N'000000', 102, N'0,100,102', N'财务部门', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL) +GO + +CREATE TABLE sys_dict_data +( + dict_code bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dict_sort int DEFAULT ((0)) NULL, + dict_label nvarchar(100) DEFAULT '' NULL, + dict_value nvarchar(100) DEFAULT '' NULL, + dict_type nvarchar(100) DEFAULT '' NULL, + css_class nvarchar(100) NULL, + list_class nvarchar(100) NULL, + is_default nchar(1) DEFAULT ('N') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_dict__19CBC34B661AF3B3 PRIMARY KEY CLUSTERED (dict_code) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典排序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典标签' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_label' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典键值' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_value' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'样式属性(其他样式扩展)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'css_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'表格回显样式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'list_class' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否默认(Y是 N否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'is_default' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典数据表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_data' +GO + +INSERT sys_dict_data VALUES (1, N'000000', 1, N'男', N'0', N'sys_user_sex', N'', N'', N'Y', 103, 1, getdate(), NULL, NULL, N'性别男') +GO +INSERT sys_dict_data VALUES (2, N'000000', 2, N'女', N'1', N'sys_user_sex', N'', N'', N'N', 103, 1, getdate(), NULL, NULL, N'性别女') +GO +INSERT sys_dict_data VALUES (3, N'000000', 3, N'未知', N'2', N'sys_user_sex', N'', N'', N'N', 103, 1, getdate(), NULL, NULL, N'性别未知') +GO +INSERT sys_dict_data VALUES (4, N'000000', 1, N'显示', N'0', N'sys_show_hide', N'', N'primary', N'Y', 103, 1, getdate(), NULL, NULL, N'显示菜单') +GO +INSERT sys_dict_data VALUES (5, N'000000', 2, N'隐藏', N'1', N'sys_show_hide', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'隐藏菜单') +GO +INSERT sys_dict_data VALUES (6, N'000000', 1, N'正常', N'0', N'sys_normal_disable', N'', N'primary', N'Y', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (7, N'000000', 2, N'停用', N'1', N'sys_normal_disable', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO +INSERT sys_dict_data VALUES (8, N'000000', 1, N'正常', N'0', N'sys_job_status', N'', N'primary', N'Y', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (9, N'000000', 2, N'暂停', N'1', N'sys_job_status', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO +INSERT sys_dict_data VALUES (10, N'000000', 1, N'默认', N'DEFAULT', N'sys_job_group', N'', N'', N'Y', 103, 1, getdate(), NULL, NULL, N'默认分组') +GO +INSERT sys_dict_data VALUES (11, N'000000', 2, N'系统', N'SYSTEM', N'sys_job_group', N'', N'', N'N', 103, 1, getdate(), NULL, NULL, N'系统分组') +GO +INSERT sys_dict_data VALUES (12, N'000000', 1, N'是', N'Y', N'sys_yes_no', N'', N'primary', N'Y', 103, 1, getdate(), NULL, NULL, N'系统默认是') +GO +INSERT sys_dict_data VALUES (13, N'000000', 2, N'否', N'N', N'sys_yes_no', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'系统默认否') +GO +INSERT sys_dict_data VALUES (14, N'000000', 1, N'通知', N'1', N'sys_notice_type', N'', N'warning', N'Y', 103, 1, getdate(), NULL, NULL, N'通知') +GO +INSERT sys_dict_data VALUES (15, N'000000', 2, N'公告', N'2', N'sys_notice_type', N'', N'success', N'N', 103, 1, getdate(), NULL, NULL, N'公告') +GO +INSERT sys_dict_data VALUES (16, N'000000', 1, N'正常', N'0', N'sys_notice_status', N'', N'primary', N'Y', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (17, N'000000', 2, N'关闭', N'1', N'sys_notice_status', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'关闭状态') +GO +INSERT sys_dict_data VALUES (29, N'000000', 99, N'其他', N'0', N'sys_oper_type', N'', N'info', N'N', 103, 1, getdate(), NULL, NULL, N'其他操作'); +GO +INSERT sys_dict_data VALUES (18, N'000000', 1, N'新增', N'1', N'sys_oper_type', N'', N'info', N'N', 103, 1, getdate(), NULL, NULL, N'新增操作') +GO +INSERT sys_dict_data VALUES (19, N'000000', 2, N'修改', N'2', N'sys_oper_type', N'', N'info', N'N', 103, 1, getdate(), NULL, NULL, N'修改操作') +GO +INSERT sys_dict_data VALUES (20, N'000000', 3, N'删除', N'3', N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'删除操作') +GO +INSERT sys_dict_data VALUES (21, N'000000', 4, N'授权', N'4', N'sys_oper_type', N'', N'primary', N'N', 103, 1, getdate(), NULL, NULL, N'授权操作') +GO +INSERT sys_dict_data VALUES (22, N'000000', 5, N'导出', N'5', N'sys_oper_type', N'', N'warning', N'N', 103, 1, getdate(), NULL, NULL, N'导出操作') +GO +INSERT sys_dict_data VALUES (23, N'000000', 6, N'导入', N'6', N'sys_oper_type', N'', N'warning', N'N', 103, 1, getdate(), NULL, NULL, N'导入操作') +GO +INSERT sys_dict_data VALUES (24, N'000000', 7, N'强退', N'7', N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'强退操作') +GO +INSERT sys_dict_data VALUES (25, N'000000', 8, N'生成代码', N'8', N'sys_oper_type', N'', N'warning', N'N', 103, 1, getdate(), NULL, NULL, N'生成操作') +GO +INSERT sys_dict_data VALUES (26, N'000000', 9, N'清空数据', N'9', N'sys_oper_type', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'清空操作') +GO +INSERT sys_dict_data VALUES (27, N'000000', 1, N'成功', N'0', N'sys_common_status', N'', N'primary', N'N', 103, 1, getdate(), NULL, NULL, N'正常状态') +GO +INSERT sys_dict_data VALUES (28, N'000000', 2, N'失败', N'1', N'sys_common_status', N'', N'danger', N'N', 103, 1, getdate(), NULL, NULL, N'停用状态') +GO +INSERT sys_dict_data VALUES (30, N'000000', 0, N'密码认证', N'password', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'密码认证') +GO +INSERT sys_dict_data VALUES (31, N'000000', 0, N'短信认证', N'sms', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'短信认证') +GO +INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'邮件认证') +GO +INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序认证') +GO +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +GO +INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'pc', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'PC') +GO +INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'android', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'安卓') +GO +INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'ios', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'iOS') +GO +INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'xcx', N'sys_device_type', N'', N'default', N'N', 103, 1, getdate(), NULL, NULL, N'小程序') +GO + +CREATE TABLE sys_dict_type +( + dict_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dict_name nvarchar(100) DEFAULT '' NULL, + dict_type nvarchar(100) DEFAULT '' NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_dict__3BD4186C409C5391 PRIMARY KEY CLUSTERED (dict_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX sys_dict_type_index1 ON sys_dict_type (tenant_id, dict_type) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'dict_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'字典类型表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dict_type' +GO + +INSERT sys_dict_type VALUES (1, N'000000', N'用户性别', N'sys_user_sex', 103, 1, getdate(), NULL, NULL, N'用户性别列表') +GO +INSERT sys_dict_type VALUES (2, N'000000', N'菜单状态', N'sys_show_hide', 103, 1, getdate(), NULL, NULL, N'菜单状态列表') +GO +INSERT sys_dict_type VALUES (3, N'000000', N'系统开关', N'sys_normal_disable', 103, 1, getdate(), NULL, NULL, N'系统开关列表') +GO +INSERT sys_dict_type VALUES (4, N'000000', N'任务状态', N'sys_job_status', 103, 1, getdate(), NULL, NULL, N'任务状态列表') +GO +INSERT sys_dict_type VALUES (5, N'000000', N'任务分组', N'sys_job_group', 103, 1, getdate(), NULL, NULL, N'任务分组列表') +GO +INSERT sys_dict_type VALUES (6, N'000000', N'系统是否', N'sys_yes_no', 103, 1, getdate(), NULL, NULL, N'系统是否列表') +GO +INSERT sys_dict_type VALUES (7, N'000000', N'通知类型', N'sys_notice_type', 103, 1, getdate(), NULL, NULL, N'通知类型列表') +GO +INSERT sys_dict_type VALUES (8, N'000000', N'通知状态', N'sys_notice_status', 103, 1, getdate(), NULL, NULL, N'通知状态列表') +GO +INSERT sys_dict_type VALUES (9, N'000000', N'操作类型', N'sys_oper_type', 103, 1, getdate(), NULL, NULL, N'操作类型列表') +GO +INSERT sys_dict_type VALUES (10, N'000000', N'系统状态', N'sys_common_status', 103, 1, getdate(), NULL, NULL, N'登录状态列表') +GO +INSERT sys_dict_type VALUES (11, N'000000', N'授权类型', N'sys_grant_type', 103, 1, getdate(), NULL, NULL, N'认证授权类型') +GO +INSERT sys_dict_type VALUES (12, N'000000', N'设备类型', N'sys_device_type', 103, 1, getdate(), NULL, NULL, N'客户端设备类型') +GO + +CREATE TABLE sys_logininfor +( + info_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + user_name nvarchar(50) DEFAULT '' NULL, + client_key nvarchar(32) DEFAULT '' NULL, + device_type nvarchar(32) DEFAULT '' NULL, + ipaddr nvarchar(128) DEFAULT '' NULL, + login_location nvarchar(255) DEFAULT '' NULL, + browser nvarchar(50) DEFAULT '' NULL, + os nvarchar(50) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + msg nvarchar(255) DEFAULT '' NULL, + login_time datetime2(7) NULL, + CONSTRAINT PK__sys_logi__3D8A9C1A1854AE10 PRIMARY KEY CLUSTERED (info_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX idx_sys_logininfor_s ON sys_logininfor (status) +GO +CREATE NONCLUSTERED INDEX idx_sys_logininfor_lt ON sys_logininfor (login_time) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'info_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'客户端' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'client_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'设备类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'device_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录IP地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'ipaddr' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'浏览器类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'browser' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作系统' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'os' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录状态(0成功 1失败)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'提示消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'访问时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'login_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'系统访问记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor' +GO + +CREATE TABLE sys_menu +( + menu_id bigint NOT NULL, + menu_name nvarchar(50) NOT NULL, + parent_id bigint DEFAULT ((0)) NULL, + order_num int DEFAULT ((0)) NULL, + path nvarchar(200) DEFAULT '' NULL, + component nvarchar(255) NULL, + query_param nvarchar(255) NULL, + is_frame int DEFAULT ((1)) NULL, + is_cache int DEFAULT ((0)) NULL, + menu_type nchar(1) DEFAULT '' NULL, + visible nchar(1) DEFAULT ((0)) NULL, + status nchar(1) DEFAULT ((0)) NULL, + perms nvarchar(100) NULL, + icon nvarchar(100) DEFAULT ('#') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) DEFAULT '' NULL, + CONSTRAINT PK__sys_menu__4CA0FADCF8545C58 PRIMARY KEY CLUSTERED (menu_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'父菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'parent_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'order_num' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'组件路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'component' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'路由参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'query_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否为外链(0是 1否)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_frame' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'是否缓存(0缓存 1不缓存)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'is_cache' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单类型(M目录 C菜单 F按钮)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'menu_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示状态(0显示 1隐藏)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'visible' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'权限标识' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'perms' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单图标' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'icon' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单权限表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_menu' +GO + +INSERT sys_menu VALUES (1, N'系统管理', 0, 1, N'system', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'system', 103, 1, getdate(), NULL, NULL, N'系统管理目录') +GO +INSERT sys_menu VALUES (6, N'租户管理', 0, 2, N'tenant', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'chart', 103, 1, getdate(), NULL, NULL, N'租户管理目录') +GO +INSERT sys_menu VALUES (2, N'系统监控', 0, 3, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, getdate(), NULL, NULL, N'系统监控目录') +GO +INSERT sys_menu VALUES (3, N'系统工具', 0, 4, N'tool', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'tool', 103, 1, getdate(), NULL, NULL, N'系统工具目录') +GO +INSERT sys_menu VALUES (4, N'PLUS官网', 0, 5, N'https://gitee.com/dromara/fys-Vue-Plus', null, N'', 0, 0, N'M', N'0', N'0', N'', N'guide', 103, 1, getdate(), null, null, N'fys-Vue-Plus官网地址'); +GO +INSERT sys_menu VALUES (5, N'测试菜单', 0, 5, N'demo', NULL, N'', 1, 0, N'M', N'0', N'0', NULL, N'star', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (100, N'用户管理', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', 103, 1, getdate(), NULL, NULL, N'用户管理菜单') +GO +INSERT sys_menu VALUES (101, N'角色管理', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', 103, 1, getdate(), NULL, NULL, N'角色管理菜单') +GO +INSERT sys_menu VALUES (102, N'菜单管理', 1, 3, N'menu', N'system/menu/index', N'', 1, 0, N'C', N'0', N'0', N'system:menu:list', N'tree-table', 103, 1, getdate(), NULL, NULL, N'菜单管理菜单') +GO +INSERT sys_menu VALUES (103, N'部门管理', 1, 4, N'dept', N'system/dept/index', N'', 1, 0, N'C', N'0', N'0', N'system:dept:list', N'tree', 103, 1, getdate(), NULL, NULL, N'部门管理菜单') +GO +INSERT sys_menu VALUES (104, N'岗位管理', 1, 5, N'post', N'system/post/index', N'', 1, 0, N'C', N'0', N'0', N'system:post:list', N'post', 103, 1, getdate(), NULL, NULL, N'岗位管理菜单') +GO +INSERT sys_menu VALUES (105, N'字典管理', 1, 6, N'dict', N'system/dict/index', N'', 1, 0, N'C', N'0', N'0', N'system:dict:list', N'dict', 103, 1, getdate(), NULL, NULL, N'字典管理菜单') +GO +INSERT sys_menu VALUES (106, N'参数设置', 1, 7, N'config', N'system/config/index', N'', 1, 0, N'C', N'0', N'0', N'system:config:list', N'edit', 103, 1, getdate(), NULL, NULL, N'参数设置菜单') +GO +INSERT sys_menu VALUES (107, N'通知公告', 1, 8, N'notice', N'system/notice/index', N'', 1, 0, N'C', N'0', N'0', N'system:notice:list', N'message', 103, 1, getdate(), NULL, NULL, N'通知公告菜单') +GO +INSERT sys_menu VALUES (108, N'日志管理', 1, 9, N'log', N'', N'', 1, 0, N'M', N'0', N'0', N'', N'log', 103, 1, getdate(), NULL, NULL, N'日志管理菜单') +GO +INSERT sys_menu VALUES (109, N'在线用户', 2, 1, N'online', N'monitor/online/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:online:list', N'online', 103, 1, getdate(), NULL, NULL, N'在线用户菜单') +GO +INSERT sys_menu VALUES (113, N'缓存监控', 2, 5, N'cache', N'monitor/cache/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:cache:list', N'redis', 103, 1, getdate(), NULL, NULL, N'缓存监控菜单') +GO +INSERT sys_menu VALUES (115, N'代码生成', 3, 2, N'gen', N'tool/gen/index', N'', 1, 0, N'C', N'0', N'0', N'tool:gen:list', N'code', 103, 1, getdate(), NULL, NULL, N'代码生成菜单') +GO +INSERT sys_menu VALUES (121, N'租户管理', 6, 1, N'tenant', N'system/tenant/index', N'', 1, 0, N'C', N'0', N'0', N'system:tenant:list', N'code', 103, 1, getdate(), NULL, NULL, N'租户管理菜单') +GO +INSERT sys_menu VALUES (122, N'租户套餐管理', 6, 2, N'tenantPackage', N'system/tenantPackage/index', N'', 1, 0, N'C', N'0', N'0', N'system:tenantPackage:list', N'code', 103, 1, getdate(), NULL, NULL, N'租户套餐管理菜单') +GO +INSERT sys_menu VALUES (123, N'客户端管理', 1, 11, N'client', N'system/client/index', N'', 1, 0, N'C', N'0', N'0', N'system:client:list', N'international', 103, 1, getdate(), NULL, NULL, N'客户端管理菜单') +GO +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO + +INSERT sys_menu VALUES (117, N'Admin监控', 2, 5, N'Admin', N'monitor/admin/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:admin:list', N'dashboard', 103, 1, getdate(), NULL, NULL, N'Admin监控菜单'); +GO +INSERT sys_menu VALUES (118, N'文件管理', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', 103, 1, getdate(), NULL, NULL, N'文件管理菜单'); +GO +INSERT sys_menu VALUES (120, N'任务调度中心', 2, 5, N'snailjob', N'monitor/snailjob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:snailjob:list', N'job', 103, 1, getdate(), NULL, NULL, N'SnailJob控制台菜单'); +GO +INSERT sys_menu VALUES (500, N'操作日志', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', 103, 1, getdate(), NULL, NULL, N'操作日志菜单') +GO +INSERT sys_menu VALUES (501, N'登录日志', 108, 2, N'logininfor', N'monitor/logininfor/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:logininfor:list', N'logininfor', 103, 1, getdate(), NULL, NULL, N'登录日志菜单') +GO +INSERT sys_menu VALUES (1001, N'用户查询', 100, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1002, N'用户新增', 100, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1003, N'用户修改', 100, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1004, N'用户删除', 100, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1005, N'用户导出', 100, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1006, N'用户导入', 100, 6, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:import', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1007, N'重置密码', 100, 7, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:user:resetPwd', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1008, N'角色查询', 101, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1009, N'角色新增', 101, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1010, N'角色修改', 101, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1011, N'角色删除', 101, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1012, N'角色导出', 101, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:role:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1013, N'菜单查询', 102, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1014, N'菜单新增', 102, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1015, N'菜单修改', 102, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1016, N'菜单删除', 102, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:menu:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1017, N'部门查询', 103, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1018, N'部门新增', 103, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1019, N'部门修改', 103, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1020, N'部门删除', 103, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dept:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1021, N'岗位查询', 104, 1, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1022, N'岗位新增', 104, 2, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1023, N'岗位修改', 104, 3, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1024, N'岗位删除', 104, 4, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1025, N'岗位导出', 104, 5, N'', N'', N'', 1, 0, N'F', N'0', N'0', N'system:post:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1026, N'字典查询', 105, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1027, N'字典新增', 105, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1028, N'字典修改', 105, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1029, N'字典删除', 105, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1030, N'字典导出', 105, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:dict:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1031, N'参数查询', 106, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1032, N'参数新增', 106, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1033, N'参数修改', 106, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1034, N'参数删除', 106, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1035, N'参数导出', 106, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:config:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1036, N'公告查询', 107, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1037, N'公告新增', 107, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:add', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1038, N'公告修改', 107, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1039, N'公告删除', 107, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:notice:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1040, N'操作查询', 500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1041, N'操作删除', 500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1042, N'日志导出', 500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:operlog:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1043, N'登录查询', 501, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1044, N'登录删除', 501, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1045, N'日志导出', 501, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:export', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1050, N'账户解锁', 501, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:logininfor:unlock', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1046, N'在线查询', 109, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1047, N'批量强退', 109, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:batchLogout', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1048, N'单条强退', 109, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'monitor:online:forceLogout', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1055, N'生成查询', 115, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:query', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1056, N'生成修改', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1057, N'生成删除', 115, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:remove', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1058, N'导入代码', 115, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:import', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1059, N'预览代码', 115, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:preview', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_menu VALUES (1060, N'生成代码', 115, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'tool:gen:code', N'#', 103, 1, getdate(), NULL, NULL, N'') +GO +-- oss相关按钮 +INSERT sys_menu VALUES (1600, N'文件查询', 118, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1601, N'文件上传', 118, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:upload', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1602, N'文件下载', 118, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:download', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1603, N'文件删除', 118, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:oss:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1620, N'配置列表', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1621, N'配置添加', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1622, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1623, N'配置删除', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 租户管理相关按钮 +INSERT sys_menu VALUES (1606, N'租户查询', 121, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1607, N'租户新增', 121, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1608, N'租户修改', 121, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1609, N'租户删除', 121, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1610, N'租户导出', 121, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenant:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 租户套餐管理相关按钮 +INSERT sys_menu VALUES (1611, N'租户套餐查询', 122, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1612, N'租户套餐新增', 122, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1613, N'租户套餐修改', 122, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1614, N'租户套餐删除', 122, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1615, N'租户套餐导出', 122, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:tenantPackage:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 客户端管理按钮 +INSERT sys_menu VALUES (1061, N'客户端管理查询', 123, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1062, N'客户端管理新增', 123, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1063, N'客户端管理修改', 123, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1064, N'客户端管理删除', 123, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1065, N'客户端管理导出', 123, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +-- 测试菜单 +INSERT sys_menu VALUES (1500, N'测试单表', 5, 1, N'demo', N'demo/demo/index', N'', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', 103, 1, getdate(), NULL, NULL, N'测试单表菜单'); +GO +INSERT sys_menu VALUES (1501, N'测试单表查询', 1500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1502, N'测试单表新增', 1500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1503, N'测试单表修改', 1500, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1504, N'测试单表删除', 1500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1505, N'测试单表导出', 1500, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + +INSERT sys_menu VALUES (1506, N'测试树表', 5, 1, N'tree', N'demo/tree/index', N'', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', 103, 1, getdate(), NULL, NULL, N'测试树表菜单'); +GO +INSERT sys_menu VALUES (1507, N'测试树表查询', 1506, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1508, N'测试树表新增', 1506, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1509, N'测试树表修改', 1506, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1510, N'测试树表删除', 1506, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1511, N'测试树表导出', 1506, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + +CREATE TABLE sys_notice +( + notice_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + notice_title nvarchar(50) NOT NULL, + notice_type nchar(1) NOT NULL, + notice_content nvarchar(max) NULL, + status nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(255) NULL, + CONSTRAINT PK__sys_noti__3E82A5DB0EC94801 PRIMARY KEY CLUSTERED (notice_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +TEXTIMAGE_ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告类型(1通知 2公告)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告内容' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'notice_content' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'公告状态(0正常 1关闭)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'通知公告表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_notice' +GO + +INSERT sys_notice VALUES (1, N'000000', N'温馨提醒:2018-07-01 若依新版本发布啦', N'2', N'新版本内容', N'0', 103, 1, getdate(), NULL, NULL, N'管理员') +GO +INSERT sys_notice VALUES (2, N'000000', N'维护通知:2018-07-01 若依系统凌晨维护', N'1', N'维护内容', N'0', 103, 1, getdate(), NULL, NULL, N'管理员') +GO + +CREATE TABLE sys_oper_log +( + oper_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + title nvarchar(50) DEFAULT '' NULL, + business_type int DEFAULT ((0)) NULL, + method nvarchar(100) DEFAULT '' NULL, + request_method nvarchar(10) DEFAULT '' NULL, + operator_type int DEFAULT ((0)) NULL, + oper_name nvarchar(50) DEFAULT '' NULL, + dept_name nvarchar(50) DEFAULT '' NULL, + oper_url nvarchar(255) DEFAULT '' NULL, + oper_ip nvarchar(128) DEFAULT '' NULL, + oper_location nvarchar(255) DEFAULT '' NULL, + oper_param nvarchar(4000) DEFAULT '' NULL, + json_result nvarchar(4000) DEFAULT '' NULL, + status int DEFAULT ((0)) NULL, + error_msg nvarchar(4000) DEFAULT '' NULL, + oper_time datetime2(7) NULL, + cost_time bigint DEFAULT ((0)) NULL, + CONSTRAINT PK__sys_oper__34723BF9BD954573 PRIMARY KEY CLUSTERED (oper_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX idx_sys_oper_log_bt ON sys_oper_log (business_type) +GO +CREATE NONCLUSTERED INDEX idx_sys_oper_log_s ON sys_oper_log (status) +GO +CREATE NONCLUSTERED INDEX idx_sys_oper_log_ot ON sys_oper_log (oper_time) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'日志主键' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'模块标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'业务类型(0其它 1新增 2修改 3删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'business_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'方法名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求方式' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'request_method' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作类别(0其它 1后台用户 2手机端用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'operator_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作人员' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'dept_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求URL' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_url' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'主机地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作地点' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_location' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'请求参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_param' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'返回参数' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'json_result' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作状态(0正常 1异常)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'错误消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'error_msg' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'oper_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消耗时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log', + 'COLUMN', N'cost_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'操作日志记录' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oper_log' +GO + +CREATE TABLE sys_post +( + post_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dept_id bigint NOT NULL, + post_code nvarchar(64) NOT NULL, + post_category nvarchar(100) NULL, + post_name nvarchar(50) NOT NULL, + post_sort int NOT NULL, + status nchar(1) NOT NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_post__3ED7876668E2D081 PRIMARY KEY CLUSTERED (post_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位类别编码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post' +GO + +INSERT sys_post VALUES (1, N'000000', 103, N'ceo', NULL, N'董事长', 1, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (2, N'000000', 100, N'se', NULL, N'项目经理', 2, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (3, N'000000', 100, N'hr', NULL, N'人力资源', 3, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO +INSERT sys_post VALUES (4, N'000000', 100, N'user', NULL, N'普通员工', 4, N'0', 103, 1, getdate(), NULL, NULL, N'') +GO + +CREATE TABLE sys_role +( + role_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + role_name nvarchar(30) NOT NULL, + role_key nvarchar(100) NOT NULL, + role_sort int NOT NULL, + data_scope nchar(1) DEFAULT ('1') NULL, + menu_check_strictly tinyint DEFAULT ((1)) NULL, + dept_check_strictly tinyint DEFAULT ((1)) NULL, + status nchar(1) NOT NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_role__760965CCF9383145 PRIMARY KEY CLUSTERED (role_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色名称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色权限字符串' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'显示顺序' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'role_sort' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'data_scope' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'menu_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门树选择项是否关联显示' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'dept_check_strictly' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role' +GO + +INSERT sys_role VALUES (1, N'000000', N'超级管理员', N'superadmin', 1, N'1', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'超级管理员') +GO +INSERT sys_role VALUES (3, N'000000', N'本部门及以下', N'test1', 3, N'4', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_role VALUES (4, N'000000', N'仅本人', N'test2', 4, N'5', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N''); +GO + +CREATE TABLE sys_role_dept +( + role_id bigint NOT NULL, + dept_id bigint NOT NULL, + CONSTRAINT PK__sys_role__2BC3005BABBCA08A PRIMARY KEY CLUSTERED (role_id, dept_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和部门关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_dept' +GO + +CREATE TABLE sys_role_menu +( + role_id bigint NOT NULL, + menu_id bigint NOT NULL, + CONSTRAINT PK__sys_role__A2C36A6187BA4B17 PRIMARY KEY CLUSTERED (role_id, menu_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'菜单ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu', + 'COLUMN', N'menu_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色和菜单关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_role_menu' +GO + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +INSERT sys_role_menu VALUES (3, 1); +GO +INSERT sys_role_menu VALUES (3, 5); +GO +INSERT sys_role_menu VALUES (3, 100); +GO +INSERT sys_role_menu VALUES (3, 101); +GO +INSERT sys_role_menu VALUES (3, 102); +GO +INSERT sys_role_menu VALUES (3, 103); +GO +INSERT sys_role_menu VALUES (3, 104); +GO +INSERT sys_role_menu VALUES (3, 105); +GO +INSERT sys_role_menu VALUES (3, 106); +GO +INSERT sys_role_menu VALUES (3, 107); +GO +INSERT sys_role_menu VALUES (3, 108); +GO +INSERT sys_role_menu VALUES (3, 118); +GO +INSERT sys_role_menu VALUES (3, 123); +GO +INSERT sys_role_menu VALUES (3, 130); +GO +INSERT sys_role_menu VALUES (3, 131); +GO +INSERT sys_role_menu VALUES (3, 132); +GO +INSERT sys_role_menu VALUES (3, 133); +GO +INSERT sys_role_menu VALUES (3, 500); +GO +INSERT sys_role_menu VALUES (3, 501); +GO +INSERT sys_role_menu VALUES (3, 1001); +GO +INSERT sys_role_menu VALUES (3, 1002); +GO +INSERT sys_role_menu VALUES (3, 1003); +GO +INSERT sys_role_menu VALUES (3, 1004); +GO +INSERT sys_role_menu VALUES (3, 1005); +GO +INSERT sys_role_menu VALUES (3, 1006); +GO +INSERT sys_role_menu VALUES (3, 1007); +GO +INSERT sys_role_menu VALUES (3, 1008); +GO +INSERT sys_role_menu VALUES (3, 1009); +GO +INSERT sys_role_menu VALUES (3, 1010); +GO +INSERT sys_role_menu VALUES (3, 1011); +GO +INSERT sys_role_menu VALUES (3, 1012); +GO +INSERT sys_role_menu VALUES (3, 1013); +GO +INSERT sys_role_menu VALUES (3, 1014); +GO +INSERT sys_role_menu VALUES (3, 1015); +GO +INSERT sys_role_menu VALUES (3, 1016); +GO +INSERT sys_role_menu VALUES (3, 1017); +GO +INSERT sys_role_menu VALUES (3, 1018); +GO +INSERT sys_role_menu VALUES (3, 1019); +GO +INSERT sys_role_menu VALUES (3, 1020); +GO +INSERT sys_role_menu VALUES (3, 1021); +GO +INSERT sys_role_menu VALUES (3, 1022); +GO +INSERT sys_role_menu VALUES (3, 1023); +GO +INSERT sys_role_menu VALUES (3, 1024); +GO +INSERT sys_role_menu VALUES (3, 1025); +GO +INSERT sys_role_menu VALUES (3, 1026); +GO +INSERT sys_role_menu VALUES (3, 1027); +GO +INSERT sys_role_menu VALUES (3, 1028); +GO +INSERT sys_role_menu VALUES (3, 1029); +GO +INSERT sys_role_menu VALUES (3, 1030); +GO +INSERT sys_role_menu VALUES (3, 1031); +GO +INSERT sys_role_menu VALUES (3, 1032); +GO +INSERT sys_role_menu VALUES (3, 1033); +GO +INSERT sys_role_menu VALUES (3, 1034); +GO +INSERT sys_role_menu VALUES (3, 1035); +GO +INSERT sys_role_menu VALUES (3, 1036); +GO +INSERT sys_role_menu VALUES (3, 1037); +GO +INSERT sys_role_menu VALUES (3, 1038); +GO +INSERT sys_role_menu VALUES (3, 1039); +GO +INSERT sys_role_menu VALUES (3, 1040); +GO +INSERT sys_role_menu VALUES (3, 1041); +GO +INSERT sys_role_menu VALUES (3, 1042); +GO +INSERT sys_role_menu VALUES (3, 1043); +GO +INSERT sys_role_menu VALUES (3, 1044); +GO +INSERT sys_role_menu VALUES (3, 1045); +GO +INSERT sys_role_menu VALUES (3, 1050); +GO +INSERT sys_role_menu VALUES (3, 1061); +GO +INSERT sys_role_menu VALUES (3, 1062); +GO +INSERT sys_role_menu VALUES (3, 1063); +GO +INSERT sys_role_menu VALUES (3, 1064); +GO +INSERT sys_role_menu VALUES (3, 1065); +GO +INSERT sys_role_menu VALUES (3, 1500); +GO +INSERT sys_role_menu VALUES (3, 1501); +GO +INSERT sys_role_menu VALUES (3, 1502); +GO +INSERT sys_role_menu VALUES (3, 1503); +GO +INSERT sys_role_menu VALUES (3, 1504); +GO +INSERT sys_role_menu VALUES (3, 1505); +GO +INSERT sys_role_menu VALUES (3, 1506); +GO +INSERT sys_role_menu VALUES (3, 1507); +GO +INSERT sys_role_menu VALUES (3, 1508); +GO +INSERT sys_role_menu VALUES (3, 1509); +GO +INSERT sys_role_menu VALUES (3, 1510); +GO +INSERT sys_role_menu VALUES (3, 1511); +GO +INSERT sys_role_menu VALUES (3, 1600); +GO +INSERT sys_role_menu VALUES (3, 1601); +GO +INSERT sys_role_menu VALUES (3, 1602); +GO +INSERT sys_role_menu VALUES (3, 1603); +GO +INSERT sys_role_menu VALUES (3, 1620); +GO +INSERT sys_role_menu VALUES (3, 1621); +GO +INSERT sys_role_menu VALUES (3, 1622); +GO +INSERT sys_role_menu VALUES (3, 1623); +GO +INSERT sys_role_menu VALUES (3, 11616); +GO +INSERT sys_role_menu VALUES (3, 11618); +GO +INSERT sys_role_menu VALUES (3, 11619); +GO +INSERT sys_role_menu VALUES (3, 11622); +GO +INSERT sys_role_menu VALUES (3, 11623); +GO +INSERT sys_role_menu VALUES (3, 11629); +GO +INSERT sys_role_menu VALUES (3, 11632); +GO +INSERT sys_role_menu VALUES (3, 11633); +GO +INSERT sys_role_menu VALUES (3, 11638); +GO +INSERT sys_role_menu VALUES (3, 11639); +GO +INSERT sys_role_menu VALUES (3, 11640); +GO +INSERT sys_role_menu VALUES (3, 11641); +GO +INSERT sys_role_menu VALUES (3, 11642); +GO +INSERT sys_role_menu VALUES (3, 11643); +GO +INSERT sys_role_menu VALUES (3, 11701); +GO +INSERT sys_role_menu VALUES (4, 5); +GO +INSERT sys_role_menu VALUES (4, 1500); +GO +INSERT sys_role_menu VALUES (4, 1501); +GO +INSERT sys_role_menu VALUES (4, 1502); +GO +INSERT sys_role_menu VALUES (4, 1503); +GO +INSERT sys_role_menu VALUES (4, 1504); +GO +INSERT sys_role_menu VALUES (4, 1505); +GO +INSERT sys_role_menu VALUES (4, 1506); +GO +INSERT sys_role_menu VALUES (4, 1507); +GO +INSERT sys_role_menu VALUES (4, 1508); +GO +INSERT sys_role_menu VALUES (4, 1509); +GO +INSERT sys_role_menu VALUES (4, 1510); +GO +INSERT sys_role_menu VALUES (4, 1511); +GO + +CREATE TABLE sys_user +( + user_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dept_id bigint NULL, + user_name nvarchar(30) NOT NULL, + nick_name nvarchar(30) NOT NULL, + user_type nvarchar(10) DEFAULT ('sys_user') NULL, + email nvarchar(50) DEFAULT '' NULL, + phonenumber nvarchar(11) DEFAULT '' NULL, + sex nchar(1) DEFAULT ('0') NULL, + avatar bigint NULL, + password nvarchar(100) DEFAULT '' NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + login_ip nvarchar(128) DEFAULT '' NULL, + login_date datetime2(7) NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_user__B9BE370F79170B6A PRIMARY KEY CLUSTERED (user_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'部门ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'dept_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户昵称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'nick_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户类型(sys_user系统用户)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'user_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'手机号码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'phonenumber' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户性别(0男 1女 2未知)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'sex' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'头像地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'avatar' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'密码' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'password' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'帐号状态(0正常 1停用)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'status' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录IP' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_ip' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'最后登录时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'login_date' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'备注' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user', + 'COLUMN', N'remark' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户信息表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user' +GO + +INSERT sys_user VALUES (1, N'000000', 103, N'admin', N'疯狂的狮子Li', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', NULL, N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), NULL, NULL, N'管理员') +GO +INSERT sys_user VALUES (3, N'000000', 108, N'test', N'本部门及以下 密码666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 3, getdate(), NULL); +GO +INSERT sys_user VALUES (4, N'000000', 102, N'test1', N'仅本人 密码666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 4, getdate(), NULL); +GO + +CREATE TABLE sys_user_post +( + user_id bigint NOT NULL, + post_id bigint NOT NULL, + CONSTRAINT PK__sys_user__CA534F799C04589B PRIMARY KEY CLUSTERED (user_id, post_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'岗位ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post', + 'COLUMN', N'post_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户与岗位关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_post' +GO + +INSERT sys_user_post VALUES (1, 1) +GO + +CREATE TABLE sys_user_role +( + user_id bigint NOT NULL, + role_id bigint NOT NULL, + CONSTRAINT PK__sys_user__6EDEA153FB34D8F0 PRIMARY KEY CLUSTERED (user_id, role_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'角色ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role', + 'COLUMN', N'role_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户和角色关联表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_user_role' +GO + +INSERT sys_user_role VALUES (1, 1) +GO +INSERT sys_user_role VALUES (3, 3); +GO +INSERT sys_user_role VALUES (4, 4); +GO + +CREATE TABLE sys_oss +( + oss_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + file_name nvarchar(255) DEFAULT '' NOT NULL, + original_name nvarchar(255) DEFAULT '' NOT NULL, + file_suffix nvarchar(10) DEFAULT '' NOT NULL, + url nvarchar(500) NOT NULL, + ext1 nvarchar(500) DEFAULT '' NULL, + create_dept bigint NULL, + create_time datetime2(7) NULL, + create_by bigint NULL, + update_time datetime2(7) NULL, + update_by bigint NULL, + service nvarchar(20) DEFAULT ('minio') NOT NULL, + CONSTRAINT PK__sys_oss__91241EA442389F0D PRIMARY KEY CLUSTERED (oss_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'oss_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'原名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'original_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'文件后缀名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'file_suffix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'URL地址', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'url' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'ext1' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'上传人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'服务商', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss', + 'COLUMN', N'service' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'OSS对象存储表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss' +GO + +CREATE TABLE sys_oss_config +( + oss_config_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + config_key nvarchar(20) DEFAULT '' NOT NULL, + access_key nvarchar(255) DEFAULT '' NULL, + secret_key nvarchar(255) DEFAULT '' NULL, + bucket_name nvarchar(255) DEFAULT '' NULL, + prefix nvarchar(255) DEFAULT '' NULL, + endpoint nvarchar(255) DEFAULT '' NULL, + domain nvarchar(255) DEFAULT '' NULL, + is_https nchar(1) DEFAULT ('N') NULL, + region nvarchar(255) DEFAULT '' NULL, + access_policy nchar(1) DEFAULT ('1') NOT NULL, + status nchar(1) DEFAULT ('1') NULL, + ext1 nvarchar(255) DEFAULT '' NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + remark nvarchar(500) NULL, + CONSTRAINT PK__sys_oss___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (oss_config_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'oss_config_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户编号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'tenant_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'配置key', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'config_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'accessKey', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'秘钥', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'secret_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶名称', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'bucket_name' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'前缀', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'prefix' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'访问站点', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'endpoint' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'自定义域名', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'domain' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'是否https(Y=是,N=否)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'is_https' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'域', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'region' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'桶权限类型(0=private 1=public 2=custom)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'access_policy' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'是否默认(0=是,1=否)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'status' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'扩展字段', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'ext1' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'备注', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config', + 'COLUMN', N'remark' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'对象存储配置表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_oss_config' +GO + +INSERT INTO sys_oss_config VALUES (N'1', N'000000', N'minio', N'fys', N'fys123', N'fys', N'', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'0', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'2', N'000000', N'qiniu', N'XXXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'fys', N'', N's3-cn-north-1.qiniucs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'3', N'000000', N'aliyun', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'fys', N'', N'oss-cn-beijing.aliyuncs.com', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'4', N'000000', N'qcloud', N'XXXXXXXXXXXXXXX', N'XXXXXXXXXXXXXXX', N'fys-1250000000', N'', N'cos.ap-beijing.myqcloud.com', N'',N'N', N'ap-beijing', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO +INSERT INTO sys_oss_config VALUES (N'5', N'000000', N'image', N'fys', N'fys123', N'fys', N'image', N'127.0.0.1:9000', N'',N'N', N'', N'1', N'1', N'', 103, 1, getdate(), 1, getdate(), NULL) +GO + + +CREATE TABLE sys_client +( + id bigint NOT NULL, + client_id nvarchar(64) DEFAULT '' NULL, + client_key nvarchar(32) DEFAULT '' NULL, + client_secret nvarchar(255) DEFAULT '' NULL, + grant_type nvarchar(255) DEFAULT '' NULL, + device_type nvarchar(32) DEFAULT '' NULL, + active_timeout int DEFAULT ((1800)) NULL, + timeout int DEFAULT ((604800)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL + CONSTRAINT PK__sys_client___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'客户端id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'client_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'客户端key', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'client_key' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'客户端秘钥', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'client_secret' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'授权类型', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'grant_type' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'设备类型', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'device_type' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'token活跃超时时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'active_timeout' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'token固定超时', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'timeout' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'状态(0正常 1停用)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'status' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 1代表删除)', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新者', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client', + 'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'系统授权表', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_client' +GO + +INSERT INTO sys_client VALUES (N'1', N'e5cd7e4891bf95d1d19206ce24a7b32e', N'pc', N'pc123', N'password,social', N'pc', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO +INSERT INTO sys_client VALUES (N'2', N'428a8310cd442757ae699df5d894f051', N'app', N'app123', N'password,sms,social', N'android', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO + +CREATE TABLE test_demo +( + id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + dept_id bigint NULL, + user_id bigint NULL, + order_num int DEFAULT ((0)) NULL, + test_key nvarchar(255) NULL, + value nvarchar(255) NULL, + version int DEFAULT ((0)) NULL, + create_dept bigint NULL, + create_time datetime2(0) NULL, + create_by bigint NULL, + update_time datetime2(0) NULL, + update_by bigint NULL, + del_flag int DEFAULT ((0)) NULL, + CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'租户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'排序号', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'order_num' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'key键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'test_key' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'value' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'version' +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试单表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_demo' +GO + +CREATE TABLE test_tree +( + id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT ('000000') NULL, + parent_id bigint DEFAULT ((0)) NULL, + dept_id bigint NULL, + user_id bigint NULL, + tree_name nvarchar(255) NULL, + version int DEFAULT ((0)) NULL, + create_dept bigint NULL, + create_time datetime2(0) NULL, + create_by bigint NULL, + update_time datetime2(0) NULL, + update_by bigint NULL, + del_flag int DEFAULT ((0)) NULL, + CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'主键', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'租户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'父id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'dept_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'用户id', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'user_id' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'值', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'tree_name' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'版本', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'version' +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'创建人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新时间', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'更新人', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'删除标志', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree', + 'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'测试树表', + 'SCHEMA', N'dbo', + 'TABLE', N'test_tree' +GO + +INSERT test_demo VALUES (1, N'000000', 102, 4, 1, N'测试数据权限', N'测试', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (2, N'000000', 102, 3, 2, N'子节点1', N'111', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (3, N'000000', 102, 3, 3, N'子节点2', N'222', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (4, N'000000', 108, 4, 4, N'测试数据', N'demo', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (5, N'000000', 108, 3, 13, N'子节点11', N'1111', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (6, N'000000', 108, 3, 12, N'子节点22', N'2222', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (7, N'000000', 108, 3, 11, N'子节点33', N'3333', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (8, N'000000', 108, 3, 10, N'子节点44', N'4444', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (9, N'000000', 108, 3, 9, N'子节点55', N'5555', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (10, N'000000', 108, 3, 8, N'子节点66', N'6666', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (11, N'000000', 108, 3, 7, N'子节点77', N'7777', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (12, N'000000', 108, 3, 6, N'子节点88', N'8888', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_demo VALUES (13, N'000000', 108, 3, 5, N'子节点99', N'9999', 0, 103, getdate(), 1, NULL, NULL, 0); +GO + +INSERT test_tree VALUES (1, N'000000', 0, 102, 4, N'测试数据权限', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (2, N'000000', 1, 102, 3, N'子节点1', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (3, N'000000', 2, 102, 3, N'子节点2', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (4, N'000000', 0, 108, 4, N'测试树1', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (5, N'000000', 4, 108, 3, N'子节点11', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (6, N'000000', 4, 108, 3, N'子节点22', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (7, N'000000', 4, 108, 3, N'子节点33', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (8, N'000000', 5, 108, 3, N'子节点44', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (9, N'000000', 6, 108, 3, N'子节点55', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (10, N'000000', 7, 108, 3, N'子节点66', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (11, N'000000', 7, 108, 3, N'子节点77', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (12, N'000000', 10, 108, 3, N'子节点88', 0, 103, getdate(), 1, NULL, NULL, 0); +GO +INSERT test_tree VALUES (13, N'000000', 10, 108, 3, N'子节点99', 0, 103, getdate(), 1, NULL, NULL, 0); +GO diff --git a/script/sql/sqlserver/sqlserver_ry_workflow.sql b/script/sql/sqlserver/sqlserver_ry_workflow.sql new file mode 100644 index 0000000..88d92fc --- /dev/null +++ b/script/sql/sqlserver/sqlserver_ry_workflow.sql @@ -0,0 +1,1353 @@ +-- ---------------------------- +-- 0、warm-flow-all.sql,地址:https://gitee.com/dromara/warm-flow/blob/master/sql/sqlserver/sqlserver.sql +-- ---------------------------- +CREATE TABLE flow_definition ( + id bigint NOT NULL, + flow_code nvarchar(40) NOT NULL, + flow_name nvarchar(100) NOT NULL, + category nvarchar(100) NULL, + version nvarchar(20) NOT NULL, + is_publish tinyint DEFAULT('0') NULL, + form_custom nchar(1) DEFAULT('N') NULL, + form_path nvarchar(100) NULL, + activity_status tinyint DEFAULT('1') NULL, + listener_type nvarchar(100) NULL, + listener_path nvarchar(400) NULL, + ext nvarchar(500) NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_def__3213E83FEE39AE33 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'flow_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'flow_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程类别', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'category' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程版本', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'是否发布(0未发布 1已发布 9失效)', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'is_publish' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单是否自定义(Y是 N否)', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'form_custom' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'form_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程激活状态(0挂起 1激活)', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'activity_status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'监听器类型', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'listener_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'监听器路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'listener_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'业务详情 存业务表对象json字符串', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'ext' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程定义表', +'SCHEMA', N'dbo', +'TABLE', N'flow_definition' +GO + +CREATE TABLE flow_node ( + id bigint NOT NULL, + node_type tinyint NOT NULL, + definition_id bigint NOT NULL, + node_code nvarchar(100) NOT NULL, + node_name nvarchar(100) NULL, + permission_flag nvarchar(200) NULL, + node_ratio decimal(6,3) NULL, + coordinate nvarchar(100) NULL, + any_node_skip nvarchar(100) NULL, + listener_type nvarchar(100) NULL, + listener_path nvarchar(400) NULL, + handler_type nvarchar(100) NULL, + handler_path nvarchar(400) NULL, + form_custom nchar(1) DEFAULT('N') NULL, + form_path nvarchar(100) NULL, + version nvarchar(20) NOT NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + ext nvarchar(500) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_nod__3213E83F372470DE PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程定义id', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'definition_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程节点编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程节点名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'node_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'权限标识(权限类型:权限标识,可以多个,用逗号隔开)', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'permission_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程签署比例值', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'node_ratio' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'坐标', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'coordinate' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'任意结点跳转', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'any_node_skip' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'监听器类型', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'listener_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'监听器路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'listener_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'处理器类型', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'handler_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'处理器路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'handler_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单是否自定义(Y是 N否)', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'form_custom' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'form_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'版本', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'version' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'扩展属性', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'ext' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程节点表', +'SCHEMA', N'dbo', +'TABLE', N'flow_node' +GO + +CREATE TABLE flow_skip ( + id bigint NOT NULL, + definition_id bigint NOT NULL, + now_node_code nvarchar(100) NOT NULL, + now_node_type tinyint NULL, + next_node_code nvarchar(100) NOT NULL, + next_node_type tinyint NULL, + skip_name nvarchar(100) NULL, + skip_type nvarchar(40) NULL, + skip_condition nvarchar(200) NULL, + coordinate nvarchar(100) NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_ski__3213E83F073FEE6E PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程定义id', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'definition_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'当前流程节点的编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'now_node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'now_node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'下一个流程节点的编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'next_node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'next_node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'跳转名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'skip_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'跳转类型(PASS审批通过 REJECT退回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'skip_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'跳转条件', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'skip_condition' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'坐标', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'coordinate' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点跳转关联表', +'SCHEMA', N'dbo', +'TABLE', N'flow_skip' +GO + +CREATE TABLE flow_instance ( + id bigint NOT NULL, + definition_id bigint NOT NULL, + business_id nvarchar(40) NOT NULL, + node_type tinyint NOT NULL, + node_code nvarchar(40) NOT NULL, + node_name nvarchar(100) NULL, + variable nvarchar(max) NULL, + flow_status nvarchar(20) NOT NULL, + activity_status tinyint DEFAULT('1') NULL, + def_json nvarchar(max) NULL, + create_by nvarchar(64) NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + ext nvarchar(500) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_ins__3213E83F5190FEE1 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +TEXTIMAGE_ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_definition表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'definition_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'业务id', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'business_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程节点编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程节点名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'node_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'任务变量', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'variable' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程激活状态(0挂起 1激活)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'activity_status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程定义json', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'def_json' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建者', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'扩展字段,预留给业务系统使用', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'ext' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程实例表', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance' +GO + +CREATE TABLE flow_task ( + id bigint NOT NULL, + definition_id bigint NOT NULL, + instance_id bigint NOT NULL, + node_code nvarchar(100) NOT NULL, + node_name nvarchar(100) NULL, + node_type tinyint NOT NULL, + flow_status nvarchar(20) NOT NULL, + form_custom nchar(1) DEFAULT('N') NULL, + form_path nvarchar(100) NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_tas__3213E83F5AE1F1BA PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_definition表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'definition_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_instance表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'instance_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'node_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单是否自定义(Y是 N否)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'form_custom' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'form_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'待办任务表', +'SCHEMA', N'dbo', +'TABLE', N'flow_task' +GO + +CREATE TABLE flow_his_task ( + id bigint NOT NULL, + definition_id bigint NOT NULL, + instance_id bigint NOT NULL, + task_id bigint NOT NULL, + node_code nvarchar(200) NULL, + node_name nvarchar(200) NULL, + node_type tinyint NULL, + target_node_code nvarchar(100) NULL, + target_node_name nvarchar(100) NULL, + approver nvarchar(40) NULL, + cooperate_type tinyint DEFAULT('0') NULL, + collaborator nvarchar(40) NULL, + skip_type nvarchar(10) NOT NULL, + flow_status nvarchar(20) NOT NULL, + form_custom nchar(1) DEFAULT('N') NULL, + form_path nvarchar(100) NULL, + message nvarchar(500) NULL, + variable nvarchar(max) NULL, + ext nvarchar(500) NULL, + create_time datetime2(7) NULL, + update_time datetime2(7) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_his__3213E83F67951564 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_definition表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'definition_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_instance表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'instance_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'对应flow_task表的id', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'task_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'开始节点编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'开始节点名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'node_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'node_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'目标节点编码', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'target_node_code' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'结束节点名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'target_node_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批者', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'approver' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'cooperate_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'协作人', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'collaborator' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流转类型(PASS通过 REJECT退回 NONE无动作)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'skip_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单是否自定义(Y是 N否)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'form_custom' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批表单路径', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'form_path' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批意见', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'message' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'任务变量', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'variable' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'业务详情 存业务表对象json字符串', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'ext' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'任务开始时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'审批完成时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'历史任务记录表', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task' +GO + +CREATE TABLE flow_user ( + id bigint NOT NULL, + type nchar(1) NOT NULL, + processed_by nvarchar(80) NULL, + associated bigint NOT NULL, + create_time datetime2(7) NULL, + create_by nvarchar(80) NULL, + update_time datetime2(7) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + tenant_id nvarchar(40) NULL, + CONSTRAINT PK__flow_use__3213E83FFA38CA8B PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX user_processed_type ON flow_user (processed_by ASC, type ASC) +GO +CREATE NONCLUSTERED INDEX user_associated_idx ON flow_user (associated ASC) +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键id', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'权限人', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'processed_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'任务表id', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'associated' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建人', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户id', +'SCHEMA', N'dbo', +'TABLE', N'flow_user', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程用户表', +'SCHEMA', N'dbo', +'TABLE', N'flow_user' +GO + +CREATE TABLE flow_category ( + category_id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT('000000') NULL, + parent_id bigint DEFAULT(0) NULL, + ancestors nvarchar(500) DEFAULT('') NULL, + category_name nvarchar(30) NOT NULL, + order_num int DEFAULT(0) NULL, + del_flag nchar(1) DEFAULT('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__flow_cat__D54EE9B4AE98B9C1 PRIMARY KEY CLUSTERED (category_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程分类ID', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'category_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户编号', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'父流程分类id', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'parent_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'祖级列表', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'ancestors' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程分类名称', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'category_name' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'显示顺序', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'order_num' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'删除标志(0代表存在 1代表删除)', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'del_flag' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建部门', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建者', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新者', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'flow_category', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'流程分类', +'SCHEMA', N'dbo', +'TABLE', N'flow_category' +GO + +INSERT flow_category VALUES (100, N'000000', 0, N'0', N'OA审批', 0, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (101, N'000000', 100, N'0,100', N'假勤管理', 0, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (102, N'000000', 100, N'0,100', N'人事管理', 1, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (103, N'000000', 101, N'0,100,101', N'请假', 0, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (104, N'000000', 101, N'0,100,101', N'出差', 1, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (105, N'000000', 101, N'0,100,101', N'加班', 2, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (106, N'000000', 101, N'0,100,101', N'换班', 3, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (107, N'000000', 101, N'0,100,101', N'外出', 4, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (108, N'000000', 102, N'0,100,102', N'转正', 1, N'0', 103, 1, getdate(), NULL, NULL); +GO +INSERT flow_category VALUES (109, N'000000', 102, N'0,100,102', N'离职', 2, N'0', 103, 1, getdate(), NULL, NULL); +GO + +CREATE TABLE test_leave ( + id bigint NOT NULL, + tenant_id nvarchar(20) DEFAULT('000000') NULL, + leave_type nvarchar(255) NOT NULL, + start_date datetime2(7) NOT NULL, + end_date datetime2(7) NOT NULL, + leave_days int NOT NULL, + remark nvarchar(255) NULL, + status nvarchar(255) NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__test_lea__3213E83F348788FA PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'id', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'租户编号', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'tenant_id' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'请假类型', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'leave_type' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'开始时间', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'start_date' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'结束时间', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'end_date' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'请假天数', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'leave_days' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'请假原因', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'remark' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'状态', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'status' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建部门', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'create_dept' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建者', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'create_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'create_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新者', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'update_by' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'test_leave', +'COLUMN', N'update_time' +GO + +EXEC sp_addextendedproperty +'MS_Description', N'请假申请表', +'SCHEMA', N'dbo', +'TABLE', N'test_leave' +GO + +INSERT sys_menu VALUES (11616, N'工作流', 0, 6, N'workflow', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'workflow', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11618, N'我的任务', 0, 7, N'task', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'my-task', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11619, N'我的待办', 11618, 2, N'taskWaiting', N'workflow/task/taskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11632, N'我的已办', 11618, 3, N'taskFinish', N'workflow/task/taskFinish', N'', 1, 1, N'C', N'0', N'0', N'', N'finish', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11633, N'我的抄送', 11618, 4, N'taskCopyList', N'workflow/task/taskCopyList', N'', 1, 1, N'C', N'0', N'0', N'', N'my-copy', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11620, N'流程定义', 11616, 3, N'processDefinition', N'workflow/processDefinition/index', N'', 1, 1, N'C', N'0', N'0', N'', N'process-definition', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11621, N'流程实例', 11630, 1, N'processInstance', N'workflow/processInstance/index', N'', 1, 1, N'C', N'0', N'0', N'', N'tree-table', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11622, N'流程分类', 11616, 1, N'category', N'workflow/category/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:category:list', N'category', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11629, N'我发起的', 11618, 1, N'myDocument', N'workflow/task/myDocument', N'', 1, 1, N'C', N'0', N'0', N'', N'guide', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11630, N'流程监控', 11616, 4, N'monitor', NULL, N'', 1, 0, N'M', N'0', N'0', N'', N'monitor', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11631, N'待办任务', 11630, 2, N'allTaskWaiting', N'workflow/task/allTaskWaiting', N'', 1, 1, N'C', N'0', N'0', N'', N'waiting', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO + +-- 流程分类管理相关按钮 +INSERT sys_menu VALUES (11623, N'流程分类查询', 11622, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11624, N'流程分类新增', 11622, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:add', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11625, N'流程分类修改', 11622, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11626, N'流程分类删除', 11622, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11627, N'流程分类导出', 11622, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:category:export', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO + +-- 请假测试相关按钮 +INSERT sys_menu VALUES (11638, N'请假申请', 5, 1, N'leave', N'workflow/leave/index', N'', 1, 0, N'C', N'0', N'0', N'workflow:leave:list', N'#', 103, 1, GETDATE(), NULL, NULL, N'请假申请菜单'); +GO +INSERT sys_menu VALUES (11639, N'请假申请查询', 11638, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:leave:query', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11640, N'请假申请新增', 11638, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:leave:add', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11641, N'请假申请修改', 11638, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11642, N'请假申请删除', 11638, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:leave:remove', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11643, N'请假申请导出', 11638, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'workflow:leave:export', N'#', 103, 1, GETDATE(), NULL, NULL, N''); + +INSERT sys_dict_type VALUES (13, N'000000', N'业务状态', N'wf_business_status', 103, 1, GETDATE(), NULL, NULL, N'业务状态列表'); +GO +INSERT sys_dict_type VALUES (14, N'000000', N'表单类型', N'wf_form_type', 103, 1, GETDATE(), NULL, NULL, N'表单类型列表'); +GO +INSERT sys_dict_type VALUES (15, N'000000', N'任务状态', N'wf_task_status', 103, 1, GETDATE(), NULL, NULL, N'任务状态'); +GO + +INSERT sys_dict_data VALUES (39, N'000000', 1, N'已撤销', N'cancel', N'wf_business_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'已撤销'); +GO +INSERT sys_dict_data VALUES (40, N'000000', 2, N'草稿', N'draft', N'wf_business_status', N'', N'info', N'N', 103, 1, GETDATE(), NULL, NULL, N'草稿'); +GO +INSERT sys_dict_data VALUES (41, N'000000', 3, N'待审核', N'waiting', N'wf_business_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'待审核'); +GO +INSERT sys_dict_data VALUES (42, N'000000', 4, N'已完成', N'finish', N'wf_business_status', N'', N'success', N'N', 103, 1, GETDATE(), NULL, NULL, N'已完成'); +GO +INSERT sys_dict_data VALUES (43, N'000000', 5, N'已作废', N'invalid', N'wf_business_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'已作废'); +GO +INSERT sys_dict_data VALUES (44, N'000000', 6, N'已退回', N'back', N'wf_business_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'已退回'); +GO +INSERT sys_dict_data VALUES (45, N'000000', 7, N'已终止', N'termination', N'wf_business_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'已终止'); +GO +INSERT sys_dict_data VALUES (46, N'000000', 1, N'自定义表单', N'static', N'wf_form_type', N'', N'success', N'N', 103, 1, GETDATE(), NULL, NULL, N'自定义表单'); +GO +INSERT sys_dict_data VALUES (47, N'000000', 2, N'动态表单', N'dynamic', N'wf_form_type', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'动态表单'); +GO +INSERT sys_dict_data VALUES (48, N'000000', 1, N'撤销', N'cancel', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'撤销'); +GO +INSERT sys_dict_data VALUES (49, N'000000', 2, N'通过', N'pass', N'wf_task_status', N'', N'success', N'N', 103, 1, GETDATE(), NULL, NULL, N'通过'); +GO +INSERT sys_dict_data VALUES (50, N'000000', 3, N'待审核', N'waiting', N'wf_task_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'待审核'); +GO +INSERT sys_dict_data VALUES (51, N'000000', 4, N'作废', N'invalid', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'作废'); +GO +INSERT sys_dict_data VALUES (52, N'000000', 5, N'退回', N'back', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'退回'); +GO +INSERT sys_dict_data VALUES (53, N'000000', 6, N'终止', N'termination', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'终止'); +GO +INSERT sys_dict_data VALUES (54, N'000000', 7, N'转办', N'transfer', N'wf_task_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'转办'); +GO +INSERT sys_dict_data VALUES (55, N'000000', 8, N'委托', N'depute', N'wf_task_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'委托'); +GO +INSERT sys_dict_data VALUES (56, N'000000', 9, N'抄送', N'copy', N'wf_task_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'抄送'); +GO +INSERT sys_dict_data VALUES (57, N'000000', 10, N'加签', N'sign', N'wf_task_status', N'', N'primary', N'N', 103, 1, GETDATE(), NULL, NULL, N'加签'); +GO +INSERT sys_dict_data VALUES (58, N'000000', 11, N'减签', N'sign_off', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'减签'); +GO +INSERT sys_dict_data VALUES (59, N'000000', 11, N'超时', N'timeout', N'wf_task_status', N'', N'danger', N'N', 103, 1, GETDATE(), NULL, NULL, N'超时'); +GO diff --git a/script/sql/update/oracle/update_5.0-5.1.sql b/script/sql/update/oracle/update_5.0-5.1.sql new file mode 100644 index 0000000..09cfae8 --- /dev/null +++ b/script/sql/update/oracle/update_5.0-5.1.sql @@ -0,0 +1,151 @@ +ALTER TABLE gen_table ADD (data_name VARCHAR2(200) DEFAULT ''); + +COMMENT ON COLUMN gen_table.data_name IS '数据源名称'; + +UPDATE sys_menu SET path = 'powerjob', component = 'monitor/powerjob/index', perms = 'monitor:powerjob:list', remark = 'powerjob控制台菜单' WHERE menu_id = 120; + +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +create table sys_social +( + id number(20) not null, + user_id number(20) not null, + tenant_id varchar2(20) default null, + auth_id varchar2(255) not null, + source varchar2(255) not null, + open_id varchar2(255) default null, + user_name varchar2(30) not null, + nick_name varchar2(30) default '', + email varchar2(255) default '', + avatar varchar2(500) default '', + access_token varchar2(255) not null, + expire_in number(20) default null, + refresh_token varchar2(255) default null, + access_code varchar2(255) default null, + union_id varchar2(255) default null, + scope varchar2(255) default null, + token_type varchar2(255) default null, + id_token varchar2(255) default null, + mac_algorithm varchar2(255) default null, + mac_key varchar2(255) default null, + code varchar2(255) default null, + oauth_token varchar2(255) default null, + oauth_token_secret varchar2(255) default null, + create_dept number(20), + create_by number(20), + create_time date, + update_by number(20), + update_time date, + del_flag char(1) default '0' +); + +alter table sys_social add constraint pk_sys_social primary key (id); + +comment on table sys_social is '社会化关系表'; +comment on column sys_social.id is '主键'; +comment on column sys_social.user_id is '用户ID'; +comment on column sys_social.tenant_id is '租户id'; +comment on column sys_social.auth_id is '平台+平台唯一id'; +comment on column sys_social.source is '用户来源'; +comment on column sys_social.open_id is '平台编号唯一id'; +comment on column sys_social.user_name is '登录账号'; +comment on column sys_social.nick_name is '用户昵称'; +comment on column sys_social.email is '用户邮箱'; +comment on column sys_social.avatar is '头像地址'; +comment on column sys_social.access_token is '用户的授权令牌'; +comment on column sys_social.expire_in is '用户的授权令牌的有效期,部分平台可能没有'; +comment on column sys_social.refresh_token is '刷新令牌,部分平台可能没有'; +comment on column sys_social.access_code is '平台的授权信息,部分平台可能没有'; +comment on column sys_social.union_id is '用户的 unionid'; +comment on column sys_social.scope is '授予的权限,部分平台可能没有'; +comment on column sys_social.token_type is '个别平台的授权信息,部分平台可能没有'; +comment on column sys_social.id_token is 'id token,部分平台可能没有'; +comment on column sys_social.mac_algorithm is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.mac_key is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.code is '用户的授权code,部分平台可能没有'; +comment on column sys_social.oauth_token is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.oauth_token_secret is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.create_dept is '创建部门'; +comment on column sys_social.create_by is '创建者'; +comment on column sys_social.create_time is '创建时间'; +comment on column sys_social.update_by is '更新者'; +comment on column sys_social.update_time is '更新时间'; +comment on column sys_social.del_flag is '删除标志(0代表存在 2代表删除)'; + + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +create table sys_client ( + id number(20) not null, + client_id varchar2(64) default null, + client_key varchar2(32) default null, + client_secret varchar2(255) default null, + grant_type varchar2(255) default null, + device_type varchar2(32) default null, + active_timeout number(11) default 1800, + timeout number(11) default 604800, + status char(1) default '0', + del_flag char(1) default '0', + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_client add constraint pk_sys_client primary key (id); + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主键'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.device_type is '设备类型'; +comment on column sys_client.active_timeout is 'token活跃超时时间'; +comment on column sys_client.timeout is 'token固定超时'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate); + +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', '0', 103, 1, sysdate, null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', '0', 103, 1, sysdate, null, null, '客户端设备类型'); + +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', '0', 103, 1, sysdate, null, null, '小程序'); + +-- 二级菜单 +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate, null, null, '客户端管理菜单'); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, sysdate, null, null, ''); + +-- 角色菜单权限 +insert into sys_role_menu values ('2', '1061'); +insert into sys_role_menu values ('2', '1062'); +insert into sys_role_menu values ('2', '1063'); +insert into sys_role_menu values ('2', '1064'); +insert into sys_role_menu values ('2', '1065'); + + +update sys_dept set leader = null; +ALTER TABLE sys_dept MODIFY (leader NUMBER(20)) diff --git a/script/sql/update/oracle/update_5.1.0-5.1.1.sql b/script/sql/update/oracle/update_5.1.0-5.1.1.sql new file mode 100644 index 0000000..979a4bd --- /dev/null +++ b/script/sql/update/oracle/update_5.1.0-5.1.1.sql @@ -0,0 +1,5 @@ +ALTER TABLE sys_logininfor ADD (client_key varchar2(32) DEFAULT ''); +COMMENT ON COLUMN sys_logininfor.client_key IS '客户端'; + +ALTER TABLE sys_logininfor ADD (device_type varchar2(32) DEFAULT ''); +COMMENT ON COLUMN sys_logininfor.device_type IS '设备类型'; diff --git a/script/sql/update/oracle/update_5.1.1-5.1.2.sql b/script/sql/update/oracle/update_5.1.1-5.1.2.sql new file mode 100644 index 0000000..d7c030c --- /dev/null +++ b/script/sql/update/oracle/update_5.1.1-5.1.2.sql @@ -0,0 +1,6 @@ +delete from sys_menu where menu_id in (1604, 1605); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate, null, null, ''); + diff --git a/script/sql/update/oracle/update_5.1.2-5.2.0.sql b/script/sql/update/oracle/update_5.1.2-5.2.0.sql new file mode 100644 index 0000000..1aa585a --- /dev/null +++ b/script/sql/update/oracle/update_5.1.2-5.2.0.sql @@ -0,0 +1,9 @@ +ALTER TABLE sys_dept ADD (dept_category varchar2(100) DEFAULT NULL) COMMENT '部门类别编码'; +COMMENT ON COLUMN sys_dept.dept_category IS '部门类别编码'; +ALTER TABLE sys_post ADD (dept_id number(20) NOT NULL) COMMENT '部门id'; +COMMENT ON COLUMN sys_post.dept_id IS '部门id'; +ALTER TABLE sys_post ADD (post_category VARCHAR2(100) DEFAULT NULL) COMMENT '岗位类别编码'; +COMMENT ON COLUMN sys_post.post_category IS '岗位类别编码'; +UPDATE sys_post SET dept_id = 100; +UPDATE sys_post SET dept_id = 103 where post_id = 1; +UPDATE sys_menu SET menu_name = 'SnailJob控制台', path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob控制台菜单' WHERE menu_id = 120; diff --git a/script/sql/update/oracle/update_5.3.0-5.3.1.sql b/script/sql/update/oracle/update_5.3.0-5.3.1.sql new file mode 100644 index 0000000..09000fe --- /dev/null +++ b/script/sql/update/oracle/update_5.3.0-5.3.1.sql @@ -0,0 +1,7 @@ +ALTER TABLE flow_node DROP COLUMN skip_any_node; +ALTER TABLE flow_node ADD (ext VARCHAR2(500)); +COMMENT ON COLUMN flow_node.ext IS '扩展属性'; +create index USER_ASSOCIATED_IDX on FLOW_USER (ASSOCIATED); + +ALTER TABLE sys_oss ADD (ext1 VARCHAR2(500)); +COMMENT ON COLUMN sys_oss.ext1 IS '扩展属性'; diff --git a/script/sql/update/oracle/update_5.3.1-5.4.0.sql b/script/sql/update/oracle/update_5.3.1-5.4.0.sql new file mode 100644 index 0000000..d0d0499 --- /dev/null +++ b/script/sql/update/oracle/update_5.3.1-5.4.0.sql @@ -0,0 +1,18 @@ +ALTER TABLE flow_task ADD (flow_status VARCHAR2(20)); +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social + MODIFY (access_token VARCHAR2(2000 BYTE)) + MODIFY (refresh_token VARCHAR2(2000 BYTE)); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate, null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, SYSDATE, NULL, NULL, ''); diff --git a/script/sql/update/postgres/update_5.0-5.1.sql b/script/sql/update/postgres/update_5.0-5.1.sql new file mode 100644 index 0000000..f5f0a5c --- /dev/null +++ b/script/sql/update/postgres/update_5.0-5.1.sql @@ -0,0 +1,150 @@ +ALTER TABLE gen_table ADD data_name varchar(200) default ''::varchar; + +COMMENT ON COLUMN gen_table.data_name IS '数据源名称'; + +UPDATE sys_menu SET path = 'powerjob', component = 'monitor/powerjob/index', perms = 'monitor:powerjob:list', remark = 'powerjob控制台菜单' WHERE menu_id = 120; + +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +create table sys_social +( + id int8 not null, + user_id int8 not null, + tenant_id varchar(20) default null::varchar, + auth_id varchar(255) not null, + source varchar(255) not null, + open_id varchar(255) default null::varchar, + user_name varchar(30) not null, + nick_name varchar(30) default ''::varchar, + email varchar(255) default ''::varchar, + avatar varchar(500) default ''::varchar, + access_token varchar(255) not null, + expire_in int8 default null, + refresh_token varchar(255) default null::varchar, + access_code varchar(255) default null::varchar, + union_id varchar(255) default null::varchar, + scope varchar(255) default null::varchar, + token_type varchar(255) default null::varchar, + id_token varchar(255) default null::varchar, + mac_algorithm varchar(255) default null::varchar, + mac_key varchar(255) default null::varchar, + code varchar(255) default null::varchar, + oauth_token varchar(255) default null::varchar, + oauth_token_secret varchar(255) default null::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + del_flag char default '0'::bpchar, + constraint "pk_sys_social" primary key (id) +); + +comment on table sys_social is '社会化关系表'; +comment on column sys_social.id is '主键'; +comment on column sys_social.user_id is '用户ID'; +comment on column sys_social.tenant_id is '租户id'; +comment on column sys_social.auth_id is '平台+平台唯一id'; +comment on column sys_social.source is '用户来源'; +comment on column sys_social.open_id is '平台编号唯一id'; +comment on column sys_social.user_name is '登录账号'; +comment on column sys_social.nick_name is '用户昵称'; +comment on column sys_social.email is '用户邮箱'; +comment on column sys_social.avatar is '头像地址'; +comment on column sys_social.access_token is '用户的授权令牌'; +comment on column sys_social.expire_in is '用户的授权令牌的有效期,部分平台可能没有'; +comment on column sys_social.refresh_token is '刷新令牌,部分平台可能没有'; +comment on column sys_social.access_code is '平台的授权信息,部分平台可能没有'; +comment on column sys_social.union_id is '用户的 unionid'; +comment on column sys_social.scope is '授予的权限,部分平台可能没有'; +comment on column sys_social.token_type is '个别平台的授权信息,部分平台可能没有'; +comment on column sys_social.id_token is 'id token,部分平台可能没有'; +comment on column sys_social.mac_algorithm is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.mac_key is '小米平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.code is '用户的授权code,部分平台可能没有'; +comment on column sys_social.oauth_token is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.oauth_token_secret is 'Twitter平台用户的附带属性,部分平台可能没有'; +comment on column sys_social.create_dept is '创建部门'; +comment on column sys_social.create_by is '创建者'; +comment on column sys_social.create_time is '创建时间'; +comment on column sys_social.update_by is '更新者'; +comment on column sys_social.update_time is '更新时间'; +comment on column sys_social.del_flag is '删除标志(0代表存在 2代表删除)'; + + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +drop table if exists sys_client; +create table sys_client ( + id int8, + client_id varchar(64) default ''::varchar, + client_key varchar(32) default ''::varchar, + client_secret varchar(255) default ''::varchar, + grant_type varchar(255) default ''::varchar, + device_type varchar(32) default ''::varchar, + active_timeout int4 default 1800, + timeout int4 default 604800, + status char(1) default '0'::bpchar, + del_flag char(1) default '0'::bpchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint sys_client_pk primary key (id) +); + +comment on table sys_client is '系统授权表'; +comment on column sys_client.id is '主键'; +comment on column sys_client.client_id is '客户端id'; +comment on column sys_client.client_key is '客户端key'; +comment on column sys_client.client_secret is '客户端秘钥'; +comment on column sys_client.grant_type is '授权类型'; +comment on column sys_client.device_type is '设备类型'; +comment on column sys_client.active_timeout is 'token活跃超时时间'; +comment on column sys_client.timeout is 'token固定超时'; +comment on column sys_client.status is '状态(0正常 1停用)'; +comment on column sys_client.del_flag is '删除标志(0代表存在 2代表删除)'; +comment on column sys_client.create_dept is '创建部门'; +comment on column sys_client.create_by is '创建者'; +comment on column sys_client.create_time is '创建时间'; +comment on column sys_client.update_by is '更新者'; +comment on column sys_client.update_time is '更新时间'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, now(), 1, now()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, now(), 1, now()); + +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', '0', 103, 1, now(), null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', '0', 103, 1, now(), null, null, '客户端设备类型'); + +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', '0', 103, 1, now(), null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', '0', 103, 1, now(), null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', '0', 103, 1, now(), null, null, '小程序'); + +-- 二级菜单 +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', '1', '0', 'C', '0', '0', 'system:client:list', 'international', 103, 1, now(), null, null, '客户端管理菜单'); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:remove', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:client:export', '#', 103, 1, now(), null, null, ''); + +-- 角色菜单权限 +insert into sys_role_menu values ('2', '1061'); +insert into sys_role_menu values ('2', '1062'); +insert into sys_role_menu values ('2', '1063'); +insert into sys_role_menu values ('2', '1064'); +insert into sys_role_menu values ('2', '1065'); + + +update sys_dept set leader = null; +ALTER TABLE sys_dept ALTER COLUMN leader TYPE int8; diff --git a/script/sql/update/postgres/update_5.1.0-5.1.1.sql b/script/sql/update/postgres/update_5.1.0-5.1.1.sql new file mode 100644 index 0000000..29f5507 --- /dev/null +++ b/script/sql/update/postgres/update_5.1.0-5.1.1.sql @@ -0,0 +1,5 @@ +ALTER TABLE sys_logininfor ADD client_key varchar(32) default ''::varchar; +COMMENT ON COLUMN sys_logininfor.client_key IS '客户端'; + +ALTER TABLE sys_logininfor ADD device_type varchar(32) default ''::varchar; +COMMENT ON COLUMN sys_logininfor.device_type IS '设备类型'; diff --git a/script/sql/update/postgres/update_5.1.1-5.1.2.sql b/script/sql/update/postgres/update_5.1.1-5.1.2.sql new file mode 100644 index 0000000..62eb836 --- /dev/null +++ b/script/sql/update/postgres/update_5.1.1-5.1.2.sql @@ -0,0 +1,5 @@ +delete from sys_menu where menu_id in (1604, 1605); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, now(), null, null, ''); diff --git a/script/sql/update/postgres/update_5.1.2-5.2.0.sql b/script/sql/update/postgres/update_5.1.2-5.2.0.sql new file mode 100644 index 0000000..5089a09 --- /dev/null +++ b/script/sql/update/postgres/update_5.1.2-5.2.0.sql @@ -0,0 +1,9 @@ +ALTER TABLE sys_dept ADD COLUMN dept_category varchar(100) default null::varchar; +COMMENT ON COLUMN sys_dept.dept_category IS '客户端'; +ALTER TABLE sys_post ADD COLUMN dept_id int8 NOT NULL; +COMMENT ON COLUMN sys_post.dept_id IS '部门id'; +ALTER TABLE sys_post ADD COLUMN post_category varchar(100) default null::varchar; +COMMENT ON COLUMN sys_post.post_category IS '岗位类别编码'; +UPDATE sys_post SET dept_id = 100; +UPDATE sys_post SET dept_id = 103 where post_id = 1; +UPDATE sys_menu SET menu_name = 'SnailJob控制台', path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob控制台菜单' WHERE menu_id = 120; diff --git a/script/sql/update/postgres/update_5.3.0-5.3.1.sql b/script/sql/update/postgres/update_5.3.0-5.3.1.sql new file mode 100644 index 0000000..de5dd56 --- /dev/null +++ b/script/sql/update/postgres/update_5.3.0-5.3.1.sql @@ -0,0 +1,7 @@ +ALTER TABLE flow_node DROP COLUMN skip_any_node; +ALTER TABLE flow_node ADD COLUMN ext varchar(500); +COMMENT ON COLUMN flow_node.ext IS '扩展属性'; +CREATE INDEX user_associated_idx ON FLOW_USER USING btree (associated); + +ALTER TABLE sys_oss ADD COLUMN ext1 varchar(500)); +COMMENT ON COLUMN sys_oss.ext1 IS '扩展属性'; diff --git a/script/sql/update/postgres/update_5.3.1-5.4.0.sql b/script/sql/update/postgres/update_5.3.1-5.4.0.sql new file mode 100644 index 0000000..476229c --- /dev/null +++ b/script/sql/update/postgres/update_5.3.1-5.4.0.sql @@ -0,0 +1,18 @@ +ALTER TABLE flow_task ADD COLUMN flow_status varchar(20); +COMMENT ON COLUMN flow_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_instance.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +COMMENT ON COLUMN flow_his_task.flow_status IS '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)'; + +ALTER TABLE sys_social +ALTER COLUMN access_token TYPE varchar(2000), +ALTER COLUMN refresh_token TYPE varchar(2000); + +INSERT INTO sys_menu VALUES ('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', '1', '1', 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', '1', '1', 'C', '1', '0', 'system:role:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', '1', '1', 'C', '1', '0', 'system:user:edit', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', '1', '1', 'C', '1', '0', 'system:dict:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', '1', '1', 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, now(), null, null, ''); +INSERT INTO sys_menu VALUES ('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu VALUES ('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', '1', '1', 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); diff --git a/script/sql/update/sqlserver/update_5.0-5.1.sql b/script/sql/update/sqlserver/update_5.0-5.1.sql new file mode 100644 index 0000000..6ea54a5 --- /dev/null +++ b/script/sql/update/sqlserver/update_5.0-5.1.sql @@ -0,0 +1,409 @@ +ALTER TABLE gen_table ADD data_name nvarchar(200) DEFAULT '' NULL +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'数据源名称', + 'SCHEMA', N'dbo', + 'TABLE', N'gen_table', + 'COLUMN', N'data_name' +GO + +UPDATE sys_menu SET path = 'powerjob', component = 'monitor/powerjob/index', perms = 'monitor:powerjob:list', remark = 'powerjob控制台菜单' WHERE menu_id = 120 +GO + +create table sys_social +( + id bigint NOT NULL, + user_id bigint NOT NULL, + tenant_id nvarchar(20) NULL, + auth_id nvarchar(255) NOT NULL, + source nvarchar(255) NOT NULL, + open_id nvarchar(255) NULL, + user_name nvarchar(30) NOT NULL, + nick_name nvarchar(30) DEFAULT ('') NULL, + email nvarchar(255) DEFAULT ('') NULL, + avatar nvarchar(500) DEFAULT ('') NULL, + access_token nvarchar(255) NOT NULL, + expire_in bigint NULL, + refresh_token nvarchar(255) NULL, + access_code nvarchar(255) NULL, + union_id nvarchar(255) NULL, + scope nvarchar(255) NULL, + token_type nvarchar(255) NULL, + id_token nvarchar(255) NULL, + mac_algorithm nvarchar(255) NULL, + mac_key nvarchar(255) NULL, + code nvarchar(255) NULL, + oauth_token nvarchar(255) NULL, + oauth_token_secret nvarchar(255) NULL, + create_dept bigint, + create_by bigint, + create_time datetime2(7), + update_by bigint, + update_time datetime2(7), + del_flag nchar DEFAULT ('0') NULL, + CONSTRAINT PK__sys_social__B21E8F2427725F8A PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'user_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'租户id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'tenant_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台+平台唯一id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'auth_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户来源' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'source' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台编号唯一id' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'open_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'登录账号' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'user_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户昵称' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'nick_name' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户邮箱' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'email' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'头像地址' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'avatar' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权令牌' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'access_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权令牌的有效期,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'expire_in' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'刷新令牌,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'refresh_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'平台的授权信息,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'access_code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的 unionid' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'union_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'授予的权限,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'scope' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'个别平台的授权信息,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'token_type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'id token,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'id_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'小米平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'mac_algorithm' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'小米平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'mac_key' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'用户的授权code,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'code' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'Twitter平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'oauth_token' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'Twitter平台用户的附带属性,部分平台可能没有' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'oauth_token_secret' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'删除标志(0代表存在 2代表删除)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_social', + 'COLUMN', N'update_time' +GO + + +CREATE TABLE sys_client +( + id bigint NOT NULL, + client_id nvarchar(64) DEFAULT '' NULL, + client_key nvarchar(32) DEFAULT '' NULL, + client_secret nvarchar(255) DEFAULT '' NULL, + grant_type nvarchar(255) DEFAULT '' NULL, + device_type nvarchar(32) DEFAULT '' NULL, + active_timeout int DEFAULT ((1800)) NULL, + timeout int DEFAULT ((604800)) NULL, + status nchar(1) DEFAULT ('0') NULL, + del_flag nchar(1) DEFAULT ('0') NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL + CONSTRAINT PK__sys_client___BFBDE87009ED2882 PRIMARY KEY CLUSTERED (id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +GO + +EXEC sp_addextendedproperty +'MS_Description', N'主键', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'id' +GO +EXEC sys.sp_addextendedproperty +'MS_Description', N'客户端id' , +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'client_id' +GO +EXEC sp_addextendedproperty +'MS_Description', N'客户端key', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'client_key' +GO +EXEC sp_addextendedproperty +'MS_Description', N'客户端秘钥', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'client_secret' +GO +EXEC sp_addextendedproperty +'MS_Description', N'授权类型', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'grant_type' +GO +EXEC sp_addextendedproperty +'MS_Description', N'设备类型', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'device_type' +GO +EXEC sp_addextendedproperty +'MS_Description', N'token活跃超时时间', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'active_timeout' +GO +EXEC sp_addextendedproperty +'MS_Description', N'token固定超时', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'timeout' +GO +EXEC sp_addextendedproperty +'MS_Description', N'状态(0正常 1停用)', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'status' +GO +EXEC sp_addextendedproperty +'MS_Description', N'删除标志(0代表存在 2代表删除)', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'del_flag' +GO +EXEC sys.sp_addextendedproperty +'MS_Description', N'创建部门' , +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_dept' +GO +EXEC sp_addextendedproperty +'MS_Description', N'创建者', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_by' +GO +EXEC sp_addextendedproperty +'MS_Description', N'创建时间', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'create_time' +GO +EXEC sp_addextendedproperty +'MS_Description', N'更新者', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'update_by' +GO +EXEC sp_addextendedproperty +'MS_Description', N'更新时间', +'SCHEMA', N'dbo', +'TABLE', N'sys_client', +'COLUMN', N'update_time' +GO +EXEC sp_addextendedproperty +'MS_Description', N'系统授权表', +'SCHEMA', N'dbo', +'TABLE', N'sys_client' +GO + +INSERT INTO sys_client VALUES (N'1', N'e5cd7e4891bf95d1d19206ce24a7b32e', N'pc', N'pc123', N'password,social', N'pc', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO +INSERT INTO sys_client VALUES (N'2', N'428a8310cd442757ae699df5d894f051', N'app', N'app123', N'password,sms,social', N'android', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate()) +GO + +INSERT sys_dict_type VALUES (11, N'000000', N'授权类型', N'sys_grant_type', N'0', 103, 1, getdate(), NULL, NULL, N'认证授权类型') +GO +INSERT sys_dict_type VALUES (12, N'000000', N'设备类型', N'sys_device_type', N'0', 103, 1, getdate(), NULL, NULL, N'客户端设备类型') +GO + +INSERT sys_dict_data VALUES (30, N'000000', 0, N'密码认证', N'password', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'密码认证'); +GO +INSERT sys_dict_data VALUES (31, N'000000', 0, N'短信认证', N'sms', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'短信认证') +GO +INSERT sys_dict_data VALUES (32, N'000000', 0, N'邮件认证', N'email', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'邮件认证') +GO +INSERT sys_dict_data VALUES (33, N'000000', 0, N'小程序认证', N'xcx', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'小程序认证') +GO +INSERT sys_dict_data VALUES (34, N'000000', 0, N'三方登录认证', N'social', N'sys_grant_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'三方登录认证') +GO +INSERT sys_dict_data VALUES (35, N'000000', 0, N'PC', N'`pc`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'PC') +GO +INSERT sys_dict_data VALUES (36, N'000000', 0, N'安卓', N'`android`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'安卓') +GO +INSERT sys_dict_data VALUES (37, N'000000', 0, N'iOS', N'`ios`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'iOS') +GO +INSERT sys_dict_data VALUES (38, N'000000', 0, N'小程序', N'`xcx`', N'sys_device_type', N'', N'default', N'N', N'0', 103, 1, getdate(), NULL, NULL, N'小程序') +GO + +-- 二级菜单 +INSERT sys_menu VALUES (123, N'客户端管理', 1, 11, N'client', N'system/client/index', N'', 1, 0, N'C', N'0', N'0', N'system:client:list', N'international', 103, 1, getdate(), NULL, NULL, N'客户端管理菜单') +GO +-- 客户端管理按钮 +INSERT sys_menu VALUES (1061, N'客户端管理查询', 123, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:query', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1062, N'客户端管理新增', 123, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1063, N'客户端管理修改', 123, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1064, N'客户端管理删除', 123, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1065, N'客户端管理导出', 123, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:export', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO + + +-- 角色菜单权限 +INSERT sys_role_menu VALUES (2, 1061) +GO +INSERT sys_role_menu VALUES (2, 1062) +GO +INSERT sys_role_menu VALUES (2, 1063) +GO +INSERT sys_role_menu VALUES (2, 1064) +GO +INSERT sys_role_menu VALUES (2, 1065) +GO + + +UPDATE sys_dept SET leader = null +GO +ALTER TABLE sys_dept ALTER COLUMN leader bigint NULL +GO diff --git a/script/sql/update/sqlserver/update_5.1.0-5.1.1.sql b/script/sql/update/sqlserver/update_5.1.0-5.1.1.sql new file mode 100644 index 0000000..2238536 --- /dev/null +++ b/script/sql/update/sqlserver/update_5.1.0-5.1.1.sql @@ -0,0 +1,19 @@ +ALTER TABLE sys_logininfor ADD client_key nvarchar(32) DEFAULT '' NULL +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'客户端', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'client_key' +GO + +ALTER TABLE sys_logininfor ADD device_type nvarchar(32) DEFAULT '' NULL +GO + +EXEC sp_addextendedproperty + 'MS_Description', N'设备类型', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_logininfor', + 'COLUMN', N'device_type' +GO diff --git a/script/sql/update/sqlserver/update_5.1.1-5.1.2.sql b/script/sql/update/sqlserver/update_5.1.1-5.1.2.sql new file mode 100644 index 0000000..9133772 --- /dev/null +++ b/script/sql/update/sqlserver/update_5.1.1-5.1.2.sql @@ -0,0 +1,10 @@ +DELETE FROM sys_menu WHERE menu_id IN (1604, 1605); +GO +INSERT sys_menu VALUES (1620, N'配置列表', 118, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1621, N'配置添加', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:add', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1622, N'配置编辑', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:edit', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (1623, N'配置删除', 118, 6, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:ossConfig:remove', N'#', 103, 1, getdate(), NULL, NULL, N''); +GO diff --git a/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql b/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql new file mode 100644 index 0000000..18daca4 --- /dev/null +++ b/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql @@ -0,0 +1,29 @@ +ALTER TABLE sys_dept ADD dept_category nvarchar(100) DEFAULT NULL +EXEC sp_addextendedproperty + 'MS_Description', N'部门类别编码', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_dept', + 'COLUMN', N'dept_category' +GO +ALTER TABLE sys_post ADD dept_id bigint NOT NULL +GO +ALTER TABLE sys_post ADD post_category nvarchar(100) DEFAULT NULL +GO +EXEC sp_addextendedproperty + 'MS_Description', N'部门id', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'dept_id' +GO +EXEC sp_addextendedproperty + 'MS_Description', N'岗位类别编码', + 'SCHEMA', N'dbo', + 'TABLE', N'sys_post', + 'COLUMN', N'post_category' +GO +UPDATE sys_post SET dept_id = 100 +GO +UPDATE sys_post SET dept_id = 103 where post_id = 1 +GO +UPDATE sys_menu SET menu_name = N'SnailJob控制台', path = N'snailjob', component = N'monitor/snailjob/index', perms = N'monitor:snailjob:list', remark = N'SnailJob控制台菜单' WHERE menu_id = 120 +GO diff --git a/script/sql/update/sqlserver/update_5.3.0-5.3.1.sql b/script/sql/update/sqlserver/update_5.3.0-5.3.1.sql new file mode 100644 index 0000000..50859de --- /dev/null +++ b/script/sql/update/sqlserver/update_5.3.0-5.3.1.sql @@ -0,0 +1,21 @@ +ALTER TABLE flow_node DROP COLUMN skip_any_node; +ALTER TABLE flow_node ADD ext nvarchar(500) NULL; + +EXEC sp_addextendedproperty +'MS_Description', N'扩展属性', +'SCHEMA', N'dbo', +'TABLE', N'flow_node', +'COLUMN', N'ext' +GO + +CREATE NONCLUSTERED INDEX user_associated_idx ON flow_user (associated ASC) +GO + +ALTER TABLE sys_oss ADD ext1 nvarchar(500) NULL; + +EXEC sp_addextendedproperty +'MS_Description', N'扩展属性', +'SCHEMA', N'dbo', +'TABLE', N'sys_oss', +'COLUMN', N'ext1' +GO diff --git a/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql new file mode 100644 index 0000000..c5ec57c --- /dev/null +++ b/script/sql/update/sqlserver/update_5.3.1-5.4.0.sql @@ -0,0 +1,63 @@ +ALTER TABLE flow_task ADD flow_status nvarchar(20) NULL; + +EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_task', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_instance', +'COLUMN', N'flow_status' +GO + +IF ((SELECT COUNT(*) FROM ::fn_listextendedproperty('MS_Description', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status')) > 0) + EXEC sp_updateextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +ELSE + EXEC sp_addextendedproperty +'MS_Description', N'流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)', +'SCHEMA', N'dbo', +'TABLE', N'flow_his_task', +'COLUMN', N'flow_status' +GO + +-- sys_social 表修改列 +ALTER TABLE sys_social ALTER COLUMN access_token VARCHAR(2000) NOT NULL +GO +ALTER TABLE sys_social ALTER COLUMN refresh_token VARCHAR(2000) NULL +GO + +INSERT sys_menu VALUES (116, N'修改生成配置', 3, 2, N'gen-edit/index/:tableId(\\d+)', N'tool/gen/editTable', N'', 1, 1, N'C', N'1', N'0', N'tool:gen:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (130, N'分配用户', 1, 2, N'role-auth/user/:roleId(\\d+)', N'system/role/authUser', N'', 1, 1, N'C', N'1', N'0', N'system:role:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (131, N'分配角色', 1, 1, N'user-auth/role/:userId(\\d+)', N'system/user/authRole', N'', 1, 1, N'C', N'1', N'0', N'system:user:edit', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (132, N'字典数据', 1, 6, N'dict-data/index/:dictId(\\d+)', N'system/dict/data', N'', 1, 1, N'C', N'1', N'0', N'system:dict:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (133, N'文件配置管理', 1, 10, N'oss-config/index', N'system/oss/config', N'', 1, 1, N'C', N'1', N'0', N'system:ossConfig:list', N'#', 103, 1, getdate(), null, null, N''); +GO +INSERT sys_menu VALUES (11700, N'流程设计', 11616, 5, N'design/index', N'workflow/processDefinition/design', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO +INSERT sys_menu VALUES (11701, N'请假申请', 11616, 6, N'leaveEdit/index', N'workflow/leave/leaveEdit', N'', 1, 1, N'C', N'1', N'0', N'workflow:leave:edit', N'#', 103, 1, GETDATE(), NULL, NULL, N''); +GO diff --git a/script/sql/update/update_5.0-5.1.sql b/script/sql/update/update_5.0-5.1.sql new file mode 100644 index 0000000..871bda3 --- /dev/null +++ b/script/sql/update/update_5.0-5.1.sql @@ -0,0 +1,101 @@ +ALTER TABLE gen_table ADD COLUMN data_name varchar(200) NULL DEFAULT '' COMMENT '数据源名称' AFTER table_id; + +UPDATE sys_menu SET path = 'powerjob', component = 'monitor/powerjob/index', perms = 'monitor:powerjob:list', remark = 'powerjob控制台菜单' WHERE menu_id = 120; + +-- ---------------------------- +-- 第三方平台授权表 +-- ---------------------------- +drop table if exists sys_social; +create table sys_social +( + id bigint not null comment '主键', + user_id bigint not null comment '用户ID', + tenant_id varchar(20) default null comment '租户id', + auth_id varchar(255) not null comment '平台+平台唯一id', + source varchar(255) not null comment '用户来源', + open_id varchar(255) default null comment '平台编号唯一id', + user_name varchar(30) not null comment '登录账号', + nick_name varchar(30) default '' comment '用户昵称', + email varchar(255) default '' comment '用户邮箱', + avatar varchar(500) default '' comment '头像地址', + access_token varchar(255) not null comment '用户的授权令牌', + expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有', + refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有', + access_code varchar(255) default null comment '平台的授权信息,部分平台可能没有', + union_id varchar(255) default null comment '用户的 unionid', + scope varchar(255) default null comment '授予的权限,部分平台可能没有', + token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有', + id_token varchar(255) default null comment 'id token,部分平台可能没有', + mac_algorithm varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有', + mac_key varchar(255) default null comment '小米平台用户的附带属性,部分平台可能没有', + code varchar(255) default null comment '用户的授权code,部分平台可能没有', + oauth_token varchar(255) default null comment 'Twitter平台用户的附带属性,部分平台可能没有', + oauth_token_secret varchar(255) default null comment 'Twitter平台用户的附带属性,部分平台可能没有', + create_dept bigint(20) comment '创建部门', + create_by bigint(20) comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) comment '更新者', + update_time datetime comment '更新时间', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + PRIMARY KEY (id) +) engine=innodb comment = '社会化关系表'; + + +-- ---------------------------- +-- 系统授权表 +-- ---------------------------- +drop table if exists sys_client; +create table sys_client ( + id bigint(20) not null comment 'id', + client_id varchar(64) default null comment '客户端id', + client_key varchar(32) default null comment '客户端key', + client_secret varchar(255) default null comment '客户端秘钥', + grant_type varchar(255) default null comment '授权类型', + device_type varchar(32) default null comment '设备类型', + active_timeout int(11) default 1800 comment 'token活跃超时时间', + timeout int(11) default 604800 comment 'token固定超时', + status char(1) default '0' comment '状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime default null comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime default null comment '更新时间', + primary key (id) +) engine=innodb comment='系统授权表'; + +insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate()); +insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate()); + +insert into sys_dict_type values(11, '000000', '授权类型', 'sys_grant_type', 103, 1, sysdate(), null, null, '认证授权类型'); +insert into sys_dict_type values(12, '000000', '设备类型', 'sys_device_type', 103, 1, sysdate(), null, null, '客户端设备类型'); + +insert into sys_dict_data values(30, '000000', 0, '密码认证', 'password', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '密码认证'); +insert into sys_dict_data values(31, '000000', 0, '短信认证', 'sms', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '短信认证'); +insert into sys_dict_data values(32, '000000', 0, '邮件认证', 'email', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '邮件认证'); +insert into sys_dict_data values(33, '000000', 0, '小程序认证', 'xcx', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '小程序认证'); +insert into sys_dict_data values(34, '000000', 0, '三方登录认证', 'social', 'sys_grant_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '三方登录认证'); +insert into sys_dict_data values(35, '000000', 0, 'PC', 'pc', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'PC'); +insert into sys_dict_data values(36, '000000', 0, '安卓', 'android', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '安卓'); +insert into sys_dict_data values(37, '000000', 0, 'iOS', 'ios', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, 'iOS'); +insert into sys_dict_data values(38, '000000', 0, '小程序', 'xcx', 'sys_device_type', '', 'default', 'N', 103, 1, sysdate(), null, null, '小程序'); + +-- 二级菜单 +insert into sys_menu values('123', '客户端管理', '1', '11', 'client', 'system/client/index', '', 1, 0, 'C', '0', '0', 'system:client:list', 'international', 103, 1, sysdate(), null, null, '客户端管理菜单'); +-- 客户端管理按钮 +insert into sys_menu values('1061', '客户端管理查询', '123', '1', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1062', '客户端管理新增', '123', '2', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1063', '客户端管理修改', '123', '3', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1064', '客户端管理删除', '123', '4', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1065', '客户端管理导出', '123', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export', '#', 103, 1, sysdate(), null, null, ''); + +-- 角色菜单权限 +insert into sys_role_menu values ('2', '1061'); +insert into sys_role_menu values ('2', '1062'); +insert into sys_role_menu values ('2', '1063'); +insert into sys_role_menu values ('2', '1064'); +insert into sys_role_menu values ('2', '1065'); + + +update sys_dept set leader = null; +alter table sys_dept modify column leader bigint null default null comment '负责人' after order_num; diff --git a/script/sql/update/update_5.1.0-5.1.1.sql b/script/sql/update/update_5.1.0-5.1.1.sql new file mode 100644 index 0000000..1dea49b --- /dev/null +++ b/script/sql/update/update_5.1.0-5.1.1.sql @@ -0,0 +1,3 @@ +ALTER TABLE sys_logininfor + ADD COLUMN client_key VARCHAR(32) NULL DEFAULT NULL COMMENT '客户端' AFTER `user_name`, + ADD COLUMN device_type VARCHAR(32) NULL DEFAULT NULL COMMENT '设备类型' AFTER `client_key`; diff --git a/script/sql/update/update_5.1.1-5.1.2.sql b/script/sql/update/update_5.1.1-5.1.2.sql new file mode 100644 index 0000000..314743f --- /dev/null +++ b/script/sql/update/update_5.1.1-5.1.2.sql @@ -0,0 +1,5 @@ +delete from sys_menu where menu_id in (1604, 1605); +insert into sys_menu values('1620', '配置列表', '118', '5', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1621', '配置添加', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1622', '配置编辑', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('1623', '配置删除', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate(), null, null, ''); diff --git a/script/sql/update/update_5.1.2-5.2.0.sql b/script/sql/update/update_5.1.2-5.2.0.sql new file mode 100644 index 0000000..33384e7 --- /dev/null +++ b/script/sql/update/update_5.1.2-5.2.0.sql @@ -0,0 +1,5 @@ +ALTER TABLE sys_dept ADD dept_category VARCHAR(100) DEFAULT NULL COMMENT '部门类别编码'; +ALTER TABLE sys_post ADD dept_id BIGINT(20) NOT NULL COMMENT '部门id', ADD post_category VARCHAR(100) DEFAULT NULL COMMENT '岗位类别编码'; +UPDATE sys_post SET dept_id = 100; +UPDATE sys_post SET dept_id = 103 where post_id = 1; +UPDATE sys_menu SET menu_name = 'SnailJob控制台', path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob控制台菜单' WHERE menu_id = 120; diff --git a/script/sql/update/update_5.3.0-5.3.1.sql b/script/sql/update/update_5.3.0-5.3.1.sql new file mode 100644 index 0000000..f45cc40 --- /dev/null +++ b/script/sql/update/update_5.3.0-5.3.1.sql @@ -0,0 +1,7 @@ +ALTER TABLE `flow_node` DROP COLUMN `skip_any_node`; +ALTER TABLE `flow_node` + ADD COLUMN `ext` text NULL COMMENT '扩展属性' AFTER `update_time`; +ALTER TABLE `flow_user` ADD INDEX `user_associated`(`associated`) USING BTREE + +ALTER TABLE `sys_oss` + ADD COLUMN `ext1` text NULL COMMENT '扩展属性' AFTER `url`; diff --git a/script/sql/update/update_5.3.1-5.4.0.sql b/script/sql/update/update_5.3.1-5.4.0.sql new file mode 100644 index 0000000..d5062c3 --- /dev/null +++ b/script/sql/update/update_5.3.1-5.4.0.sql @@ -0,0 +1,21 @@ +ALTER TABLE `flow_task` + ADD COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `node_type`; + +ALTER TABLE `flow_instance` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `variable`; + +ALTER TABLE `flow_his_task` + MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)' AFTER `skip_type`; + +ALTER TABLE `sys_social` + MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`; +ALTER TABLE `sys_social` + MODIFY COLUMN `refresh_token` varchar(2000) DEFAULT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`; + +insert into sys_menu values('116', '修改生成配置', '3', '2', 'gen-edit/index/:tableId(\\d+)', 'tool/gen/editTable', '', 1, 1, 'C', '1', '0', 'tool:gen:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('130', '分配用户', '1', '2', 'role-auth/user/:roleId(\\d+)', 'system/role/authUser', '', 1, 1, 'C', '1', '0', 'system:role:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('131', '分配角色', '1', '1', 'user-auth/role/:userId(\\d+)', 'system/user/authRole', '', 1, 1, 'C', '1', '0', 'system:user:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('132', '字典数据', '1', '6', 'dict-data/index/:dictId(\\d+)', 'system/dict/data', '', 1, 1, 'C', '1', '0', 'system:dict:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('133', '文件配置管理', '1', '10', 'oss-config/index', 'system/oss/config', '', 1, 1, 'C', '1', '0', 'system:ossConfig:list', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11700', '流程设计', '11616', '5', 'design/index', 'workflow/processDefinition/design', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values('11701', '请假申请', '11616', '6', 'leaveEdit/index', 'workflow/leave/leaveEdit', '', 1, 1, 'C', '1', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), null, null, '');