Compare commits
36 Commits
v5.10.1.sp
...
v5.11.0.sp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97eb30fc11 | ||
|
|
3fda79ec8d | ||
|
|
a08a4cf06c | ||
|
|
0264dec97b | ||
|
|
500cbf8268 | ||
|
|
82732c2993 | ||
|
|
69a1941be6 | ||
|
|
80ffeac471 | ||
|
|
8826dc3259 | ||
|
|
b87fb6a07f | ||
|
|
e9ba5b96f2 | ||
|
|
0ee7337789 | ||
|
|
1356893d4e | ||
|
|
080b48559f | ||
|
|
af61f14a9a | ||
|
|
b53e257085 | ||
|
|
988eac224d | ||
|
|
186d18c160 | ||
|
|
132f814c2d | ||
|
|
f8736420de | ||
|
|
6dca1ded69 | ||
|
|
0b3b0731ff | ||
|
|
5bcc66d3f7 | ||
|
|
c14f464c3f | ||
|
|
75e6ccf888 | ||
|
|
3bf68070fe | ||
|
|
0374499e8a | ||
|
|
0b6a1157cc | ||
|
|
d76f991a9f | ||
|
|
00ec4d17c9 | ||
|
|
de33f20797 | ||
|
|
5f56575df4 | ||
|
|
4cade5fa31 | ||
|
|
de2c13279b | ||
|
|
87116f0fc7 | ||
|
|
3e68536eea |
@@ -37,7 +37,7 @@
|
||||
* QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
|
||||
* 微信群:如果二维码过期,请尝试刷新图片,或者添加客服微信 jeesitex 邀请您进群
|
||||
|
||||
<p style="padding-left:40px"><a href="https://jeesite.com/assets/images/wxg_cur.png" target="_blank">
|
||||
<p style="padding-left:40px"><a href="https://jeesite.com/assets/images/wxg_cur.png" target="_blank">
|
||||
<img alt="JeeSite微信群" src="https://jeesite.com/assets/images/wxg_cur.png" width="200"/></a>
|
||||
</p>
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
|
||||
* 2021 年终发布 Vue3 的前后分离版本,使得 JeeSite 拥有同一个后台服务 Web 来支撑分离版和全栈版两套前端技术栈。
|
||||
|
||||
* 对接常见 AI 大模型(OpenAPI、Ollama、DeepSeek等),支持检索增强生成 RAG 技术,实现企业知识库智能对话。
|
||||
|
||||
* 支持国产化软硬件环境,如国产芯片、操作系统、数据库、中间件、国密算法等。
|
||||
|
||||
## 核心优势
|
||||
@@ -117,6 +119,7 @@
|
||||
|
||||
## 生态系统
|
||||
|
||||
* AI 模块:<https://gitee.com/thinkgem/jeesite5/blob/v5.springboot3/modules/cms-ai>
|
||||
* 分布式微服务(Spring Cloud):<https://gitee.com/thinkgem/jeesite-cloud>
|
||||
* Flowable业务流程引擎(BPM):<http://jeesite.com/docs/bpm/>
|
||||
* 多站点内容管理模块(CMS):<https://jeesite.com/docs/cms/>
|
||||
@@ -133,8 +136,8 @@
|
||||
|
||||
### 在线演示
|
||||
|
||||
1. 全栈版地址:<http://demo.jeesite.com>
|
||||
2. Vue3分离版地址:<http://vue.jeesite.com>
|
||||
1. Vue3版地址:<https://vue.jeesite.com>
|
||||
2. 全栈版地址:<https://demo.jeesite.com>
|
||||
|
||||
### 本地运行
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ import java.util.UUID;
|
||||
*/
|
||||
public class IdGen {
|
||||
|
||||
private static SecureRandom random = new SecureRandom();
|
||||
private static IdWorker idWorker = new IdWorker(-1, -1);
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
private static final IdWorker idWorker = new IdWorker(-1, -1);
|
||||
|
||||
/**
|
||||
* 生成UUID, 中间无-分割.
|
||||
|
||||
@@ -172,7 +172,7 @@ public class PropertiesUtils {
|
||||
private static final Pattern p1 = Pattern.compile("\\$\\{.*?\\}");
|
||||
|
||||
/**
|
||||
* 获取属性值,取不到从System.getProperty()获取,都取不到返回null
|
||||
* 获取属性值,取不到从System.getProperty和System.getenv获取,都取不到返回null
|
||||
*/
|
||||
public String getProperty(String key) {
|
||||
if (environment != null){
|
||||
@@ -194,9 +194,10 @@ public class PropertiesUtils {
|
||||
String systemProperty = System.getProperty(key);
|
||||
if (systemProperty != null) {
|
||||
return systemProperty;
|
||||
}else{
|
||||
return System.getenv(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -182,8 +182,8 @@ public class JsonMapper extends ObjectMapper {
|
||||
* 反序列化POJO或简单Collection如List<String>.
|
||||
* 如果JSON字符串为Null或"null"字符串, 返回Null.
|
||||
* 如果JSON字符串为"[]", 返回空集合.
|
||||
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
|
||||
* @see #fromJson(String, JavaType)
|
||||
* 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String, Class)
|
||||
* @see #fromJson(String, Class)
|
||||
*/
|
||||
public <T> T fromJsonString(String jsonString, Class<T> clazz) {
|
||||
if (StringUtils.isEmpty(jsonString) || "<CLOB>".equals(jsonString)) {
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
=ZTree v3.x (JQuery Tree插件) 更新日志=
|
||||
|
||||
<font color="red">为了更好的优化及扩展zTree, 因此决定升级为v3.x,并且对之前的v2.x不兼容,会有很多结构上的修改,对此深感无奈与抱歉,请大家谅解。</font>
|
||||
<font color="red">
|
||||
|
||||
具体修改内容可参考:
|
||||
|
||||
* [http://www.ztree.me/v3/api.php zTree v3.0 API 文档]
|
||||
|
||||
* [http://www.ztree.me/v3/demo.php#_101 zTree v3.0 Demo 演示]
|
||||
|
||||
* [http://www.ztree.me/v3/faq.php#_101 zTree v3.0 常见问题]
|
||||
|
||||
</font>
|
||||
|
||||
<font color=#041594>
|
||||
*2013.03.11* v3.5.12
|
||||
* 【修改】由于 jquery 1.9 中移除 event.srcElement 导致的 js 报错的bug。
|
||||
* 【修改】在异步加载模式下,使用 moveNode 方法,且 moveType != "inner" 时,也会导致 targetNode 自动加载子节点的 bug
|
||||
* 【修改】对已经显示的节点(nochecked=true)使用 showNodes 或 showNode 方法后,导致勾选框出现的bug。
|
||||
* 【修改】对已经隐藏的节点(nochecked=false)使用 hideNodes 或 hideNode 方法后,导致勾选框消失的bug。
|
||||
* 【修改】getNodesByParamFuzzy 支持 大小写模糊。
|
||||
* 【修改】className 结构,提取 _consts.className.BUTTON / LEVEL / ICO_LOADING / SWITCH,便于快速修改 css 冲突。
|
||||
例如:与 WordPress 产生冲突后,直接修改 core 中的 "button" 和 "level" 即可。 Issue: https://github.com/zTree/zTree_v3/issues/2
|
||||
|
||||
*2013.01.28* v3.5.02
|
||||
* 【增加】setting.check.chkDisabledInherit 属性,用于设置 chkDisabled 在初始化时子节点是否可以继承父节点的 chkDisabled 属性
|
||||
* 【删除】内部 noSel 方法,使用 selectstart事件 和 "-moz-user-select"样式 处理禁止 节点文字被选择的功能
|
||||
* 【修改】不兼容 jQuery 1.9 的bug
|
||||
* 【修改】onDrop 的触发规则,保证异步加载模式下,可以在延迟加载结束后触发,避免 onDrop 中被拖拽的节点是已经更新后的数据。
|
||||
* 【修改】setChkDisabled 方法,增加 inheritParent, inheritChildren 参数设置是否让父子节点继承 disabled
|
||||
* 【修改】异步加载时 拼接参数的方法,由 string 修改为 json 对象
|
||||
* 【修正】1-2-3 3级节点时,如果 2级节点 全部设置为 nocheck 或 chkDisabled后,勾选3级节点时,1级节点的半勾选状态错误的 bug
|
||||
* 【修改】Demo: checkbox_nocheck.html & checkbox_chkDisabled.html;
|
||||
* 【修改】Demo: edit_super.html,增加 showRenameBtn & showRemoveBtn 的演示
|
||||
* 【修改】Demo: asyncForAll, 将 post 修改为 get;为了避免由于 IE10 的 bug 造成的客户端 以及 服务端崩溃
|
||||
IE10 ajax Post 无法提交参数的bug (http://bugs.jquery.com/ticket/12790)
|
||||
|
||||
*2012.12.21* v3.5.01
|
||||
* 【优化】clone 方法
|
||||
* 【修正】对于初始化无 children 属性的父节点进行 reAsyncChildNodes 操作时出错的 bug
|
||||
* 【修正】beforeRename 回调中使用 cancelEditName 方法后,再 return false 导致无法重新进行编辑的 bug
|
||||
* 【修正】exedit 扩展包让 setting.data.key.url 失效的 bug
|
||||
* 【修正】setting.check.autoCheckTrigger 设置为 true 时,onCheck 回调缺少 event 参数的 bug
|
||||
* 【修正】singlepath.html Demo 中的 bug
|
||||
|
||||
*2012.11.20* v3.5
|
||||
* 【优化】原先的 clone 方法 (特别感谢:愚人码头)
|
||||
* 【修改】隐藏父节点后,使用 expandAll 方法导致 父节点展开的 bug
|
||||
* 【修改】使用 jQuery v1.7 以上时,设置 zTree 容器 ul 隐藏(visibility: hidden;)后, 调用 selectNode 导致 IE 浏览器报错 Can't move focus 的 bug
|
||||
* 【修改】正在异步加载时,执行 destory 或 init 方法后,异步加载的节点影响新树的 bug
|
||||
* 【修改】方法 reAsyncChildNodes 在 refresh 的时候未清空内部 cache 导致内存泄露 的 bug
|
||||
* 【修改】批量节点拖拽到其他父节点内(inner)时,导致顺序反转 的 bug
|
||||
* 【修改】对于 使用 html格式的 节点无法触发 双击事件 的 bug
|
||||
* 【修改】onCheck 回调中的 event ,保证与触发事件中的 event 一致
|
||||
* 【修改】异步加载时,在 onNodeCreated 中执行 selectNode 后,导致节点折叠的 bug
|
||||
* 【修改】API 中 dataFilter 的参数名称 childNodes -> responseData
|
||||
* 【修改】API 中 iconSkin 的 举例内容
|
||||
* 【修改】API 中 chkDisabled 的说明
|
||||
* 【修改】Demo 中 index.html 内的 loadReady 重复绑定问题
|
||||
|
||||
*2012.09.03* v3.4
|
||||
* 【增加】 Demo —— OutLook 样式的左侧菜单
|
||||
* 【增加】清空 zTree 的方法 $.fn.zTree.destory(treeId) & zTree.destory()
|
||||
|
||||
* 【修改】core核心文件内 _eventProxy 方法中获取 tId 的方法,提高 DOM 的灵活性
|
||||
* 【修改】初始化时 多层父节点的 checkbox 半选状态计算错误的 bug
|
||||
* 【修改】同时选中父、子节点后,利用 getSelectedNodes 获取选中节点并利用 removeNode 删除时报错的 bug
|
||||
* 【修改】treeNode.chkDisabled / nocheck 属性,支持字符串格式的 "false"/"true"
|
||||
* 【修改】异步加载模式下无法利用 server 返回 xml 并且 在 dataFilter 中继续处理的 bug
|
||||
* 【修改】title 只允许设置为 string 类型值的问题。 修正后允许设置为 number 类型的值
|
||||
* 【修改】zId 计数规则 & Cache 保存,减少 IE9 的 bug 造成的内存泄漏
|
||||
* 【修改】API 页面搜索功能导致 IE 崩溃的 bug
|
||||
|
||||
*2012.07.16* v3.3
|
||||
* 【增加】扩展库 exhide -- 节点隐藏功能
|
||||
|
||||
* 【修改】getNodesByFilter 方法,添加 invokeParam 自定义参数
|
||||
* 【修改】拖拽中测试代码未删除,导致出现黄颜色的 iframe 遮罩层的 bug
|
||||
* 【修改】延迟加载方法 对于使用 expandAll 进行全部展开时,导致 onNodeCreated 回调 和 addDiyDom 方法触发过早的 bug
|
||||
* 【修改】使用 moveNode 移动尚未生成 DOM 的节点时,视图会出现异常的 bug
|
||||
* 【修改】删除节点后,相关节点的 isFirstNode 属性未重置的 bug
|
||||
* 【修改】getPreNode(),getNextNode() 方法在对于特殊情况时计算错误的 bug
|
||||
* 【修改】设置 title 之后,如果重新将 title 内容设置为空后,会导致无法更新 title 的 bug
|
||||
* 【修改】针对 setting.check.chkStyle=="radio" && setting.check.radioType=="all" 的情况时,getTreeCheckedNodes方法优化,找到一个结果就 break
|
||||
* 【修改】zTreeObj.getCheckedNodes(false) 在 radioType = "all" 时计算错误的 bug
|
||||
* 【修改】完善 API 中 beforeDrop / onDrop 的关于 treeId 的说明
|
||||
|
||||
*2012.05.13* v3.2
|
||||
* 【增加】setting.data.key.url 允许修改 treeNode.url 属性
|
||||
* 【增加】getNodesByFilter(filter, isSingle) 方法
|
||||
* 【增加】"与其他 DOM 拖拽互动" 的 Demo (http://www.ztree.me/v3/demo.php#_511)
|
||||
* 【增加】"异步加载模式下全部展开" 的 Demo (http://www.ztree.me/v3/demo.php#_512)
|
||||
|
||||
* 【修改】代码结构,将 addNodes、removeNode、removeChildNodes 方法 和 beforeRemove、onRemove 回调 转移到 core 内
|
||||
* 【修改】IE7的环境下无子节点的父节点反复展开出现多余空行的 bug
|
||||
* 【修改】异步加载时,如果出现网络异常等,会导致 图标显示错误的 bug
|
||||
* 【修改】dataFilter中 return null 导致异常 的 bug
|
||||
* 【修改】removeChildNodes 方法清空子节点后,无法正常添加节点的 bug
|
||||
* 【修改】moveNode 后节点中的自定义元素的事件丢失的 bug
|
||||
* 【修改】moveNode 方法中设置 isSilent = true 时,如果移动到已展开的 父节点后,出现异常的 bug
|
||||
* 【修改】onClick/onDrag/onDrop 回调中 event 不是原始 event 的 bug
|
||||
* 【修改】onDrop 回调中 当拖拽无效时,无法获得 treeNodes 的 bug
|
||||
* 【修改】onDrop 无法判断拖拽是 移动还是复制的问题
|
||||
* 【修改】未开启异步加载模式时,拖拽节点到子节点为空的父节点内时 出现异常 的 bug
|
||||
* 【修改】拖拽过程中,反复在 父节点图标上划动时,会出现停顿的 bug
|
||||
(需要css 结构—— button -> span.button)
|
||||
|
||||
* 【修改】拖拽操作时箭头 与 targetNode 背景之间的细节现实问题,便于用户拖拽时更容易区分 prev、next 和 inner 操作
|
||||
* 【修改】拖拽操作时IE6/7 下 在 节点<a> 右侧 10px 内会导致 targetNode = root 的 bug
|
||||
* 【修改】编辑模式下 默认的编辑按钮、删除按钮点击后,如果相应的 before 回调 return false 时会触发 onClick 回调的 bug
|
||||
|
||||
*2012.02.14* v3.1
|
||||
* 【增加】ajax 的参数 setting.async.contentType ,让提交参数适用于 json 数据提交 (主要适用于 .Net 的开发)。
|
||||
* 【增加】setting.edit.editNameSelectAll, 用于设定编辑节点名称时初次显示 input 后 text 内容为全选
|
||||
* 【修改】异步加载 规则,不再仅仅依靠父节点的子节点数来判定,增加内部属性 zAsync,保证默认状态下父节点及时无子节点也只能异步加载一次,除非使用 reAsyncChildNodes 方法强行控制异步加载。
|
||||
* 【修改】放大浏览器后导致 界面出现多余连接线的bug (需要更新:icon 图标和 css )
|
||||
* 【修改】在编辑状态,如果节点名超过编辑框宽度,左右键在框内不起作用的bug(IE 6 7 8 出现)
|
||||
CSS 中 filter:alpha(opacity=80) 造成的,应该是 ie 的 bug,需要更新 css 文件
|
||||
* 【修改】title 设置后,如果属性不存在,则默认为 title 为空,便于数据容错和用户灵活使用
|
||||
* 【修改】editName 方法如果针对尚未展开的 父节点,会导致该父节点自动展开的 bug
|
||||
* 【修改】title 中存在标签时导致 title 显示异常的bug(例如:蓝色字22%"'`<input/>`)
|
||||
|
||||
*2012.01.10* v3.0
|
||||
* 【增加】setting.check.autoCheckTrigger 默认值 false,可以设置联动选中时是否触发事件回调函数
|
||||
* 【增加】setting.callback.beforeEditName 回调函数,以保证用户可以捕获点击编辑按钮的事件
|
||||
* 【增加】treeNode.chkDisabled 属性,显示 checkbox 但是用户无法修改 checkbox 状态,并且该 checkbox 会影响父节点的 checkbox 的半选状态
|
||||
* 【增加】setting.check.nocheckInherit 属性,用户设置子节点继承 nocheck 属性,用于批量初始化节点,不适用于已经显示的节点
|
||||
* 【增加】setting.edit.drag.autoExpandTrigger 默认值 false,可以设置自动展开、折叠操作时是否触发事件回调函数
|
||||
* 【增加】setting.view.nameIsHTML 默认值 false,允许用户对 name 设置 DOM 对象
|
||||
* 【增加】treeNode.click 属性的说明文档
|
||||
* 【增加】treeObj.setChkDisabled 方法用于设置 checkbox / radio disabled 状态
|
||||
* 【增加】treeNode.halfCheck 属性,用于强制设定节点的半选状态
|
||||
|
||||
* 【修改】异步加载 & 编辑功能 共存时,拖拽节点 或 增加节点 导致 ie 上报错的 bug (apply 方法引起)
|
||||
* 【修改】zTreeStyle 样式冲突
|
||||
* 【修改】setting.data.key.title 默认值设置为 "",初始化时自动赋值为 setting.data.key.name 这样可避免希望 title 与 name 一致的用户反复设置参数
|
||||
* 【修改】点击叶子节点的连接线会触发 expand 事件的 bug
|
||||
* 【修改】IE 下 点击叶子节点连线会出现虚线框的 bug
|
||||
* 【修改】updateNode 导致 checkbox 半选状态错误的 bug
|
||||
* 【修改】checkNode 方法实现 toggle 操作, 取消 expandAll 方法的 toggle 操作
|
||||
* 【修改】zTree 内鼠标移动会抢页面上 input 内的焦点的 bug
|
||||
* 【修改】beforeRename / onRename 的触发方式——即使名称内容未改变也会触发,便于用户配合 beforeEditName 捕获编辑状态的结束,赋予用户更多调整规则的权利
|
||||
* 【修改】与 easyUI 共存时无法拖拽的bug
|
||||
* 【修改】beforeRename 在 Firefox 下如果利用 alert,会触发两次的 bug
|
||||
* 【修改】checkNode/expandNode/removeNode 方法,默认不触发回调函数,恢复 v2.6 的默认状态,同时增加 callbackFlag 参数,设置为 true 时,可以触发回调函数
|
||||
* 【修改】IE9下“根据参数查找节点”的Demo 报错:行14 重新声明常量属性(Demo 自身的问题,定义了history变量)
|
||||
* 【修改】初始化 zTree 时 onNodeCreated 事件回调函数中无法 用 getZTreeObj 获取 zTree 对象的 bug
|
||||
* 【修改】setting.edit.drag.prev / next / inner 参数,增加被拖拽的节点集合
|
||||
* 【修改】异步加载模式下,otherParam 使用Array数组会出错的 bug。例如: ["id", "1", "name", "test"]
|
||||
* 【修改】FireFox 下多棵树拖拽异常的 bug
|
||||
* 【修改】exedit 中调用 excheck库的方法时没有进行容错处理,导致如果只加入 exedit 而没有 excheck的时候,会出现 js 错误
|
||||
* 【修改】显示 checkbox 的 zTree 在编辑模式下,移动节点不会更新父节点半选状态的 bug
|
||||
* 【修改】treeNode.childs --> children; treeObject.removeChilds --> removeChildNodes; setting.data.key.childs --> children(英文不好惹的祸!抱歉了!)
|
||||
* 【修改】onRemove 回调中得到的 treeNode 还可以查找 preNode、nextNode 的bug。 修正后,getPreNode 和 getNextNode 都返回 null; 为了便于查找父节点,getParentNode 仍保留
|
||||
* 【修改】简单数据模式下,如果 id 与 pId 的值相同会导致该节点无法正常加载的 bug
|
||||
* 【修改】移动或删除中间节点会导致最后一个节点连接线图标变小的 bug
|
||||
|
||||
*2011.09.05* v3.0 beta
|
||||
* 【修改】zTree 的 js 代码架构全面修改,并且拆分
|
||||
* 【修改】zTree 的 css 样式全面修改,对浏览器可以更好地兼容,同时解决了以前1个像素差的问题
|
||||
* 【优化】采用延迟加载技术,一次性加载大数据量的节点性能飞速提升
|
||||
* 【增加】支持多节点同时选中、拖拽
|
||||
* 【增加】checkNode、checkAllNodes 等多种方法
|
||||
* 【增加】IE6 自动取消动画展开、折叠的功能
|
||||
* 【修正】异步加载 & 编辑模式 能够更完美的共存
|
||||
* 【修正】setting 配置更加合理,并且增加了若干项配置参数
|
||||
* 【修正】treeNode 节点数据的属性更加合理,并且增加了一些方法
|
||||
* 【修正】拖拽操作更加灵活方便,更容易制定自己的规则
|
||||
* 【修正】其他若干修改,详细对比请参考 url:[http://www.ztree.me/v3/faq.php#_101 zTree v3.0 常见问题]
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -24,13 +24,14 @@ import java.util.Date;
|
||||
@Column(name="category", attrName="category", label="问题分类"),
|
||||
@Column(name="content", attrName="content", label="问题和意见"),
|
||||
@Column(name="contact", attrName="contact", label="联系方式"),
|
||||
@Column(includeEntity=DataEntity.class),
|
||||
@Column(name="create_by_name", attrName="createByName", label="提问人员姓名", queryType=QueryType.LIKE),
|
||||
@Column(name="device_info", attrName="deviceInfo", label="设备信息"),
|
||||
@Column(name="reply_date", attrName="replyDate", label="回复时间"),
|
||||
@Column(name="reply_content", attrName="replyContent", label="回复意见"),
|
||||
@Column(name="reply_user_code", attrName="replyUserCode", label="回复人员"),
|
||||
@Column(name="reply_user_name", attrName="replyUserName", label="回复人员姓名", queryType=QueryType.LIKE),
|
||||
@Column(name="status", attrName="status", label="状态", isUpdate=true), // save时,允许更新status字段
|
||||
@Column(includeEntity=DataEntity.class),
|
||||
}, orderBy="a.create_date DESC"
|
||||
)
|
||||
public class AppComment extends DataEntity<AppComment> {
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
*/
|
||||
package com.jeesite.modules.app.web;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.entity.Page;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.modules.app.entity.AppComment;
|
||||
import com.jeesite.modules.app.service.AppCommentService;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -20,11 +22,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.entity.Page;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.modules.app.entity.AppComment;
|
||||
import com.jeesite.modules.app.service.AppCommentService;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* APP意见反馈Controller
|
||||
@@ -97,7 +95,6 @@ public class AppCommentController extends BaseController {
|
||||
}
|
||||
}
|
||||
appCommentService.save(appComment);
|
||||
appCommentService.updateStatus(appComment);
|
||||
return renderResult(Global.TRUE, text("保存意见成功!"));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,4 +24,5 @@
|
||||
5.9.1
|
||||
5.9.2
|
||||
5.10.0
|
||||
5.10.1
|
||||
5.10.1
|
||||
5.11.0
|
||||
136
modules/cms-ai/README.md
Normal file
136
modules/cms-ai/README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
## 模块简介
|
||||
|
||||
本模块基于 Spring AI 和 JeeSite 内容管理系统(CMS)并结合了检索增强生成(Retrieval-Augmented Generation, RAG)技术
|
||||
和先进的人工智能算法(AI),打造了一个强大的企业级知识管理和智能对话平台。该模块专为企业设计,旨在通过高效的知识获取和精准的对话能力,
|
||||
提升企业的信息管理效率和员工的工作效能。
|
||||
|
||||
检索增强生成 RAG 技术使系统能够自动从海量的企业文档中检索最相关的信息,并将其融入到生成的回答中,确保每一次查询都
|
||||
能获得最新且准确的结果。这种检索与生成相结合的方式,不仅提高了信息检索的准确性,还增强了回答的上下文关联性,
|
||||
特别适合处理复杂的企业知识库。
|
||||
|
||||
此外该模块,支持云上大模型和本地部署的大模型,如:Ollama、DeepSeek、通义千问,理论上支持所有 OpenAPI 标准接口的 AI 提供商。
|
||||
并能无缝集成多种嵌入式 AI 模型的向量数据库,如 Chroma、PGVector、Elasticsearch、Milvus 等,实现高效的数据存储、检索及分析。
|
||||
无论是大规模数据集还是高度专业化的领域知识,JeeSite CMS + RAG + AI 都能提供定制化解决方案,满足企业多样化的业务需求和技术要求。
|
||||
企业可以轻松管理和访问复杂的信息资源,促进内部知识共享和创新,从而在竞争激烈的市场环境中保持领先地位。
|
||||
|
||||
优势:本模块结构清晰,代码简洁易懂,不管是正式项目、或是学习 AI 技术、都能轻松应对读懂源代码。
|
||||
|
||||
## 在线演示
|
||||
|
||||
<https://vue.jeesite.com/cms/chat/index>
|
||||
|
||||
## AI 模型配置
|
||||
|
||||
支持的 AI 模型列表:<https://docs.spring.io/spring-ai/reference/1.0/api/index.html>
|
||||
|
||||
* 线上模型:理论上支持所有 [OpenAPI](https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api) 标准接口的 AI 提供商。
|
||||
|
||||
* 本地模型:使用 [Ollama](https://ollama.com) 安装方法,本文不多赘述,网上有很多安装资料。
|
||||
|
||||
* 模型类型包括:聊天对话模型和嵌入式向量库模型,需注意 dimensions 维度参数,要和模型要求的匹配。
|
||||
|
||||
具体配置项详见 `jeesite-cms-ai.yml` 文件,有注释。
|
||||
|
||||
## 向量数据库配置
|
||||
|
||||
支持的向量库列表:<https://docs.spring.io/spring-ai/reference/1.0/api/vectordbs.html>
|
||||
|
||||
* Chroma
|
||||
* PGVector
|
||||
* Elasticsearch
|
||||
* Milvus
|
||||
* ...
|
||||
|
||||
具体配置项详见 `jeesite-cms-ai.yml` 文件,有注释。
|
||||
|
||||
### 安装 Chroma
|
||||
|
||||
```sh
|
||||
docker run --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:0.5.20
|
||||
```
|
||||
|
||||
### 安装 PGVector
|
||||
|
||||
```sh
|
||||
docker run -d --name pgvector -p 5433:5432 -e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg17
|
||||
```
|
||||
|
||||
* 进入容器
|
||||
|
||||
```sql
|
||||
docker exec -it pgvector psql -U postgres
|
||||
```
|
||||
|
||||
* 建库语句
|
||||
|
||||
```sql
|
||||
CREATE DATABASE "jeesite-ai";
|
||||
|
||||
-- 激活数据库
|
||||
\connect "jeesite-ai";
|
||||
|
||||
-- 建立数据表和索引
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
CREATE EXTENSION IF NOT EXISTS hstore;
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- 使用 all-minilm 模型时创建
|
||||
DROP TABLE IF EXISTS vector_store_384;
|
||||
CREATE TABLE IF NOT EXISTS vector_store_384 (
|
||||
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
content text,
|
||||
metadata json,
|
||||
embedding vector(384)
|
||||
);
|
||||
CREATE INDEX ON vector_store_384 USING HNSW (embedding vector_cosine_ops);
|
||||
|
||||
-- 使用 nomic-embed-text 模型时创建
|
||||
DROP TABLE IF EXISTS vector_store_786;
|
||||
CREATE TABLE IF NOT EXISTS vector_store_786 (
|
||||
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
content text,
|
||||
metadata json,
|
||||
embedding vector(768)
|
||||
);
|
||||
CREATE INDEX ON vector_store_786 USING HNSW (embedding vector_cosine_ops);
|
||||
|
||||
-- 使用 bge-m3 模型时创建
|
||||
DROP TABLE IF EXISTS vector_store_1024;
|
||||
CREATE TABLE IF NOT EXISTS vector_store_1024 (
|
||||
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
|
||||
content text,
|
||||
metadata json,
|
||||
embedding vector(1024)
|
||||
);
|
||||
CREATE INDEX ON vector_store_1024 USING HNSW (embedding vector_cosine_ops);
|
||||
```
|
||||
|
||||
## 创建 AI 菜单
|
||||
|
||||
系统管理 -> 系统设置 -> 菜单管理 -> 新增
|
||||
|
||||
* 菜单名称:AI 助手
|
||||
* 菜单地址:/cms/chat/index
|
||||
|
||||
## 授权协议声明
|
||||
|
||||
1. 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款。
|
||||
2. 不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
|
||||
3. 在使用本软件时,由于它集成了众多第三方开源软件,请共同遵守这些开源软件的使用许可条款规定。
|
||||
4. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议、版权声明和其他原作者
|
||||
规定需要包含的说明(请尊重原作者的著作权,不要删除或修改文件中的`Copyright`和`@author`信息)
|
||||
更不要,全局替换源代码中的 jeesite 或 ThinkGem 等字样,否则你将违反本协议条款承担责任。
|
||||
5. 您若套用本软件的一些代码或功能参考,请保留源文件中的版权和作者,需要在您的软件介绍明显位置
|
||||
说明出处,举例:本软件基于 JeeSite 快速开发平台,并附带链接:http://jeesite.com
|
||||
6. 任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
|
||||
7. 如果你对本软件有改进,希望可以贡献给我们,共同进步。
|
||||
8. 本项目已申请软件著作权,请尊重开源,感谢阅读。
|
||||
9. 无用户数限制,无在线人数限制,放心使用。
|
||||
|
||||
## 技术支持与服务
|
||||
|
||||
* 本软件免费,我们也提供了相应的收费服务,因为:
|
||||
* 没有资金的支撑就很难得到发展,特别是一个好的产品,如果 JeeSite 帮助了您,请为我们点赞。支持我们,您可以获得更多回馈,我们会把公益事业做的更好,开放更多资源,回报社区和社会。请给我们一些动力吧,在此非常感谢已支持我们的朋友!
|
||||
* **联系我们**:请访问技术支持与服务页面:<http://s.jeesite.com>
|
||||
22
modules/cms-ai/bin/deploy.bat
Normal file
22
modules/cms-ai/bin/deploy.bat
Normal file
@@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
rem /**
|
||||
rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
rem * No deletion without permission, or be held responsible to law.
|
||||
rem *
|
||||
rem * Author: ThinkGem@163.com
|
||||
rem */
|
||||
echo.
|
||||
echo [<5B><>Ϣ] <20><><EFBFBD>̵<F0B9A4B3>Maven<65><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
call mvn -v
|
||||
echo.
|
||||
|
||||
cd ..
|
||||
call mvn clean deploy -Dmaven.test.skip=true -Pdeploy
|
||||
|
||||
cd bin
|
||||
pause
|
||||
18
modules/cms-ai/bin/deploy.sh
Normal file
18
modules/cms-ai/bin/deploy.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# /**
|
||||
# * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
# * No deletion without permission, or be held responsible to law.
|
||||
# *
|
||||
# * Author: ThinkGem@163.com
|
||||
# */
|
||||
echo ""
|
||||
echo "[信息] 部署工程到Maven服务器。"
|
||||
echo ""
|
||||
|
||||
mvn -v
|
||||
echo ""
|
||||
|
||||
cd ..
|
||||
mvn clean deploy -Dmaven.test.skip=true -Pdeploy
|
||||
|
||||
cd bin
|
||||
22
modules/cms-ai/bin/package.bat
Normal file
22
modules/cms-ai/bin/package.bat
Normal file
@@ -0,0 +1,22 @@
|
||||
@echo off
|
||||
rem /**
|
||||
rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
rem * No deletion without permission, or be held responsible to law.
|
||||
rem *
|
||||
rem * Author: ThinkGem@163.com
|
||||
rem */
|
||||
echo.
|
||||
echo [<5B><>Ϣ] <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>jar<61><72><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
call mvn -v
|
||||
echo.
|
||||
|
||||
cd ..
|
||||
call mvn clean install -Dmaven.test.skip=true -Ppackage
|
||||
|
||||
cd bin
|
||||
pause
|
||||
18
modules/cms-ai/bin/package.sh
Normal file
18
modules/cms-ai/bin/package.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# /**
|
||||
# * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
# * No deletion without permission, or be held responsible to law.
|
||||
# *
|
||||
# * Author: ThinkGem@163.com
|
||||
# */
|
||||
echo ""
|
||||
echo "[信息] 打包安装工程,生成jar包文件。"
|
||||
echo ""
|
||||
|
||||
mvn -v
|
||||
echo ""
|
||||
|
||||
cd ..
|
||||
mvn clean install -Dmaven.test.skip=true -Ppackage
|
||||
|
||||
cd bin
|
||||
3590
modules/cms-ai/db/cms-ai.erm
Normal file
3590
modules/cms-ai/db/cms-ai.erm
Normal file
File diff suppressed because it is too large
Load Diff
127
modules/cms-ai/pom.xml
Normal file
127
modules/cms-ai/pom.xml
Normal file
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>jeesite-module-cms-ai</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>JeeSite Module CMS+RAG+AI</name>
|
||||
<url>http://jeesite.com</url>
|
||||
<inceptionYear>2013-Now</inceptionYear>
|
||||
|
||||
<properties>
|
||||
|
||||
<spring-ai.version>1.0.0-M6</spring-ai.version>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-module-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-module-cms</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 云上大模型 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 本地大模型 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Chroma 向量数据库 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- PG 向量数据库
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- ES 向量数据库
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-elasticsearch-store-spring-boot-starter</artifactId>
|
||||
</dependency> -->
|
||||
|
||||
<!-- Milvus 向量数据库
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-milvus-store-spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-reload4j</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-resolver-dns-native-macos</artifactId>
|
||||
<classifier>osx-aarch_64</classifier>
|
||||
</dependency> -->
|
||||
|
||||
<!-- HTML 转 Markdown -->
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark-html2md-converter</artifactId>
|
||||
<version>0.64.8</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>thinkgem</id>
|
||||
<name>WangZhen</name>
|
||||
<email>thinkgem at 163.com</email>
|
||||
<roles><role>Project lead</role></roles>
|
||||
<timezone>+8</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<organization>
|
||||
<name>JeeSite</name>
|
||||
<url>http://jeesite.com</url>
|
||||
</organization>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.ai.config;
|
||||
|
||||
import com.jeesite.common.datasource.DataSourceHolder;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* AI 聊天配置类
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Configuration
|
||||
public class CmsAiChatConfig {
|
||||
|
||||
/**
|
||||
* PG向量库数据源
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnProperty(name = "jdbc.ds_pgvector.type")
|
||||
public JdbcTemplate pgVectorStoreJdbcTemplate() throws SQLException {
|
||||
DataSource dataSource = DataSourceHolder.getRoutingDataSource()
|
||||
.createDataSource("ds_pgvector");
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天对话客户端
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Bean
|
||||
public ChatClient chatClient(ChatClient.Builder builder) {
|
||||
return builder
|
||||
.defaultSystem("你是我的知识库AI助手,请帮我解答我提出的相关问题。")
|
||||
.build();
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public BatchingStrategy batchingStrategy() {
|
||||
// return new TokenCountBatchingStrategy(EncodingType.CL100K_BASE, Integer.MAX_VALUE, 0.1);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.ai.service;
|
||||
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.lang.TimeUtils;
|
||||
import com.jeesite.common.utils.PageUtils;
|
||||
import com.jeesite.modules.cms.entity.Article;
|
||||
import com.jeesite.modules.cms.service.ArticleVectorStore;
|
||||
import com.jeesite.modules.cms.utils.CmsUtils;
|
||||
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* CMS 文章向量库存储
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Service
|
||||
public class ArticleVectorStoreImpl implements ArticleVectorStore {
|
||||
|
||||
protected Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private VectorStore vectorStore;
|
||||
|
||||
/**
|
||||
* 保存文章到向量库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Override
|
||||
public void save(Article article) {
|
||||
Map<String, Object> metadata = MapUtils.newHashMap();
|
||||
metadata.put("id", article.getId());
|
||||
metadata.put("siteCode", article.getCategory().getSite().getSiteCode());
|
||||
metadata.put("categoryCode", article.getCategory().getCategoryCode());
|
||||
metadata.put("categoryName", article.getCategory().getCategoryName());
|
||||
metadata.put("title", article.getTitle());
|
||||
metadata.put("href", article.getHref());
|
||||
metadata.put("keywords", article.getKeywords());
|
||||
metadata.put("description", article.getDescription());
|
||||
metadata.put("url", article.getUrl());
|
||||
metadata.put("status", article.getStatus());
|
||||
metadata.put("createBy", article.getCreateBy());
|
||||
metadata.put("createDate", article.getCreateDate());
|
||||
metadata.put("updateBy", article.getUpdateBy());
|
||||
metadata.put("updateDate", article.getUpdateDate());
|
||||
String content = article.getTitle() + ", " + article.getKeywords() + ", "
|
||||
+ article.getDescription() + ", " + StringUtils.toMobileHtml(
|
||||
article.getArticleData().getContent());
|
||||
String markdown = FlexmarkHtmlConverter.builder().build().convert(content);
|
||||
List<Document> documents = List.of(new Document(article.getId(), markdown, metadata));
|
||||
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
|
||||
this.delete(article); // 删除原数据
|
||||
ListUtils.pageList(splitDocuments, 64, params -> {
|
||||
vectorStore.add((List<Document>)params[0]); // 增加新数据
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除向量库文章
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Override
|
||||
public void delete(Article article) {
|
||||
if (StringUtils.isNotBlank(article.getId())) {
|
||||
vectorStore.delete(new FilterExpressionBuilder().eq("id", article.getId()).build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建向量库文章
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public String rebuild(Article article) {
|
||||
logger.debug("开始重建向量库。 siteCode: {}, categoryCode: {}",
|
||||
article.getCategory().getSite().getSiteCode(),
|
||||
article.getCategory().getCategoryCode());
|
||||
long start = System.currentTimeMillis();
|
||||
try{
|
||||
article.setIsQueryArticleData(true); // 查询文章内容
|
||||
PageUtils.findList(article, null, e -> {
|
||||
List<Article> list = CmsUtils.getArticleService().findList((Article) e);
|
||||
if (!list.isEmpty()) {
|
||||
list.forEach(this::save);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}catch(Exception ex){
|
||||
logger.error("重建向量库失败", ex);
|
||||
return "重建向量库失败:" + ex.getMessage();
|
||||
}
|
||||
String message = "重建向量库完成! 用时" + TimeUtils.formatTime(System.currentTimeMillis() - start) + "。";
|
||||
logger.debug(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.ai.service;
|
||||
|
||||
import com.jeesite.common.cache.CacheUtils;
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import org.springframework.ai.chat.memory.ChatMemory;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI 对话消息存储
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Service
|
||||
public class CacheChatMemory implements ChatMemory {
|
||||
|
||||
private static final String CMS_CHAT_MSG_CACHE = "cmsChatMsgCache";
|
||||
|
||||
@Override
|
||||
public void add(String conversationId, List<Message> messages) {
|
||||
List<Message> conversationHistory = CacheUtils.get(CMS_CHAT_MSG_CACHE, conversationId);
|
||||
if (conversationHistory == null) {
|
||||
conversationHistory = ListUtils.newArrayList();
|
||||
}
|
||||
conversationHistory.addAll(messages);
|
||||
CacheUtils.put(CMS_CHAT_MSG_CACHE, conversationId, conversationHistory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Message> get(String conversationId, int lastN) {
|
||||
List<Message> all = CacheUtils.get(CMS_CHAT_MSG_CACHE, conversationId);
|
||||
return all != null ? all.stream().skip(Math.max(0, all.size() - lastN)).toList() : List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String conversationId) {
|
||||
CacheUtils.remove(CMS_CHAT_MSG_CACHE, conversationId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.ai.service;
|
||||
|
||||
import com.jeesite.common.cache.CacheUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
import com.jeesite.common.idgen.IdGen;
|
||||
import com.jeesite.common.lang.DateUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.service.BaseService;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
|
||||
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
|
||||
import org.springframework.ai.chat.memory.ChatMemory;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.vectorstore.SearchRequest;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* AI 聊天服务类
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@Service
|
||||
public class CmsAiChatService extends BaseService {
|
||||
|
||||
private static final String CMS_CHAT_CACHE = "cmsChatCache";
|
||||
|
||||
@Autowired
|
||||
private ChatClient chatClient;
|
||||
@Autowired
|
||||
private ChatMemory chatMemory;
|
||||
@Autowired
|
||||
private VectorStore vectorStore;
|
||||
|
||||
/**
|
||||
* 获取聊天对话消息
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public List<Message> getChatMessage(String conversationId) {
|
||||
return chatMemory.get(conversationId, 100);
|
||||
}
|
||||
|
||||
private static String getChatCacheKey() {
|
||||
String key = UserUtils.getUser().getId();
|
||||
if (StringUtils.isBlank(key)) {
|
||||
key = UserUtils.getSession().getId().toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public Map<String, Map<String, Object>> getChatCacheMap() {
|
||||
Map<String, Map<String, Object>> cache = CacheUtils.get(CMS_CHAT_CACHE, getChatCacheKey());
|
||||
if (cache == null) {
|
||||
cache = MapUtils.newHashMap();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建或更新聊天对话
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public Map<String, Object> saveChatConversation(String conversationId, String title) {
|
||||
if (StringUtils.isBlank(conversationId)) {
|
||||
conversationId = IdGen.nextId();
|
||||
}
|
||||
if (StringUtils.isBlank(title)) {
|
||||
title = "新对话 " + DateUtils.getTime();
|
||||
}
|
||||
Map<String, Object> map = MapUtils.newHashMap();
|
||||
map.put("id", conversationId);
|
||||
map.put("title", title);
|
||||
Map<String, Map<String, Object>> cache = getChatCacheMap();
|
||||
cache.put(conversationId, map);
|
||||
CacheUtils.put(CMS_CHAT_CACHE, getChatCacheKey(), cache);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除聊天对话
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public void deleteChatConversation(String conversationId) {
|
||||
Map<String, Map<String, Object>> cache = getChatCacheMap();
|
||||
cache.remove(conversationId);
|
||||
CacheUtils.put(CMS_CHAT_CACHE, getChatCacheKey(), cache);
|
||||
chatMemory.clear(conversationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天对话,流输出
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public Flux<ChatResponse> chatStream(String conversationId, String message) {
|
||||
return chatClient.prompt()
|
||||
.messages(new UserMessage(message))
|
||||
.advisors(
|
||||
new MessageChatMemoryAdvisor(chatMemory, conversationId, 1024),
|
||||
new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().similarityThreshold(0.6F).topK(6).build()))
|
||||
.stream()
|
||||
.chatResponse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.ai.web;
|
||||
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.modules.cms.ai.service.CmsAiChatService;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* AI 聊天控制器类
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("${adminPath}/cms/chat")
|
||||
public class CmsAiChatController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private CmsAiChatService cmsAiChatService;
|
||||
|
||||
/**
|
||||
* 获取聊天对话消息
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequestMapping("/message")
|
||||
public List<Message> message(String id) {
|
||||
return cmsAiChatService.getChatMessage(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天对话列表
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequestMapping("/list")
|
||||
public Collection<Map<String, Object>> list() {
|
||||
return cmsAiChatService.getChatCacheMap().values().stream()
|
||||
.sorted(Comparator.comparing(map -> (String) map.get("id"),
|
||||
Comparator.reverseOrder())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建或更新聊天对话
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequestMapping("/save")
|
||||
public String save(String id, String title) {
|
||||
Map<String, Object> map = cmsAiChatService.saveChatConversation(id, title);
|
||||
return renderResult(Global.TRUE, "保存成功", map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除聊天对话
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequestMapping("/delete")
|
||||
public String delete(String id) {
|
||||
cmsAiChatService.deleteChatConversation(id);
|
||||
return renderResult(Global.TRUE, "删除成功", id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天对话,流输出
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequestMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<ChatResponse> stream(String id, String message) {
|
||||
return cmsAiChatService.chatStream(id, message);
|
||||
}
|
||||
|
||||
}
|
||||
12
modules/cms-ai/src/main/resources/application-assistant.yml
Normal file
12
modules/cms-ai/src/main/resources/application-assistant.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
## 重要提示(Tip):
|
||||
|
||||
## 请勿在该配置文件中添加其它任何配置(添加也不会生效)。
|
||||
## 该文件,仅仅是为了让 jeesite-cms-ai.yml 文件,
|
||||
## 在 IDEA 中有一个自动完成及帮助提示,并无其它用意。
|
||||
## 参数配置请在 jeesite-cms-ai.yml 文件中添加。
|
||||
|
||||
spring:
|
||||
config:
|
||||
import:
|
||||
- classpath:config/jeesite-cms-ai.yml
|
||||
133
modules/cms-ai/src/main/resources/config/jeesite-cms-ai.yml
Normal file
133
modules/cms-ai/src/main/resources/config/jeesite-cms-ai.yml
Normal file
@@ -0,0 +1,133 @@
|
||||
# 温馨提示:不建议直接修改此文件,为了平台升级方便,建议将需要修改的参数值,复制到application.yml里进行覆盖该参数值。
|
||||
|
||||
spring:
|
||||
ai:
|
||||
|
||||
# 云上大模型(使用该模型,请开启 enabled 参数)
|
||||
openai:
|
||||
base-url: https://api.siliconflow.cn
|
||||
api-key: ${SFLOW_APP_KEY}
|
||||
#base-url: https://ai.gitee.com
|
||||
#api-key: ${GITEE_APP_KEY}
|
||||
# 聊天对话模型
|
||||
chat:
|
||||
enabled: true
|
||||
options:
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
#model: DeepSeek-R1-Distill-Qwen-14B
|
||||
max-tokens: 1024
|
||||
temperature: 0.6
|
||||
top-p: 0.7
|
||||
frequency-penalty: 0
|
||||
logprobs: true
|
||||
# 向量库知识库模型(注意:不同的模型维度不同)
|
||||
embedding:
|
||||
enabled: true
|
||||
options:
|
||||
model: BAAI/bge-m3
|
||||
#model: bge-large-zh-v1.5
|
||||
dimensions: 512
|
||||
|
||||
# 本地大模型配置(使用该模型,请开启 enabled 参数)
|
||||
ollama:
|
||||
base-url: http://localhost:11434
|
||||
# 聊天对话模型
|
||||
chat:
|
||||
enabled: false
|
||||
options:
|
||||
#model: qwen2.5
|
||||
model: deepseek-r1:7b
|
||||
max-tokens: 1024
|
||||
temperature: 0.6
|
||||
top-p: 0.7
|
||||
frequency-penalty: 0
|
||||
# 向量库知识库模型(注意:不同的模型维度不同)
|
||||
embedding:
|
||||
enabled: false
|
||||
# 维度 dimensions 设置为 384
|
||||
#model: all-minilm:33m
|
||||
# 维度 dimensions 设置为 768
|
||||
#model: nomic-embed-text
|
||||
# 维度 dimensions 设置为 1024
|
||||
model: bge-m3
|
||||
|
||||
# 向量数据库配置
|
||||
vectorstore:
|
||||
|
||||
# Chroma 向量数据库
|
||||
chroma:
|
||||
client:
|
||||
host: http://localhost
|
||||
port: 8000
|
||||
initialize-schema: true
|
||||
collection-name: vector_store
|
||||
|
||||
# # Postgresql 向量数据库(PG 连接配置,见下文,需要手动建表)
|
||||
# pgvector:
|
||||
# id-type: TEXT
|
||||
# index-type: HNSW
|
||||
# distance-type: COSINE_DISTANCE
|
||||
# initialize-schema: false
|
||||
# #table-name: vector_store_384
|
||||
# #dimensions: 384
|
||||
# #table-name: vector_store_786
|
||||
# #dimensions: 768
|
||||
# table-name: vector_store_1024
|
||||
# dimensions: 1024
|
||||
# batching-strategy: TOKEN_COUNT
|
||||
# max-document-batch-size: 10000
|
||||
|
||||
# # ES 向量数据库(ES 连接配置,见下文)
|
||||
# elasticsearch:
|
||||
# index-name: vector-index
|
||||
# initialize-schema: true
|
||||
# dimensions: 1024
|
||||
# similarity: cosine
|
||||
# batching-strategy: TOKEN_COUNT
|
||||
|
||||
# # Milvus 向量数据库(字符串长度不超过65535)
|
||||
# milvus:
|
||||
# client:
|
||||
# host: "localhost"
|
||||
# port: 19530
|
||||
# username: "root"
|
||||
# password: "milvus"
|
||||
# initialize-schema: true
|
||||
# database-name: "default2"
|
||||
# collection-name: "vector_store2"
|
||||
# embedding-dimension: 384
|
||||
# index-type: HNSW
|
||||
# metric-type: COSINE
|
||||
|
||||
# ========= Postgresql 向量数据库数据源 =========
|
||||
|
||||
#jdbc:
|
||||
# ds_pgvector:
|
||||
# type: postgresql
|
||||
# driver: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://127.0.0.1:5433/jeesite-ai
|
||||
# username: postgres
|
||||
# password: postgres
|
||||
# testSql: SELECT 1
|
||||
|
||||
# ========= ES 向量数据库连接配置 =========
|
||||
|
||||
#spring.elasticsearch:
|
||||
# enabled: true
|
||||
# socket-timeout: 120s
|
||||
# connection-timeout: 120s
|
||||
# uris: http://127.0.0.1:9200
|
||||
# username: elastic
|
||||
# password: elastic
|
||||
|
||||
# 对话消息存缓存,可自定义存数据库
|
||||
j2cache:
|
||||
caffeine:
|
||||
region:
|
||||
# 对话消息的超期时间,默认 30天,根据需要可以设置更久。
|
||||
cmsChatCache: 100000, 30d
|
||||
cmsChatMsgCache: 100000, 30d
|
||||
|
||||
#logging:
|
||||
# level:
|
||||
# org.springframework: debug
|
||||
@@ -0,0 +1 @@
|
||||
5.11.0
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
<artifactId>jeesite-module-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ import java.util.Date;
|
||||
@Column(name = "site_code"),
|
||||
@Column(name = "site_name"),
|
||||
})
|
||||
}, extWhereKeys = "dsfCategory", orderBy = "a.update_date DESC"
|
||||
}, extWhereKeys = "dsfCategory", orderBy = "a.weight DESC, a.update_date DESC"
|
||||
)
|
||||
public class Article extends DataEntity<Article> {
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.properties.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* CmsProperties
|
||||
* @author ThinkGem
|
||||
* @version 2022-4-10
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "cms")
|
||||
public class CmsProperties {
|
||||
|
||||
private PageCache pageCache = new PageCache();
|
||||
|
||||
public static class PageCache {
|
||||
|
||||
/**
|
||||
* 是否开启页面静态化缓存
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 缓存名称标识
|
||||
*/
|
||||
private String cacheName = "cmsPageCache";
|
||||
|
||||
/**
|
||||
* 拦截的网页地址
|
||||
*/
|
||||
private String urlPatterns = "${frontPath}/*";
|
||||
|
||||
/**
|
||||
* 只静态化 .html 后缀的网页
|
||||
*/
|
||||
private String urlSuffixes = ".html";
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getCacheName() {
|
||||
return cacheName;
|
||||
}
|
||||
|
||||
public void setCacheName(String cacheName) {
|
||||
this.cacheName = cacheName;
|
||||
}
|
||||
|
||||
public String getUrlPatterns() {
|
||||
return urlPatterns;
|
||||
}
|
||||
|
||||
public void setUrlPatterns(String urlPatterns) {
|
||||
this.urlPatterns = urlPatterns;
|
||||
}
|
||||
|
||||
public String getUrlSuffixes() {
|
||||
return urlSuffixes;
|
||||
}
|
||||
|
||||
public void setUrlSuffixes(String urlSuffixes) {
|
||||
this.urlSuffixes = urlSuffixes;
|
||||
}
|
||||
}
|
||||
|
||||
public PageCache getPageCache() {
|
||||
return pageCache;
|
||||
}
|
||||
|
||||
public void setPageCache(PageCache pageCache) {
|
||||
this.pageCache = pageCache;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,8 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
|
||||
@Autowired(required = false)
|
||||
private ArticleIndexService articleIndexService;
|
||||
@Autowired(required = false)
|
||||
private ArticleVectorStore articleVectorStore;
|
||||
@Autowired(required = false)
|
||||
private PageCacheService pageCacheService;
|
||||
|
||||
private static final ExecutorService updateExpiredWeightThreadPool = new ThreadPoolExecutor(5, 20,
|
||||
@@ -166,6 +168,10 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
|
||||
if (articleIndexService != null && Article.STATUS_NORMAL.equals(article.getStatus())) {
|
||||
articleIndexService.save(article);
|
||||
}
|
||||
// 保存文章到向量数据库
|
||||
if (articleVectorStore != null && Article.STATUS_NORMAL.equals(article.getStatus())) {
|
||||
articleVectorStore.save(article);
|
||||
}
|
||||
// 清理首页、栏目和文章页面缓存
|
||||
if (pageCacheService != null) {
|
||||
pageCacheService.clearCache(article);
|
||||
@@ -188,6 +194,14 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
|
||||
articleIndexService.delete(article);
|
||||
}
|
||||
}
|
||||
// 保存文章到向量数据库
|
||||
if (articleVectorStore != null) {
|
||||
if (Article.STATUS_NORMAL.equals(article.getStatus())) {
|
||||
articleVectorStore.save(article);
|
||||
} else {
|
||||
articleVectorStore.delete(article);
|
||||
}
|
||||
}
|
||||
// 清理首页、栏目和文章页面缓存
|
||||
if (pageCacheService != null) {
|
||||
pageCacheService.clearCache(article);
|
||||
@@ -221,6 +235,10 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
|
||||
if (articleIndexService != null) {
|
||||
articleIndexService.delete(article);
|
||||
}
|
||||
// 保存文章到向量数据库
|
||||
if (articleVectorStore != null) {
|
||||
articleVectorStore.delete(article);
|
||||
}
|
||||
// 清理首页、栏目和文章页面缓存
|
||||
if (pageCacheService != null) {
|
||||
pageCacheService.clearCache(article);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.modules.cms.service;
|
||||
|
||||
import com.jeesite.common.entity.Page;
|
||||
import com.jeesite.modules.cms.entity.Article;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文章向量存储服务类
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public interface ArticleVectorStore {
|
||||
|
||||
/**
|
||||
* 保存索引
|
||||
* @author ThinkGem
|
||||
*/
|
||||
void save(Article article);
|
||||
|
||||
/**
|
||||
* 删除索引
|
||||
* @author ThinkGem
|
||||
*/
|
||||
void delete(Article article);
|
||||
|
||||
/**
|
||||
* 重建向量库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
String rebuild(Article article);
|
||||
|
||||
}
|
||||
@@ -27,6 +27,8 @@ public class CategoryService extends TreeService<CategoryDao, Category> {
|
||||
@Autowired(required = false)
|
||||
private ArticleIndexService articleIndexService;
|
||||
@Autowired(required = false)
|
||||
private ArticleVectorStore articleVectorStore;
|
||||
@Autowired(required = false)
|
||||
private PageCacheService pageCacheService;
|
||||
|
||||
/**
|
||||
@@ -125,4 +127,15 @@ public class CategoryService extends TreeService<CategoryDao, Category> {
|
||||
return articleIndexService.rebuild(new Article(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建向量数据库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public String rebuildVectorStore(Category category) {
|
||||
if (articleVectorStore == null) {
|
||||
return text("您好,系统未安装全文检索模块");
|
||||
}
|
||||
return articleVectorStore.rebuild(new Article(category));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,8 @@ public class SiteService extends CrudService<SiteDao, Site> {
|
||||
@Autowired(required = false)
|
||||
private ArticleIndexService articleIndexService;
|
||||
@Autowired(required = false)
|
||||
private ArticleVectorStore articleVectorStore;
|
||||
@Autowired(required = false)
|
||||
private PageCacheService pageCacheService;
|
||||
|
||||
/**
|
||||
@@ -120,5 +122,16 @@ public class SiteService extends CrudService<SiteDao, Site> {
|
||||
}
|
||||
return articleIndexService.rebuild(new Article(new Category(site)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建向量数据库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
public String rebuildVectorStore(Site site) {
|
||||
if (articleVectorStore == null) {
|
||||
return text("您好,系统未安装内容管理AI模块");
|
||||
}
|
||||
return articleVectorStore.rebuild(new Article(new Category(site)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -256,6 +256,17 @@ public class CategoryController extends BaseController {
|
||||
return renderResult(Global.TRUE, categoryService.rebuildIndex(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建向量数据库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequiresPermissions("cms:category:rebuildVectorStore")
|
||||
@ResponseBody
|
||||
@RequestMapping(value = "rebuildVectorStore")
|
||||
public String rebuildVectorStore(Category category) {
|
||||
return renderResult(Global.TRUE, categoryService.rebuildVectorStore(category));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树结构数据
|
||||
* @param excludeCode 排除的Code
|
||||
|
||||
@@ -8,12 +8,12 @@ import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.entity.Page;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.common.web.CookieUtils;
|
||||
import com.jeesite.modules.cms.entity.Site;
|
||||
import com.jeesite.modules.cms.service.FileTempleteService;
|
||||
import com.jeesite.modules.cms.service.SiteService;
|
||||
import com.jeesite.modules.sys.utils.CorpUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -24,8 +24,6 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
@@ -144,6 +142,17 @@ public class SiteController extends BaseController {
|
||||
public String rebuildIndex(Site site) {
|
||||
return renderResult(Global.TRUE, siteService.rebuildIndex(site));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建向量数据库
|
||||
* @author ThinkGem
|
||||
*/
|
||||
@RequiresPermissions("cms:site:rebuildVectorStore")
|
||||
@ResponseBody
|
||||
@RequestMapping(value = "rebuildVectorStore")
|
||||
public String rebuildVectorStore(Site site) {
|
||||
return renderResult(Global.TRUE, siteService.rebuildVectorStore(site));
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择站点
|
||||
|
||||
12
modules/cms/src/main/resources/application-assistant.yml
Normal file
12
modules/cms/src/main/resources/application-assistant.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
## 重要提示(Tip):
|
||||
|
||||
## 请勿在该配置文件中添加其它任何配置(添加也不会生效)。
|
||||
## 该文件,仅仅是为了让 jeesite-cms.yml 文件,
|
||||
## 在 IDEA 中有一个自动完成及帮助提示,并无其它用意。
|
||||
## 参数配置请在 jeesite-cms.yml 文件中添加。
|
||||
|
||||
spring:
|
||||
config:
|
||||
import:
|
||||
- classpath:config/jeesite-cms.yml
|
||||
@@ -3,7 +3,7 @@
|
||||
cms:
|
||||
pageCache:
|
||||
# 是否开启页面静态化缓存
|
||||
#enabled: true
|
||||
enabled: false
|
||||
# 缓存名称标识
|
||||
cacheName: cmsPageCache
|
||||
# 拦截的网页地址
|
||||
@@ -20,5 +20,7 @@ j2cache:
|
||||
#spring:
|
||||
# elasticsearch:
|
||||
# enabled: true
|
||||
# uris: http://Win11:9200
|
||||
# uris: http://127.0.0.1:9200
|
||||
# connection-timeout: 120s
|
||||
# username: elastic
|
||||
# password: elastic
|
||||
|
||||
@@ -32,4 +32,5 @@
|
||||
5.9.1
|
||||
5.9.2
|
||||
5.10.0
|
||||
5.10.1
|
||||
5.10.1
|
||||
5.11.0
|
||||
@@ -4,6 +4,10 @@ body>.navbar{-webkit-transition:background-color .3s ease-in;transition:backgrou
|
||||
body>.navbar-transparent{background-color:transparent}
|
||||
body>.navbar-transparent .navbar-nav>.open>a{background-color:transparent!important}
|
||||
}
|
||||
h1,.h1{font-size:26px}
|
||||
h2,.h2{font-size:24px}
|
||||
h3,.h3{font-size:22px}
|
||||
p{margin:5px 0 10px;line-height:1.75;}
|
||||
#home{padding-top:0}
|
||||
#home .navbar-brand{padding:13.5px 15px 12.5px}
|
||||
#home .navbar-brand>img{display:inline;margin:0 10px;height:100%}
|
||||
@@ -80,7 +84,7 @@ footer p{clear:left;margin-bottom:0}
|
||||
.article-title {color:#333;font-size:30px;text-align:center;border-bottom:1px solid #ddd;padding:15px 20px 20px 20px;margin-bottom:30px;}
|
||||
.article-info {border-top:1px solid #ddd;padding:10px;margin:30px 0 0;}
|
||||
.article-desc {padding:8px 10px 8px;margin:10px 20px 20px 35px;font-size:14px;}
|
||||
.article-content {padding-top:20px;}
|
||||
.article-content {padding-top:10px;}
|
||||
|
||||
.pagination {margin:8px 0;display:block;/* text-align:center; */font-size:13px;} /* .pagination .controls a{border:0;} */
|
||||
.pagination>li>a, .pagination>li>span {min-width:37px;text-align:center;padding:6px;border:1px solid #ddd;background:transparent;/* border-radius:3px; */}
|
||||
|
||||
@@ -85,7 +85,7 @@ $('#dataGrid').dataGrid({
|
||||
{header:'${text("展现方式")}', name:'showModes', index:'a.show_modes', width:150, fixed:true, align:"center", formatter: function(val, obj, row, act){
|
||||
return js.getDictLabel("#{@DictUtils.getDictListJson('cms_show_modes')}", val, '未知', true);
|
||||
}},
|
||||
{header:'${text("操作")}', name:'actions', width:150, formatter: function(val, obj, row, act){
|
||||
{header:'${text("操作")}', name:'actions', width:180, formatter: function(val, obj, row, act){
|
||||
var actions = [];
|
||||
//# if(hasPermi('cms:category:edit')){
|
||||
actions.push('<a href="${ctx}/cms/category/form?categoryCode='+row.categoryCode+'" class="btnList" title="${text("编辑栏目表")}"><i class="fa fa-pencil"></i></a> ');
|
||||
@@ -98,7 +98,10 @@ $('#dataGrid').dataGrid({
|
||||
actions.push('<a href="${ctx}/cms/category/delete?categoryCode='+row.categoryCode+'" class="btnList" title="${text("删除栏目表")}" data-confirm="${text("确认要删除该栏目表及所有子栏目表吗?")}" data-deltreenode="'+row.id+'"><i class="fa fa-trash-o"></i></a> ');
|
||||
actions.push('<a href="${ctx}/cms/category/form?parentCode='+row.id+'&site.siteCode=${category.site.siteCode}" class="btnList" title="${text("新增下级栏目表")}"><i class="fa fa-plus-square"></i></a> ');
|
||||
//# if(hasPermi('cms:category:rebuildIndex')){
|
||||
actions.push('<a href="${ctx}/cms/category/rebuildIndex?categoryCode='+row.categoryCode+'" class="btnList" title="${text("重建该栏目索引")}" data-confirm="${text("确认重建该栏目下文章索引吗")}?"><i class="fa fa-crosshairs"></i></a> ');
|
||||
actions.push('<a href="${ctx}/cms/category/rebuildIndex?categoryCode='+row.categoryCode+'" class="btnList" title="${text("重建该栏目索引")}" data-confirm="${text("确认重建该栏目文章索引吗")}?"><i class="fa fa-crosshairs"></i></a> ');
|
||||
//# }
|
||||
//# if(hasPermi('cms:category:rebuildVectorStore')){
|
||||
actions.push('<a href="${ctx}/cms/category/rebuildVectorStore?categoryCode='+row.categoryCode+'" class="btnList" title="${text("重建该栏目向量数据库")}" data-confirm="${text("确认重建该栏目文章向量数据库吗")}?"><i class="fa fa-database"></i></a> ');
|
||||
//# }
|
||||
if (row.status == Global.STATUS_NORMAL){
|
||||
actions.push('<a href="${ctxFront}/list-'+row.categoryCode+'" target="_blank" title="${text("访问栏目")}"><i class="fa fa-globe"></i></a> ');
|
||||
|
||||
@@ -67,7 +67,7 @@ $('#dataGrid').dataGrid({
|
||||
return js.getDictLabel("#{@DictUtils.getDictListJson('sys_search_status')}", val, '${text("未知")}', true);
|
||||
}},
|
||||
{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:150, align:"center"},
|
||||
{header:'${text("操作")}', name:'actions', width:150, formatter: function(val, obj, row, act){
|
||||
{header:'${text("操作")}', name:'actions', width:160, formatter: function(val, obj, row, act){
|
||||
var actions = [];
|
||||
//# if(hasPermi('cms:site:edit')){
|
||||
actions.push('<a href="${ctx}/cms/site/form?siteCode='+row.siteCode+'" class="btnList" title="${text("编辑站点")}"><i class="fa fa-pencil"></i></a> ');
|
||||
@@ -81,6 +81,9 @@ $('#dataGrid').dataGrid({
|
||||
//# if(hasPermi('cms:site:rebuildIndex')){
|
||||
actions.push('<a href="${ctx}/cms/site/rebuildIndex?siteCode='+row.siteCode+'" class="btnList" title="${text("重建该站点索引")}" data-confirm="${text("确认重建该站点文章索引吗")}?"><i class="fa fa-crosshairs"></i></a> ');
|
||||
//# }
|
||||
//# if(hasPermi('cms:site:rebuildVectorStore')){
|
||||
actions.push('<a href="${ctx}/cms/site/rebuildVectorStore?siteCode='+row.siteCode+'" class="btnList" title="${text("重建该站点向量数据库")}" data-confirm="${text("确认重建该站点文章向量数据库吗")}?"><i class="fa fa-database"></i></a> ');
|
||||
//# }
|
||||
if (row.status == Global.STATUS_NORMAL){
|
||||
actions.push('<a href="${ctxFront}/index-'+row.siteCode+'" target="_blank" title="${text("访问站点")}"><i class="fa fa-globe"></i></a> ');
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ public interface EmpUserDao extends CrudDao<EmpUser> {
|
||||
* 根据部门编码查询用户,仅返回基本信息
|
||||
*/
|
||||
List<EmpUser> findUserListByOfficeCodes(EmpUser empUser);
|
||||
|
||||
/**
|
||||
* 根据公司编码查询用户,仅返回基本信息
|
||||
*/
|
||||
List<EmpUser> findUserListByCompanyCodes(EmpUser empUser);
|
||||
|
||||
/**
|
||||
* 根据角色编码查询用户,仅返回基本信息
|
||||
|
||||
@@ -338,6 +338,7 @@ public class InitCoreData extends BaseInitDataTests {
|
||||
job.setConcurrent(Global.NO);
|
||||
job.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
|
||||
job.setStatus(JobEntity.STATUS_PAUSED);
|
||||
job.setRemarks("实时推送和设定计划推送时间的定时推送消息。");
|
||||
jobDao.insert(job);
|
||||
job = new JobEntity(MsgLocalMergePushTask.class.getSimpleName(), "SYSTEM");
|
||||
job.setDescription("消息推送服务 (合并推送)");
|
||||
@@ -346,6 +347,19 @@ public class InitCoreData extends BaseInitDataTests {
|
||||
job.setConcurrent(Global.NO);
|
||||
job.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
|
||||
job.setStatus(JobEntity.STATUS_PAUSED);
|
||||
job.setRemarks("不重要的通知进行汇总,30分钟或更长执行一次,将多条消息合并为一条消息延迟推送给用户。");
|
||||
jobDao.insert(job);
|
||||
job = new JobEntity("DeleteLogBefore", "SYSTEM");
|
||||
job.setDescription("清理访问日志 (3个月前)");
|
||||
job.setInvokeTarget("logService.deleteLogBefore(0, 3, 0)");
|
||||
job.setCronExpression("0 0 0 1 1/1 ? *");
|
||||
job.setConcurrent(Global.NO);
|
||||
job.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
|
||||
job.setStatus(JobEntity.STATUS_PAUSED);
|
||||
job.setRemarks("1、清理1年前的所有日志:logService.deleteLogBefore(1, 0, 0)\n" +
|
||||
"2、清理6个月前的所有日志:logService.deleteLogBefore(0, 6, 0)\n" +
|
||||
"3、清理7天前的所有日志:logService.deleteLogBefore(0, 0, 7)\n" +
|
||||
"4、清理1年6个月前的所有日志:logService.deleteLogBefore(1, 6, 0)");
|
||||
jobDao.insert(job);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -47,6 +47,11 @@ public interface EmpUserService extends CrudServiceApi<EmpUser> {
|
||||
* 根据部门编码查询用户,仅返回基本信息
|
||||
*/
|
||||
List<EmpUser> findUserListByOfficeCodes(EmpUser empUser);
|
||||
|
||||
/**
|
||||
* 根据公司编码查询用户,仅返回基本信息
|
||||
*/
|
||||
List<EmpUser> findUserListByCompanyCodes(EmpUser empUser);
|
||||
|
||||
/**
|
||||
* 根据角色编码查询用户,仅返回基本信息
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
package com.jeesite.modules.sys.service.support;
|
||||
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.service.TreeService;
|
||||
import com.jeesite.common.utils.PageUtils;
|
||||
import com.jeesite.modules.sys.dao.CompanyDao;
|
||||
import com.jeesite.modules.sys.dao.CompanyOfficeDao;
|
||||
import com.jeesite.modules.sys.entity.Company;
|
||||
import com.jeesite.modules.sys.entity.CompanyOffice;
|
||||
import com.jeesite.modules.sys.entity.EmpUser;
|
||||
import com.jeesite.modules.sys.service.CompanyService;
|
||||
import com.jeesite.modules.sys.service.DataScopeService;
|
||||
import com.jeesite.modules.sys.service.EmpUserService;
|
||||
import com.jeesite.modules.sys.utils.EmpUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -29,9 +33,10 @@ public class CompanyServiceSupport extends TreeService<CompanyDao, Company>
|
||||
|
||||
@Autowired
|
||||
private CompanyOfficeDao companyOfficeDao;
|
||||
|
||||
@Autowired
|
||||
private DataScopeService dataScopeService;
|
||||
@Autowired
|
||||
private EmpUserService empUserService;
|
||||
|
||||
/**
|
||||
* 获取单条数据
|
||||
@@ -86,7 +91,7 @@ public class CompanyServiceSupport extends TreeService<CompanyDao, Company>
|
||||
companyOfficeDao.insertBatch(list, null);
|
||||
}
|
||||
// 清理公司相关缓存
|
||||
clearCompanyCache();
|
||||
clearCompanyCache(company);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,9 +100,10 @@ public class CompanyServiceSupport extends TreeService<CompanyDao, Company>
|
||||
@Override
|
||||
@Transactional
|
||||
public void delete(Company company) {
|
||||
company.sqlMap().markIdDelete();
|
||||
super.delete(company);
|
||||
// 清理公司相关缓存
|
||||
clearCompanyCache();
|
||||
clearCompanyCache(company);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,15 +114,37 @@ public class CompanyServiceSupport extends TreeService<CompanyDao, Company>
|
||||
public void updateStatus(Company company) {
|
||||
dao.updateStatus(company);
|
||||
// 清理公司相关缓存
|
||||
clearCompanyCache();
|
||||
clearCompanyCache(company);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理公司相关缓存
|
||||
*/
|
||||
private void clearCompanyCache(){
|
||||
// EmpUtils.removeCache(EmpUtils.CACHE_COMPANY_LIST);
|
||||
private void clearCompanyCache(Company company){
|
||||
EmpUtils.removeCache(EmpUtils.CACHE_COMPANY_ALL_LIST);
|
||||
// 清理公司下的用户缓存,包含子公司
|
||||
if (company == null || StringUtils.isBlank(company.getCompanyCode())){
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(company.getParentCode())){
|
||||
company = get(company);
|
||||
if (company == null){
|
||||
return;
|
||||
}
|
||||
}
|
||||
Company where = new Company();
|
||||
where.setStatus(Company.STATUS_NORMAL);
|
||||
where.setParentCodes(company.getParentCodes() + company.getCompanyCode() + ",%");
|
||||
EmpUser empUserWhere = new EmpUser();
|
||||
empUserWhere.setCodes(this.findByParentCodesLike(where).stream().map(Company::getCompanyCode).toArray(String[]::new));
|
||||
if (empUserWhere.getCodes().length == 0) {
|
||||
return;
|
||||
}
|
||||
PageUtils.findList(empUserWhere, null, e -> {
|
||||
List<EmpUser> empUserList = empUserService.findUserListByCompanyCodes((EmpUser)e);
|
||||
empUserList.forEach(UserUtils::clearCache);
|
||||
return !empUserList.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,6 +115,13 @@ public class EmpUserServiceSupport extends CrudService<EmpUserDao, EmpUser>
|
||||
public List<EmpUser> findUserListByOfficeCodes(EmpUser empUser){
|
||||
return dao.findUserListByOfficeCodes(empUser);
|
||||
}
|
||||
/**
|
||||
* 根据公司编码查询用户,仅返回基本信息
|
||||
*/
|
||||
@Override
|
||||
public List<EmpUser> findUserListByCompanyCodes(EmpUser empUser){
|
||||
return dao.findUserListByCompanyCodes(empUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色编码查询用户,仅返回基本信息
|
||||
|
||||
@@ -5,21 +5,26 @@
|
||||
package com.jeesite.modules.sys.service.support;
|
||||
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.service.ServiceException;
|
||||
import com.jeesite.common.service.TreeService;
|
||||
import com.jeesite.common.utils.PageUtils;
|
||||
import com.jeesite.common.utils.excel.ExcelImport;
|
||||
import com.jeesite.common.validator.ValidatorUtils;
|
||||
import com.jeesite.modules.sys.dao.OfficeDao;
|
||||
import com.jeesite.modules.sys.entity.EmpUser;
|
||||
import com.jeesite.modules.sys.entity.Office;
|
||||
import com.jeesite.modules.sys.service.DataScopeService;
|
||||
import com.jeesite.modules.sys.service.EmpUserService;
|
||||
import com.jeesite.modules.sys.service.OfficeService;
|
||||
import com.jeesite.modules.sys.utils.EmpUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -32,6 +37,8 @@ public class OfficeServiceSupport extends TreeService<OfficeDao, Office>
|
||||
|
||||
@Autowired
|
||||
private DataScopeService dataScopeService;
|
||||
@Autowired
|
||||
private EmpUserService empUserService;
|
||||
|
||||
/**
|
||||
* 获取单条数据
|
||||
@@ -74,7 +81,7 @@ public class OfficeServiceSupport extends TreeService<OfficeDao, Office>
|
||||
}
|
||||
super.save(office);
|
||||
// 清理部门相关缓存
|
||||
clearOfficeCache();
|
||||
clearOfficeCache(office);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +156,7 @@ public class OfficeServiceSupport extends TreeService<OfficeDao, Office>
|
||||
public void updateStatus(Office office) {
|
||||
super.updateStatus(office);
|
||||
// 清理部门相关缓存
|
||||
clearOfficeCache();
|
||||
clearOfficeCache(office);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,15 +168,37 @@ public class OfficeServiceSupport extends TreeService<OfficeDao, Office>
|
||||
office.sqlMap().markIdDelete();
|
||||
super.delete(office);
|
||||
// 清理部门相关缓存
|
||||
clearOfficeCache();
|
||||
clearOfficeCache(office);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理部门相关缓存
|
||||
*/
|
||||
private void clearOfficeCache(){
|
||||
// EmpUtils.removeCache(EmpUtils.CACHE_OFFICE_LIST);
|
||||
private void clearOfficeCache(Office office){
|
||||
EmpUtils.removeCache(EmpUtils.CACHE_OFFICE_ALL_LIST);
|
||||
// 清理组织下的用户缓存,包含子机构
|
||||
if (office == null || StringUtils.isBlank(office.getOfficeCode())){
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(office.getParentCode())){
|
||||
office = get(office);
|
||||
if (office == null){
|
||||
return;
|
||||
}
|
||||
}
|
||||
Office where = new Office();
|
||||
where.setStatus(Office.STATUS_NORMAL);
|
||||
where.setParentCodes(office.getParentCodes() + office.getOfficeCode() + ",%");
|
||||
EmpUser empUserWhere = new EmpUser();
|
||||
empUserWhere.setCodes(this.findByParentCodesLike(where).stream().map(Office::getOfficeCode).toArray(String[]::new));
|
||||
if (empUserWhere.getCodes().length == 0) {
|
||||
return;
|
||||
}
|
||||
PageUtils.findList(empUserWhere, null, e -> {
|
||||
List<EmpUser> empUserList = empUserService.findUserListByOfficeCodes((EmpUser)e);
|
||||
empUserList.forEach(UserUtils::clearCache);
|
||||
return !empUserList.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,9 +74,10 @@ public class AccountController extends BaseController{
|
||||
@Parameters({
|
||||
@Parameter(name = "mobile", description = "手机号码", required = true),
|
||||
@Parameter(name = "validCode", description = "图片验证码,防止重复机器人", required = true),
|
||||
@Parameter(name = "corpCode", description = "所属租户"),
|
||||
})
|
||||
public String getLoginValidCode(String mobile, String validCode, HttpServletRequest request) {
|
||||
return getValidCode("login", mobile, validCode, "mobile", request, "登录验证码");
|
||||
public String getLoginValidCode(String mobile, String validCode, String corpCode, HttpServletRequest request) {
|
||||
return getValidCode("login", mobile, validCode, "mobile", corpCode, request, "登录验证码");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,13 +145,14 @@ public class AccountController extends BaseController{
|
||||
@Parameter(name = "loginCode", description = "登录账号", required = true),
|
||||
@Parameter(name = "validCode", description = "图片验证码,防止重复机器人", required = true),
|
||||
@Parameter(name = "validType", description = "验证方式(mobile、email)", required = true),
|
||||
@Parameter(name = "corpCode", description = "所属租户"),
|
||||
})
|
||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "响应对象", content = @Content( schemaProperties = {
|
||||
@SchemaProperty(name = "result", schema = @Schema(description = "结果状态")),
|
||||
@SchemaProperty(name = "message", schema = @Schema(description = "返回消息")),
|
||||
}))})
|
||||
public String getFpValidCode(User user, String validCode, String validType, HttpServletRequest request) {
|
||||
return getValidCode("fp", user.getLoginCode(), validCode, validType, request, "找回密码");
|
||||
public String getFpValidCode(User user, String validCode, String validType, String corpCode, HttpServletRequest request) {
|
||||
return getValidCode("fp", user.getLoginCode(), validCode, validType, corpCode, request, "找回密码");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +195,7 @@ public class AccountController extends BaseController{
|
||||
* 获取验证码
|
||||
* @author ThinkGem
|
||||
*/
|
||||
private String getValidCode(String type, String loginCode, String validCode, String validType, HttpServletRequest request, String msgTitle) {
|
||||
private String getValidCode(String type, String loginCode, String validCode, String validType, String corpCode, HttpServletRequest request, String msgTitle) {
|
||||
// 校验图片验证码,防止重复机器人。
|
||||
if (!ValidCodeUtils.validate(request, validCode)){
|
||||
return renderResult(Global.FALSE, text("图片验证码不正确或已失效,请点击图片刷新!"));
|
||||
@@ -206,6 +208,7 @@ public class AccountController extends BaseController{
|
||||
if ("login".equals(type)){
|
||||
User where = new User();
|
||||
where.setMobile(loginCode);
|
||||
where.setCorpCode_(corpCode);
|
||||
where.setStatus(User.STATUS_NORMAL);
|
||||
List<User> userList = userService.findListByMobile(where);
|
||||
if (!userList.isEmpty()){
|
||||
@@ -225,7 +228,7 @@ public class AccountController extends BaseController{
|
||||
return renderResult(Global.FALSE, text("手机号不正确!"));
|
||||
}
|
||||
} else {
|
||||
u = UserUtils.getByLoginCode(loginCode);
|
||||
u = UserUtils.getByLoginCode(loginCode, corpCode);
|
||||
if(u == null){
|
||||
return renderResult(Global.FALSE, text("登录账号不正确!"));
|
||||
}
|
||||
@@ -300,14 +303,15 @@ public class AccountController extends BaseController{
|
||||
@Parameters({
|
||||
@Parameter(name = "loginCode", description = "登录账号", required = true),
|
||||
@Parameter(name = "validCode", description = "图片验证码,防止重复机器人", required = true),
|
||||
@Parameter(name = "corpCode", description = "所属租户"),
|
||||
})
|
||||
public String getPwdQuestion(String loginCode, String validCode, HttpServletRequest request) {
|
||||
public String getPwdQuestion(String loginCode, String validCode, String corpCode, HttpServletRequest request) {
|
||||
// 校验图片验证码,防止重复机器人。
|
||||
if (!ValidCodeUtils.validate(request, validCode)){
|
||||
return renderResult(Global.FALSE, text("图片验证码不正确或已失效,请点击图片刷新!"));
|
||||
}
|
||||
// 账号是否存在验证
|
||||
User u = UserUtils.getByLoginCode(loginCode);
|
||||
User u = UserUtils.getByLoginCode(loginCode, corpCode);
|
||||
if (u == null){
|
||||
return renderResult(Global.FALSE, text("登录账号不正确!"));
|
||||
}
|
||||
@@ -353,7 +357,7 @@ public class AccountController extends BaseController{
|
||||
public String savePwdByPwdQuestion(User user, HttpServletRequest request) {
|
||||
String userCode = UserUtils.getCache("fpUserCode");
|
||||
String loginCode = UserUtils.getCache("fpLoginCode");
|
||||
|
||||
|
||||
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
|
||||
if (!(userCode != null && loginCode != null && loginCode.equals(user.getLoginCode()))){
|
||||
return renderResult(Global.FALSE, text("请重新获取保密问题!"));
|
||||
@@ -368,7 +372,7 @@ public class AccountController extends BaseController{
|
||||
}
|
||||
|
||||
// 验证三个密保问题是否正确。
|
||||
User u = UserUtils.getByLoginCode(user.getLoginCode());
|
||||
User u = UserUtils.get(userCode);
|
||||
if (!(u != null && loginCode.equals(user.getLoginCode())
|
||||
&& PwdUtils.validatePassword(user.getPwdQuestionAnswer(), u.getPwdQuestionAnswer())
|
||||
&& PwdUtils.validatePassword(user.getPwdQuestionAnswer2(), u.getPwdQuestionAnswer2())
|
||||
|
||||
@@ -11,8 +11,10 @@ import com.jeesite.common.idgen.IdGen;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.modules.sys.entity.Company;
|
||||
import com.jeesite.modules.sys.entity.EmpUser;
|
||||
import com.jeesite.modules.sys.entity.Office;
|
||||
import com.jeesite.modules.sys.service.CompanyService;
|
||||
import com.jeesite.modules.sys.service.EmpUserService;
|
||||
import com.jeesite.modules.sys.service.OfficeService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -43,9 +45,10 @@ public class CompanyController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private CompanyService companyService;
|
||||
|
||||
@Autowired
|
||||
private OfficeService officeService;
|
||||
@Autowired
|
||||
private EmpUserService empUserService;
|
||||
|
||||
/**
|
||||
* 获取公司
|
||||
@@ -208,6 +211,14 @@ public class CompanyController extends BaseController {
|
||||
@RequestMapping(value = "delete")
|
||||
@ResponseBody
|
||||
public String delete(Company company) {
|
||||
if (Global.getConfigToBoolean("sys.company.notAllowDeleteIfUserExists", "false")) {
|
||||
EmpUser empUserWhere = new EmpUser();
|
||||
empUserWhere.getEmployee().getCompany().setIsQueryChildren(true);
|
||||
empUserWhere.getEmployee().getCompany().setCompanyCode(company.getCompanyCode());
|
||||
if (empUserService.findCount(empUserWhere) > 0) {
|
||||
return renderResult(Global.FALSE, text("不允许删除包含用户的公司"));
|
||||
}
|
||||
}
|
||||
companyService.delete(company);
|
||||
return renderResult(Global.TRUE, text("删除公司''{0}''成功", company.getCompanyName()));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.utils.excel.ExcelExport;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
|
||||
import com.jeesite.common.web.BaseController;
|
||||
import com.jeesite.modules.sys.entity.EmpUser;
|
||||
import com.jeesite.modules.sys.entity.Office;
|
||||
import com.jeesite.modules.sys.service.EmpUserService;
|
||||
import com.jeesite.modules.sys.service.OfficeService;
|
||||
import com.jeesite.modules.sys.web.user.EmpUserController;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -46,7 +48,8 @@ public class OfficeController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private OfficeService officeService;
|
||||
|
||||
@Autowired
|
||||
private EmpUserService empUserService;
|
||||
@Autowired
|
||||
private EmpUserController empUserController;
|
||||
|
||||
@@ -260,6 +263,14 @@ public class OfficeController extends BaseController {
|
||||
@RequestMapping(value = "delete")
|
||||
@ResponseBody
|
||||
public String delete(Office office) {
|
||||
if (Global.getConfigToBoolean("sys.office.notAllowDeleteIfUserExists", "false")) {
|
||||
EmpUser empUserWhere = new EmpUser();
|
||||
empUserWhere.getEmployee().getOffice().setIsQueryChildren(true);
|
||||
empUserWhere.getEmployee().getOffice().setOfficeCode(office.getOfficeCode());
|
||||
if (empUserService.findCount(empUserWhere) > 0) {
|
||||
return renderResult(Global.FALSE, text("不允许删除包含用户的机构"));
|
||||
}
|
||||
}
|
||||
officeService.delete(office);
|
||||
return renderResult(Global.TRUE, text("删除机构''{0}''成功", office.getOfficeName()));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package com.jeesite.modules.sys.web.user;
|
||||
|
||||
import com.alibaba.fastjson.JSONValidator;
|
||||
import com.jeesite.common.cache.CacheUtils;
|
||||
import com.jeesite.common.codec.EncodeUtils;
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
@@ -70,8 +71,15 @@ public class EmpUserController extends BaseController {
|
||||
private RoleService roleService;
|
||||
|
||||
@ModelAttribute
|
||||
public EmpUser get(String userCode, boolean isNewRecord) {
|
||||
return empUserService.get(userCode, isNewRecord);
|
||||
public EmpUser get(String userCode, boolean isNewRecord, Boolean isAll, String ctrlPermi) {
|
||||
EmpUser empUser = new EmpUser();
|
||||
empUser.setUserCode(userCode);
|
||||
empUser.setIsNewRecord(isNewRecord);
|
||||
// 更严格的权限控制,对单条数据进行数据权限过滤(isAll 是一个开关,正常不需要添加)
|
||||
if (StringUtils.isNotBlank(userCode) && !(isAll != null && isAll) || Global.isStrictMode()) {
|
||||
empUserService.addDataScopeFilter(empUser, ctrlPermi);
|
||||
}
|
||||
return empUserService.getAndValid(empUser);
|
||||
}
|
||||
|
||||
@RequiresPermissions("sys:empUser:view")
|
||||
@@ -111,6 +119,12 @@ public class EmpUserController extends BaseController {
|
||||
empUserService.addDataScopeFilter(empUser, ctrlPermi);
|
||||
}
|
||||
empUser.setPage(new Page<>(request, response));
|
||||
// // 定义字段权限属性名(可根据权限配置库查询出来)排除和包含同时存在时,排除优先级高于包含
|
||||
// Set<String> attrNames = SetUtils.newHashSet("userCode", "employee.office.officeCode");
|
||||
// // 查询 SQL 结果集中,不包含 userCode、employee.office.officeCode 值返回
|
||||
// empUser.sqlMap().getColumn().setExcludeAttrNames(attrNames);
|
||||
// // 查询 SQL 结果集中,仅包含 userCode、employee.office.officeCode 值返回
|
||||
// empUser.sqlMap().getColumn().setIncludeAttrNames(attrNames);
|
||||
Page<EmpUser> page = empUserService.findPage(empUser);
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,6 @@
|
||||
*/
|
||||
package com.jeesite.modules.sys.web.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.alibaba.fastjson.JSONValidator;
|
||||
import com.jeesite.common.codec.DesUtils;
|
||||
import com.jeesite.common.codec.EncodeUtils;
|
||||
@@ -29,6 +16,17 @@ import com.jeesite.modules.sys.entity.User;
|
||||
import com.jeesite.modules.sys.service.UserService;
|
||||
import com.jeesite.modules.sys.utils.PwdUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
|
||||
/**
|
||||
@@ -78,12 +76,18 @@ public class UserController extends BaseController {
|
||||
*/
|
||||
@RequiresPermissions("user")
|
||||
@RequestMapping(value = "info")
|
||||
public String info(User user, String op, Model model) {
|
||||
public String info(String op, Model model) {
|
||||
if (StringUtils.isBlank(op)){
|
||||
op = "base";
|
||||
}
|
||||
User u = UserUtils.getUser();
|
||||
model.addAttribute("op", op);
|
||||
model.addAttribute("user", UserUtils.getUser());
|
||||
model.addAttribute("user", u);
|
||||
if (StringUtils.equals(op, "pqa")){
|
||||
model.addAttribute("pwdQuestion", u.getPwdQuestion());
|
||||
model.addAttribute("pwdQuestion2", u.getPwdQuestion2());
|
||||
model.addAttribute("pwdQuestion3", u.getPwdQuestion3());
|
||||
}
|
||||
return "modules/sys/user/userInfo";
|
||||
}
|
||||
|
||||
@@ -93,7 +97,7 @@ public class UserController extends BaseController {
|
||||
@RequiresPermissions("user")
|
||||
@PostMapping(value = "infoSaveBase")
|
||||
@ResponseBody
|
||||
public String infoSaveBase(User user, HttpServletRequest request) {
|
||||
public String infoSaveBase(User user) {
|
||||
if (StringUtils.isBlank(user.getUserName())){
|
||||
return renderResult(Global.FALSE, text("sys.user.userNameNotBlank"));
|
||||
}
|
||||
@@ -116,8 +120,7 @@ public class UserController extends BaseController {
|
||||
@RequiresPermissions("user")
|
||||
@PostMapping(value = "infoSavePwd")
|
||||
@ResponseBody
|
||||
public String infoSavePwd(User user, String oldPassword, String newPassword,
|
||||
String confirmNewPassword) {
|
||||
public String infoSavePwd(String oldPassword, String newPassword, String confirmNewPassword) {
|
||||
User currentUser = UserUtils.getUser();
|
||||
// 登录密码解密(解决密码明文传输安全问题)
|
||||
String secretKey = Global.getProperty("shiro.loginSubmit.secretKey");
|
||||
|
||||
12
modules/core/src/main/resources/application-assistant.yml
Normal file
12
modules/core/src/main/resources/application-assistant.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
## 重要提示(Tip):
|
||||
|
||||
## 请勿在该配置文件中添加其它任何配置(添加也不会生效)。
|
||||
## 该文件,仅仅是为了让 jeesite-core.yml 文件,
|
||||
## 在 IDEA 中有一个自动完成及帮助提示,并无其它用意。
|
||||
## 参数配置请在 jeesite-core.yml 文件中添加。
|
||||
|
||||
spring:
|
||||
config:
|
||||
import:
|
||||
- classpath:config/jeesite-core.yml
|
||||
@@ -623,6 +623,7 @@ j2cache:
|
||||
# active -> 主动清除,二级缓存过期主动通知各节点清除,优点在于所有节点可以同时收到缓存清除,存储模式需要设置为 generic
|
||||
# blend -> 两种模式一起运作,对于各个节点缓存准确以及及时性要求高的可以使用,正常用前两种模式中一个就可
|
||||
cache_clean_mode: passive
|
||||
|
||||
# Web 相关
|
||||
web:
|
||||
|
||||
@@ -799,10 +800,6 @@ video:
|
||||
# 将mp4视频的元数据信息转到视频第一帧
|
||||
qtFaststartFile: d:/tools/video/qt-faststart/qt-faststart.exe
|
||||
|
||||
# 文件管理是否启用租户模式
|
||||
filemanager:
|
||||
useCorpModel: false
|
||||
|
||||
#======================================#
|
||||
#========== Message settings ==========#
|
||||
#======================================#
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
<logger name="org.springframework.boot.web.embedded" level="INFO" />
|
||||
<logger name="org.apache.catalina.core.StandardEngine" level="INFO" />
|
||||
<logger name="net.oschina.j2cache.caffeine.CaffeineProvider" level="ERROR" />
|
||||
<logger name="org.apache.fury.serializer.ObjectStreamSerializer" level="ERROR" />
|
||||
<logger name="org.apache.fury.config.FuryBuilder" level="ERROR" />
|
||||
<logger name="ShardingSphere-SQL" level="DEBUG" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
typeHandler="com.jeesite.common.mybatis.type.AesTypeHandler"/>
|
||||
</resultMap> -->
|
||||
|
||||
<!-- 查询数据
|
||||
<!-- 查询数据
|
||||
<select id="findList" resultMap="empUserResult"> -->
|
||||
<select id="findList" resultType="EmpUser">
|
||||
SELECT ${sqlMap.column.toSql()}
|
||||
@@ -54,50 +54,71 @@
|
||||
<!-- 查询全部用户,仅返回基本信息 -->
|
||||
<select id="findUserList" resultType="EmpUser">
|
||||
SELECT
|
||||
<include refid="userColumns"/>
|
||||
<include refid="userColumns"/>
|
||||
FROM ${_prefix}sys_user a
|
||||
WHERE a.status = #{STATUS_NORMAL}
|
||||
AND a.user_type = #{USER_TYPE_EMPLOYEE}
|
||||
<if test="global.useCorpModel">
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
ORDER BY a.user_code
|
||||
</select>
|
||||
|
||||
<!-- 根据部门编码查询用户,仅返回基本信息 -->
|
||||
<select id="findUserListByOfficeCodes" resultType="EmpUser">
|
||||
SELECT
|
||||
<include refid="userColumns"/>
|
||||
<include refid="userColumns"/>
|
||||
FROM ${_prefix}sys_user a
|
||||
JOIN ${_prefix}sys_employee e ON e.emp_code = a.ref_code
|
||||
JOIN ${_prefix}sys_office o ON o.office_code = e.office_code
|
||||
WHERE a.status = #{STATUS_NORMAL}
|
||||
AND a.user_type = #{USER_TYPE_EMPLOYEE}
|
||||
<if test="global.useCorpModel">
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND e.status = #{STATUS_NORMAL}
|
||||
AND o.status = #{STATUS_NORMAL}
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND e.status = #{STATUS_NORMAL}
|
||||
AND o.status = #{STATUS_NORMAL}
|
||||
AND o.office_code IN
|
||||
<foreach item="code" index="index" collection="codes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
ORDER BY a.user_code
|
||||
</select>
|
||||
|
||||
<!-- 根据公司编码查询用户,仅返回基本信息 -->
|
||||
<select id="findUserListByCompanyCodes" resultType="EmpUser">
|
||||
SELECT
|
||||
<include refid="userColumns"/>
|
||||
FROM ${_prefix}sys_user a
|
||||
JOIN ${_prefix}sys_employee e ON e.emp_code = a.ref_code
|
||||
JOIN ${_prefix}sys_company o ON o.company_code = e.company_code
|
||||
WHERE a.status = #{STATUS_NORMAL}
|
||||
AND a.user_type = #{USER_TYPE_EMPLOYEE}
|
||||
<if test="global.useCorpModel">
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND e.status = #{STATUS_NORMAL}
|
||||
AND o.status = #{STATUS_NORMAL}
|
||||
AND o.company_code IN
|
||||
<foreach item="code" index="index" collection="codes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
</foreach>
|
||||
ORDER BY a.user_code
|
||||
</select>
|
||||
|
||||
<!-- 根据角色编码查询用户,仅返回基本信息 -->
|
||||
<select id="findUserListByRoleCodes" resultType="EmpUser">
|
||||
SELECT
|
||||
<include refid="userColumns"/>
|
||||
<include refid="userColumns"/>
|
||||
FROM ${_prefix}sys_user a
|
||||
JOIN ${_prefix}sys_user_role ur2 ON ur2.user_code = a.user_code
|
||||
JOIN ${_prefix}sys_role r ON r.role_code = ur2.role_code
|
||||
WHERE a.status = #{STATUS_NORMAL}
|
||||
AND a.user_type = #{USER_TYPE_EMPLOYEE}
|
||||
<if test="global.useCorpModel">
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND r.status = #{STATUS_NORMAL}
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND r.status = #{STATUS_NORMAL}
|
||||
AND r.role_code IN
|
||||
<foreach item="code" index="index" collection="codes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
@@ -108,7 +129,7 @@
|
||||
<!-- 根据岗位编码查询用户,仅返回基本信息 -->
|
||||
<select id="findUserListByPostCodes" resultType="EmpUser">
|
||||
SELECT
|
||||
<include refid="userColumns"/>
|
||||
<include refid="userColumns"/>
|
||||
FROM ${_prefix}sys_user a
|
||||
JOIN ${_prefix}sys_employee e ON e.emp_code = a.ref_code
|
||||
JOIN ${_prefix}sys_employee_post ep ON ep.emp_code = e.emp_code
|
||||
@@ -116,10 +137,10 @@
|
||||
WHERE a.status = #{STATUS_NORMAL}
|
||||
AND a.user_type = #{USER_TYPE_EMPLOYEE}
|
||||
<if test="global.useCorpModel">
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND e.status = #{STATUS_NORMAL}
|
||||
AND p.status = #{STATUS_NORMAL}
|
||||
AND a.corp_code = #{corpCode}
|
||||
</if>
|
||||
AND e.status = #{STATUS_NORMAL}
|
||||
AND p.status = #{STATUS_NORMAL}
|
||||
AND p.post_code IN
|
||||
<foreach item="code" index="index" collection="codes" open="(" separator="," close=")">
|
||||
#{code}
|
||||
|
||||
@@ -281,9 +281,9 @@
|
||||
<template>module_cloud/web/db/erm.xml</template>
|
||||
<template>module_cloud/web/src/main/java/package.xml</template>
|
||||
<template>module_cloud/web/src/main/java/startClass.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/bootstrap.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/bootstrap-elk.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/bootstrap-prod.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/application.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/application-elk.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/application-prod.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/logback-spring.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/logback-spring-elk.xml</template>
|
||||
<template>module_cloud/web/src/main/resources/config/logback-spring-prod.xml</template>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
No deletion without permission, or be held responsible to law. -->
|
||||
<template>
|
||||
<name>bootstrap</name>
|
||||
<name>application-elk</name>
|
||||
<filePath>${baseDir}/${moduleCode}/${moduleCode}/src/main/resources/config</filePath>
|
||||
<fileName>bootstrap-elk.yml</fileName>
|
||||
<fileName>application-elk.yml</fileName>
|
||||
<content><![CDATA[
|
||||
|
||||
# 使用环境配置,只需 JVM 参数里加:-Dspring.profiles.active=prod,elk
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
No deletion without permission, or be held responsible to law. -->
|
||||
<template>
|
||||
<name>bootstrap</name>
|
||||
<name>application-prod</name>
|
||||
<filePath>${baseDir}/${moduleCode}/${moduleCode}/src/main/resources/config</filePath>
|
||||
<fileName>bootstrap-prod.yml</fileName>
|
||||
<fileName>application-prod.yml</fileName>
|
||||
<content><![CDATA[
|
||||
|
||||
# 使用环境配置,只需 JVM 参数里加:-Dspring.profiles.active=prod
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
No deletion without permission, or be held responsible to law. -->
|
||||
<template>
|
||||
<name>bootstrap</name>
|
||||
<name>application</name>
|
||||
<filePath>${baseDir}/${moduleCode}/${moduleCode}/src/main/resources/config</filePath>
|
||||
<fileName>bootstrap.yml</fileName>
|
||||
<fileName>application.yml</fileName>
|
||||
<content><![CDATA[
|
||||
#======================================#
|
||||
#========== Server settings ===========#
|
||||
@@ -54,9 +54,9 @@ spring:
|
||||
# - 'optional:configserver:'
|
||||
import:
|
||||
- 'nacos:application.yml'
|
||||
- 'nacos:application-${spring.profiles.active}.yml'
|
||||
- 'nacos:${spring.application.name}.yml'
|
||||
- 'nacos:${spring.application.name}-${spring.profiles.active}.yml'
|
||||
- 'nacos:application-\${spring.profiles.active}.yml'
|
||||
- 'nacos:\${spring.application.name}.yml'
|
||||
- 'nacos:\${spring.application.name}-\${spring.profiles.active}.yml'
|
||||
|
||||
# 分布式配置中心
|
||||
cloud:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
No deletion without permission, or be held responsible to law. -->
|
||||
<template>
|
||||
<name>logback-spring</name>
|
||||
<name>logback-spring-elk</name>
|
||||
<filePath>${baseDir}/${moduleCode}/${moduleCode}/src/main/resources/config</filePath>
|
||||
<fileName>logback-spring-elk.xml</fileName>
|
||||
<content><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
No deletion without permission, or be held responsible to law. -->
|
||||
<template>
|
||||
<name>logback-spring</name>
|
||||
<name>logback-spring-prod</name>
|
||||
<filePath>${baseDir}/${moduleCode}/${moduleCode}/src/main/resources/config</filePath>
|
||||
<fileName>logback-spring-prod.xml</fileName>
|
||||
<content><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
@@ -56,7 +56,7 @@ if(table.isTreeEntity){
|
||||
( {{ record.${table.treeViewCodeAttrName} }} )
|
||||
</span>
|
||||
<% } %>
|
||||
<a @click="handleForm({ ${idParam} })">
|
||||
<a @click="handleForm({ ${idParam} })" :title="record.${table.treeViewNameAttrName}">
|
||||
{{ record.${table.treeViewNameAttrName} }}
|
||||
</a>
|
||||
<%
|
||||
@@ -71,7 +71,7 @@ if(table.isTreeEntity){
|
||||
continue;
|
||||
}
|
||||
%>
|
||||
<a @click="handleForm({ ${idParam} })">
|
||||
<a @click="handleForm({ ${idParam} })" :title="record.${c.attrName}">
|
||||
{{ record.${c.attrName} }}
|
||||
</a>
|
||||
<%
|
||||
|
||||
@@ -7,8 +7,8 @@ var message = @ObjectUtils.toString(@request.getAttribute('message'));
|
||||
if (isBlank(message)){
|
||||
var ex = @ExceptionUtils.getThrowable(request);
|
||||
if (ex != null){
|
||||
if(@StringUtils.startsWith(@ex.getMessage(), 'msg:')){
|
||||
message = @StringUtils.replace(@ex.getMessage(), 'msg:', '');
|
||||
if(@StringUtils.startsWith(ex.message, 'msg:')){
|
||||
message = @StringUtils.replace(ex.message, 'msg:', '');
|
||||
}else if (type.fullName(ex) == 'org.springframework.validation.BindException'
|
||||
|| type.fullName(ex) == 'org.springframework.web.bind.MethodArgumentNotValidException'){
|
||||
for (var e in ex.globalErrors){
|
||||
|
||||
@@ -80,10 +80,8 @@ if (p.dataMap){
|
||||
|
||||
%>
|
||||
<div id="${p.id}_wup" class="wup_container ${p.isMini?'mini':''}">
|
||||
<% if(isNotBlank(p.bizType)){ %>
|
||||
<input id="${p.id}" name="${p.name}" value="" autocomplete="off" class="wup_input ${p.uploadType} ${p.class}" data-msg-required="${p.dataMsgRequired}"/>
|
||||
<input id="${p.id}__del" name="${p.nameDel}" value="" type="hidden"/>
|
||||
<% } %>
|
||||
<div class="area">
|
||||
<% if(p.uploadType == 'image'){ %>
|
||||
<div id="${p.id}Uploader" class="wup_img">
|
||||
|
||||
@@ -18,6 +18,16 @@
|
||||
<option value="question">使用保密问题找回您的密码</option>
|
||||
</select>
|
||||
</div>
|
||||
<% if(@Global.isUseCorpModel()){ %>
|
||||
<div class="form-group has-feedback">
|
||||
<#form:treeselect id="fa_corp" title="${text('选择租户')}" allowClear="true"
|
||||
name="corpCode" value="${@CorpUtils.getCurrentCorpCode()}" labelName="corpName"
|
||||
labelValue="(${@CorpUtils.getCurrentCorpCode()}) ${@CorpUtils.getCurrentCorpName()}"
|
||||
url="${ctx}/sys/corpAdmin/treeData?isShowCode=true"
|
||||
class="required" data-msg-required="请选择所属租户."
|
||||
placeholder="${text('所属租户')}"/>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="form-group has-feedback">
|
||||
<span class="fa fa-user form-control-feedback"></span>
|
||||
<input type="text" id="fp_loginCode" name="loginCode" class="form-control required" data-msg-required="请填写登录账号." placeholder="登录账号" />
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
<% if(@Global.isUseCorpModel()){ %>
|
||||
<div class="form-group has-feedback">
|
||||
<#form:treeselect id="reg_corp" title="${text('选择租户')}" allowClear="true"
|
||||
name="corpCode" value="0" labelName="corpName" labelValue="JeeSite"
|
||||
name="corpCode" value="${@CorpUtils.getCurrentCorpCode()}" labelName="corpName"
|
||||
labelValue="(${@CorpUtils.getCurrentCorpCode()}) ${@CorpUtils.getCurrentCorpName()}"
|
||||
url="${ctx}/sys/corpAdmin/treeData?isShowCode=true"
|
||||
class="required" data-msg-required="请选择所属租户."
|
||||
placeholder="${text('所属租户')}"/>
|
||||
|
||||
@@ -34,6 +34,14 @@
|
||||
<% }else if(isNotBlank(message!)){ %>
|
||||
<h4 class="login-box-msg text-red">${message}</h4>
|
||||
<% } %>
|
||||
<% if(@Global.getConfigToBoolean('user.loginCodeCorpUnique', 'false')){ %>
|
||||
<div class="form-group has-feedback">
|
||||
<#form:treeselect id="switchCorpSelect" title="${text('登录租户')}" allowClear="false"
|
||||
name="param_corpCode" value="${@CorpUtils.getCurrentCorpCode()}"
|
||||
labelValue="(${@CorpUtils.getCurrentCorpCode()}) ${@CorpUtils.getCurrentCorpName()}"
|
||||
url="${ctxAdmin}/sys/corpAdmin/treeData?isShowCode=true" placeholder="${text('登录租户')}"/>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="form-group has-feedback tab-pane tab-1 tab-3 active">
|
||||
<span class="icon-user form-control-feedback" title="${text('登录账号')}"></span>
|
||||
<#form:input type="text" name="username" class="form-control required"
|
||||
@@ -53,14 +61,6 @@
|
||||
<#form:input type="password" name="password" class="form-control required"
|
||||
data-msg-required="${text('请填写登录密码.')}" placeholder="${text('登录密码')}" autocomplete="off"/>
|
||||
</div>
|
||||
<% if(@Global.getConfigToBoolean('user.loginCodeCorpUnique', 'false')){ %>
|
||||
<div class="form-group has-feedback">
|
||||
<#form:treeselect id="switchCorpSelect" title="${text('登录租户')}" allowClear="false"
|
||||
name="param_corpCode" value="${@CorpUtils.getCurrentCorpCode()}"
|
||||
labelValue="(${@CorpUtils.getCurrentCorpCode()}) ${@CorpUtils.getCurrentCorpName()}"
|
||||
url="${ctxAdmin}/sys/corpAdmin/treeData?isShowCode=true" placeholder="${text('登录租户')}"/>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="form-group has-feedback" id="isValidCodeLogin" style="display:${isValidCodeLogin?'blank':'none'}">
|
||||
<#form:validcode name="validCode" isRequired="true" isRemote="true" isLazy="${!isValidCodeLogin}"/>
|
||||
</div>
|
||||
|
||||
@@ -157,8 +157,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-10">
|
||||
<div class="row mr20 pr20">
|
||||
<div class="text-center mr20 pr20">
|
||||
<button type="submit" class="btn btn-sm btn-primary"><i class="fa fa-check"></i> ${text('保 存')}</button>
|
||||
<button type="button" class="btn btn-sm btn-default" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
|
||||
</div>
|
||||
@@ -177,8 +177,8 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('登录密码')}:</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="control-label col-sm-2">${text('登录密码')}:</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="validPassword" name="validPassword" type="password" autocomplete="new-password" value="" maxlength="50" minlength="3" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -187,39 +187,49 @@
|
||||
<% }else{ %>
|
||||
<div class="form-unit">${text('旧的密保问题及答案')}</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题')}(1)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题')}1:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestion" name="oldPwdQuestion" type="text" value="${user.pwdQuestion}" maxlength="50" minlength="3" readonly="readonly" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题答案')}(1)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题答案')}1:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestionAnswer" name="oldPwdQuestionAnswer" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题')}(2)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题')}2:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestion2" name="oldPwdQuestion2" type="text" value="${user.pwdQuestion2}" maxlength="50" minlength="3" readonly="readonly" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题答案')}(2)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题答案')}2:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestionAnswer2" name="oldPwdQuestionAnswer2" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题')}(3)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题')}3:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestion3" name="oldPwdQuestion3" type="text" value="${user.pwdQuestion3}" maxlength="50" minlength="3" readonly="readonly" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('旧密保问题答案')}(3)</label>
|
||||
<label class="control-label col-sm-4">${text('旧密保问题答案')}3:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="oldPwdQuestionAnswer3" name="oldPwdQuestionAnswer3" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
@@ -229,39 +239,49 @@
|
||||
<% } %>
|
||||
<div class="form-unit">${text('新的密保问题及答案')}</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题')}(1):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题')}1:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestion" name="pwdQuestion" type="text" value="${user.pwdQuestion}" maxlength="50" minlength="3" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题答案')}(1):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题答案')}1:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestionAnswer" name="pwdQuestionAnswer" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题')}(2):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题')}2:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestion2" name="pwdQuestion2" type="text" value="${user.pwdQuestion2}" maxlength="50" minlength="3" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题答案')}(2):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题答案')}2:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestionAnswer2" name="pwdQuestionAnswer2" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题')}(3):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题')}3:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestion3" name="pwdQuestion3" type="text" value="${user.pwdQuestion3}" maxlength="50" minlength="3" class="form-control required"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">${text('新密保问题答案')}(3):</label>
|
||||
<label class="control-label col-sm-4">${text('新密保问题答案')}3:</label>
|
||||
<div class="col-sm-8">
|
||||
<input id="pwdQuestionAnswer3" name="pwdQuestionAnswer3" type="text" value="" maxlength="50" minlength="1" class="form-control required"/>
|
||||
</div>
|
||||
@@ -270,8 +290,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-10">
|
||||
<div class="row mr20 pr20">
|
||||
<div class="text-center mr20 pr20">
|
||||
<button type="submit" class="btn btn-sm btn-primary"><i class="fa fa-check"></i> ${text('保 存')}</button>
|
||||
<button type="button" class="btn btn-sm btn-default" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ productName: JeeSite Demo
|
||||
companyName: ThinkGem
|
||||
|
||||
# 产品版本、版权年份
|
||||
productVersion: V5.10
|
||||
productVersion: V5.11
|
||||
copyrightYear: 2025
|
||||
|
||||
# 数据库连接
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-modules</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>JeeSite Modules</name>
|
||||
@@ -16,6 +16,7 @@
|
||||
<module>core</module>
|
||||
<module>app</module>
|
||||
<module>cms</module>
|
||||
<module>cms-ai</module>
|
||||
<module>static</module>
|
||||
<module>test</module>
|
||||
</modules>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ $(function(){
|
||||
js.ajaxSubmit(ctxPath + '/account/getFpValidCode', {
|
||||
validType: $('#fp_validType').val(),
|
||||
loginCode : $('#fp_loginCode').val(),
|
||||
validCode : $('#fp_validCode').val()
|
||||
validCode : $('#fp_validCode').val(),
|
||||
corpCode : $('#fp_corpCode').val()
|
||||
}, function(data){
|
||||
js.showMessage(data.message);
|
||||
if (data.result == 'true'){
|
||||
@@ -61,7 +62,8 @@ $(function(){
|
||||
$('#fp_getQuestion').click(function() {
|
||||
js.ajaxSubmit(ctxPath + '/account/getPwdQuestion', {
|
||||
loginCode : $('#fp_loginCode').val(),
|
||||
validCode : $('#fp_validCode').val()
|
||||
validCode : $('#fp_validCode').val(),
|
||||
corpCode : $('#fp_corpCode').val()
|
||||
}, function(data){
|
||||
js.showMessage(data.message);
|
||||
if (data.result == 'true'){
|
||||
|
||||
@@ -66,7 +66,8 @@ $(function(){
|
||||
var $this = this;
|
||||
js.ajaxSubmit(ctxPath + '/account/getLoginValidCode', {
|
||||
mobile : $('#mobile').val(),
|
||||
validCode : $('#validCode').val()
|
||||
validCode : $('#validCode').val(),
|
||||
corpCode : $('#switchCorpSelectCode').val()
|
||||
}, function(data){
|
||||
js.showMessage(data.message);
|
||||
if (data.result == 'true'){
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
// 工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的从新定义
|
||||
, toolbars: [[
|
||||
'fullscreen', /*'source', '|', */'undo', 'redo', '|',
|
||||
'fullscreen', 'undo', 'redo', '|',
|
||||
'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|',
|
||||
'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', /*'cleardoc', */'|',
|
||||
'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',
|
||||
@@ -45,7 +45,7 @@
|
||||
/*'simpleupload', */'insertimage', 'emotion', 'scrawl', 'insertvideo', /*'music', */'attachment', 'map',/* 'gmap', 'insertframe', 'insertcode', 'webapp', 'pagebreak', */'template', 'background', '|',
|
||||
'horizontal', 'date', 'time', 'spechars', /*'snapscreen', */'wordimage', '|',
|
||||
'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols', /*'charts', */'|',
|
||||
'print', 'preview', 'searchreplace', 'drafts', 'help'
|
||||
'print', 'preview', 'searchreplace', 'drafts', 'source', 'help',
|
||||
]]
|
||||
|
||||
// 简单的工具栏
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>JeeSite Parent</name>
|
||||
@@ -57,7 +57,6 @@
|
||||
<druid.version>1.2.23</druid.version>
|
||||
<shiro.version>2.0.2</shiro.version>
|
||||
<quartz.version>2.4.0-rc3</quartz.version>
|
||||
<j2cache.version>2.8.0-release</j2cache.version>
|
||||
<swagger3.version>2.2.27</swagger3.version>
|
||||
<liquibase.version>4.20.0</liquibase.version>
|
||||
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>JeeSite</name>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-root</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>JeeSite Root</name>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ productName: JeeSite Demo
|
||||
companyName: ThinkGem
|
||||
|
||||
# 产品版本、版权年份
|
||||
productVersion: V5.10
|
||||
productVersion: V5.11
|
||||
copyrightYear: 2025
|
||||
|
||||
# 是否演示模式
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-vue-dist</artifactId>
|
||||
<version>5.10.1-SNAPSHOT</version>
|
||||
<version>5.11.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -8,7 +8,7 @@ productName: JeeSite Demo
|
||||
companyName: ThinkGem
|
||||
|
||||
# 产品版本、版权年份
|
||||
productVersion: V5.10
|
||||
productVersion: V5.11
|
||||
copyrightYear: 2025
|
||||
|
||||
# 是否演示模式
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ productName: JeeSite Demo
|
||||
companyName: ThinkGem
|
||||
|
||||
# 产品版本、版权年份
|
||||
productVersion: V5.10
|
||||
productVersion: V5.11
|
||||
copyrightYear: 2025
|
||||
|
||||
# 是否演示模式
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
<logger name="org.springframework.boot.web.embedded" level="INFO" />
|
||||
<logger name="org.apache.catalina.core.StandardEngine" level="INFO" />
|
||||
<logger name="net.oschina.j2cache.caffeine.CaffeineProvider" level="ERROR" />
|
||||
<logger name="org.apache.fury.serializer.ObjectStreamSerializer" level="ERROR" />
|
||||
<logger name="org.apache.fury.config.FuryBuilder" level="ERROR" />
|
||||
<logger name="ShardingSphere-SQL" level="DEBUG" />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-parent</artifactId>
|
||||
<version>5.10.1.springboot3-SNAPSHOT</version>
|
||||
<version>5.11.0.springboot3-SNAPSHOT</version>
|
||||
<relativePath>../parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -66,6 +66,13 @@
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 内容管理 AI + RAG 模块
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-module-cms-ai</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 内容管理-页面静态化(标准版)
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
|
||||
@@ -8,7 +8,7 @@ productName: JeeSite Demo
|
||||
companyName: ThinkGem
|
||||
|
||||
# 产品版本、版权年份
|
||||
productVersion: V5.10
|
||||
productVersion: V5.11
|
||||
copyrightYear: 2025
|
||||
|
||||
# 是否演示模式
|
||||
|
||||
Reference in New Issue
Block a user