diff --git a/README-EN.md b/README-EN.md index b83cae23c..493f342b5 100644 --- a/README-EN.md +++ b/README-EN.md @@ -69,6 +69,7 @@ Jeecg-Boot AI low code platform can be applied in the development of any J2EE pr Starts the project ----------------------------------- +- [Development Environment setup](https://help.jeecg.com/java/setup/tools) - [IDEA Quick start](https://help.jeecg.com/java/setup/idea/startup) - [Docker Quick start](https://help.jeecg.com/java/docker/quick) diff --git a/README.md b/README.md index eb5f7e0c8..827655bfb 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ JeecgBoot低代码平台,可以应用在任何J2EE项目的开发中,支持 启动项目 ----------------------------------- +- [开发环境搭建](https://help.jeecg.com/java/setup/tools) - [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup) - [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick) diff --git a/check_jeecgenv.py b/check_jeecgenv.py new file mode 100644 index 000000000..77235829f --- /dev/null +++ b/check_jeecgenv.py @@ -0,0 +1,216 @@ +import os +import subprocess +import re +import sys +from typing import Tuple, Optional + +def run_command(cmd: str) -> Tuple[int, str]: + """执行命令并返回退出码和输出""" + try: + result = subprocess.run(cmd, shell=True, check=False, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True) + return result.returncode, result.stdout.strip() + except Exception as e: + return -1, str(e) + +def check_java() -> bool: + """检查JDK 17+是否安装""" + print("\n检查JDK 17+...") + rc, output = run_command("java -version 2>&1") + if rc != 0: + print("❌ 未检测到Java,请安装JDK 17+") + return False + + version_pattern = r'"(\d+)(?:\.\d+)*(?:_\d+)?' + match = re.search(version_pattern, output) + if not match: + print("❌ 无法解析Java版本") + return False + + version = int(match.group(1)) + if version >= 17: + print(f"✅ JDK版本 {version} (满足17+要求)") + return True + else: + print(f"❌ JDK版本 {version} (需要17+)") + return False + +def check_maven() -> bool: + """检查Maven是否安装""" + print("\n检查Maven...") + rc, output = run_command("mvn -v") + if rc == 0: + print("✅ Maven已安装") + return True + else: + print("❌ Maven未安装") + return False + +def check_node() -> bool: + """检查Node.js 20+是否安装""" + print("\n检查Node.js 20+...") + rc, output = run_command("node -v") + if rc != 0: + print("❌ Node.js未安装") + return False + + version_pattern = r'v(\d+)\.\d+\.\d+' + match = re.search(version_pattern, output) + if not match: + print("❌ 无法解析Node.js版本") + return False + + version = int(match.group(1)) + if version >= 20: + print(f"✅ Node.js版本 {version} (满足20+要求)") + return True + else: + print(f"❌ Node.js版本 {version} (需要20+)") + return False + +def check_pnpm() -> bool: + """检查PNPM 9+是否安装""" + print("\n检查PNPM 9+...") + rc, output = run_command("pnpm -v") + if rc != 0: + print("❌ PNPM未安装") + return False + + try: + # 处理可能的版本号格式:v9.0.0 或 9.0.0 或 9 + version_str = output.strip().lstrip('v').split('.')[0] + version = int(version_str) + + if version >= 9: + print(f"✅ PNPM版本 {output.strip()} (满足9+要求)") + return True + else: + print(f"❌ PNPM版本 {output.strip()} (需要9+)") + return False + except (ValueError, IndexError): + print(f"❌ 无法解析PNPM版本: {output.strip()}") + return False + +def check_redis_connection() -> bool: + """检查Redis连接""" + print("\n检查Redis连接...") + print("⚠️ 请确保已配置Redis连接信息并在jeecg-boot项目中正确配置") + print("⚠️ 此检查需要根据实际项目配置进行验证") + print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml") + return True + +def check_mysql_connection() -> bool: + """检查MySQL连接""" + print("\n检查MySQL连接...") + print("⚠️ 请确保已配置MySQL连接信息并在jeecg-boot项目中正确配置") + print("⚠️ 此检查需要根据实际项目配置进行验证") + print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml") + return True + +def print_mysql_config(): + """打印MySQL配置并提示需要修改的位置""" + print("\nMySQL配置参考 (请检查以下配置是否正确):") + print(""" +spring.datasource.dynamic.datasource: + master: + url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root # ← 可能需要修改 + password: root # ← 可能需要修改 + driver-class-name: com.mysql.cj.jdbc.Driver +""") + +def check_ai_vector_db() -> bool: + """检查AI向量库(pgvector)配置""" + print("\n检查AI知识库向量库配置...") + print("⚠️ 如果需要使用AI知识库功能,请配置pgvector向量库") + print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml") + print("\n配置参考:") + print(""" +jeecg.ai-rag: + embed-store: + host: 127.0.0.1 # ← 可能需要修改 + port: 5432 # ← 可能需要修改 + database: postgres # ← 可能需要修改 + user: postgres # ← 可能需要修改 + password: postgres # ← 可能需要修改 + table: embeddings # ← 可能需要修改 +""") + print("⚠️ 注意: 请确保已安装PostgreSQL并添加pgvector扩展!docker安装参考:https://help.jeecg.com/aigc/config") + return True + +def check_ai_config() -> bool: + """检查AI账号配置""" + print("\n检查AI功能配置...") + print("⚠️ 如果需要使用AI聊天功能,请配置AI账号信息") + print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml") + print("\n配置参考:") + print(""" +jeecg: + # AI集成 + ai-chat: + enabled: true # ← 启用AI功能 + model: deepseek-chat # ← 模型名称 + apiKey: ?? # ← 必须修改为您的API Key + apiHost: https://api.deepseek.com/v1 # ← API地址 + timeout: 60 # ← 超时时间(秒) +""") + print("⚠️ 注意: 请确保已获取有效的API Key并正确配置!AI账号注册获取参考: https://help.jeecg.com/java/deepSeekSupport") + return True + + +def print_redis_config(): + """打印Redis配置并提示需要修改的位置""" + print("\nRedis配置参考 (请检查以下配置是否正确):") + print(""" +spring.redis: + database: 0 + host: 127.0.0.1 # ← 可能需要修改 + port: 6379 # ← 可能需要修改 + password: '' # ← 如果需要密码请修改 +""") + +def main(): + print("="*50) + print("JeecgBoot 运行环境检查脚本") + print("="*50) + + all_checks_passed = True + + # 检查各项依赖 + if not check_java(): + all_checks_passed = False + + if not check_maven(): + all_checks_passed = False + + if not check_node(): + all_checks_passed = False + + if not check_pnpm(): + all_checks_passed = False + + # 数据库提示 + print("="*50) + check_redis_connection() + print_redis_config() + print("="*50) + check_mysql_connection() + print_mysql_config() + print("="*50) + check_ai_config() + print("="*50) + check_ai_vector_db() + + print("\n" + "="*50) + if all_checks_passed: + print("✅ 所有基础环境检查通过") + print("⚠️ 注意: 请确保Redis和MySQL、AI账号、向量库pgvector 已正确配置并连接成功") + else: + print("❌ 部分环境检查未通过,请根据上述提示解决问题") + + print("="*50) + +if __name__ == "__main__": + main() + input("\n按回车键退出...") # 等待用户输入 \ No newline at end of file diff --git a/jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.dmp b/jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.dmp new file mode 100644 index 000000000..1807bc06e Binary files /dev/null and b/jeecg-boot/db/其他数据库脚本/jeecgboot-oracle11g.dmp differ diff --git a/jeecg-boot/db/其他数据库脚本/oracle11g dmp说明.txt b/jeecg-boot/db/其他数据库脚本/oracle11g dmp说明.txt new file mode 100644 index 000000000..5fc578b59 --- /dev/null +++ b/jeecg-boot/db/其他数据库脚本/oracle11g dmp说明.txt @@ -0,0 +1,5 @@ +oracle导出编码: export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK + +导出用户: jeecgbootos + +导入命令: imp scott/tiger@orcl file=jeecgboot-oracle11g.dmp \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java index 76b9d509f..1afd78720 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java @@ -11,6 +11,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.baomidou.mybatisplus.core.metadata.OrderItem; import org.apache.commons.beanutils.PropertyUtils; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.DataBaseConstant; @@ -257,8 +258,69 @@ public class QueryGenerator { if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) { order = parameterMap.get(ORDER_TYPE)[0]; } - log.debug("排序规则>>列:" + column + ",排序方式:" + order); + + if(oConvertUtils.isNotEmpty(column)){ + log.info("单字段排序规则>> column:" + column + ",排序方式:" + order); + } + // 1. 列表多字段排序优先 + if(parameterMap!=null&& parameterMap.containsKey("sortInfoString")) { + // 多字段排序 + String sortInfoString = parameterMap.get("sortInfoString")[0]; + log.info("多字段排序规则>> sortInfoString:" + sortInfoString); + List orderItemList = SqlConcatUtil.getQueryConditionOrders(column, order, sortInfoString); + log.info(orderItemList.toString()); + if (orderItemList != null && !orderItemList.isEmpty()) { + for (OrderItem item : orderItemList) { + // 一、获取排序数据库字段 + String columnName = item.getColumn(); + // 1.字典字段,去掉字典翻译文本后缀 + if(columnName.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) { + columnName = columnName.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX)); + } + // 2.实体驼峰字段转为数据库字段 + columnName = SqlInjectionUtil.getSqlInjectSortField(columnName); + + // 二、设置字段排序规则 + if (item.isAsc()) { + queryWrapper.orderByAsc(columnName); + } else { + queryWrapper.orderByDesc(columnName); + } + } + } + return; + } + + // 2. 列表单字段默认排序 + if(oConvertUtils.isEmpty(column) && parameterMap!=null&& parameterMap.containsKey("defSortString")) { + // 多字段排序 + String sortInfoString = parameterMap.get("defSortString")[0]; + log.info("默认多字段排序规则>> defSortString:" + sortInfoString); + List orderItemList = SqlConcatUtil.getQueryConditionOrders(column, order, sortInfoString); + log.info(orderItemList.toString()); + if (orderItemList != null && !orderItemList.isEmpty()) { + for (OrderItem item : orderItemList) { + // 一、获取排序数据库字段 + String columnName = item.getColumn(); + // 1.字典字段,去掉字典翻译文本后缀 + if(columnName.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) { + columnName = columnName.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX)); + } + // 2.实体驼峰字段转为数据库字段 + columnName = SqlInjectionUtil.getSqlInjectSortField(columnName); + + // 二、设置字段排序规则 + if (item.isAsc()) { + queryWrapper.orderByAsc(columnName); + } else { + queryWrapper.orderByDesc(columnName); + } + } + } + return; + } + //update-begin-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错 //TODO 避免用户自定义表无默认字段创建时间,导致排序报错 if(DataBaseConstant.CREATE_TIME.equals(column) && !fieldColumnMap.containsKey(DataBaseConstant.CREATE_TIME)){ diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java index 9178144d7..f1d668375 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/SqlConcatUtil.java @@ -1,13 +1,22 @@ package org.jeecg.common.system.util; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.OrderItem; import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.DataBaseConstant; import org.jeecg.common.constant.SymbolConstant; +import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.system.query.QueryGenerator; import org.jeecg.common.system.query.QueryRuleEnum; import org.jeecg.common.util.CommonUtils; import org.jeecg.common.util.oConvertUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -239,5 +248,47 @@ public class SqlConcatUtil { private static String getDbType() { return CommonUtils.getDatabaseType(); } + + /** + * 获取前端传过来的 "多字段排序信息: sortInfoString" + * @return + */ + public static List getQueryConditionOrders(String column, String order, String queryInfoString){ + List list = new ArrayList<>(); + if(oConvertUtils.isEmpty(queryInfoString)){ + //默认以创建时间倒序查询 + if(CommonConstant.ORDER_TYPE_DESC.equalsIgnoreCase(order)){ + list.add(OrderItem.desc(column)); + }else{ + list.add(OrderItem.asc(column)); + } + }else{ + // 【TV360X-967】URL解码(微服务下需要) + if (queryInfoString.contains("%22column%22")) { + log.info("queryInfoString 原生 = {}", queryInfoString); + try { + queryInfoString = URLDecoder.decode(queryInfoString, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new JeecgBootException(e); + } + log.info("queryInfoString 解码 = {}", queryInfoString); + } + JSONArray array = JSONArray.parseArray(queryInfoString); + Iterator it = array.iterator(); + while(it.hasNext()){ + JSONObject json = (JSONObject)it.next(); + String tempColumn = json.getString("column"); + if(oConvertUtils.isNotEmpty(tempColumn)){ + String tempOrder = json.getString("order"); + if(CommonConstant.ORDER_TYPE_DESC.equalsIgnoreCase(tempOrder)){ + list.add(OrderItem.desc(tempColumn)); + }else{ + list.add(OrderItem.asc(tempColumn)); + } + } + } + } + return list; + } } diff --git a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-account/src/main/resources/application.yml b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-account/src/main/resources/application.yml index 9df3c2e8f..e494597ce 100644 --- a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-account/src/main/resources/application.yml +++ b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-account/src/main/resources/application.yml @@ -12,6 +12,11 @@ spring: enabled: false application: name: seata-account + cloud: + nacos: + config: + import-check: + enabled: false main: allow-bean-definition-overriding: true autoconfigure: @@ -21,18 +26,19 @@ spring: username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver - schema: classpath:sql/schema-account.sql - cloud: - nacos: - config: - import-check: - enabled: false + sql: + init: + schema-locations: classpath:sql/schema-account.sql seata: -# enable-auto-data-source-proxy: true + enable-auto-data-source-proxy: false service: grouplist: default: 127.0.0.1:8091 vgroup-mapping: springboot-seata-group: default # seata 事务组编号 用于TC集群名 - tx-service-group: springboot-seata-group \ No newline at end of file + tx-service-group: springboot-seata-group + +# 无用配置,为了避免扫码全代码导致启动慢 +minidao: + base-package: org.jeecg.modules.jmreport.* \ No newline at end of file diff --git a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-order/src/main/resources/application.yml b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-order/src/main/resources/application.yml index f1e69de6c..0443d879b 100644 --- a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-order/src/main/resources/application.yml +++ b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-order/src/main/resources/application.yml @@ -12,6 +12,11 @@ spring: enabled: false application: name: seata-order + cloud: + nacos: + config: + import-check: + enabled: false main: allow-bean-definition-overriding: true autoconfigure: @@ -21,18 +26,19 @@ spring: url: jdbc:mysql://127.0.0.1:3306/jeecg_order?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false username: root password: root - schema: classpath:sql/schema-order.sql - cloud: - nacos: - config: - import-check: - enabled: false + sql: + init: + schema-locations: classpath:sql/schema-order.sql seata: -# enable-auto-data-source-proxy: false + enable-auto-data-source-proxy: false service: grouplist: default: 127.0.0.1:8091 vgroup-mapping: springboot-seata-group: default # seata 事务组编号 用于TC集群名 - tx-service-group: springboot-seata-group \ No newline at end of file + tx-service-group: springboot-seata-group + +# 无用配置,为了避免扫码全代码导致启动慢 +minidao: + base-package: org.jeecg.modules.jmreport.* \ No newline at end of file diff --git a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-product/src/main/resources/application.yml b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-product/src/main/resources/application.yml index 1bae31669..15b765e95 100644 --- a/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-product/src/main/resources/application.yml +++ b/jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-product/src/main/resources/application.yml @@ -1,17 +1,13 @@ server: port: 5003 spring: - data: - redis: - ##redis 单机环境配置 - host: localhost - port: 6379 - database: 0 - password: - ssl: - enabled: false application: name: seata-product + cloud: + nacos: + config: + import-check: + enabled: false main: allow-bean-definition-overriding: true autoconfigure: @@ -21,18 +17,19 @@ spring: url: jdbc:mysql://127.0.0.1:3306/jeecg_product?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false username: root password: root - schema: classpath:sql/schema-product.sql - cloud: - nacos: - config: - import-check: - enabled: false + sql: + init: + schema-locations: classpath:sql/schema-product.sql seata: -# enable-auto-data-source-proxy: false + enable-auto-data-source-proxy: false service: grouplist: default: 127.0.0.1:8091 vgroup-mapping: springboot-seata-group: default # seata 事务组编号 用于TC集群名 - tx-service-group: springboot-seata-group \ No newline at end of file + tx-service-group: springboot-seata-group + +# 无用配置,为了避免扫码全代码导致启动慢 +minidao: + base-package: org.jeecg.modules.jmreport.* \ No newline at end of file diff --git a/jeecgboot-vue3/src/components/Table/src/hooks/useDataSource.ts b/jeecgboot-vue3/src/components/Table/src/hooks/useDataSource.ts index 6d1ec9812..e019d279a 100644 --- a/jeecgboot-vue3/src/components/Table/src/hooks/useDataSource.ts +++ b/jeecgboot-vue3/src/components/Table/src/hooks/useDataSource.ts @@ -238,12 +238,20 @@ export function useDataSource( const { sortInfo = {}, filterInfo } = searchState; + // 扩展默认排序多字段数组写法 + let defSortInfo: Recordable | undefined = {}; + if (defSort && Array.isArray(defSort) && defSort.length > 0) { + defSortInfo['defSortString'] = JSON.stringify(defSort); + } else { + defSortInfo = defSort; + } + let params: Recordable = { ...pageParams, // 由于 getFieldsValue 返回的不是逗号分割的数据,所以改用 validate ...(useSearchForm ? await validate() : {}), ...searchInfo, - ...defSort, + ...defSortInfo, ...(opt?.searchInfo ?? {}), ...sortInfo, ...filterInfo, diff --git a/jeecgboot-vue3/src/components/Table/src/types/table.ts b/jeecgboot-vue3/src/components/Table/src/types/table.ts index 9afc19958..8cc12fb51 100644 --- a/jeecgboot-vue3/src/components/Table/src/types/table.ts +++ b/jeecgboot-vue3/src/components/Table/src/types/table.ts @@ -190,7 +190,7 @@ export interface BasicTableProps { // 额外的请求参数 searchInfo?: Recordable; // 默认的排序参数 - defSort?: Recordable; + defSort?: Recordable | Recordable[]; // 使用搜索表单 useSearchForm?: boolean; // 表单配置 diff --git a/jeecgboot-vue3/src/views/system/examples/demo/index.vue b/jeecgboot-vue3/src/views/system/examples/demo/index.vue index bedc7a177..43367f5b2 100644 --- a/jeecgboot-vue3/src/views/system/examples/demo/index.vue +++ b/jeecgboot-vue3/src/views/system/examples/demo/index.vue @@ -123,11 +123,17 @@ style: { textAlign: 'left' }, }, }, - //自定义默认排序 - defSort: { - column: 'createTime,sex', - order: 'desc', - }, + //自定义默认多字段排序 + defSort: [ + { + column: 'name', + order: 'desc', + }, + { + column: 'sex', + order: 'asc', + }, + ], striped: true, useSearchForm: true, showTableSetting: true,