diff --git a/nacos/LICENSE b/nacos/LICENSE new file mode 100644 index 0000000..5be5ed2 --- /dev/null +++ b/nacos/LICENSE @@ -0,0 +1,316 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (properties) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------ +This product has a bundle logback, which is available under the EPL v1.0 License. +The source code of logback can be found at https://github.com/qos-ch/logback. + +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2015, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v1.0 as published by +the Eclipse Foundation + + or (per the licensee's choosing) + +under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation. + +------ +This product has a bundle slf4j, which is available under the MIT License. +The source code of slf4j can be found at https://github.com/qos-ch/slf4j. + + Copyright (c) 2004-2017 QOS.ch + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------ +This product has a bundle javassist, which is available under the ASL2 License. +The source code of javassist can be found at https://github.com/jboss-javassist/javassist. + + Copyright (C) 1999- by Shigeru Chiba, All rights reserved. + + Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation simple. + It is a class library for editing bytecodes in Java; it enables Java programs to define a new class + at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, + Javassist provides two levels of API: source level and bytecode level. If the users use the source- level API, + they can edit a class file without knowledge of the specifications of the Java bytecode. + The whole API is designed with only the vocabulary of the Java language. + You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. + On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors. + + This software is distributed under the Mozilla Public License Version 1.1, + the GNU Lesser General Public License Version 2.1 or later, or the Apache License Version 2.0. + +------ +This product has a bundle jna, which is available under the ASL2 License. +The source code of jna can be found at https://github.com/java-native-access/jna. + + This copy of JNA is licensed under the + Apache (Software) License, version 2.0 ("the License"). + See the License for details about distribution rights, and the + specific rights regarding derivate works. + + You may obtain a copy of the License at: + + http://www.apache.org/licenses/ + + A copy is also included in the downloadable source code package + containing JNA, in file "AL2.0", under the same directory + as this file. +------ +This product has a bundle guava, which is available under the ASL2 License. +The source code of guava can be found at https://github.com/google/guava. + + Copyright (C) 2007 The Guava authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +------ +This product has a bundle OpenMessaging, which is available under the ASL2 License. +The source code of OpenMessaging can be found at https://github.com/openmessaging/openmessaging. + + Copyright (C) 2017 The OpenMessaging authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/nacos/NOTICE b/nacos/NOTICE new file mode 100644 index 0000000..3a2af57 --- /dev/null +++ b/nacos/NOTICE @@ -0,0 +1,36 @@ +Nacos +Copyright 2018-2019 The Apache Software Foundation + +This product includes software developed at +The Alibaba MiddleWare Group. + +------ +This product has a bundle netty: + The Spring oot Project + ================= + +Please visit the Netty web site for more information: + + * http://netty.io/ + +Copyright 2014 The Netty Project + +The Netty Project licenses this file to you under the Apache License, +version 2.0 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. + +Also, please refer to each LICENSE..txt file, which is located in +the 'license' directory of the distribution file, for the license terms of the +components that this product depends on. + +------ +This product has a bundle commons-lang, which includes software from the Spring Framework, +under the Apache License 2.0 (see: StringUtils.containsWhitespace()) \ No newline at end of file diff --git a/nacos/bin/shutdown.cmd b/nacos/bin/shutdown.cmd new file mode 100644 index 0000000..19cf53f --- /dev/null +++ b/nacos/bin/shutdown.cmd @@ -0,0 +1,40 @@ +@echo off +:: Copyright 1999-2018 Alibaba Group Holding Ltd. +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. + +if not exist "%JAVA_HOME%\bin\jps.exe" ( + echo Please set the JAVA_HOME variable in your environment to the correct JDK directory. + echo JDK8 or later is recommended! + EXIT /B 1 +) + +setlocal + +set "PATH=%JAVA_HOME%\bin;%PATH%" + +echo Killing Nacos server... + +:: Find and kill Nacos server process +set "NACOS_RUNNING=false" +for /f "tokens=1" %%i in ('jps -m ^| find "nacos.nacos"') do ( + set "NACOS_RUNNING=true" + taskkill /F /PID %%i +) + +if "%NACOS_RUNNING%"=="true" ( + echo Nacos server stopped. +) else ( + echo Nacos server is not running. +) + +echo Done! diff --git a/nacos/bin/shutdown.sh b/nacos/bin/shutdown.sh new file mode 100644 index 0000000..5394e60 --- /dev/null +++ b/nacos/bin/shutdown.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright 1999-2018 Alibaba Group Holding Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cd `dirname $0`/../target +target_dir=`pwd` + +pid=`pgrep -f nacos.nacos` +if [ -z "$pid" ] ; then + echo "No nacosServer running." + exit -1; +fi + +echo "The nacosServer(${pid}) is running..." + +kill ${pid} + +echo "Send shutdown request to nacosServer(${pid}) OK" diff --git a/nacos/bin/startup.cmd b/nacos/bin/startup.cmd new file mode 100644 index 0000000..805f2a0 --- /dev/null +++ b/nacos/bin/startup.cmd @@ -0,0 +1,96 @@ +@echo off +rem Copyright 1999-2018 Alibaba Group Holding Ltd. +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. +if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1 +set "JAVA=%JAVA_HOME%\bin\java.exe" + +setlocal enabledelayedexpansion + +set BASE_DIR=%~dp0 +rem added double quotation marks to avoid the issue caused by the folder names containing spaces. +rem removed the last 5 chars(which means \bin\) to get the base DIR. +set BASE_DIR="%BASE_DIR:~0,-5%" + +set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/ + +set MODE="cluster" +set FUNCTION_MODE="all" +set SERVER=nacos-server +set MODE_INDEX=-1 +set FUNCTION_MODE_INDEX=-1 +set SERVER_INDEX=-1 +set EMBEDDED_STORAGE_INDEX=-1 +set EMBEDDED_STORAGE="" + + +set i=0 +for %%a in (%*) do ( + if "%%a" == "-m" ( set /a MODE_INDEX=!i!+1 ) + if "%%a" == "-f" ( set /a FUNCTION_MODE_INDEX=!i!+1 ) + if "%%a" == "-s" ( set /a SERVER_INDEX=!i!+1 ) + if "%%a" == "-p" ( set /a EMBEDDED_STORAGE_INDEX=!i!+1 ) + set /a i+=1 +) + +set i=0 +for %%a in (%*) do ( + if %MODE_INDEX% == !i! ( set MODE="%%a" ) + if %FUNCTION_MODE_INDEX% == !i! ( set FUNCTION_MODE="%%a" ) + if %SERVER_INDEX% == !i! (set SERVER="%%a") + if %EMBEDDED_STORAGE_INDEX% == !i! (set EMBEDDED_STORAGE="%%a") + set /a i+=1 +) + +rem if nacos startup mode is standalone +if %MODE% == "standalone" ( + echo "nacos is starting with standalone" + set "NACOS_OPTS=-Dnacos.standalone=true" + if "%CUSTOM_NACOS_MEMORY%"=="" ( set "CUSTOM_NACOS_MEMORY=-Xms512m -Xmx512m -Xmn256m" ) + set "NACOS_JVM_OPTS=%CUSTOM_NACOS_MEMORY%" +) + +rem if nacos startup mode is cluster +if %MODE% == "cluster" ( + echo "nacos is starting with cluster" + if %EMBEDDED_STORAGE% == "embedded" ( + set "NACOS_OPTS=-DembeddedStorage=true" + ) + if "%CUSTOM_NACOS_MEMORY%"=="" ( set "CUSTOM_NACOS_MEMORY=-Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m" ) + set "NACOS_JVM_OPTS=-server %CUSTOM_NACOS_MEMORY% -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=%BASE_DIR%\logs\java_heapdump.hprof -XX:-UseLargePages" +) + +rem set nacos's functionMode +if %FUNCTION_MODE% == "config" ( + set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.functionMode=config" +) + +if %FUNCTION_MODE% == "naming" ( + set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.functionMode=naming" +) + +rem set nacos options +set "NACOS_OPTS=%NACOS_OPTS% -Dloader.path=%BASE_DIR%/plugins,%BASE_DIR%/plugins/health,%BASE_DIR%/plugins/cmdb,%BASE_DIR%/plugins/selector" +set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.home=%BASE_DIR%" +set "NACOS_OPTS=%NACOS_OPTS% -jar %BASE_DIR%\target\%SERVER%.jar" + +rem set nacos spring config location +set "NACOS_CONFIG_OPTS=--spring.config.additional-location=%CUSTOM_SEARCH_LOCATIONS%" + +rem set nacos log4j file location +set "NACOS_LOG4J_OPTS=--logging.config=%BASE_DIR%/conf/nacos-logback.xml" + + +set COMMAND="%JAVA%" %NACOS_JVM_OPTS% %NACOS_OPTS% %NACOS_CONFIG_OPTS% %NACOS_LOG4J_OPTS% nacos.nacos %* + +rem start nacos command +%COMMAND% diff --git a/nacos/bin/startup.sh b/nacos/bin/startup.sh new file mode 100644 index 0000000..cfe2f48 --- /dev/null +++ b/nacos/bin/startup.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Copyright 1999-2018 Alibaba Group Holding Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cygwin=false +darwin=false +os400=false +case "`uname`" in +CYGWIN*) cygwin=true;; +Darwin*) darwin=true;; +OS400*) os400=true;; +esac +error_exit () +{ + echo "ERROR: $1 !!" + exit 1 +} +[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java +[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java +[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java +[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME + +if [ -z "$JAVA_HOME" ]; then + if $darwin; then + + if [ -x '/usr/libexec/java_home' ] ; then + export JAVA_HOME=`/usr/libexec/java_home` + + elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then + export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" + fi + else + JAVA_PATH=`dirname $(readlink -f $(which javac))` + if [ "x$JAVA_PATH" != "x" ]; then + export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null` + fi + fi + if [ -z "$JAVA_HOME" ]; then + JAVA_PATH=$(which java) + if [ -z "$JAVA_PATH" ]; then + error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!" + fi + JAVA_HOME=$(dirname "$JAVA_PATH")/.. + export JAVA_HOME=$(cd "$JAVA_HOME" && pwd) + fi +fi + +export SERVER="nacos-server" +export MODE="cluster" +export FUNCTION_MODE="all" +export MEMBER_LIST="" +export EMBEDDED_STORAGE="" +while getopts ":m:f:s:c:p:" opt +do + case $opt in + m) + MODE=$OPTARG;; + f) + FUNCTION_MODE=$OPTARG;; + s) + SERVER=$OPTARG;; + c) + MEMBER_LIST=$OPTARG;; + p) + EMBEDDED_STORAGE=$OPTARG;; + ?) + echo "Unknown parameter" + exit 1;; + esac +done + +export JAVA_HOME +export JAVA="$JAVA_HOME/bin/java" +export BASE_DIR=`cd $(dirname $0)/..; pwd` +export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/ + +#=========================================================================================== +# JVM Configuration +#=========================================================================================== +if [[ "${MODE}" == "standalone" ]]; then + JAVA_OPT="${JAVA_OPT} ${CUSTOM_NACOS_MEMORY:- -Xms512m -Xmx512m -Xmn256m}" + JAVA_OPT="${JAVA_OPT} -Dnacos.standalone=true" +else + if [[ "${EMBEDDED_STORAGE}" == "embedded" ]]; then + JAVA_OPT="${JAVA_OPT} -DembeddedStorage=true" + fi + JAVA_OPT="${JAVA_OPT} -server ${CUSTOM_NACOS_MEMORY:- -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m}" + JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof" + JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages" +fi + +if [[ "${FUNCTION_MODE}" == "config" ]]; then + JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=config" +elif [[ "${FUNCTION_MODE}" == "naming" ]]; then + JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=naming" +fi + +JAVA_OPT="${JAVA_OPT} -Dnacos.member.list=${MEMBER_LIST}" + +JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p') +if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then + JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${BASE_DIR}/logs/nacos_gc.log:time,tags:filecount=10,filesize=100m" +else + JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 " + JAVA_OPT_EXT_FIX="-Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext" + JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" +fi + +JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins,${BASE_DIR}/plugins/health,${BASE_DIR}/plugins/cmdb,${BASE_DIR}/plugins/selector" +JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" +JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar" +JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}" +JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}" +JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml" +JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288" + +if [ ! -d "${BASE_DIR}/logs" ]; then + mkdir ${BASE_DIR}/logs +fi + +echo "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}" + +if [[ "${MODE}" == "standalone" ]]; then + echo "nacos is starting with standalone" +else + echo "nacos is starting with cluster" +fi + +# check the start.out log output file +if [ ! -f "${BASE_DIR}/logs/start.out" ]; then + touch "${BASE_DIR}/logs/start.out" +fi +# start +echo "$JAVA $JAVA_OPT_EXT_FIX ${JAVA_OPT}" > ${BASE_DIR}/logs/start.out 2>&1 & + +if [[ "$JAVA_OPT_EXT_FIX" == "" ]]; then + nohup "$JAVA" ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 & +else + nohup "$JAVA" "$JAVA_OPT_EXT_FIX" ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 & +fi + +echo "nacos is starting. you can check the ${BASE_DIR}/logs/start.out" diff --git a/nacos/conf/1.4.0-ipv6_support-update.sql b/nacos/conf/1.4.0-ipv6_support-update.sql new file mode 100644 index 0000000..f480147 --- /dev/null +++ b/nacos/conf/1.4.0-ipv6_support-update.sql @@ -0,0 +1,27 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +ALTER TABLE `config_info_tag` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `his_config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL AFTER `src_user`; + +ALTER TABLE `config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `config_info_beta` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; \ No newline at end of file diff --git a/nacos/conf/announcement_en-US.conf b/nacos/conf/announcement_en-US.conf new file mode 100644 index 0000000..19e8dbc --- /dev/null +++ b/nacos/conf/announcement_en-US.conf @@ -0,0 +1 @@ +Authentication has not been enabled in cluster, please refer to Documentation to enable~ \ No newline at end of file diff --git a/nacos/conf/announcement_zh-CN.conf b/nacos/conf/announcement_zh-CN.conf new file mode 100644 index 0000000..c18d8a4 --- /dev/null +++ b/nacos/conf/announcement_zh-CN.conf @@ -0,0 +1 @@ +当前集群没有开启鉴权,请参考文档开启鉴权~ \ No newline at end of file diff --git a/nacos/conf/application.properties b/nacos/conf/application.properties new file mode 100644 index 0000000..caa7da1 --- /dev/null +++ b/nacos/conf/application.properties @@ -0,0 +1,297 @@ +# +# Copyright 1999-2021 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#*************** Spring Boot Related Configurations ***************# +### Default web context path: +server.servlet.contextPath=/nacos +### Include message field +server.error.include-message=ALWAYS +### Default web server port: +server.port=8848 + +#*************** Network Related Configurations ***************# +### If prefer hostname over ip for Nacos server addresses in cluster.conf: +# nacos.inetutils.prefer-hostname-over-ip=false + +### Specify local server's IP: +# nacos.inetutils.ip-address= + + +#*************** Config Module Related Configurations ***************# +### If use MySQL as datasource: +### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced. +# spring.datasource.platform=mysql +spring.sql.init.platform=mysql + +### Count of DB: +db.num=1 + +### Connect URL of DB: +db.url.0=jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC +db.user.0=root +db.password.0=root + +### Connection pool configuration: hikariCP +db.pool.config.connectionTimeout=30000 +db.pool.config.validationTimeout=10000 +db.pool.config.maximumPoolSize=20 +db.pool.config.minimumIdle=2 + +### the maximum retry times for push +nacos.config.push.maxRetryTime=50 + +#*************** Naming Module Related Configurations ***************# + +### If enable data warmup. If set to false, the server would accept request without local data preparation: +# nacos.naming.data.warmup=true + +### If enable the instance auto expiration, kind like of health check of instance: +# nacos.naming.expireInstance=true + +### Add in 2.0.0 +### The interval to clean empty service, unit: milliseconds. +# nacos.naming.clean.empty-service.interval=60000 + +### The expired time to clean empty service, unit: milliseconds. +# nacos.naming.clean.empty-service.expired-time=60000 + +### The interval to clean expired metadata, unit: milliseconds. +# nacos.naming.clean.expired-metadata.interval=5000 + +### The expired time to clean metadata, unit: milliseconds. +# nacos.naming.clean.expired-metadata.expired-time=60000 + +### The delay time before push task to execute from service changed, unit: milliseconds. +# nacos.naming.push.pushTaskDelay=500 + +### The timeout for push task execute, unit: milliseconds. +# nacos.naming.push.pushTaskTimeout=5000 + +### The delay time for retrying failed push task, unit: milliseconds. +# nacos.naming.push.pushTaskRetryDelay=1000 + +### Since 2.0.3 +### The expired time for inactive client, unit: milliseconds. +# nacos.naming.client.expired.time=180000 + +#*************** CMDB Module Related Configurations ***************# +### The interval to dump external CMDB in seconds: +# nacos.cmdb.dumpTaskInterval=3600 + +### The interval of polling data change event in seconds: +# nacos.cmdb.eventTaskInterval=10 + +### The interval of loading labels in seconds: +# nacos.cmdb.labelTaskInterval=300 + +### If turn on data loading task: +# nacos.cmdb.loadDataAtStart=false + +#***********Metrics for tomcat **************************# +server.tomcat.mbeanregistry.enabled=true + +#***********Expose prometheus and health **************************# +#management.endpoints.web.exposure.include=prometheus,health + +### Metrics for elastic search +management.metrics.export.elastic.enabled=false +#management.metrics.export.elastic.host=http://localhost:9200 + +### Metrics for influx +management.metrics.export.influx.enabled=false +#management.metrics.export.influx.db=springboot +#management.metrics.export.influx.uri=http://localhost:8086 +#management.metrics.export.influx.auto-create-db=true +#management.metrics.export.influx.consistency=one +#management.metrics.export.influx.compressed=true + +#*************** Access Log Related Configurations ***************# +### If turn on the access log: +server.tomcat.accesslog.enabled=true + +### file name pattern, one file per hour +server.tomcat.accesslog.rotate=true +server.tomcat.accesslog.file-date-format=.yyyy-MM-dd-HH +### The access log pattern: +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i + +### The directory of access log: +server.tomcat.basedir=file:. + +#*************** Access Control Related Configurations ***************# +### If enable spring security, this option is deprecated in 1.2.0: +#spring.security.enabled=false + +### The ignore urls of auth +nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** + +### The auth system to use, currently only 'nacos' and 'ldap' is supported: +nacos.core.auth.system.type=nacos + +### If turn on auth system: +nacos.core.auth.enabled=false + +### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. +nacos.core.auth.caching.enabled=true + +### Since 1.4.1, Turn on/off white auth for user-agent: nacos-server, only for upgrade from old version. +nacos.core.auth.enable.userAgentAuthWhite=false + +### Since 1.4.1, worked when nacos.core.auth.enabled=true and nacos.core.auth.enable.userAgentAuthWhite=false. +### The two properties is the white list for auth and used by identity the request from other server. +nacos.core.auth.server.identity.key= +nacos.core.auth.server.identity.value= + +### worked when nacos.core.auth.system.type=nacos +### The token expiration in seconds: +nacos.core.auth.plugin.nacos.token.cache.enable=false +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +### The default token (Base64 String): +nacos.core.auth.plugin.nacos.token.secret.key= + +### worked when nacos.core.auth.system.type=ldap\uFF0C{0} is Placeholder,replace login username +#nacos.core.auth.ldap.url=ldap://localhost:389 +#nacos.core.auth.ldap.basedc=dc=example,dc=org +#nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} +#nacos.core.auth.ldap.password=admin +#nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org +#nacos.core.auth.ldap.filter.prefix=uid +#nacos.core.auth.ldap.case.sensitive=true +#nacos.core.auth.ldap.ignore.partial.result.exception=false + +#*************** Control Plugin Related Configurations ***************# +# plugin type +#nacos.plugin.control.manager.type=nacos + +# local control rule storage dir, default ${nacos.home}/data/connection and ${nacos.home}/data/tps +#nacos.plugin.control.rule.local.basedir=${nacos.home} + +# external control rule storage type, if exist +#nacos.plugin.control.rule.external.storage= + +#*************** Config Change Plugin Related Configurations ***************# +# webhook +#nacos.core.config.plugin.webhook.enabled=false +# It is recommended to use EB https://help.aliyun.com/document_detail/413974.html +#nacos.core.config.plugin.webhook.url=http://localhost:8080/webhook/send?token=*** +# The content push max capacity ,byte +#nacos.core.config.plugin.webhook.contentMaxCapacity=102400 + +# whitelist +#nacos.core.config.plugin.whitelist.enabled=false +# The import file suffixs +#nacos.core.config.plugin.whitelist.suffixs=xml,text,properties,yaml,html +# fileformatcheck,which validate the import file of type and content +#nacos.core.config.plugin.fileformatcheck.enabled=false + +#*************** Istio Related Configurations ***************# +### If turn on the MCP server: +nacos.istio.mcp.server.enabled=false + +#*************** Core Related Configurations ***************# + +### set the WorkerID manually +# nacos.core.snowflake.worker-id= + +### Member-MetaData +# nacos.core.member.meta.site= +# nacos.core.member.meta.adweight= +# nacos.core.member.meta.weight= + +### MemberLookup +### Addressing pattern category, If set, the priority is highest +# nacos.core.member.lookup.type=[file,address-server] +## Set the cluster list with a configuration file or command-line argument +# nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 +## for AddressServerMemberLookup +# Maximum number of retries to query the address server upon initialization +# nacos.core.address-server.retry=5 +## Server domain name address of [address-server] mode +# address.server.domain=jmenv.tbsite.net +## Server port of [address-server] mode +# address.server.port=8080 +## Request address of [address-server] mode +# address.server.url=/nacos/serverlist + +#*************** JRaft Related Configurations ***************# + +### Sets the Raft cluster election timeout, default value is 5 second +# nacos.core.protocol.raft.data.election_timeout_ms=5000 +### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute +# nacos.core.protocol.raft.data.snapshot_interval_secs=30 +### raft internal worker threads +# nacos.core.protocol.raft.data.core_thread_num=8 +### Number of threads required for raft business request processing +# nacos.core.protocol.raft.data.cli_service_thread_num=4 +### raft linear read strategy. Safe linear reads are used by default, that is, the Leader tenure is confirmed by heartbeat +# nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe +### rpc request timeout, default 5 seconds +# nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000 + +#*************** Distro Related Configurations ***************# + +### Distro data sync delay time, when sync task delayed, task will be merged for same data key. Default 1 second. +# nacos.core.protocol.distro.data.sync.delayMs=1000 + +### Distro data sync timeout for one sync data, default 3 seconds. +# nacos.core.protocol.distro.data.sync.timeoutMs=3000 + +### Distro data sync retry delay time when sync data failed or timeout, same behavior with delayMs, default 3 seconds. +# nacos.core.protocol.distro.data.sync.retryDelayMs=3000 + +### Distro data verify interval time, verify synced data whether expired for a interval. Default 5 seconds. +# nacos.core.protocol.distro.data.verify.intervalMs=5000 + +### Distro data verify timeout for one verify, default 3 seconds. +# nacos.core.protocol.distro.data.verify.timeoutMs=3000 + +### Distro data load retry delay when load snapshot data failed, default 30 seconds. +# nacos.core.protocol.distro.data.load.retryDelayMs=30000 + +### enable to support prometheus service discovery +#nacos.prometheus.metrics.enabled=true + +### Since 2.3 +#*************** Grpc Configurations ***************# + +## sdk grpc(between nacos server and client) configuration +## Sets the maximum message size allowed to be received on the server. +#nacos.remote.server.grpc.sdk.max-inbound-message-size=10485760 + +## Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. +#nacos.remote.server.grpc.sdk.keep-alive-time=7200000 + +## Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. +#nacos.remote.server.grpc.sdk.keep-alive-timeout=20000 + + +## Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes +#nacos.remote.server.grpc.sdk.permit-keep-alive-time=300000 + +## cluster grpc(inside the nacos server) configuration +#nacos.remote.server.grpc.cluster.max-inbound-message-size=10485760 + +## Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. +#nacos.remote.server.grpc.cluster.keep-alive-time=7200000 + +## Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. +#nacos.remote.server.grpc.cluster.keep-alive-timeout=20000 + +## Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes +#nacos.remote.server.grpc.cluster.permit-keep-alive-time=300000 + +## open nacos default console ui +#nacos.console.ui.enabled=true \ No newline at end of file diff --git a/nacos/conf/application.properties.example b/nacos/conf/application.properties.example new file mode 100644 index 0000000..453630e --- /dev/null +++ b/nacos/conf/application.properties.example @@ -0,0 +1,271 @@ +# +# Copyright 1999-2021 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#*************** Spring Boot Related Configurations ***************# +### Default web context path: +server.servlet.contextPath=/nacos +### Include message field +server.error.include-message=ALWAYS +### Default web server port: +server.port=8848 + +#*************** Network Related Configurations ***************# +### If prefer hostname over ip for Nacos server addresses in cluster.conf: +# nacos.inetutils.prefer-hostname-over-ip=false + +### Specify local server's IP: +# nacos.inetutils.ip-address= + + +#*************** Config Module Related Configurations ***************# +### If use MySQL as datasource: +### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced. +# spring.datasource.platform=mysql +# spring.sql.init.platform=mysql + +### Count of DB: +# db.num=1 + +### Connect URL of DB: +# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC +# db.user.0=nacos +# db.password.0=nacos + +### Connection pool configuration: hikariCP +db.pool.config.connectionTimeout=30000 +db.pool.config.validationTimeout=10000 +db.pool.config.maximumPoolSize=20 +db.pool.config.minimumIdle=2 + +### the maximum retry times for push +nacos.config.push.maxRetryTime=50 + +#*************** Naming Module Related Configurations ***************# +### If enable data warmup. If set to false, the server would accept request without local data preparation: +# nacos.naming.data.warmup=true + +### If enable the instance auto expiration, kind like of health check of instance: +# nacos.naming.expireInstance=true + +### will be removed and replaced by `nacos.naming.clean` properties +nacos.naming.empty-service.auto-clean=true +nacos.naming.empty-service.clean.initial-delay-ms=50000 +nacos.naming.empty-service.clean.period-time-ms=30000 + +### Add in 2.0.0 +### The interval to clean empty service, unit: milliseconds. +# nacos.naming.clean.empty-service.interval=60000 + +### The expired time to clean empty service, unit: milliseconds. +# nacos.naming.clean.empty-service.expired-time=60000 + +### The interval to clean expired metadata, unit: milliseconds. +# nacos.naming.clean.expired-metadata.interval=5000 + +### The expired time to clean metadata, unit: milliseconds. +# nacos.naming.clean.expired-metadata.expired-time=60000 + +### The delay time before push task to execute from service changed, unit: milliseconds. +# nacos.naming.push.pushTaskDelay=500 + +### The timeout for push task execute, unit: milliseconds. +# nacos.naming.push.pushTaskTimeout=5000 + +### The delay time for retrying failed push task, unit: milliseconds. +# nacos.naming.push.pushTaskRetryDelay=1000 + +### Since 2.0.3 +### The expired time for inactive client, unit: milliseconds. +# nacos.naming.client.expired.time=180000 + +#*************** CMDB Module Related Configurations ***************# +### The interval to dump external CMDB in seconds: +# nacos.cmdb.dumpTaskInterval=3600 + +### The interval of polling data change event in seconds: +# nacos.cmdb.eventTaskInterval=10 + +### The interval of loading labels in seconds: +# nacos.cmdb.labelTaskInterval=300 + +### If turn on data loading task: +# nacos.cmdb.loadDataAtStart=false + + +#*************** Metrics Related Configurations ***************# +### Metrics for prometheus +#management.endpoints.web.exposure.include=* + +### Metrics for elastic search +management.metrics.export.elastic.enabled=false +#management.metrics.export.elastic.host=http://localhost:9200 + +### Metrics for influx +management.metrics.export.influx.enabled=false +#management.metrics.export.influx.db=springboot +#management.metrics.export.influx.uri=http://localhost:8086 +#management.metrics.export.influx.auto-create-db=true +#management.metrics.export.influx.consistency=one +#management.metrics.export.influx.compressed=true + +#*************** Access Log Related Configurations ***************# +### If turn on the access log: +server.tomcat.accesslog.enabled=true + +### The access log pattern: +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i + +### The directory of access log: +server.tomcat.basedir=file:. + +#*************** Access Control Related Configurations ***************# +### If enable spring security, this option is deprecated in 1.2.0: +#spring.security.enabled=false + +### The ignore urls of auth, is deprecated in 1.2.0: +nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** + +### The auth system to use, currently only 'nacos' and 'ldap' is supported: +nacos.core.auth.system.type=nacos + +### If turn on auth system: +nacos.core.auth.enabled=false + +### worked when nacos.core.auth.system.type=ldap,{0} is Placeholder,replace login username +#nacos.core.auth.system.type=ldap +#nacos.core.auth.ldap.url=ldap://localhost:389 +#nacos.core.auth.ldap.basedc=dc=example,dc=org +#nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} +#nacos.core.auth.ldap.password=admin +#nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org +#nacos.core.auth.ldap.filter.prefix=uid +#nacos.core.auth.ldap.case.sensitive=true +#nacos.core.auth.ldap.ignore.partial.result.exception=false + + +### worked when nacos.core.auth.system.type=nacos +### The token expiration in seconds: +nacos.core.auth.plugin.nacos.token.cache.enable=false +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +### The default token (Base64 String): +nacos.core.auth.plugin.nacos.token.secret.key= + +### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. +nacos.core.auth.caching.enabled=true + +### Since 1.4.1, Turn on/off white auth for user-agent: nacos-server, only for upgrade from old version. +nacos.core.auth.enable.userAgentAuthWhite=false + +### Since 1.4.1, worked when nacos.core.auth.enabled=true and nacos.core.auth.enable.userAgentAuthWhite=false. +### The two properties is the white list for auth and used by identity the request from other server. +#nacos.core.auth.server.identity.key=example +#nacos.core.auth.server.identity.value=example + +#*************** Istio Related Configurations ***************# +### If turn on the MCP server: +nacos.istio.mcp.server.enabled=false + +#*************** Core Related Configurations ***************# + +### set the WorkerID manually +# nacos.core.snowflake.worker-id= + +### Member-MetaData +# nacos.core.member.meta.site= +# nacos.core.member.meta.adweight= +# nacos.core.member.meta.weight= + +### MemberLookup +### Addressing pattern category, If set, the priority is highest +# nacos.core.member.lookup.type=[file,address-server] +## Set the cluster list with a configuration file or command-line argument +# nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 +## for AddressServerMemberLookup +# Maximum number of retries to query the address server upon initialization +# nacos.core.address-server.retry=5 +## Server domain name address of [address-server] mode +# address.server.domain=jmenv.tbsite.net +## Server port of [address-server] mode +# address.server.port=8080 +## Request address of [address-server] mode +# address.server.url=/nacos/serverlist + +#*************** JRaft Related Configurations ***************# + +### Sets the Raft cluster election timeout, default value is 5 second +# nacos.core.protocol.raft.data.election_timeout_ms=5000 +### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute +# nacos.core.protocol.raft.data.snapshot_interval_secs=30 +### raft internal worker threads +# nacos.core.protocol.raft.data.core_thread_num=8 +### Number of threads required for raft business request processing +# nacos.core.protocol.raft.data.cli_service_thread_num=4 +### raft linear read strategy. Safe linear reads are used by default, that is, the Leader tenure is confirmed by heartbeat +# nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe +### rpc request timeout, default 5 seconds +# nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000 + +#*************** Distro Related Configurations ***************# + +### Distro data sync delay time, when sync task delayed, task will be merged for same data key. Default 1 second. +# nacos.core.protocol.distro.data.sync.delayMs=1000 + +### Distro data sync timeout for one sync data, default 3 seconds. +# nacos.core.protocol.distro.data.sync.timeoutMs=3000 + +### Distro data sync retry delay time when sync data failed or timeout, same behavior with delayMs, default 3 seconds. +# nacos.core.protocol.distro.data.sync.retryDelayMs=3000 + +### Distro data verify interval time, verify synced data whether expired for a interval. Default 5 seconds. +# nacos.core.protocol.distro.data.verify.intervalMs=5000 + +### Distro data verify timeout for one verify, default 3 seconds. +# nacos.core.protocol.distro.data.verify.timeoutMs=3000 + +### Distro data load retry delay when load snapshot data failed, default 30 seconds. +# nacos.core.protocol.distro.data.load.retryDelayMs=30000 + +### enable to support prometheus service discovery +#nacos.prometheus.metrics.enabled=true + +### Since 2.3 +#*************** Grpc Configurations ***************# + +## sdk grpc(between nacos server and client) configuration +## Sets the maximum message size allowed to be received on the server. +#nacos.remote.server.grpc.sdk.max-inbound-message-size=10485760 + +## Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. +#nacos.remote.server.grpc.sdk.keep-alive-time=7200000 + +## Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. +#nacos.remote.server.grpc.sdk.keep-alive-timeout=20000 + + +## Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes +#nacos.remote.server.grpc.sdk.permit-keep-alive-time=300000 + +## cluster grpc(inside the nacos server) configuration +#nacos.remote.server.grpc.cluster.max-inbound-message-size=10485760 + +## Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. +#nacos.remote.server.grpc.cluster.keep-alive-time=7200000 + +## Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. +#nacos.remote.server.grpc.cluster.keep-alive-timeout=20000 + +## Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes +#nacos.remote.server.grpc.cluster.permit-keep-alive-time=300000 \ No newline at end of file diff --git a/nacos/conf/cluster.conf.example b/nacos/conf/cluster.conf.example new file mode 100644 index 0000000..3a7c227 --- /dev/null +++ b/nacos/conf/cluster.conf.example @@ -0,0 +1,21 @@ +# +# Copyright 1999-2021 Alibaba Group Holding Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#it is ip +#example +192.168.16.101:8847 +192.168.16.102 +192.168.16.103 diff --git a/nacos/conf/console-guide.conf b/nacos/conf/console-guide.conf new file mode 100644 index 0000000..e63b8cf --- /dev/null +++ b/nacos/conf/console-guide.conf @@ -0,0 +1 @@ +当前节点已关闭Nacos开源控制台使用,请修改application.properties中的nacos.console.ui.enabled参数为true打开开源控制台使用,详情查看文档中关于关闭默认控制台部分。 \ No newline at end of file diff --git a/nacos/conf/derby-schema.sql b/nacos/conf/derby-schema.sql new file mode 100644 index 0000000..f4e93a3 --- /dev/null +++ b/nacos/conf/derby-schema.sql @@ -0,0 +1,227 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CREATE SCHEMA nacos AUTHORIZATION nacos; + +CREATE TABLE config_info ( + id bigint NOT NULL generated by default as identity, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) default '', + app_name varchar(128), + content CLOB, + md5 varchar(32) DEFAULT NULL, + gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + src_user varchar(128) DEFAULT NULL, + src_ip varchar(50) DEFAULT NULL, + c_desc varchar(256) DEFAULT NULL, + c_use varchar(64) DEFAULT NULL, + effect varchar(64) DEFAULT NULL, + type varchar(64) DEFAULT NULL, + c_schema LONG VARCHAR DEFAULT NULL, + encrypted_data_key LONG VARCHAR DEFAULT NULL, + constraint configinfo_id_key PRIMARY KEY (id), + constraint uk_configinfo_datagrouptenant UNIQUE (data_id,group_id,tenant_id)); + +CREATE INDEX configinfo_dataid_key_idx ON config_info(data_id); +CREATE INDEX configinfo_groupid_key_idx ON config_info(group_id); +CREATE INDEX configinfo_dataid_group_key_idx ON config_info(data_id, group_id); + +CREATE TABLE his_config_info ( + id bigint NOT NULL, + nid bigint NOT NULL generated by default as identity, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) default '', + app_name varchar(128), + content CLOB, + md5 varchar(32) DEFAULT NULL, + gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00.000', + gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00.000', + src_user varchar(128), + src_ip varchar(50) DEFAULT NULL, + op_type char(10) DEFAULT NULL, + encrypted_data_key LONG VARCHAR DEFAULT NULL, + constraint hisconfiginfo_nid_key PRIMARY KEY (nid)); + +CREATE INDEX hisconfiginfo_dataid_key_idx ON his_config_info(data_id); +CREATE INDEX hisconfiginfo_gmt_create_idx ON his_config_info(gmt_create); +CREATE INDEX hisconfiginfo_gmt_modified_idx ON his_config_info(gmt_modified); + + +CREATE TABLE config_info_beta ( + id bigint NOT NULL generated by default as identity, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) default '', + app_name varchar(128), + content CLOB, + beta_ips varchar(1024), + md5 varchar(32) DEFAULT NULL, + gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + src_user varchar(128), + src_ip varchar(50) DEFAULT NULL, + encrypted_data_key LONG VARCHAR DEFAULT NULL, + constraint configinfobeta_id_key PRIMARY KEY (id), + constraint uk_configinfobeta_datagrouptenant UNIQUE (data_id,group_id,tenant_id)); + +CREATE TABLE config_info_tag ( + id bigint NOT NULL generated by default as identity, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) default '', + tag_id varchar(128) NOT NULL, + app_name varchar(128), + content CLOB, + md5 varchar(32) DEFAULT NULL, + gmt_create timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + src_user varchar(128), + src_ip varchar(50) DEFAULT NULL, + constraint configinfotag_id_key PRIMARY KEY (id), + constraint uk_configinfotag_datagrouptenanttag UNIQUE (data_id,group_id,tenant_id,tag_id)); + +CREATE TABLE config_info_aggr ( + id bigint NOT NULL generated by default as identity, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) default '', + datum_id varchar(255) NOT NULL, + app_name varchar(128), + content CLOB, + gmt_modified timestamp NOT NULL DEFAULT '2010-05-05 00:00:00', + constraint configinfoaggr_id_key PRIMARY KEY (id), + constraint uk_configinfoaggr_datagrouptenantdatum UNIQUE (data_id,group_id,tenant_id,datum_id)); + +CREATE TABLE app_list ( + id bigint NOT NULL generated by default as identity, + app_name varchar(128) NOT NULL, + is_dynamic_collect_disabled smallint DEFAULT 0, + last_sub_info_collected_time timestamp DEFAULT '1970-01-01 08:00:00.0', + sub_info_lock_owner varchar(128), + sub_info_lock_time timestamp DEFAULT '1970-01-01 08:00:00.0', + constraint applist_id_key PRIMARY KEY (id), + constraint uk_appname UNIQUE (app_name)); + +CREATE TABLE app_configdata_relation_subs ( + id bigint NOT NULL generated by default as identity, + app_name varchar(128) NOT NULL, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + gmt_modified timestamp DEFAULT '2010-05-05 00:00:00', + constraint configdatarelationsubs_id_key PRIMARY KEY (id), + constraint uk_app_sub_config_datagroup UNIQUE (app_name, data_id, group_id)); + + +CREATE TABLE app_configdata_relation_pubs ( + id bigint NOT NULL generated by default as identity, + app_name varchar(128) NOT NULL, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + gmt_modified timestamp DEFAULT '2010-05-05 00:00:00', + constraint configdatarelationpubs_id_key PRIMARY KEY (id), + constraint uk_app_pub_config_datagroup UNIQUE (app_name, data_id, group_id)); + +CREATE TABLE config_tags_relation ( + id bigint NOT NULL, + tag_name varchar(128) NOT NULL, + tag_type varchar(64) DEFAULT NULL, + data_id varchar(255) NOT NULL, + group_id varchar(128) NOT NULL, + tenant_id varchar(128) DEFAULT '', + nid bigint NOT NULL generated by default as identity, + constraint config_tags_id_key PRIMARY KEY (nid), + constraint uk_configtagrelation_configidtag UNIQUE (id, tag_name, tag_type)); + +CREATE INDEX config_tags_tenant_id_idx ON config_tags_relation(tenant_id); + +CREATE TABLE group_capacity ( + id bigint NOT NULL generated by default as identity, + group_id varchar(128) DEFAULT '', + quota int DEFAULT 0, + usage int DEFAULT 0, + max_size int DEFAULT 0, + max_aggr_count int DEFAULT 0, + max_aggr_size int DEFAULT 0, + max_history_count int DEFAULT 0, + gmt_create timestamp DEFAULT '2010-05-05 00:00:00', + gmt_modified timestamp DEFAULT '2010-05-05 00:00:00', + constraint group_capacity_id_key PRIMARY KEY (id), + constraint uk_group_id UNIQUE (group_id)); + +CREATE TABLE tenant_capacity ( + id bigint NOT NULL generated by default as identity, + tenant_id varchar(128) DEFAULT '', + quota int DEFAULT 0, + usage int DEFAULT 0, + max_size int DEFAULT 0, + max_aggr_count int DEFAULT 0, + max_aggr_size int DEFAULT 0, + max_history_count int DEFAULT 0, + gmt_create timestamp DEFAULT '2010-05-05 00:00:00', + gmt_modified timestamp DEFAULT '2010-05-05 00:00:00', + constraint tenant_capacity_id_key PRIMARY KEY (id), + constraint uk_tenant_id UNIQUE (tenant_id)); + +CREATE TABLE tenant_info ( + id bigint NOT NULL generated by default as identity, + kp varchar(128) NOT NULL, + tenant_id varchar(128) DEFAULT '', + tenant_name varchar(128) DEFAULT '', + tenant_desc varchar(256) DEFAULT NULL, + create_source varchar(32) DEFAULT NULL, + gmt_create bigint NOT NULL, + gmt_modified bigint NOT NULL, + constraint tenant_info_id_key PRIMARY KEY (id), + constraint uk_tenant_info_kptenantid UNIQUE (kp,tenant_id)); +CREATE INDEX tenant_info_tenant_id_idx ON tenant_info(tenant_id); + +CREATE TABLE users ( + username varchar(50) NOT NULL PRIMARY KEY, + password varchar(500) NOT NULL, + enabled boolean NOT NULL DEFAULT true +); + +CREATE TABLE roles ( + username varchar(50) NOT NULL, + role varchar(50) NOT NULL, + constraint uk_username_role UNIQUE (username,role) +); + +CREATE TABLE permissions ( + role varchar(50) NOT NULL, + resource varchar(512) NOT NULL, + action varchar(8) NOT NULL, + constraint uk_role_permission UNIQUE (role,resource,action) +); + + +/******************************************/ +/* ipv6 support */ +/******************************************/ +ALTER TABLE `config_info_tag` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `his_config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL AFTER `src_user`; + +ALTER TABLE `config_info` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; + +ALTER TABLE `config_info_beta` +MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`; \ No newline at end of file diff --git a/nacos/conf/mysql-schema.sql b/nacos/conf/mysql-schema.sql new file mode 100644 index 0000000..d3c0660 --- /dev/null +++ b/nacos/conf/mysql-schema.sql @@ -0,0 +1,210 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/******************************************/ +/* 表名称 = config_info */ +/******************************************/ +CREATE TABLE `config_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) DEFAULT NULL COMMENT 'group_id', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description', + `c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage', + `effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述', + `type` varchar(64) DEFAULT NULL COMMENT '配置的类型', + `c_schema` text COMMENT '配置的模式', + `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; + +/******************************************/ +/* 表名称 = config_info_aggr */ +/******************************************/ +CREATE TABLE `config_info_aggr` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', + `content` longtext NOT NULL COMMENT '内容', + `gmt_modified` datetime NOT NULL COMMENT '修改时间', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段'; + + +/******************************************/ +/* 表名称 = config_info_beta */ +/******************************************/ +CREATE TABLE `config_info_beta` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; + +/******************************************/ +/* 表名称 = config_info_tag */ +/******************************************/ +CREATE TABLE `config_info_tag` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; + +/******************************************/ +/* 表名称 = config_tags_relation */ +/******************************************/ +CREATE TABLE `config_tags_relation` ( + `id` bigint(20) NOT NULL COMMENT 'id', + `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', + `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', + `nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识', + PRIMARY KEY (`nid`), + UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; + +/******************************************/ +/* 表名称 = group_capacity */ +/******************************************/ +CREATE TABLE `group_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_group_id` (`group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; + +/******************************************/ +/* 表名称 = his_config_info */ +/******************************************/ +CREATE TABLE `his_config_info` ( + `id` bigint(20) unsigned NOT NULL COMMENT 'id', + `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识', + `data_id` varchar(255) NOT NULL COMMENT 'data_id', + `group_id` varchar(128) NOT NULL COMMENT 'group_id', + `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', + `content` longtext NOT NULL COMMENT 'content', + `md5` varchar(32) DEFAULT NULL COMMENT 'md5', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `src_user` text COMMENT 'source user', + `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', + `op_type` char(10) DEFAULT NULL COMMENT 'operation type', + `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', + `encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥', + PRIMARY KEY (`nid`), + KEY `idx_gmt_create` (`gmt_create`), + KEY `idx_gmt_modified` (`gmt_modified`), + KEY `idx_did` (`data_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; + + +/******************************************/ +/* 表名称 = tenant_capacity */ +/******************************************/ +CREATE TABLE `tenant_capacity` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', + `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', + `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', + `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', + `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', + `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', + `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', + `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; + + +CREATE TABLE `tenant_info` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', + `kp` varchar(128) NOT NULL COMMENT 'kp', + `tenant_id` varchar(128) default '' COMMENT 'tenant_id', + `tenant_name` varchar(128) default '' COMMENT 'tenant_name', + `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', + `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', + `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', + `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), + KEY `idx_tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; + +CREATE TABLE `users` ( + `username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username', + `password` varchar(500) NOT NULL COMMENT 'password', + `enabled` boolean NOT NULL COMMENT 'enabled' +); + +CREATE TABLE `roles` ( + `username` varchar(50) NOT NULL COMMENT 'username', + `role` varchar(50) NOT NULL COMMENT 'role', + UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE +); + +CREATE TABLE `permissions` ( + `role` varchar(50) NOT NULL COMMENT 'role', + `resource` varchar(128) NOT NULL COMMENT 'resource', + `action` varchar(8) NOT NULL COMMENT 'action', + UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE +); + diff --git a/nacos/conf/nacos-logback.xml b/nacos/conf/nacos-logback.xml new file mode 100644 index 0000000..1682f2d --- /dev/null +++ b/nacos/conf/nacos-logback.xml @@ -0,0 +1,797 @@ + + + + + + + + + + ${nacos.home}/logs/cmdb-main.log + true + + ${nacos.home}/logs/cmdb-main.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/naming-server.log + true + + ${LOG_HOME}/naming-server.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + 0 + 1024 + true + + + + + ${LOG_HOME}/naming-raft.log + true + + ${LOG_HOME}/naming-raft.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + 0 + 1024 + true + + + + + + ${LOG_HOME}/naming-distro.log + true + + ${LOG_HOME}/naming-distro.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + 0 + 1024 + true + + + + + ${LOG_HOME}/naming-event.log + true + + ${LOG_HOME}/naming-event.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + 0 + 1024 + true + + + + + ${LOG_HOME}/naming-push.log + true + + ${LOG_HOME}/naming-push.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/naming-rt.log + true + + ${LOG_HOME}/naming-rt.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %msg%n + UTF-8 + + + + + ${LOG_HOME}/naming-performance.log + true + + ${LOG_HOME}/naming-performance.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + ${LOG_HOME}/config-dump.log + true + + ${LOG_HOME}/config-dump.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-pull.log + true + + ${LOG_HOME}/config-pull.log.%d{yyyy-MM-dd}.%i + 20MB + 7 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-fatal.log + true + + ${LOG_HOME}/config-fatal.log.%d{yyyy-MM-dd}.%i + 20MB + 7 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-memory.log + true + + ${LOG_HOME}/config-memory.log.%d{yyyy-MM-dd}.%i + 20MB + 7 + 128MB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/config-pull-check.log + true + + ${LOG_HOME}/config-pull-check.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %msg%n + UTF-8 + + + + + ${LOG_HOME}/config-client-request.log + true + + ${LOG_HOME}/config-client-request.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date|%msg%n + UTF-8 + + + + + ${LOG_HOME}/config-trace.log + true + + ${LOG_HOME}/config-trace.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date|%msg%n + UTF-8 + + + + + ${LOG_HOME}/config-notify.log + true + + ${LOG_HOME}/config-notify.log.%d{yyyy-MM-dd}.%i + 1GB + 7 + 3GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/config-server.log + true + + ${LOG_HOME}/config-server.log.%d{yyyy-MM-dd}.%i + 50MB + 7 + 512MB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/nacos.log + true + + ${LOG_HOME}/nacos.log.%d{yyyy-MM-dd}.%i + 50MB + 7 + 512MB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/nacos-address.log + true + + ${LOG_HOME}/nacos-address.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/istio-main.log + true + + ${LOG_HOME}/istio-main.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/core-auth.log + true + + ${LOG_HOME}/core-auth.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/protocol-raft.log + true + + ${LOG_HOME}/protocol-raft.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/protocol-distro.log + true + + ${LOG_HOME}/protocol-distro.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/nacos-cluster.log + true + + ${LOG_HOME}/nacos-cluster.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/alipay-jraft.log + true + + ${LOG_HOME}/alipay-jraft.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + + ${LOG_HOME}/plugin-control-connection.log + true + + ${LOG_HOME}/plugin-control-connection.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 2GB + true + + + %date %level %msg%n%n + UTF-8 + + + + ${LOG_HOME}/plugin-control-tps.log + true + + ${LOG_HOME}/plugin-control-tps.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 2GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/plugin-control.log + true + + ${LOG_HOME}/plugin-control.%d{yyyy-MM-dd}.%i + 2GB + 14 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + ${LOG_HOME}/remote.log + true + + ${LOG_HOME}/remote.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/remote-digest.log + true + + ${LOG_HOME}/remote-digest.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/remote-push.log + true + + ${LOG_HOME}/remote-push.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + ${LOG_HOME}/nacos-persistence.log + true + + ${LOG_HOME}/nacos-persistence.log.%d{yyyy-MM-dd}.%i + 2GB + 7 + 7GB + true + + + %date %level %msg%n%n + UTF-8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 72f6c76..7e12e91 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ yudao-module-mall yudao-module-erp yudao-module-crm + yudao-module-langchat @@ -33,7 +34,7 @@ 2.3.0-jdk8-SNAPSHOT - 1.8 + 17 ${java.version} ${java.version} 3.0.0-M5 diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml index 450e875..896de43 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application-local.yaml @@ -3,7 +3,7 @@ spring: cloud: nacos: - server-addr: 10.31.0.128:8848 # Nacos 服务器地址 + server-addr: 192.168.0.17:8848 # Nacos 服务器地址 username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 @@ -61,30 +61,30 @@ spring: primary: master datasource: master: - url: jdbc:mysql://10.31.0.128:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 username: root - password: chinasoft@123 + password: root # username: sa # SQL Server 连接的示例 # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 # password: SYSDBA # DM 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://10.31.0.128:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true username: root - password: chinasoft@123 + password: root # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: - host: 10.31.0.128 # 地址 + host: 192.168.0.17 # 地址 port: 6379 # 端口 database: 0 # 数据库索引 -# password: 123456 # 密码,建议生产环境开启 + password: 123456 # 密码,建议生产环境开启 --- #################### MQ 消息队列相关配置 #################### diff --git a/yudao-module-langchat/.flattened-pom.xml b/yudao-module-langchat/.flattened-pom.xml new file mode 100644 index 0000000..23287cb --- /dev/null +++ b/yudao-module-langchat/.flattened-pom.xml @@ -0,0 +1,144 @@ + + + 4.0.0 + cn.iocoder.cloud + yudao-module-langchat + 2.3.0-jdk8-SNAPSHOT + 芋道项目基础脚手架 + https://github.com/YunaiV/ruoyi-vue-pro/yudao-module-langchat + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-data-permission + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-ip + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + 2.3.0-jdk8-SNAPSHOT + compile + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2021.0.6.1 + compile + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2021.0.6.1 + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-job + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-mq + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + 2.3.0-jdk8-SNAPSHOT + compile + + + org.springframework.boot + spring-boot-starter-mail + 2.7.18 + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + 2.3.0-jdk8-SNAPSHOT + compile + + + com.xingyuv + spring-boot-starter-justauth + 1.0.8 + compile + + + cn.hutool + hutool-core + + + + + com.github.binarywang + wx-java-mp-spring-boot-starter + 4.6.0 + compile + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + 4.6.0 + compile + + + com.xingyuv + spring-boot-starter-captcha-plus + 1.0.8 + compile + + + + + huaweicloud + huawei + https://mirrors.huaweicloud.com/repository/maven/ + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + diff --git a/yudao-module-langchat/pom.xml b/yudao-module-langchat/pom.xml new file mode 100644 index 0000000..603f922 --- /dev/null +++ b/yudao-module-langchat/pom.xml @@ -0,0 +1,22 @@ + + + + cn.iocoder.cloud + yudao + ${revision} + + 4.0.0 + + yudao-module-langchat-api + yudao-module-langchat-biz + + yudao-module-langchat + pom + + ${project.artifactId} + + + + diff --git a/yudao-module-langchat/yudao-module-langchat-api/pom.xml b/yudao-module-langchat/yudao-module-langchat-api/pom.xml new file mode 100644 index 0000000..d2735da --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + cn.iocoder.cloud + yudao-module-langchat + ${revision} + + + yudao-module-langchat-api + jar + + ${project.artifactId} + + + langchat 模块 API,暴露给其它模块调用 + + + + + cn.iocoder.cloud + yudao-common + + + \ No newline at end of file diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/CacheConst.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/CacheConst.java new file mode 100644 index 0000000..16a9fb2 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/CacheConst.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +/** + * @author tycoding + * @since 2024/1/15 + */ +public interface CacheConst { + + /** + * 系统所有Redis缓存Key前缀 prefix + */ + String REDIS_KEY_PREFIX = "langchat:"; + + /** + * Auth缓存前缀 + */ + String AUTH_PREFIX = REDIS_KEY_PREFIX + "auth:"; + + /** + * Auth Session缓存前缀 + */ + String AUTH_SESSION_PREFIX = AUTH_PREFIX + "session:"; + + /** + * Auth Session缓存变量前缀 + */ + String AUTH_USER_INFO_KEY = "USER_INFO"; + + /** + * Auth Token缓存变量前缀 + */ + String AUTH_TOKEN_INFO_KEY = "TOKEN_INFO"; + + /** + * 用户信息缓存 + */ + String USER_DETAIL_KEY = REDIS_KEY_PREFIX + "user_details"; + + /** + * 验证码缓存前缀 + */ + String CAPTCHA_PREFIX = REDIS_KEY_PREFIX + "auth:captcha:"; + +} diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ChatErrorEnum.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ChatErrorEnum.java new file mode 100644 index 0000000..976400c --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ChatErrorEnum.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.langchat.enums; + +import lombok.AllArgsConstructor; + +/** + * @author GB + * @desc + * @since 2024-08-21 + */ +@AllArgsConstructor +public enum ChatErrorEnum { + API_KEY_IS_NULL(1000, "模型 %s %s api key 为空,请检查配置"), + BASE_URL_IS_NULL(1003, "模型 %s %s base url 为空,请检查配置"), + SECRET_KEY_IS_NULL(1005, "模型 %s %s base secret Key 为空,请检查配置"), + ; + + /** + * 错误码 + */ + private int errorCode; + /** + * 错误描述,用于展示给用户 + */ + private String errorDesc; + + public int getErrorCode() { + return this.errorCode; + } + + public String getErrorDesc(String modelName, String type) { + return this.errorDesc.formatted(modelName, type); + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedConst.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedConst.java new file mode 100644 index 0000000..d83aac2 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedConst.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +/** + * @author tycoding + * @since 2024/6/6 + */ +public interface EmbedConst { + + String ORIGIN_TYPE_INPUT = "INPUT"; + String ORIGIN_TYPE_UPLOAD = "UPLOAD"; + + String KNOWLEDGE = "knowledgeId"; + String FILENAME = "docsName"; + + String CLAZZ_NAME_OPENAI = "OpenAiEmbeddingModel"; + String CLAZZ_NAME_QIANFAN = "QianfanEmbeddingModel"; + String CLAZZ_NAME_QIANWEN = "QwenEmbeddingModel"; + String CLAZZ_NAME_ZHIPU = "ZhipuAiEmbeddingModel"; + String CLAZZ_NAME_OLLAMA = "OllamaEmbeddingModel"; +} diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedStoreEnum.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedStoreEnum.java new file mode 100644 index 0000000..9f14456 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/EmbedStoreEnum.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +import lombok.Getter; + +/** + * @author tycoding + * @since 2024/10/28 + */ +@Getter +public enum EmbedStoreEnum { + + REDIS, + PGVECTOR, + MILVUS, + ; +} diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ModelConst.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ModelConst.java new file mode 100644 index 0000000..c7bb4df --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ModelConst.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +/** + * @author tycoding + * @since 2024/1/6 + */ +public interface ModelConst { + + String TEXT_SUFFIX = "_text"; + String IMAGE_SUFFIX = "_image"; +} diff --git a/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ProviderEnum.java b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ProviderEnum.java new file mode 100644 index 0000000..e402b30 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-api/src/main/java/cn/iocoder/yudao/module/langchat/enums/ProviderEnum.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +import lombok.Getter; + +/** + * @author tycoding + * @since 2024/6/16 + */ +@Getter +public enum ProviderEnum { + + OPENAI, + AZURE_OPENAI, + GEMINI, + OLLAMA, + CLAUDE, + Q_FAN, + Q_WEN, + ZHIPU, + YI, + DOUYIN, + DEEPSEEK, + SILICON, + SPARK, + ; + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/.flattened-pom.xml b/yudao-module-langchat/yudao-module-langchat-biz/.flattened-pom.xml new file mode 100644 index 0000000..947f2d0 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/.flattened-pom.xml @@ -0,0 +1,144 @@ + + + 4.0.0 + cn.iocoder.cloud + yudao-module-langchat-biz + 2.3.0-jdk8-SNAPSHOT + 芋道项目基础脚手架 + https://github.com/YunaiV/ruoyi-vue-pro/yudao-module-langchat-biz + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-data-permission + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-ip + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + 2.3.0-jdk8-SNAPSHOT + compile + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2021.0.6.1 + compile + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2021.0.6.1 + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-job + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-mq + 2.3.0-jdk8-SNAPSHOT + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + 2.3.0-jdk8-SNAPSHOT + compile + + + org.springframework.boot + spring-boot-starter-mail + 2.7.18 + compile + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + 2.3.0-jdk8-SNAPSHOT + compile + + + com.xingyuv + spring-boot-starter-justauth + 1.0.8 + compile + + + cn.hutool + hutool-core + + + + + com.github.binarywang + wx-java-mp-spring-boot-starter + 4.6.0 + compile + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + 4.6.0 + compile + + + com.xingyuv + spring-boot-starter-captcha-plus + 1.0.8 + compile + + + + + huaweicloud + huawei + https://mirrors.huaweicloud.com/repository/maven/ + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/pom.xml b/yudao-module-langchat/yudao-module-langchat-biz/pom.xml new file mode 100644 index 0000000..5171210 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/pom.xml @@ -0,0 +1,258 @@ + + + + cn.iocoder.cloud + yudao-module-langchat + ${revision} + + 4.0.0 + yudao-module-langchat-biz + jar + + ${project.artifactId} + + + 1.0.0-beta1 + + + + + + + + + + dev.langchain4j + langchain4j + ${langchain4j.version} + + + dev.langchain4j + langchain4j-core + ${langchain4j.version} + + + dev.langchain4j + langchain4j-community-redis + ${langchain4j.version} + + + dev.langchain4j + langchain4j-pgvector + ${langchain4j.version} + + + dev.langchain4j + langchain4j-milvus + ${langchain4j.version} + + + dev.langchain4j + langchain4j-document-parser-apache-tika + ${langchain4j.version} + + + dev.langchain4j + langchain4j-ollama + ${langchain4j.version} + + + dev.langchain4j + langchain4j-open-ai + ${langchain4j.version} + + + dev.langchain4j + langchain4j-community-qianfan + ${langchain4j.version} + + + dev.langchain4j + langchain4j-community-dashscope + ${langchain4j.version} + + + org.slf4j + slf4j-simple + + + + + dev.langchain4j + langchain4j-community-zhipu-ai + ${langchain4j.version} + + + + org.dromara.x-file-storage + x-file-storage-core + 2.2.1 + + + com.qiniu + qiniu-java-sdk + 7.12.1 + + + com.aliyun.oss + aliyun-sdk-oss + 3.16.1 + + + com.qcloud + cos_api + 5.6.137 + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-env + + + + + cn.iocoder.cloud + yudao-common + + + cn.iocoder.cloud + yudao-module-langchat-api + 2.3.0-jdk8-SNAPSHOT + + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-data-permission + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-tenant + + + cn.iocoder.cloud + yudao-spring-boot-starter-biz-ip + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-security + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.cloud + yudao-spring-boot-starter-redis + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-job + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-mq + + + + + + + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-test + test + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-excel + + + + org.springframework.boot + spring-boot-starter-mail + + + + + cn.iocoder.cloud + yudao-spring-boot-starter-monitor + + + + + com.xingyuv + spring-boot-starter-justauth + + + + com.github.binarywang + wx-java-mp-spring-boot-starter + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + + + + com.xingyuv + spring-boot-starter-captcha-plus + + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/LangChatApplication.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/LangChatApplication.java new file mode 100644 index 0000000..04ab1cc --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/LangChatApplication.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat; + +import cn.iocoder.yudao.module.langchat.config.EnableFileStorage; +import cn.iocoder.yudao.module.langchat.config.SpringFileStorageProperties; +import cn.iocoder.yudao.module.langchat.properties.ChatProps; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author tycoding + * @since 2024/2/4 + */ +@EnableFileStorage +@SpringBootApplication +@EnableConfigurationProperties({ + SpringFileStorageProperties.class, + ChatProps.class +}) +public class LangChatApplication { + + public static void main(String[] args) { + SpringApplication.run(LangChatApplication.class, args); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/annotation/ApiLog.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/annotation/ApiLog.java new file mode 100644 index 0000000..3c1ba34 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/annotation/ApiLog.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义日志记录切面注解 + * + * @author tycoding + * @since 2024/4/15 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiLog { + + String value() default ""; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/EmbeddingRefreshEvent.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/EmbeddingRefreshEvent.java new file mode 100644 index 0000000..3b039ed --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/EmbeddingRefreshEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.component; + +import org.springframework.context.ApplicationEvent; + +/** + * @author tycoding + * @since 2024/6/16 + */ +public class EmbeddingRefreshEvent extends ApplicationEvent { + private static final long serialVersionUID = 4109980679877560773L; + + public EmbeddingRefreshEvent(Object source) { + super(source); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ModelTypeEnum.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ModelTypeEnum.java new file mode 100644 index 0000000..10d5097 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ModelTypeEnum.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.component; + +import lombok.Getter; + +/** + * @author tycoding + * @since 2024/6/16 + */ +@Getter +public enum ModelTypeEnum { + + CHAT, + EMBEDDING, + TEXT_IMAGE, + WEB_SEARCH; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ProviderRefreshEvent.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ProviderRefreshEvent.java new file mode 100644 index 0000000..0e1a56c --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/ProviderRefreshEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.component; + +import org.springframework.context.ApplicationEvent; + +/** + * @author tycoding + * @since 2024/6/16 + */ +public class ProviderRefreshEvent extends ApplicationEvent { + private static final long serialVersionUID = 4109980679877560773L; + + public ProviderRefreshEvent(Object source) { + super(source); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/SpringContextHolder.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/SpringContextHolder.java new file mode 100644 index 0000000..f8e0b87 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/component/SpringContextHolder.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.component; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.stereotype.Service; + +/** + * @author tycoding + * @since 2024/1/19 + */ +@Service +public class SpringContextHolder implements ApplicationContextAware { + + private static ApplicationContext applicationContext = null; + + public static void publishEvent(ApplicationEvent event) { + if (applicationContext == null) { + return; + } + applicationContext.publishEvent(event); + } + + public T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContextHolder.applicationContext = applicationContext; + } + + public void registerBean(String beanName, Object beanInstance) { + BeanDefinitionRegistry beanDefinitionRegistry = + (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory(); + + BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder + .genericBeanDefinition((Class) beanInstance.getClass(), () -> beanInstance); + + BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); + + beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition); + } + + public void unregisterBean(String beanName) { + BeanDefinitionRegistry beanDefinitionRegistry = + (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory(); + + if (beanDefinitionRegistry.containsBeanDefinition(beanName)) { + beanDefinitionRegistry.removeBeanDefinition(beanName); + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/EnableFileStorage.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/EnableFileStorage.java new file mode 100644 index 0000000..4f17374 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/EnableFileStorage.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.config; + +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/** + * 启用文件存储,会自动根据配置文件进行加载 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +@Import({FileStorageAutoConfiguration.class, SpringFileStorageProperties.class}) +public @interface EnableFileStorage {} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/FileStorageAutoConfiguration.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/FileStorageAutoConfiguration.java new file mode 100644 index 0000000..46f8857 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/FileStorageAutoConfiguration.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.config; + +import cn.iocoder.yudao.module.langchat.wrapper.MultipartFileWrapperAdapter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileStorageService; +import org.dromara.x.file.storage.core.FileStorageServiceBuilder; +import org.dromara.x.file.storage.core.aspect.FileStorageAspect; +import org.dromara.x.file.storage.core.file.FileWrapperAdapter; +import org.dromara.x.file.storage.core.platform.FileStorage; +import org.dromara.x.file.storage.core.platform.FileStorageClientFactory; +import org.dromara.x.file.storage.core.recorder.DefaultFileRecorder; +import org.dromara.x.file.storage.core.recorder.FileRecorder; +import org.dromara.x.file.storage.core.tika.ContentTypeDetect; +import org.dromara.x.file.storage.core.tika.DefaultTikaFactory; +import org.dromara.x.file.storage.core.tika.TikaContentTypeDetect; +import org.dromara.x.file.storage.core.tika.TikaFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Configuration +@ConditionalOnMissingBean(FileStorageService.class) +public class FileStorageAutoConfiguration { + + @Autowired + private SpringFileStorageProperties properties; + + @Autowired + private ApplicationContext applicationContext; + + /** + * 当没有找到 FileRecorder 时使用默认的 FileRecorder + */ + @Bean + @ConditionalOnMissingBean(FileRecorder.class) + public FileRecorder fileRecorder() { + log.warn("没有找到 FileRecorder 的实现类,文件上传之外的部分功能无法正常使用,必须实现该接口才能使用完整功能!"); + return new DefaultFileRecorder(); + } + + /** + * Tika 工厂类型,用于识别上传的文件的 MINE + */ + @Bean + @ConditionalOnMissingBean(TikaFactory.class) + public TikaFactory tikaFactory() { + return new DefaultTikaFactory(); + } + + /** + * 识别文件的 MIME 类型 + */ + @Bean + @ConditionalOnMissingBean(ContentTypeDetect.class) + public ContentTypeDetect contentTypeDetect(TikaFactory tikaFactory) { + return new TikaContentTypeDetect(tikaFactory); + } + + /** + * 文件存储服务 + */ + @Bean(destroyMethod = "destroy") + public FileStorageService fileStorageService( + FileRecorder fileRecorder, + @Autowired(required = false) List> fileStorageLists, + @Autowired(required = false) List aspectList, + @Autowired(required = false) List fileWrapperAdapterList, + ContentTypeDetect contentTypeDetect, + @Autowired(required = false) List>> clientFactoryList) { + + if (fileStorageLists == null) fileStorageLists = new ArrayList<>(); + if (aspectList == null) aspectList = new ArrayList<>(); + if (fileWrapperAdapterList == null) fileWrapperAdapterList = new ArrayList<>(); + if (clientFactoryList == null) clientFactoryList = new ArrayList<>(); + + FileStorageServiceBuilder builder = FileStorageServiceBuilder.create(properties.toFileStorageProperties()) + .setFileRecorder(fileRecorder) + .setAspectList(aspectList) + .setContentTypeDetect(contentTypeDetect) + .setFileWrapperAdapterList(fileWrapperAdapterList) + .setClientFactoryList(clientFactoryList); + + fileStorageLists.forEach(builder::addFileStorage); + + if (properties.getEnableByteFileWrapper()) { + builder.addByteFileWrapperAdapter(); + } + if (properties.getEnableUriFileWrapper()) { + builder.addUriFileWrapperAdapter(); + } + if (properties.getEnableInputStreamFileWrapper()) { + builder.addInputStreamFileWrapperAdapter(); + } + if (properties.getEnableLocalFileWrapper()) { + builder.addLocalFileWrapperAdapter(); + } + if (properties.getEnableHttpServletRequestFileWrapper()) { + if (FileStorageServiceBuilder.doesNotExistClass("javax.servlet.http.HttpServletRequest") + && FileStorageServiceBuilder.doesNotExistClass("jakarta.servlet.http.HttpServletRequest")) { + log.warn( + "当前未检测到 Servlet 环境,无法加载 HttpServletRequest 的文件包装适配器,请将参数【dromara.x-file-storage.enable-http-servlet-request-file-wrapper】设置为 【false】来消除此警告"); + } else { + builder.addHttpServletRequestFileWrapperAdapter(); + } + } + if (properties.getEnableMultipartFileWrapper()) { + if (FileStorageServiceBuilder.doesNotExistClass("org.springframework.web.multipart.MultipartFile")) { + log.warn( + "当前未检测到 SpringWeb 环境,无法加载 MultipartFile 的文件包装适配器,请将参数【dromara.x-file-storage.enable-multipart-file-wrapper】设置为 【false】来消除此警告"); + } else { + builder.addFileWrapperAdapter(new MultipartFileWrapperAdapter()); + } + } + + if (FileStorageServiceBuilder.doesNotExistClass("org.springframework.web.servlet.config.annotation.WebMvcConfigurer")) { + long localAccessNum = properties.getLocal().stream() + .filter(SpringFileStorageProperties.SpringLocalConfig::getEnableStorage) + .filter(SpringFileStorageProperties.SpringLocalConfig::getEnableAccess) + .count(); + long localPlusAccessNum = properties.getLocalPlus().stream() + .filter(SpringFileStorageProperties.SpringLocalPlusConfig::getEnableStorage) + .filter(SpringFileStorageProperties.SpringLocalPlusConfig::getEnableAccess) + .count(); + + if (localAccessNum + localPlusAccessNum > 0) { + log.warn("当前未检测到 SpringWeb 环境,无法开启本地存储平台的本地访问功能,请将关闭本地访问来消除此警告"); + } + } + + return builder.build(); + } + + /** + * 对 FileStorageService 注入自己的代理对象,不然会导致针对 FileStorageService 的代理方法不生效 + */ + @EventListener(ContextRefreshedEvent.class) + public void onContextRefreshedEvent() { + FileStorageService service = applicationContext.getBean(FileStorageService.class); + service.setSelf(service); + } + + /** + * 本地存储文件访问自动配置类 + */ + @Configuration + @ConditionalOnClass(name = "org.springframework.web.servlet.config.annotation.WebMvcConfigurer") + public static class FileStorageLocalFileAccessAutoConfiguration { + @Autowired + private SpringFileStorageProperties properties; + + /** + * 配置本地存储的访问地址 + */ + @Bean + public WebMvcConfigurer fileStorageWebMvcConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) { + for (SpringFileStorageProperties.SpringLocalConfig local : properties.getLocal()) { + if (local.getEnableStorage() && local.getEnableAccess()) { + registry.addResourceHandler(local.getPathPatterns()) + .addResourceLocations("file:" + local.getBasePath()); + } + } + for (SpringFileStorageProperties.SpringLocalPlusConfig local : properties.getLocalPlus()) { + if (local.getEnableStorage() && local.getEnableAccess()) { + registry.addResourceHandler(local.getPathPatterns()) + .addResourceLocations("file:" + local.getStoragePath()); + } + } + } + }; + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/SpringFileStorageProperties.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/SpringFileStorageProperties.java new file mode 100644 index 0000000..0f31e0d --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/config/SpringFileStorageProperties.java @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.dromara.x.file.storage.core.FileStorageProperties; +import org.dromara.x.file.storage.core.FileStorageProperties.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Data +@Accessors(chain = true) +@Component +@ConditionalOnMissingBean(SpringFileStorageProperties.class) +@ConfigurationProperties(prefix = "langchat.oss") +public class SpringFileStorageProperties { + + /** + * 默认存储平台 + */ + private String defaultPlatform = "local"; + /** + * 缩略图后缀,例如【.min.jpg】【.png】 + */ + private String thumbnailSuffix = ".min.jpg"; + /** + * 上传时不支持元数据时抛出异常 + */ + private Boolean uploadNotSupportMetadataThrowException = true; + /** + * 上传时不支持 ACL 时抛出异常 + */ + private Boolean uploadNotSupportAclThrowException = true; + /** + * 复制时不支持元数据时抛出异常 + */ + private Boolean copyNotSupportMetadataThrowException = true; + /** + * 复制时不支持 ACL 时抛出异常 + */ + private Boolean copyNotSupportAclThrowException = true; + /** + * 移动时不支持元数据时抛出异常 + */ + private Boolean moveNotSupportMetadataThrowException = true; + /** + * 移动时不支持 ACL 时抛出异常 + */ + private Boolean moveNotSupportAclThrowException = true; + /** + * 启用 byte[] 文件包装适配器 + */ + private Boolean enableByteFileWrapper = true; + /** + * 启用 URI 文件包装适配器,包含 URL 和 String + */ + private Boolean enableUriFileWrapper = true; + /** + * 启用 InputStream 文件包装适配器 + */ + private Boolean enableInputStreamFileWrapper = true; + /** + * 启用本地文件包装适配器 + */ + private Boolean enableLocalFileWrapper = true; + /** + * 启用 HttpServletRequest 文件包装适配器 + */ + private Boolean enableHttpServletRequestFileWrapper = true; + /** + * 启用 MultipartFile 文件包装适配器 + */ + private Boolean enableMultipartFileWrapper = true; + /** + * 本地存储 + */ + @Deprecated + private List local = new ArrayList<>(); + /** + * 本地存储 + */ + private List localPlus = new ArrayList<>(); + /** + * 华为云 OBS + */ + private List huaweiObs = new ArrayList<>(); + /** + * 阿里云 OSS + */ + private List aliyunOss = new ArrayList<>(); + /** + * 七牛云 Kodo + */ + private List qiniuKodo = new ArrayList<>(); + /** + * 腾讯云 COS + */ + private List tencentCos = new ArrayList<>(); + /** + * 百度云 BOS + */ + private List baiduBos = new ArrayList<>(); + /** + * 又拍云 USS + */ + private List upyunUss = new ArrayList<>(); + /** + * MinIO USS + */ + private List minio = new ArrayList<>(); + + /** + * Amazon S3 + */ + private List amazonS3 = new ArrayList<>(); + + /** + * FTP + */ + private List ftp = new ArrayList<>(); + + /** + * FTP + */ + private List sftp = new ArrayList<>(); + + /** + * WebDAV + */ + private List webdav = new ArrayList<>(); + + /** + * GoogleCloud Storage + */ + private List googleCloudStorage = new ArrayList<>(); + + /** + * FastDFS + */ + private List fastdfs = new ArrayList<>(); + + /** + * Azure Blob Storage + */ + private List azureBlob = new ArrayList<>(); + + /** + * 转换成 FileStorageProperties ,并过滤掉没有启用的存储平台 + */ + public FileStorageProperties toFileStorageProperties() { + FileStorageProperties properties = new FileStorageProperties(); + properties.setDefaultPlatform(defaultPlatform); + properties.setThumbnailSuffix(thumbnailSuffix); + properties.setUploadNotSupportMetadataThrowException(uploadNotSupportMetadataThrowException); + properties.setUploadNotSupportAclThrowException(uploadNotSupportAclThrowException); + properties.setCopyNotSupportMetadataThrowException(copyNotSupportMetadataThrowException); + properties.setCopyNotSupportAclThrowException(copyNotSupportAclThrowException); + properties.setMoveNotSupportMetadataThrowException(moveNotSupportMetadataThrowException); + properties.setMoveNotSupportAclThrowException(moveNotSupportAclThrowException); + properties.setLocal( + local.stream().filter(SpringLocalConfig::getEnableStorage).collect(Collectors.toList())); + properties.setLocalPlus(localPlus.stream() + .filter(SpringLocalPlusConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setHuaweiObs(huaweiObs.stream() + .filter(SpringHuaweiObsConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setAliyunOss(aliyunOss.stream() + .filter(SpringAliyunOssConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setQiniuKodo(qiniuKodo.stream() + .filter(SpringQiniuKodoConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setTencentCos(tencentCos.stream() + .filter(SpringTencentCosConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setBaiduBos( + baiduBos.stream().filter(SpringBaiduBosConfig::getEnableStorage).collect(Collectors.toList())); + properties.setUpyunUss( + upyunUss.stream().filter(SpringUpyunUssConfig::getEnableStorage).collect(Collectors.toList())); + properties.setMinio( + minio.stream().filter(SpringMinioConfig::getEnableStorage).collect(Collectors.toList())); + properties.setAmazonS3( + amazonS3.stream().filter(SpringAmazonS3Config::getEnableStorage).collect(Collectors.toList())); + properties.setFtp(ftp.stream().filter(SpringFtpConfig::getEnableStorage).collect(Collectors.toList())); + properties.setSftp( + sftp.stream().filter(SpringSftpConfig::getEnableStorage).collect(Collectors.toList())); + properties.setWebdav( + webdav.stream().filter(SpringWebDavConfig::getEnableStorage).collect(Collectors.toList())); + properties.setGoogleCloudStorage(googleCloudStorage.stream() + .filter(SpringGoogleCloudStorageConfig::getEnableStorage) + .collect(Collectors.toList())); + properties.setFastdfs( + fastdfs.stream().filter(SpringFastDfsConfig::getEnableStorage).collect(Collectors.toList())); + properties.setAzureBlob(azureBlob.stream() + .filter(SpringAzureBlobStorageConfig::getEnableStorage) + .collect(Collectors.toList())); + + return properties; + } + + /** + * 本地存储 + */ + @Deprecated + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringLocalConfig extends LocalConfig { + /** + * 本地存储访问路径 + */ + private String[] pathPatterns = new String[0]; + /** + * 启用本地存储 + */ + private Boolean enableStorage = false; + /** + * 启用本地访问 + */ + private Boolean enableAccess = false; + } + + /** + * 本地存储升级版 + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringLocalPlusConfig extends LocalPlusConfig { + /** + * 本地存储访问路径 + */ + private String[] pathPatterns = new String[0]; + /** + * 启用本地存储 + */ + private Boolean enableStorage = false; + /** + * 启用本地访问 + */ + private Boolean enableAccess = false; + } + + /** + * 华为云 OBS + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringHuaweiObsConfig extends HuaweiObsConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * 阿里云 OSS + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringAliyunOssConfig extends AliyunOssConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * 七牛云 Kodo + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringQiniuKodoConfig extends QiniuKodoConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * 腾讯云 COS + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringTencentCosConfig extends TencentCosConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * 百度云 BOS + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringBaiduBosConfig extends BaiduBosConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * 又拍云 USS + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringUpyunUssConfig extends UpyunUssConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * MinIO + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringMinioConfig extends MinioConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * Amazon S3 + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringAmazonS3Config extends AmazonS3Config { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * FTP + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringFtpConfig extends FtpConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * SFTP + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringSftpConfig extends SftpConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * WebDAV + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringWebDavConfig extends WebDavConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * GoogleCloud Storage + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringGoogleCloudStorageConfig extends GoogleCloudStorageConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * FastDFS Storage + * @author XS + * @date 2023/10/23 + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringFastDfsConfig extends FastDfsConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } + + /** + * AzureBlob Storage + */ + @Data + @Accessors(chain = true) + @EqualsAndHashCode(callSuper = true) + public static class SpringAzureBlobStorageConfig extends AzureBlobStorageConfig { + /** + * 启用存储 + */ + private Boolean enableStorage = false; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/constant/CommonConst.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/constant/CommonConst.java new file mode 100644 index 0000000..362f16f --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/constant/CommonConst.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.constant; + +/** + * @author tycoding + * @since 2024/1/15 + */ +public interface CommonConst { + + /** + * UTF-8 编码 + */ + String UTF_8 = "utf-8"; + + /** + * 菜单类型:menu + */ + String MENU_TYPE_MENU = "menu"; + + /** + * 菜单类型:button + */ + String MENU_TYPE_BUTTON = "button"; + + /** + * 菜单:默认Icon图标 + */ + String MENU_ICON = "alert"; + + String LAYOUT = "Layout"; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcAppController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcAppController.java new file mode 100644 index 0000000..1fec977 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcAppController.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + +import cn.hutool.core.lang.Dict; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import cn.iocoder.yudao.module.langchat.entity.AigcAppApi; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppApiService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.store.AppStore; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/aigc/app") +public class AigcAppController { + + private final AigcAppService aigcAppService; + private final AigcAppApiService aigcAppApiService; + private final AppStore appStore; + private final AigcKnowledgeService knowledgeService; + + @GetMapping("/channel/api/{appId}") + @TenantIgnore + public CommonResult getApiChanel(@PathVariable String appId) { + List list = aigcAppApiService.list(Wrappers.lambdaQuery().eq(AigcAppApi::getAppId, appId)); + return CommonResult.success(list.isEmpty() ? null : list.get(0)); + } + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcApp data) { + return CommonResult.success(aigcAppService.list(data)); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult page(AigcApp data, QueryPage queryPage) { + return CommonResult.success(MybatisUtil.getData(aigcAppService.page(MybatisUtil.wrap(data, queryPage), + Wrappers.lambdaQuery() + .like(StringUtils.isNotEmpty(data.getName()), AigcApp::getName, data.getName()) + ))); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + AigcApp app = aigcAppService.getById(id); + return CommonResult.success(app); + } + + @PostMapping + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:app:add')") + public CommonResult add(@RequestBody AigcApp data) { + data.setCreateTime(new Date()); + data.setSaveTime(new Date()); + aigcAppService.save(data); + appStore.init(); + return CommonResult.success(null); + } + + @PutMapping + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:app:update')") + public CommonResult update(@RequestBody AigcApp data) { + // 校验知识库是否是同纬度 + List knowledgeIds = data.getKnowledgeIds(); + if (knowledgeIds != null && !knowledgeIds.isEmpty()) { + List list = knowledgeService.list(Wrappers.lambdaQuery().in(AigcKnowledge::getId, knowledgeIds)); + Set modelIds = new HashSet<>(); + Set storeIds = new HashSet<>(); + list.forEach(know -> { + modelIds.add(know.getEmbedModelId()); + storeIds.add(know.getEmbedStoreId()); + }); + if (modelIds.size() > 1) { + return CommonResult.error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "请选择相同向量库数据源配置的知识库"); + } + if (storeIds.size() > 1) { + return CommonResult.error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "请选择相同向量模型配置的知识库"); +// throw new ServiceException("请选择相同向量模型配置的知识库"); + } + } + + data.setSaveTime(new Date()); + aigcAppService.updateById(data); + appStore.init(); + return CommonResult.success(null); + } + + @DeleteMapping("/{id}") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:app:delete')") + public CommonResult delete(@PathVariable String id) { + aigcAppService.removeById(id); + appStore.init(); + return CommonResult.success(null); + } +} \ No newline at end of file diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsController.java new file mode 100644 index 0000000..cd78165 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsController.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat/aigc/docs") +public class AigcDocsController { + + private final AigcDocsMapper docsMapper; + private final EmbeddingService embeddingService; + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcDocs data) { + return CommonResult.success(docsMapper.selectList(Wrappers.lambdaQuery().orderByDesc(AigcDocs::getCreateTime))); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult list(AigcDocs data, QueryPage queryPage) { + Page page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize()); + return CommonResult.success(MybatisUtil.getData(docsMapper.selectPage(page, Wrappers.lambdaQuery() + .eq(data.getKnowledgeId() != null, AigcDocs::getKnowledgeId, data.getKnowledgeId()) + .eq(data.getSliceStatus() != null, AigcDocs::getSliceStatus, data.getSliceStatus()) + .orderByDesc(AigcDocs::getCreateTime) + ))); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + return CommonResult.success(docsMapper.selectById(id)); + } + + @PostMapping + @ApiLog("新增文档") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:add')") + public CommonResult add(@RequestBody AigcDocs data) { + docsMapper.insert(data); + return CommonResult.success("success"); + } + + @PutMapping + @ApiLog("修改文档") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:update')") + public CommonResult update(@RequestBody AigcDocs data) { + docsMapper.updateById(data); + return CommonResult.success("success"); + } + + @DeleteMapping("/{id}") + @ApiLog("删除文档") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:delete')") + @Transactional + public CommonResult delete(@PathVariable String id) { + // 删除切面数据 + embeddingService.clearDocSlices(id); + + // 删除文档 + docsMapper.deleteById(id); + return CommonResult.success("success"); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsSliceController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsSliceController.java new file mode 100644 index 0000000..b83026b --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcDocsSliceController.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsSliceMapper; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat/aigc/docs/slice") +public class AigcDocsSliceController { + + private final AigcDocsSliceMapper docsSliceMapper; + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcDocsSlice data) { + return CommonResult.success(docsSliceMapper.selectList(Wrappers.lambdaQuery().orderByDesc(AigcDocsSlice::getCreateTime))); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult list(AigcDocsSlice data, QueryPage queryPage) { + Page page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize()); + return CommonResult.success(MybatisUtil.getData(docsSliceMapper.selectPage(page, Wrappers.lambdaQuery() + .eq(data.getKnowledgeId() != null, AigcDocsSlice::getKnowledgeId, data.getKnowledgeId()) + .eq(data.getDocsId() != null, AigcDocsSlice::getDocsId, data.getDocsId()) + .orderByDesc(AigcDocsSlice::getCreateTime) + ))); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + return CommonResult.success(docsSliceMapper.selectById(id)); + } + + @PostMapping + @ApiLog("新增切片数据") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:slice:add')") + public CommonResult add(@RequestBody AigcDocsSlice data) { + data.setCreateTime(new Date()); + docsSliceMapper.insert(data); + return CommonResult.success("success"); + } + + @PutMapping + @ApiLog("修改切片数据") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:slice:update')") + public CommonResult update(@RequestBody AigcDocsSlice data) { + docsSliceMapper.updateById(data); + return CommonResult.success("success"); + } + + @DeleteMapping("/{id}") + @ApiLog("删除切片数据") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:docs:slice:delete')") + public CommonResult delete(@PathVariable String id) { + docsSliceMapper.deleteById(id); + return CommonResult.success("success"); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcEmbedStoreController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcEmbedStoreController.java new file mode 100644 index 0000000..e976708 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcEmbedStoreController.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.component.EmbeddingRefreshEvent; +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.component.SpringContextHolder; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat/aigc/embed-store") +public class AigcEmbedStoreController { + + private final AigcEmbedStoreService embedStoreService; + private final SpringContextHolder contextHolder; + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcEmbedStore data) { + List list = embedStoreService.list(Wrappers.lambdaQuery()); + list.forEach(this::hide); + return CommonResult.success(list); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult page(AigcEmbedStore embedStore, QueryPage queryPage) { + IPage page = embedStoreService.page(MybatisUtil.wrap(embedStore, queryPage), + Wrappers.lambdaQuery()); + page.getRecords().forEach(this::hide); + return CommonResult.success(MybatisUtil.getData(page)); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + AigcEmbedStore store = embedStoreService.getById(id); + hide(store); + return CommonResult.success(store); + } + + @PostMapping + @ApiLog("新增向量库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:embed-store:add')") + public CommonResult add(@RequestBody AigcEmbedStore data) { + if (StrUtil.isNotBlank(data.getPassword()) && data.getPassword().contains("*")) { + data.setPassword(null); + } + embedStoreService.save(data); + SpringContextHolder.publishEvent(new EmbeddingRefreshEvent(data)); + return CommonResult.success(data); + } + + @PutMapping + @ApiLog("修改向量库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:embed-store:update')") + public CommonResult update(@RequestBody AigcEmbedStore data) { + if (StrUtil.isNotBlank(data.getPassword()) && data.getPassword().contains("*")) { + data.setPassword(null); + } + embedStoreService.updateById(data); + SpringContextHolder.publishEvent(new EmbeddingRefreshEvent(data)); + return CommonResult.success("success"); + } + + @DeleteMapping("/{id}") + @ApiLog("删除向量库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:embed-store:delete')") + public CommonResult delete(@PathVariable String id) { + AigcEmbedStore store = embedStoreService.getById(id); + if (store != null) { + embedStoreService.removeById(id); + contextHolder.unregisterBean(id); + } + return CommonResult.success("success"); + } + + private void hide(AigcEmbedStore data) { + if (data == null || StrUtil.isBlank(data.getPassword())) { + return; + } + String key = StrUtil.hide(data.getPassword(), 0, data.getPassword().length()); + data.setPassword(key); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcKnowledgeController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcKnowledgeController.java new file mode 100644 index 0000000..8f28617 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcKnowledgeController.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper; +import cn.iocoder.yudao.module.langchat.provider.KnowledgeStoreFactory; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat/aigc/knowledge") +public class AigcKnowledgeController { + + private final AigcKnowledgeService kbService; + private final AigcDocsMapper docsMapper; + private final AigcEmbedStoreService embedStoreService; + private final AigcModelService modelService; + private final KnowledgeStoreFactory knowledgeStore; + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcKnowledge data) { + List list = kbService.list(Wrappers.lambdaQuery().orderByDesc(AigcKnowledge::getCreateTime)); + build(list); + return CommonResult.success(list); + } + + private void build(List records) { + Map> embedStoreMap = embedStoreService.list().stream().collect(Collectors.groupingBy(AigcEmbedStore::getId)); + Map> embedModelMap = modelService.list().stream().collect(Collectors.groupingBy(AigcModel::getId)); + Map> docsMap = docsMapper.selectList(Wrappers.lambdaQuery()).stream().collect(Collectors.groupingBy(AigcDocs::getKnowledgeId)); + records.forEach(item -> { + List docs = docsMap.get(item.getId()); + if (docs != null) { + item.setDocsNum(docs.size()); + item.setTotalSize(docs.stream().filter(d -> d.getSize() != null).mapToLong(AigcDocs::getSize).sum()); + } + if (item.getEmbedModelId() != null) { + List list = embedModelMap.get(item.getEmbedModelId()); + item.setEmbedModel(list == null ? null : list.get(0)); + } + if (item.getEmbedStoreId() != null) { + List list = embedStoreMap.get(item.getEmbedStoreId()); + item.setEmbedStore(list == null ? null : list.get(0)); + } + }); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult list(AigcKnowledge data, QueryPage queryPage) { + Page page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize()); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() + .like(!StrUtil.isBlank(data.getName()), AigcKnowledge::getName, data.getName()) + .orderByDesc(AigcKnowledge::getCreateTime); + Page iPage = kbService.page(page, queryWrapper); + + build(iPage.getRecords()); + + return CommonResult.success(MybatisUtil.getData(iPage)); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + AigcKnowledge knowledge = kbService.getById(id); + if (knowledge.getEmbedStoreId() != null) { + knowledge.setEmbedStore(embedStoreService.getById(knowledge.getEmbedStoreId())); + } + if (knowledge.getEmbedModelId() != null) { + knowledge.setEmbedModel(modelService.getById(knowledge.getEmbedModelId())); + } + return CommonResult.success(knowledge); + } + + @PostMapping + @ApiLog("新增知识库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:knowledge:add')") + public CommonResult add(@RequestBody AigcKnowledge data) { + data.setCreateTime(String.valueOf(System.currentTimeMillis())); + kbService.save(data); + knowledgeStore.init(); + return CommonResult.success("success"); + } + + @PutMapping + @ApiLog("更新知识库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:knowledge:update')") + public CommonResult update(@RequestBody AigcKnowledge data) { + kbService.updateById(data); + knowledgeStore.init(); + return CommonResult.success("success"); + } + + @DeleteMapping("/{id}") + @ApiLog("删除知识库") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:knowledge:delete')") + public CommonResult delete(@PathVariable String id) { + kbService.removeKnowledge(id); + knowledgeStore.init(); + return CommonResult.success("success"); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcMessageController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcMessageController.java new file mode 100644 index 0000000..3407a87 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcMessageController.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.AllArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +/** + * @author tycoding + * @since 2024/1/19 + */ +@RequestMapping("/chat/aigc/message") +@RestController +@AllArgsConstructor +public class AigcMessageController { + + private final AigcMessageService aigcMessageService; + + @GetMapping("/page") + @TenantIgnore + public CommonResult list(AigcMessage data, QueryPage queryPage) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() + .like(!StrUtil.isBlank(data.getMessage()), AigcMessage::getMessage, data.getMessage()) + .like(!StrUtil.isBlank(data.getUsername()), AigcMessage::getUsername, data.getUsername()) + .eq(!StrUtil.isBlank(data.getRole()), AigcMessage::getRole, data.getRole()) + .orderByDesc(AigcMessage::getCreateTime); + IPage iPage = aigcMessageService.page(MybatisUtil.wrap(data, queryPage), queryWrapper); + return CommonResult.success(MybatisUtil.getData(iPage)); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult getById(@PathVariable String id) { + return CommonResult.success(aigcMessageService.getById(id)); + } + + @DeleteMapping("/{id}") + @ApiLog("删除会话消息") + @TenantIgnore + @PreAuthorize("@ss.hasPermission('aigc:message:delete')") + public CommonResult del(@PathVariable String id) { + return CommonResult.success(aigcMessageService.removeById(id)); + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcModelController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcModelController.java new file mode 100644 index 0000000..352321a --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcModelController.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.component.ProviderRefreshEvent; +import cn.iocoder.yudao.module.langchat.component.SpringContextHolder; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import cn.iocoder.yudao.module.langchat.annotation.ApiLog; +import cn.iocoder.yudao.module.langchat.utils.MybatisUtil; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/chat/aigc/model") +public class AigcModelController { + + private final AigcModelService modelService; + private final SpringContextHolder contextHolder; + + @GetMapping("/list") + @TenantIgnore + public CommonResult> list(AigcModel data) { + return CommonResult.success(modelService.list(data)); + } + + @GetMapping("/page") + @TenantIgnore + public CommonResult list(AigcModel data, QueryPage queryPage) { + Page iPage = modelService.page(data, queryPage); + return CommonResult.success(MybatisUtil.getData(iPage)); + } + + @GetMapping("/{id}") + @TenantIgnore + public CommonResult findById(@PathVariable String id) { + return CommonResult.success(modelService.selectById(id)); + } + + @PostMapping + @ApiLog("添加模型") + @PreAuthorize("@ss.hasPermission('aigc:model:add')") + @TenantIgnore + public CommonResult add(@RequestBody AigcModel data) { + if (StrUtil.isNotBlank(data.getApiKey()) && data.getApiKey().contains("*")) { + data.setApiKey(null); + } + if (StrUtil.isNotBlank(data.getSecretKey()) && data.getSecretKey().contains("*")) { + data.setSecretKey(null); + } + modelService.save(data); + SpringContextHolder.publishEvent(new ProviderRefreshEvent(data)); + return CommonResult.success("success"); + } + + @PutMapping + @ApiLog("修改模型") + @PreAuthorize("@ss.hasPermission('aigc:model:update')") + @TenantIgnore + public CommonResult update(@RequestBody AigcModel data) { + if (StrUtil.isNotBlank(data.getApiKey()) && data.getApiKey().contains("*")) { + data.setApiKey(null); + } + if (StrUtil.isNotBlank(data.getSecretKey()) && data.getSecretKey().contains("*")) { + data.setSecretKey(null); + } + modelService.updateById(data); + SpringContextHolder.publishEvent(new ProviderRefreshEvent(data)); + return CommonResult.success("success"); + } + + @DeleteMapping("/{id}") + @ApiLog("删除模型") + @PreAuthorize("@ss.hasPermission('aigc:model:delete')") + @TenantIgnore + public CommonResult delete(@PathVariable String id) { + modelService.removeById(id); + + // Delete dynamically registered beans, according to ID + contextHolder.unregisterBean(id); + return CommonResult.success("success"); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcOssController.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcOssController.java new file mode 100644 index 0000000..dbc7a3a --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/aigc/AigcOssController.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.aigc; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/1/19 + */ +@RequestMapping("/aigc/oss") +@RestController +@AllArgsConstructor +public class AigcOssController { + + private final AigcOssService aigcOssService; + + @GetMapping("/list") + public CommonResult list() { + List list = aigcOssService.list(Wrappers.lambdaQuery() + .eq(AigcOss::getUserId, SecurityFrameworkUtils.getLoginUserId()) + .orderByDesc(AigcOss::getCreateTime) + ); + return CommonResult.success(null); + } + + @PostMapping("/upload") + public CommonResult upload(MultipartFile file) { + return CommonResult.success(aigcOssService.upload(file, String.valueOf(SecurityFrameworkUtils.getLoginUserId()))); + } + + @PutMapping + public CommonResult update(@RequestBody AigcOss data) { + aigcOssService.updateById(data); + return CommonResult.success(null); + } + + @DeleteMapping("/{id}") + public CommonResult delete(@PathVariable String id) { + aigcOssService.removeById(id); + return CommonResult.success(null); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/ChatEndpoint.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/ChatEndpoint.java new file mode 100644 index 0000000..a94b8f3 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/ChatEndpoint.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.endpoint; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatRes; +import cn.iocoder.yudao.module.langchat.dal.dto.ImageR; +import cn.iocoder.yudao.module.langchat.dal.dto.PromptConst; +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.RoleEnum; +import cn.iocoder.yudao.module.langchat.properties.ChatProps; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import cn.iocoder.yudao.module.langchat.service.core.ChatService; +import cn.iocoder.yudao.module.langchat.service.core.impl.PersistentChatMemoryStore; +import cn.iocoder.yudao.module.langchat.utils.PromptUtil; +import cn.iocoder.yudao.module.langchat.utils.StreamEmitter; +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.data.message.ChatMessage; +import dev.langchain4j.data.message.SystemMessage; +import dev.langchain4j.data.message.UserMessage; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author tycoding + * @since 2024/1/30 + */ +@Slf4j +@RequestMapping("/aigc") +@RestController +@AllArgsConstructor +public class ChatEndpoint { + + private final ChatService chatService; + private final AigcMessageService messageService; + private final AigcModelService aigcModelService; + private final AigcAppService appService; + private final ChatProps chatProps; + + @PostMapping("/chat/completions") + public SseEmitter chat(@RequestBody ChatReq req) { + StreamEmitter emitter = new StreamEmitter(); + req.setEmitter(emitter); + + req.setUserId(String.valueOf(SecurityFrameworkUtils.getLoginUserId())); + req.setUsername(SecurityFrameworkUtils.getLoginUserNickname()); + ExecutorService executor = Executors.newSingleThreadExecutor(); + req.setExecutor(executor); + return emitter.streaming(executor, () -> { + chatService.chat(req); + }); + } + + @GetMapping("/app/info") + public CommonResult appInfo(@RequestParam String appId, String conversationId) { + AigcApp app = appService.getById(appId); + if (StrUtil.isBlank(conversationId)) { + conversationId = app.getId(); + } + + if (StrUtil.isNotBlank(app.getPrompt())) { + // initialize chat memory + SystemMessage message = new SystemMessage(app.getPrompt()); + PersistentChatMemoryStore.init(conversationId, message); + } + + return CommonResult.success(app); + } + + @GetMapping("/chat/messages/{conversationId}") + public CommonResult messages(@PathVariable String conversationId) { + List list = messageService.getMessages(conversationId, String.valueOf(SecurityFrameworkUtils.getLoginUserId())); + + // initialize chat memory + List chatMessages = new ArrayList<>(); + list.forEach(item -> { + if (chatMessages.size() >= chatProps.getMemoryMaxMessage()) { + return; + } + if (item.getRole().equals(RoleEnum.ASSISTANT.getName())) { + chatMessages.add(new AiMessage(item.getMessage())); + } else { + chatMessages.add(new UserMessage(item.getMessage())); + } + }); + PersistentChatMemoryStore.init(conversationId, chatMessages); + return CommonResult.success(list); + } + + @DeleteMapping("/chat/messages/clean/{conversationId}") + public CommonResult cleanMessage(@PathVariable String conversationId) { + messageService.clearMessage(conversationId); + + // clean chat memory + PersistentChatMemoryStore.clean(conversationId); + return CommonResult.success(null); + } + + @PostMapping("/chat/mindmap") + public CommonResult mindmap(@RequestBody ChatReq req) { + req.setPrompt(PromptUtil.build(req.getMessage(), PromptConst.MINDMAP)); + return CommonResult.success(new ChatRes(chatService.text(req))); + } + + @PostMapping("/chat/image") + public CommonResult image(@RequestBody ImageR req) { + req.setPrompt(PromptUtil.build(req.getMessage(), PromptConst.IMAGE)); + return CommonResult.success(chatService.image(req)); + } + + @GetMapping("/chat/getImageModels") + public CommonResult> getImageModels() { + List list = aigcModelService.getImageModels(); + list.forEach(i -> { + i.setApiKey(null); + i.setSecretKey(null); + }); + return CommonResult.success(list); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/EmbeddingEndpoint.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/EmbeddingEndpoint.java new file mode 100644 index 0000000..5930f2b --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/controller/admin/endpoint/EmbeddingEndpoint.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.controller.admin.endpoint; + +import ch.qos.logback.core.net.LoginAuthenticator; +import cn.hutool.core.util.StrUtil; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR; +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import cn.iocoder.yudao.module.langchat.enums.EmbedConst; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService; +import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService; +import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService; +import cn.iocoder.yudao.module.langchat.task.TaskManager; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.concurrent.Executors; + +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * @author tycoding + * @since 2024/4/25 + */ +@Slf4j +@RestController +@AllArgsConstructor +@RequestMapping("/chat/aigc/embedding") +public class EmbeddingEndpoint { + + private final LangEmbeddingService langEmbeddingService; + private final AigcKnowledgeService aigcKnowledgeService; + private final AigcDocsMapper aigcDocsMapper; + private final AigcOssService aigcOssService; + private final EmbeddingService embeddingService; + + @PostMapping("/text") + @PreAuthorize("@ss.hasPermission('aigc:embedding:text')") + @TenantIgnore + public CommonResult text(@RequestBody AigcDocs data) { + if (StrUtil.isBlankIfStr(data.getContent())) { + throw new ServiceException(-1003,"文档内容不能为空"); + } + if (StrUtil.isBlank(data.getId())) { + aigcKnowledgeService.addDocs(data); + } + data.setType(EmbedConst.ORIGIN_TYPE_INPUT).setSliceStatus(false); + + try { + EmbeddingR embeddingR = langEmbeddingService.embeddingText( + new ChatReq().setMessage(data.getContent()) + .setDocsName(data.getType()) + .setDocsId(data.getId()) + .setKnowledgeId(data.getKnowledgeId())); + + aigcKnowledgeService.addDocsSlice(new AigcDocsSlice() + .setKnowledgeId(data.getKnowledgeId()) + .setDocsId(data.getId()) + .setVectorId(embeddingR.getVectorId()) + .setName(data.getName()) + .setContent(embeddingR.getText()) + ); + + aigcKnowledgeService.updateDocs(new AigcDocs().setId(data.getId()).setSliceStatus(true).setSliceNum(1)); + } catch (Exception e) { + e.printStackTrace(); + + // del data + aigcKnowledgeService.removeSlicesOfDoc(data.getId()); + } + return CommonResult.success("success"); + } + + @PostMapping("/docs/{knowledgeId}") + @PreAuthorize("@ss.hasPermission('aigc:embedding:docs')") + @TenantIgnore + public CommonResult docs(MultipartFile file, @PathVariable String knowledgeId) { + // 1.1 获得用户信息 + String userId = getLoginUserId().toString(); + AigcOss oss = aigcOssService.upload(file, userId); + AigcDocs data = new AigcDocs() + .setName(oss.getOriginalFilename()) + .setSliceStatus(false) + .setUrl(oss.getUrl()) + .setSize(file.getSize()) + .setType(EmbedConst.ORIGIN_TYPE_UPLOAD) + .setKnowledgeId(knowledgeId); + aigcKnowledgeService.addDocs(data); + TaskManager.submitTask(userId, Executors.callable(() -> { + embeddingService.embedDocsSlice(data, oss.getUrl()); + })); + return CommonResult.success("success"); + } + + @GetMapping("/re-embed/{docsId}") + @TenantIgnore + public CommonResult reEmbed(@PathVariable String docsId) { + String userId = getLoginUserId().toString(); + AigcDocs docs = aigcDocsMapper.selectById(docsId); + if (docs == null) { + throw new ServiceException(-1003,"没有查询到文档数据"); + } + if (EmbedConst.ORIGIN_TYPE_INPUT.equals(docs.getType())) { + text(docs); + } + if (EmbedConst.ORIGIN_TYPE_UPLOAD.equals(docs.getType())) { + // clear before re-embed + embeddingService.clearDocSlices(docsId); + TaskManager.submitTask(userId, Executors.callable(() -> { + embeddingService.embedDocsSlice(docs, docs.getUrl()); + })); + } + return CommonResult.success("success"); + } + + @PostMapping("/search") + @TenantIgnore + public CommonResult search(@RequestBody AigcDocs data) { + return CommonResult.success(embeddingService.search(data)); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatReq.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatReq.java new file mode 100644 index 0000000..3006e34 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatReq.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.dal.dto; + + +import cn.iocoder.yudao.module.langchat.utils.StreamEmitter; +import dev.langchain4j.model.input.Prompt; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * @author tycoding + * @since 2024/1/30 + */ +@Data +@Accessors(chain = true) +public class ChatReq { + + private String appId; + private String modelId; + private String modelName; + private String modelProvider; + + private String message; + + private String conversationId; + + private String userId; + + private String username; + + private String chatId; + + private String promptText; + + private String docsName; + + private String knowledgeId; + private List knowledgeIds = new ArrayList<>(); + + private String docsId; + + private String url; + + private String role; + + private Prompt prompt; + + private StreamEmitter emitter; + private Executor executor; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatRes.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatRes.java new file mode 100644 index 0000000..f77fca4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ChatRes.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.dal.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author tycoding + * @since 2024/1/29 + */ +@Data +@Accessors(chain = true) +public class ChatRes { + + private boolean isDone = false; + + private String message; + + private Integer usedToken; + + private long time; + + public ChatRes(String message) { + this.message = message; + } + + public ChatRes(Integer usedToken, long startTime) { + this.isDone = true; + this.usedToken = usedToken; + this.time = System.currentTimeMillis() - startTime; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/EmbeddingR.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/EmbeddingR.java new file mode 100644 index 0000000..afff8c3 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/EmbeddingR.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.dal.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author tycoding + * @since 2024/4/26 + */ +@Data +@Accessors(chain = true) +public class EmbeddingR { + + /** + * 写入到vector store的ID + */ + private String vectorId; + + /** + * 文档ID + */ + private String docsId; + + /** + * 知识库ID + */ + private String knowledgeId; + + /** + * Embedding后切片的文本 + */ + private String text; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ImageR.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ImageR.java new file mode 100644 index 0000000..433ee50 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/ImageR.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.dal.dto; + +import dev.langchain4j.model.input.Prompt; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author tycoding + * @since 2024/1/6 + */ +@Data +@Accessors(chain = true) +public class ImageR { + + private String modelId; + private String modelName; + private String modelProvider; + + private Prompt prompt; + + /** + * 内容 + */ + private String message; + + /** + * 质量 + */ + private String quality; + + /** + * 尺寸 + */ + private String size; + + /** + * 风格 + */ + private String style; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/PromptConst.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/PromptConst.java new file mode 100644 index 0000000..30439a9 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/dal/dto/PromptConst.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.dal.dto; + +/** + * @author tycoding + * @since 2024/3/1 + */ +public interface PromptConst { + + String QUESTION = "question"; + + String EMPTY = """ + + ------ + {{question}} + ------ + """; + + String DOCUMENT = "You are good at analyzing documents. Please analyze my questions according to the following documents, question: [{{question}}], [docs]"; + + String MINDMAP = """ + # Role + You are a Markdown outline format engineer who focuses on answering user questions. You can quickly and accurately convert user questions into refined Markdown outline titles, and refine the specific details of each title. + + ## Skills + ### Skill 1: Identify user question intent + - Accurately understand the specific content and needs of user questions. + ### Skill 2: Convert to Markdown outline + - Simplify user questions into Markdown outline-style titles. + ### Skill 3: Return to user + - Return the optimized outline to the user. + + ## Constraints + - Only return the organized Markdown format content, without other explanation information + - Answer the question in the language used by the user. + - Return the answer in Markdown style, keep the main title as concise as possible; and refine the specific step information of each main title in the subtitle. + """; + + String WRITE = """ + # 角色 + 你是一名专业文案撰写师。你擅长运用行业领域相关知识,以专业的视角为用户生成Markdown文档。 + + ## 技能 + ### 技能 1: 写作 + - 提取用户输入的主题和关键信息。 + + ### 技能 2: 专业知识应用 + - 了解相关行业的相关知识。 + - 在撰写内容时,运用专业的语言和视角。 + + ### 技能 3: 遵循Markdown格式 + - 拆分文档内容,以Markdown大纲格式分段内容,更易于用户阅读 + + ## 限制 + - 只讨论写作相关的话题,不要返回其他任何内容和解释。 + - 始终以用户输入的信息为主题,撰写内容。 + """; + + String IMAGE = """ + Please generate the corresponding pictures according to the following requirements. + """; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcApp.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcApp.java new file mode 100644 index 0000000..59b3943 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcApp.java @@ -0,0 +1,80 @@ + +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.Data; +import lombok.experimental.Accessors; +import org.apache.ibatis.type.JdbcType; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@Data +@TableName(autoResultMap = true) +@Accessors(chain = true) +public class AigcApp implements Serializable { + private static final long serialVersionUID = -94917153262781949L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + private String modelId; + + @TableField(typeHandler = FastjsonTypeHandler.class, jdbcType = JdbcType.VARCHAR) + private List knowledgeIds; + + /** + * 名称 + */ + private String name; + private String cover; + + /** + * Prompt + */ + private String prompt; + + /** + * 应用描述 + */ + private String des; + + /** + * 创建时间 + */ + private Date saveTime; + private Date createTime; + + @TableField(exist = false) + private AigcModel model; + @TableField(exist = false) + private List knowledges = new ArrayList<>(); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcAppApi.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcAppApi.java new file mode 100644 index 0000000..2911629 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcAppApi.java @@ -0,0 +1,50 @@ + +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@Data +@Accessors(chain = true) +public class AigcAppApi implements Serializable { + private static final long serialVersionUID = -94917153262781949L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + private String appId; + private String apiKey; + private String channel; + private Date createTime; + + @TableField(exist = false) + private AigcApp app; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcConversation.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcConversation.java new file mode 100644 index 0000000..7f3d945 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcConversation.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Data +@Accessors(chain = true) +public class AigcConversation implements Serializable { + + private static final long serialVersionUID = -19545329638997333L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 提示词ID + */ + private String promptId; + + /** + * 用户ID + */ + private String userId; + + /** + * 会话标题 + */ + private String title; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 用户名 + */ + @TableField(exist = false) + private String username; + + /** + * 对话次数 + */ + @TableField(exist = false) + private Integer chatTotal; + /** + * Token消耗量 + */ + @TableField(exist = false) + private Integer tokenUsed; + /** + * 最后一次对话时间 + */ + @TableField(exist = false) + private Date endTime; +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocs.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocs.java new file mode 100644 index 0000000..15d107f --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocs.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +public class AigcDocs implements Serializable { + private static final long serialVersionUID = 548724967827903685L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 知识库ID + */ + private String knowledgeId; + + /** + * 名称 + */ + private String name; + + /** + * 类型 + */ + private String type; + + /** + * 来源 + */ + private String origin; + private String url; + + /** + * 文件大小 + */ + private Long size; + + /** + * 切片数量 + */ + private Integer sliceNum; + + /** + * 切片状态 + */ + private Boolean sliceStatus; + + /** + * 文档内容 + */ + private String content; + + /** + * 创建时间 + */ + private Date createTime; + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocsSlice.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocsSlice.java new file mode 100644 index 0000000..ef72ec4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcDocsSlice.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +public class AigcDocsSlice implements Serializable { + private static final long serialVersionUID = -3093489071059867065L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 向量库的ID + */ + private String vectorId; + + /** + * 文档ID + */ + private String docsId; + + /** + * 知识库ID + */ + private String knowledgeId; + + /** + * 文档名称 + */ + private String name; + + /** + * 切片内容 + */ + private String content; + + /** + * 字符数量 + */ + private Integer wordNum; + + /** + * 是否Embedding + */ + private Boolean status = false; + + /** + * 创建时间 + */ + private Date createTime; +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcEmbedStore.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcEmbedStore.java new file mode 100644 index 0000000..3e54664 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcEmbedStore.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author tycoding + * @since 2024/10/28 + */ +@Data +@TableName(autoResultMap = true) +public class AigcEmbedStore implements Serializable { + private static final long serialVersionUID = 548724967827903685L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + private String name; + + private String provider; + private String host; + private Integer port; + private String username; + private String password; + private String databaseName; + private String tableName; + private Integer dimension; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcKnowledge.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcKnowledge.java new file mode 100644 index 0000000..f745969 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcKnowledge.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Data +@TableName(autoResultMap = true) +public class AigcKnowledge implements Serializable { + private static final long serialVersionUID = 548724967827903685L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String embedStoreId; + private String embedModelId; + + /** + * 知识库名称 + */ + private String name; + + /** + * 封面 + */ + private String cover; + + /** + * 描述 + */ + private String des; + + /** + * 创建时间 + */ + private String createTime; + + @TableField(exist = false) + private Integer docsNum = 0; + @TableField(exist = false) + private Long totalSize = 0L; + @TableField(exist = false) + private List docs = new ArrayList<>(); + + @TableField(exist = false) + private AigcEmbedStore embedStore; + @TableField(exist = false) + private AigcModel embedModel; +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcMessage.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcMessage.java new file mode 100644 index 0000000..0ba2510 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcMessage.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Data +@Accessors(chain = true) +public class AigcMessage implements Serializable { + + private static final long serialVersionUID = -19545329638997333L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 消息ID + */ + private String chatId; + + /** + * 会话ID + */ + private String conversationId; + + /** + * 用户ID + */ + private String userId; + /** + * 用户名 + */ + private String username; + + /** + * 请求IP + */ + private String ip; + + private Integer tokens; + private Integer promptTokens; + + /** + * 角色,user、assistant + */ + private String role; + + /** + * 消息内容 + */ + private String model; + + /** + * 消息内容 + */ + private String message; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcModel.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcModel.java new file mode 100644 index 0000000..de79361 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcModel.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Data +@Accessors(chain = true) +public class AigcModel implements Serializable { + private static final long serialVersionUID = -19545329638997333L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String type; + private String model; + private String provider; + private String name; + private Integer responseLimit; + private Double temperature = 0.2; + private Double topP = 0.0; + private String apiKey; + private String secretKey; + private String baseUrl; + private String endpoint; + private String geminiLocation; + private String geminiProject; + private String azureDeploymentName; + private String imageSize; + private String imageQuality; + private String imageStyle; + private Integer dimension; +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcOss.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcOss.java new file mode 100644 index 0000000..66551db --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/AigcOss.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @author tycoding + * @since 2024/1/6 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +public class AigcOss extends OssR { + private static final long serialVersionUID = -250127374910520163L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 用户ID + */ + private String userId; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/OssR.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/OssR.java new file mode 100644 index 0000000..1ac1c64 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/OssR.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * OSS 文件对象 + * + * @author tycoding + * @since 2024/1/30 + */ +@Data +@Accessors(chain = true) +public class OssR implements Serializable { + private static final long serialVersionUID = 5117927170776709434L; + + private String ossId; + private String url; + private Long size; + private String filename; + private String originalFilename; + private String basePath; + private String path; + private String ext; + private String contentType; + private String platform; + private Date createTime; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysDept.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysDept.java new file mode 100644 index 0000000..5b8740a --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysDept.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 部门表(Dept)实体类 + * + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +public class SysDept implements Serializable { + private static final long serialVersionUID = -94917153262781949L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 上级部门ID + */ + private String parentId; + + /** + * 部门名称 + */ + private String name; + + /** + * 排序 + */ + private Integer orderNo; + + /** + * 描述 + */ + private String des; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysRole.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysRole.java new file mode 100644 index 0000000..d136161 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysRole.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 角色表(Role)实体类 + * + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +public class SysRole implements Serializable { + private static final long serialVersionUID = 547891924677981054L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 角色名称 + */ + private String name; + + /** + * 角色别名 + */ + private String code; + + /** + * 描述 + */ + private String des; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysUser.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysUser.java new file mode 100644 index 0000000..065e23b --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/SysUser.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户表(User)实体类 + * + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +@TableName("sys_user") +public class SysUser implements Serializable { + private static final long serialVersionUID = -94827981963832107L; + + /** + * 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 真实姓名 + */ + private String realName; + + /** + * 性别 + */ + private String sex; + + /** + * 邮箱 + */ + private String email; + + /** + * 部门ID + */ + private String deptId; + + /** + * 头像 + */ + private String avatar; + + /** + * 手机 + */ + private String phone; + + /** + * 状态 0锁定 1有效 + */ + private Boolean status; + + /** + * 创建时间 + */ + private Date createTime; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/UserInfo.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/UserInfo.java new file mode 100644 index 0000000..5acf0c1 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/entity/UserInfo.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.entity; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 自定义Oauth2 授权成功后存储的用户数据 + * + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Accessors(chain = true) +public class UserInfo extends SysUser implements Serializable { + private static final long serialVersionUID = 547891924677981054L; + + /** + * 用户所属部门 + */ + private SysDept dept; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 角色ID列表 + */ + private List roleIds; + + /** + * 用户角色列表 + */ + private List roles; + + /** + * 用户权限标识 + */ + private Set perms; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/enums/RoleEnum.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/enums/RoleEnum.java new file mode 100644 index 0000000..4aa5388 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/enums/RoleEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.enums; + +import lombok.Getter; + +/** + * @author tycoding + * @since 2024/2/20 + */ +@Getter +public enum RoleEnum { + USER("user"), + ASSISTANT("assistant"), + SYSTEM("system"), + ; + + private final String name; + + RoleEnum(String name) { + this.name = name; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/package-info.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/package-info.java new file mode 100644 index 0000000..182ad4d --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 system 模块的 framework 封装 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.langchat.framework; diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/ApiConstants.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/ApiConstants.java new file mode 100644 index 0000000..c33df65 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/ApiConstants.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.langchat.framework.security; + +import cn.iocoder.yudao.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author 芋道源码 + */ +public class ApiConstants { + + /** + * 服务名 + * + * 注意,需要保证和 spring.application.name 保持一致 + */ + public static final String NAME = "langChat-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/langChat"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/config/SecurityConfiguration.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..d4ecb65 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.langchat.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.langchat.framework.security.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * System 模块的 Security 配置 + */ +@Configuration(proxyBeanMethods = false, value = "systemSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("systemAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 + // Swagger 接口文档 + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // Spring Boot Actuator 的安全配置 + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/core/package-info.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/core/package-info.java new file mode 100644 index 0000000..efe5cd9 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.langchat.framework.security.core; diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppApiMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppApiMapper.java new file mode 100644 index 0000000..3b76290 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppApiMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcAppApi; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@Mapper +public interface AigcAppApiMapper extends BaseMapper { +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppMapper.java new file mode 100644 index 0000000..d8c1b64 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcAppMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@Mapper +public interface AigcAppMapper extends BaseMapper { +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcConversationMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcConversationMapper.java new file mode 100644 index 0000000..516393a --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcConversationMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcConversation; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Mapper +public interface AigcConversationMapper extends BaseMapper { + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsMapper.java new file mode 100644 index 0000000..e7d0ad0 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Mapper +public interface AigcDocsMapper extends BaseMapper { + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsSliceMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsSliceMapper.java new file mode 100644 index 0000000..9be517b --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcDocsSliceMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Mapper +public interface AigcDocsSliceMapper extends BaseMapper { + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcEmbedStoreMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcEmbedStoreMapper.java new file mode 100644 index 0000000..d7e913c --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcEmbedStoreMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/10/28 + */ +@Mapper +public interface AigcEmbedStoreMapper extends BaseMapper { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcKnowledgeMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcKnowledgeMapper.java new file mode 100644 index 0000000..ba8bfc4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcKnowledgeMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Mapper +public interface AigcKnowledgeMapper extends BaseMapper { + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcMessageMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcMessageMapper.java new file mode 100644 index 0000000..1b3bb49 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcMessageMapper.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.hutool.core.lang.Dict; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Mapper +public interface AigcMessageMapper extends BaseMapper { + + @Select(""" + WITH RECURSIVE DateRange AS ( + SELECT CURDATE() AS date + UNION ALL + SELECT date - INTERVAL 1 DAY + FROM DateRange + WHERE date > DATE_SUB(CURDATE(), INTERVAL 31 DAY) + ) + SELECT + d.date, + COALESCE(COUNT(*), 0) AS tokens + FROM + DateRange d + LEFT JOIN + aigc_message m + ON + DATE(m.create_time) = d.date + AND m.role = 'assistant' + GROUP BY + d.date + ORDER BY + d.date DESC; + """) + List getReqChartBy30(); + + @Select(""" + SELECT + COALESCE(DATE_FORMAT(create_time, '%Y-%m'), 0) as month, + COALESCE(COUNT(*), 0) as count + FROM + aigc_message + WHERE + create_time >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR) + AND role = 'assistant' + GROUP BY + month + ORDER BY + month ASC; + """) + List getReqChart(); + + @Select(""" + WITH RECURSIVE DateRange AS ( + SELECT CURDATE() AS date + UNION ALL + SELECT date - INTERVAL 1 DAY + FROM DateRange + WHERE date > DATE_SUB(CURDATE(), INTERVAL 31 DAY) + ) + SELECT + d.date, + COALESCE(SUM(m.tokens), 0) AS tokens + FROM + DateRange d + LEFT JOIN + aigc_message m + ON + DATE(m.create_time) = d.date + AND m.role = 'assistant' + GROUP BY + d.date + ORDER BY + d.date DESC; + """) + List getTokenChartBy30(); + + @Select(""" + SELECT + COALESCE(DATE_FORMAT(create_time, '%Y-%m'), 0) as month, + COALESCE(SUM(tokens), 0) as count + FROM + aigc_message + WHERE + create_time >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR) + AND role = 'assistant' + GROUP BY + month + ORDER BY + month ASC; + """) + List getTokenChart(); + + @Select(""" + SELECT + COALESCE(COUNT(*), 0) AS totalReq, + COALESCE(SUM( CASE WHEN DATE ( create_time ) = CURDATE() THEN 1 ELSE 0 END ), 0) AS curReq + FROM + aigc_message + WHERE + role = 'assistant' + """) + Dict getCount(); + + @Select(""" + SELECT + COALESCE(SUM(tokens), 0) AS totalToken, + COALESCE(SUM(CASE WHEN DATE(create_time) = CURDATE() THEN tokens ELSE 0 END), 0) AS curToken + FROM + aigc_message; + """) + Dict getTotalSum(); +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcModelMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcModelMapper.java new file mode 100644 index 0000000..bbb6305 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcModelMapper.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Mapper +public interface AigcModelMapper extends BaseMapper { + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcOssMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcOssMapper.java new file mode 100644 index 0000000..e21a1c9 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/AigcOssMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author tycoding + * @since 2024/1/19 + */ +@Mapper +public interface AigcOssMapper extends BaseMapper { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/SysUserMapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/SysUserMapper.java new file mode 100644 index 0000000..4bf4ee2 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/mapper/SysUserMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.mapper; + +import cn.hutool.core.lang.Dict; +import cn.iocoder.yudao.module.langchat.entity.SysUser; +import cn.iocoder.yudao.module.langchat.entity.UserInfo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +/** + * 用户表(User)表数据库访问层 + * + * @author tycoding + * @since 2024/4/15 + */ +@Mapper +public interface SysUserMapper extends BaseMapper { + + @Select(""" + SELECT + COALESCE(COUNT(*), 0) AS totalUser, + COALESCE(SUM( CASE WHEN YEAR ( create_time ) = YEAR ( CURDATE()) AND MONTH ( create_time ) = MONTH ( CURDATE()) THEN 1 ELSE 0 END ), 0) AS curUser + FROM + sys_user; + """) + Dict getCount(); + + IPage page(IPage page, UserInfo user, String ignoreId, String ignoreName); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/ChatProps.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/ChatProps.java new file mode 100644 index 0000000..7b6ee21 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/ChatProps.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author tycoding + * @since 2024/8/21 + */ +@Data +@ConfigurationProperties("langchat.chat") +public class ChatProps { + + /** + * 上下文的长度 + */ + private Integer memoryMaxMessage = 20; + + /** + * 前端渲染的消息长度(过长会导致页面渲染卡顿) + */ + private Integer previewMaxMessage = 100; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/LangChatProps.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/LangChatProps.java new file mode 100644 index 0000000..82d96c7 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/properties/LangChatProps.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Data +@Component +@ConfigurationProperties(prefix = "langchat") +public class LangChatProps { +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingProvider.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingProvider.java new file mode 100644 index 0000000..7c03110 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingProvider.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider; + +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import dev.langchain4j.data.document.DocumentSplitter; +import dev.langchain4j.data.document.splitter.DocumentSplitters; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingStore; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +/** + * @author tycoding + * @since 2024/3/8 + */ +@Slf4j +@Component +@AllArgsConstructor +public class EmbeddingProvider { + + private final EmbeddingStoreFactory embeddingStoreFactory; + private final KnowledgeStoreFactory knowledgeStoreFactory; + private final ModelStoreFactory modelStoreFactory; + + public static DocumentSplitter splitter() { + return DocumentSplitters.recursive(300, 20); + } + + public EmbeddingModel getEmbeddingModel(List knowledgeIds) { + List storeIds = new ArrayList<>(); + knowledgeIds.forEach(id -> { + if (knowledgeStoreFactory.containsKnowledge(id)) { + AigcKnowledge data = knowledgeStoreFactory.getKnowledge(id); + if (data.getEmbedModelId() != null) { + storeIds.add(data.getEmbedModelId()); + } + } + }); + if (storeIds.isEmpty()) { + throw new UnsupportedOperationException("知识库缺少Embedding Model配置,请先检查配置"); + } + + HashSet filterIds = new HashSet<>(storeIds); + if (filterIds.size() > 1) { + throw new UnsupportedOperationException("存在多个不同Embedding Model的知识库,请先检查配置"); + } + + return modelStoreFactory.getEmbeddingModel(storeIds.get(0)); + } + + public EmbeddingModel getEmbeddingModel(String knowledgeId) { + if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) { + AigcKnowledge data = knowledgeStoreFactory.getKnowledge(knowledgeId); + if (modelStoreFactory.containsEmbeddingModel(data.getEmbedModelId())) { + return modelStoreFactory.getEmbeddingModel(data.getEmbedModelId()); + } + } + throw new UnsupportedOperationException("没有找到匹配的Embedding向量数据库"); + } + + public EmbeddingStore getEmbeddingStore(String knowledgeId) { + if (knowledgeStoreFactory.containsKnowledge(knowledgeId)) { + AigcKnowledge data = knowledgeStoreFactory.getKnowledge(knowledgeId); + if (embeddingStoreFactory.containsEmbeddingStore(data.getEmbedStoreId())) { + return embeddingStoreFactory.getEmbeddingStore(data.getEmbedStoreId()); + } + } + throw new UnsupportedOperationException("没有找到匹配的Embedding向量数据库"); + } + + public EmbeddingStore getEmbeddingStore(List knowledgeIds) { + List storeIds = new ArrayList<>(); + knowledgeIds.forEach(id -> { + if (knowledgeStoreFactory.containsKnowledge(id)) { + AigcKnowledge data = knowledgeStoreFactory.getKnowledge(id); + if (data.getEmbedStoreId() != null) { + storeIds.add(data.getEmbedStoreId()); + } + } + }); + if (storeIds.isEmpty()) { + throw new UnsupportedOperationException("知识库缺少Embedding Store配置,请先检查配置"); + } + + HashSet filterIds = new HashSet<>(storeIds); + if (filterIds.size() > 1) { + throw new UnsupportedOperationException("存在多个不同Embedding Store数据源的知识库,请先检查配置"); + } + + return embeddingStoreFactory.getEmbeddingStore(storeIds.get(0)); + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingStoreFactory.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingStoreFactory.java new file mode 100644 index 0000000..a8798ff --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/EmbeddingStoreFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.enums.EmbedStoreEnum; +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService; +import dev.langchain4j.community.store.embedding.redis.RedisEmbeddingStore; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.store.embedding.EmbeddingStore; +import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore; +import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author tycoding + * @since 2024/10/28 + */ +@Slf4j +@Component +public class EmbeddingStoreFactory { + + @Autowired + private AigcEmbedStoreService aigcEmbedStoreService; + + private final List modelStore = new ArrayList<>(); + private final Map> embedStoreMap = new ConcurrentHashMap<>(); + + @Async + @PostConstruct + public void init() { + modelStore.clear(); + List list = aigcEmbedStoreService.list(); + list.forEach(embed -> { + try { + if (EmbedStoreEnum.REDIS.name().equalsIgnoreCase(embed.getProvider())) { + RedisEmbeddingStore.Builder builder = RedisEmbeddingStore.builder() + .host(embed.getHost()) + .port(embed.getPort()) + .indexName(embed.getDatabaseName()) + .dimension(embed.getDimension()); + if (StrUtil.isNotBlank(embed.getUsername()) && StrUtil.isNotBlank(embed.getPassword())) { + builder.user(embed.getUsername()).password(embed.getPassword()); + } + EmbeddingStore store = builder.build(); + embedStoreMap.put(embed.getId(), store); + } + if (EmbedStoreEnum.PGVECTOR.name().equalsIgnoreCase(embed.getProvider())) { + EmbeddingStore store = PgVectorEmbeddingStore.builder() + .host(embed.getHost()) + .port(embed.getPort()) + .database(embed.getDatabaseName()) + .dimension(embed.getDimension()) + .user(embed.getUsername()) + .password(embed.getPassword()) + .table(embed.getTableName()) + .indexListSize(1) + .useIndex(true) + .createTable(true) + .dropTableFirst(false) + .build(); + embedStoreMap.put(embed.getId(), store); + } + if (EmbedStoreEnum.MILVUS.name().equalsIgnoreCase(embed.getProvider())) { + EmbeddingStore store = MilvusEmbeddingStore.builder() + .host(embed.getHost()) + .port(embed.getPort()) + .databaseName(embed.getDatabaseName()) + .dimension(embed.getDimension()) + .username(embed.getUsername()) + .password(embed.getPassword()) + .collectionName(embed.getTableName()) + .build(); + embedStoreMap.put(embed.getId(), store); + } + modelStore.add(embed); + } catch (Exception e) { + e.printStackTrace(); + log.error("向量数据库初始化失败:[{}] --- [{}],数据库配置信息:[{}]", embed.getName(), embed.getProvider(), embed); + } + }); + + modelStore.forEach(i -> log.info("已成功注册Embedding Store:{}, 配置信息:{}", i.getProvider(), i)); + } + + public EmbeddingStore getEmbeddingStore(String embeddingId) { + return embedStoreMap.get(embeddingId); + } + + public boolean containsEmbeddingStore(String embeddingId) { + return embedStoreMap.containsKey(embeddingId); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/KnowledgeStoreFactory.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/KnowledgeStoreFactory.java new file mode 100644 index 0000000..84ad893 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/KnowledgeStoreFactory.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider; + + +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * @author tycoding + * @since 2024/10/29 + */ +@Slf4j +@Component +public class KnowledgeStoreFactory { + + @Autowired + private AigcKnowledgeService knowledgeService; + @Autowired + private AigcModelService modelService; + @Autowired + private AigcEmbedStoreService embedStoreService; + + private final Map knowledgeMap = new ConcurrentHashMap<>(); + + @Async + @PostConstruct + public void init() { + knowledgeMap.clear(); + List list = knowledgeService.list(); + Map> modelMap = modelService.list().stream().collect(Collectors.groupingBy(AigcModel::getId)); + Map> storeMap = embedStoreService.list().stream().collect(Collectors.groupingBy(AigcEmbedStore::getId)); + list.forEach(know -> { + if (know.getEmbedModelId() != null) { + List models = modelMap.get(know.getEmbedModelId()); + know.setEmbedModel(models == null ? null : models.get(0)); + } + if (know.getEmbedStoreId() != null) { + List stores = storeMap.get(know.getEmbedStoreId()); + know.setEmbedStore(stores == null ? null : stores.get(0)); + } + knowledgeMap.put(know.getId(), know); + }); + } + + public AigcKnowledge getKnowledge(String knowledgeId) { + return knowledgeMap.get(knowledgeId); + } + + public boolean containsKnowledge(String knowledgeId) { + return knowledgeMap.containsKey(knowledgeId); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelProvider.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelProvider.java new file mode 100644 index 0000000..93da7e4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider; + +import cn.hutool.core.util.ObjectUtil; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.image.ImageModel; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * @author tycoding + * @since 2024/3/8 + */ +@Component +@AllArgsConstructor +public class ModelProvider { + + private final ModelStoreFactory modelStoreFactory; + + public StreamingChatLanguageModel stream(String modelId) { + StreamingChatLanguageModel streamingChatModel = modelStoreFactory.getStreamingChatModel(modelId); + if (ObjectUtil.isNotEmpty(streamingChatModel)) { + return streamingChatModel; + } + throw new RuntimeException("没有匹配到模型,请检查模型配置!"); + } + + public ChatLanguageModel text(String modelId) { + ChatLanguageModel chatLanguageModel = modelStoreFactory.getChatLanguageModel(modelId); + if (ObjectUtil.isNotEmpty(chatLanguageModel)) { + return chatLanguageModel; + } + throw new RuntimeException("没有匹配到模型,请检查模型配置!"); + } + + public ImageModel image(String modelId) { + ImageModel imageModel = modelStoreFactory.getImageModel(modelId); + if (ObjectUtil.isNotEmpty(imageModel)) { + return imageModel; + } + throw new RuntimeException("没有匹配到模型,请检查模型配置!"); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelStoreFactory.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelStoreFactory.java new file mode 100644 index 0000000..07eeb60 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/ModelStoreFactory.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.langchat.enums.ModelConst; +import cn.iocoder.yudao.module.langchat.component.ModelTypeEnum; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.provider.build.ModelBuildHandler; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Async; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author tycoding + * @since 2024/6/16 + */ +@Configuration +@Slf4j +public class ModelStoreFactory { + + @Autowired + private AigcModelService aigcModelService; + @Autowired + private List modelBuildHandlers; + + private final List modelStore = new ArrayList<>(); + private final Map streamingChatMap = new ConcurrentHashMap<>(); + private final Map chatLanguageMap = new ConcurrentHashMap<>(); + private final Map embeddingModelMap = new ConcurrentHashMap<>(); + private final Map imageModelMap = new ConcurrentHashMap<>(); + + @Async + @PostConstruct + public void init() { + modelStore.clear(); + streamingChatMap.clear(); + chatLanguageMap.clear(); + embeddingModelMap.clear(); + imageModelMap.clear(); + + List list = aigcModelService.list(); + list.forEach(model -> { + if (Objects.equals(model.getBaseUrl(), "")) { + model.setBaseUrl(null); + } + + chatHandler(model); + embeddingHandler(model); + imageHandler(model); + }); + + modelStore.forEach(i -> log.info("已成功注册模型:{} -- {}, 模型配置:{}", i.getProvider(), i.getType(), i)); + } + + private void chatHandler(AigcModel model) { + try { + String type = model.getType(); + if (!ModelTypeEnum.CHAT.name().equals(type)) { + return; + } + modelBuildHandlers.forEach(x -> { + StreamingChatLanguageModel streamingChatLanguageModel = x.buildStreamingChat(model); + if (ObjectUtil.isNotEmpty(streamingChatLanguageModel)) { + streamingChatMap.put(model.getId(), streamingChatLanguageModel); + modelStore.add(model); + } + + ChatLanguageModel languageModel = x.buildChatLanguageModel(model); + if (ObjectUtil.isNotEmpty(languageModel)) { + chatLanguageMap.put(model.getId() + ModelConst.TEXT_SUFFIX, languageModel); + } + }); + } catch (Exception e) { + log.error("model 【 id: {} name: {}】streaming chat 配置报错", model.getId(), model.getName()); + } + } + + private void embeddingHandler(AigcModel model) { + try { + String type = model.getType(); + if (!ModelTypeEnum.EMBEDDING.name().equals(type)) { + return; + } + modelBuildHandlers.forEach(x -> { + EmbeddingModel embeddingModel = x.buildEmbedding(model); + if (ObjectUtil.isNotEmpty(embeddingModel)) { + embeddingModelMap.put(model.getId(), embeddingModel); + modelStore.add(model); + } + }); + + } catch (Exception e) { + log.error("model 【id{} name{}】 embedding 配置报错", model.getId(), model.getName()); + } + } + + private void imageHandler(AigcModel model) { + try { + String type = model.getType(); + if (!ModelTypeEnum.TEXT_IMAGE.name().equals(type)) { + return; + } + modelBuildHandlers.forEach(x -> { + ImageModel imageModel = x.buildImage(model); + if (ObjectUtil.isNotEmpty(imageModel)) { + imageModelMap.put(model.getId(), imageModel); + modelStore.add(model); + } + }); + } catch (Exception e) { + log.error("model 【id{} name{}】 image 配置报错", model.getId(), model.getName()); + } + } + + public StreamingChatLanguageModel getStreamingChatModel(String modelId) { + return streamingChatMap.get(modelId); + } + + public boolean containsStreamingChatModel(String modelId) { + return streamingChatMap.containsKey(modelId); + } + + public ChatLanguageModel getChatLanguageModel(String modelId) { + return chatLanguageMap.get(modelId + ModelConst.TEXT_SUFFIX); + } + + public boolean containsChatLanguageModel(String modelId) { + return chatLanguageMap.containsKey(modelId + ModelConst.TEXT_SUFFIX); + } + + public EmbeddingModel getEmbeddingModel(String modelId) { + return embeddingModelMap.get(modelId); + } + + public boolean containsEmbeddingModel(String modelId) { + return embeddingModelMap.containsKey(modelId); + } + + public ImageModel getImageModel(String modelId) { + return imageModelMap.get(modelId); + } + + public boolean containsImageModel(String modelId) { + return imageModelMap.containsKey(modelId); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ModelBuildHandler.java new file mode 100644 index 0000000..6501015 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ModelBuildHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import org.springframework.stereotype.Component; + +/** + * @author GB + * @since 2024-08-18 09:57 + */ +public interface ModelBuildHandler { + + /** + * 判断是不是当前模型 + */ + boolean whetherCurrentModel(AigcModel model); + + /** + * basic check + */ + boolean basicCheck(AigcModel model); + + /** + * streaming chat build + */ + StreamingChatLanguageModel buildStreamingChat(AigcModel model); + + /** + * chat build + */ + ChatLanguageModel buildChatLanguageModel(AigcModel model); + + /** + * embedding config + */ + EmbeddingModel buildEmbedding(AigcModel model); + + /** + * image config + */ + ImageModel buildImage(AigcModel model); + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OllamaModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OllamaModelBuildHandler.java new file mode 100644 index 0000000..cd17a7c --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OllamaModelBuildHandler.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum; +import cn.iocoder.yudao.module.langchat.enums.ProviderEnum; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import dev.langchain4j.model.ollama.OllamaChatModel; +import dev.langchain4j.model.ollama.OllamaEmbeddingModel; +import dev.langchain4j.model.ollama.OllamaStreamingChatModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * @author GB + * @since 2024-08-19 10:08 + */ +@Slf4j +@Component +public class OllamaModelBuildHandler implements ModelBuildHandler { + + @Override + public boolean whetherCurrentModel(AigcModel model) { + return ProviderEnum.OLLAMA.name().equals(model.getProvider()); + } + + @Override + public boolean basicCheck(AigcModel model) { + if (StringUtils.isBlank(model.getBaseUrl())) { + throw new ServiceException(ChatErrorEnum.BASE_URL_IS_NULL.getErrorCode(), + ChatErrorEnum.BASE_URL_IS_NULL.getErrorDesc(ProviderEnum.OLLAMA.name(), model.getType())); + } + return true; + } + + @Override + public StreamingChatLanguageModel buildStreamingChat(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OllamaStreamingChatModel + .builder() + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Ollama streaming chat 配置报错", e); + return null; + } + } + + @Override + public ChatLanguageModel buildChatLanguageModel(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OllamaChatModel + .builder() + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Ollama chat 配置报错", e); + return null; + } + } + + @Override + public EmbeddingModel buildEmbedding(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OllamaEmbeddingModel + .builder() + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .logRequests(true) + .logResponses(true) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Ollama embedding 配置报错", e); + return null; + } + } + + @Override + public ImageModel buildImage(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return null; + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Ollama image 配置报错", e); + return null; + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OpenAIModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OpenAIModelBuildHandler.java new file mode 100644 index 0000000..3a293ec --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/OpenAIModelBuildHandler.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum; +import cn.iocoder.yudao.module.langchat.enums.ProviderEnum; +import cn.iocoder.yudao.module.langchat.properties.LangChatProps; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.model.openai.OpenAiEmbeddingModel; +import dev.langchain4j.model.openai.OpenAiImageModel; +import dev.langchain4j.model.openai.OpenAiStreamingChatModel; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * @author tycoding + * @since 2024-08-19 10:08 + */ +@Slf4j +@Component +@AllArgsConstructor +public class OpenAIModelBuildHandler implements ModelBuildHandler { + + private final LangChatProps props; + + /** + * 合并处理支持OpenAI接口的模型 + */ + @Override + public boolean whetherCurrentModel(AigcModel model) { + String provider = model.getProvider(); + return ProviderEnum.OPENAI.name().equals(provider) || + ProviderEnum.GEMINI.name().equals(provider) || + ProviderEnum.CLAUDE.name().equals(provider) || + ProviderEnum.AZURE_OPENAI.name().equals(provider) || + ProviderEnum.DOUYIN.name().equals(provider) || + ProviderEnum.YI.name().equals(provider) || + ProviderEnum.SILICON.name().equals(provider) || + ProviderEnum.DEEPSEEK.name().equals(provider) || + ProviderEnum.SPARK.name().equals(provider) + ; + } + + @Override + public boolean basicCheck(AigcModel model) { + String apiKey = model.getApiKey(); + if (StrUtil.isBlank(apiKey)) { + throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(), + ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(model.getProvider().toUpperCase(), model.getType())); + } + return true; + } + + @Override + public StreamingChatLanguageModel buildStreamingChat(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OpenAiStreamingChatModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .maxTokens(model.getResponseLimit()) + .temperature(model.getTemperature()) + .logRequests(true) + .logResponses(true) + .topP(model.getTopP()) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error(model.getProvider() + " Streaming Chat 模型配置报错", e); + return null; + } + } + + @Override + public ChatLanguageModel buildChatLanguageModel(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OpenAiChatModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .maxTokens(model.getResponseLimit()) + .temperature(model.getTemperature()) + .logRequests(true) + .logResponses(true) + .topP(model.getTopP()) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error(model.getProvider() + " Chat 模型配置报错", e); + return null; + } + } + + @Override + public EmbeddingModel buildEmbedding(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + OpenAiEmbeddingModel openAiEmbeddingModel = OpenAiEmbeddingModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .dimensions(model.getDimension()) + .logRequests(true) + .logResponses(true) + .dimensions(1024) + .timeout(Duration.ofMinutes(10)) + .build(); + return openAiEmbeddingModel; + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error(model.getProvider() + " Embedding 模型配置报错", e); + return null; + } + } + + @Override + public ImageModel buildImage(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return OpenAiImageModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .modelName(model.getModel()) + .size(model.getImageSize()) + .quality(model.getImageQuality()) + .style(model.getImageStyle()) + .logRequests(true) + .logResponses(true) + .timeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error(model.getProvider() + " Image 模型配置报错", e); + return null; + } + + + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QFanModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QFanModelBuildHandler.java new file mode 100644 index 0000000..81cbade --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QFanModelBuildHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum; +import cn.iocoder.yudao.module.langchat.enums.ProviderEnum; +import dev.langchain4j.community.model.qianfan.QianfanChatModel; +import dev.langchain4j.community.model.qianfan.QianfanEmbeddingModel; +import dev.langchain4j.community.model.qianfan.QianfanStreamingChatModel; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +/** + * @author GB + * @since 2024-08-19 10:08 + */ +@Slf4j +@Component +public class QFanModelBuildHandler implements ModelBuildHandler { + + @Override + public boolean whetherCurrentModel(AigcModel model) { + return ProviderEnum.Q_FAN.name().equals(model.getProvider()); + } + + @Override + public boolean basicCheck(AigcModel model) { + if (StringUtils.isBlank(model.getApiKey())) { + throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(), + ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_FAN.name(), model.getType())); + } + if (StringUtils.isBlank(model.getSecretKey())) { + throw new ServiceException(ChatErrorEnum.SECRET_KEY_IS_NULL.getErrorCode(), + ChatErrorEnum.SECRET_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_FAN.name(), model.getType())); + } + return true; + } + + @Override + public StreamingChatLanguageModel buildStreamingChat(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return QianfanStreamingChatModel + .builder() + .apiKey(model.getApiKey()) + .secretKey(model.getSecretKey()) + .modelName(model.getModel()) + .baseUrl(model.getBaseUrl()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Qianfan streaming chat 配置报错", e); + return null; + } + } + + @Override + public ChatLanguageModel buildChatLanguageModel(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return QianfanChatModel + .builder() + .apiKey(model.getApiKey()) + .secretKey(model.getSecretKey()) + .modelName(model.getModel()) + .baseUrl(model.getBaseUrl()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Qianfan chat 配置报错", e); + return null; + } + + + } + + @Override + public EmbeddingModel buildEmbedding(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return QianfanEmbeddingModel + .builder() + .apiKey(model.getApiKey()) + .modelName(model.getModel()) + .secretKey(model.getSecretKey()) + .logRequests(true) + .logResponses(true) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Qianfan embedding 配置报错", e); + return null; + } + } + + @Override + public ImageModel buildImage(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return null; + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Qianfan image 配置报错", e); + return null; + } + + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QWenModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QWenModelBuildHandler.java new file mode 100644 index 0000000..038720e --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/QWenModelBuildHandler.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum; +import cn.iocoder.yudao.module.langchat.enums.ProviderEnum; +import dev.langchain4j.community.model.dashscope.QwenChatModel; +import dev.langchain4j.community.model.dashscope.QwenEmbeddingModel; +import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +/** + * @author GB + * @since 2024-08-19 10:08 + */ +@Slf4j +@Component +public class QWenModelBuildHandler implements ModelBuildHandler { + + @Override + public boolean whetherCurrentModel(AigcModel model) { + return ProviderEnum.Q_WEN.name().equals(model.getProvider()); + } + + @Override + public boolean basicCheck(AigcModel model) { + if (StringUtils.isBlank(model.getApiKey())) { + throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(), + ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.Q_WEN.name(), model.getType())); + } + return true; + } + + @Override + public StreamingChatLanguageModel buildStreamingChat(AigcModel model) { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + try { + return QwenStreamingChatModel + .builder() + .apiKey(model.getApiKey()) + .modelName(model.getModel()) + .baseUrl(model.getBaseUrl()) + .maxTokens(model.getResponseLimit()) + .temperature(Float.parseFloat(model.getTemperature().toString())) + .topP(model.getTopP()) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("qian wen streaming chat 配置报错", e); + return null; + } + } + + @Override + public ChatLanguageModel buildChatLanguageModel(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return QwenChatModel + .builder() + .apiKey(model.getApiKey()) + .modelName(model.getModel()) + .baseUrl(model.getBaseUrl()) + .enableSearch(true) + .maxTokens(model.getResponseLimit()) + .temperature(Float.parseFloat(model.getTemperature().toString())) + .topP(model.getTopP()) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("qian wen chat 配置报错", e); + return null; + } + } + + @Override + public EmbeddingModel buildEmbedding(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return QwenEmbeddingModel + .builder() + .apiKey(model.getApiKey()) + .modelName(model.getModel()) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("qian wen embedding 配置报错", e); + return null; + } + } + + @Override + public ImageModel buildImage(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return null; + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("qian wen image 配置报错", e); + return null; + } + + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ZhipuModelBuildHandler.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ZhipuModelBuildHandler.java new file mode 100644 index 0000000..e98b8ab --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/provider/build/ZhipuModelBuildHandler.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.provider.build; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.enums.ChatErrorEnum; +import cn.iocoder.yudao.module.langchat.enums.ProviderEnum; +import cn.iocoder.yudao.module.langchat.properties.LangChatProps; +import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel; +import dev.langchain4j.community.model.zhipu.ZhipuAiEmbeddingModel; +import dev.langchain4j.community.model.zhipu.ZhipuAiImageModel; +import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.image.ImageModel; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * @author GB + * @since 2024-08-19 + */ +@Slf4j +@Component +@AllArgsConstructor +public class ZhipuModelBuildHandler implements ModelBuildHandler { + + private final LangChatProps props; + + @Override + public boolean whetherCurrentModel(AigcModel model) { + return ProviderEnum.ZHIPU.name().equals(model.getProvider()); + } + + @Override + public boolean basicCheck(AigcModel model) { + if (StringUtils.isBlank(model.getApiKey())) { + throw new ServiceException(ChatErrorEnum.API_KEY_IS_NULL.getErrorCode(), + ChatErrorEnum.API_KEY_IS_NULL.getErrorDesc(ProviderEnum.ZHIPU.name(), model.getType())); + } + return true; + } + + @Override + public StreamingChatLanguageModel buildStreamingChat(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return ZhipuAiStreamingChatModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .model(model.getModel()) + .maxToken(model.getResponseLimit()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .callTimeout(Duration.ofMinutes(10)) + .connectTimeout(Duration.ofMinutes(10)) + .writeTimeout(Duration.ofMinutes(10)) + .readTimeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("zhipu streaming chat 配置报错", e); + return null; + } + + } + + @Override + public ChatLanguageModel buildChatLanguageModel(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return ZhipuAiChatModel + .builder() + .apiKey(model.getApiKey()) + .baseUrl(model.getBaseUrl()) + .model(model.getModel()) + .maxToken(model.getResponseLimit()) + .temperature(model.getTemperature()) + .topP(model.getTopP()) + .logRequests(true) + .logResponses(true) + .callTimeout(Duration.ofMinutes(10)) + .connectTimeout(Duration.ofMinutes(10)) + .writeTimeout(Duration.ofMinutes(10)) + .readTimeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("zhipu chat 配置报错", e); + return null; + } + + } + + @Override + public EmbeddingModel buildEmbedding(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return ZhipuAiEmbeddingModel + .builder() + .apiKey(model.getApiKey()) + .model(model.getModel()) + .baseUrl(model.getBaseUrl()) + .logRequests(true) + .logResponses(true) + .callTimeout(Duration.ofMinutes(10)) + .connectTimeout(Duration.ofMinutes(10)) + .writeTimeout(Duration.ofMinutes(10)) + .readTimeout(Duration.ofMinutes(10)) + .dimensions(1024) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("zhipu embedding 配置报错", e); + return null; + } + } + + @Override + public ImageModel buildImage(AigcModel model) { + try { + if (!whetherCurrentModel(model)) { + return null; + } + if (!basicCheck(model)) { + return null; + } + return ZhipuAiImageModel + .builder() + .apiKey(model.getApiKey()) + .model(model.getModel()) + .baseUrl(model.getBaseUrl()) + .logRequests(true) + .logResponses(true) + .callTimeout(Duration.ofMinutes(10)) + .connectTimeout(Duration.ofMinutes(10)) + .writeTimeout(Duration.ofMinutes(10)) + .readTimeout(Duration.ofMinutes(10)) + .build(); + } catch (ServiceException e) { + log.error(e.getMessage()); + throw e; + } catch (Exception e) { + log.error("zhipu image 配置报错", e); + return null; + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppApiService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppApiService.java new file mode 100644 index 0000000..377dc98 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppApiService.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcAppApi; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @author tycoding + * @since 2024/7/26 + */ +public interface AigcAppApiService extends IService { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppService.java new file mode 100644 index 0000000..a2bf5dc --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcAppService.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/7/26 + */ +public interface AigcAppService extends IService { + + List list(AigcApp data); + + AigcApp getById(String id); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcEmbedStoreService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcEmbedStoreService.java new file mode 100644 index 0000000..d946413 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcEmbedStoreService.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @author tycoding + * @since 2024/10/28 + */ +public interface AigcEmbedStoreService extends IService { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcKnowledgeService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcKnowledgeService.java new file mode 100644 index 0000000..40a6647 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcKnowledgeService.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/15 + */ +public interface AigcKnowledgeService extends IService { + + void addDocs(AigcDocs data); + + void updateDocs(AigcDocs data); + + void addDocsSlice(AigcDocsSlice data); + + void updateDocsSlice(AigcDocsSlice data); + + List listSliceVectorIdsOfDoc(String docsId); + + List getDocsByKb(String knowledgeId); + + void removeKnowledge(String knowledgeId); + + void removeSlicesOfDoc(String docsId); +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcMessageService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcMessageService.java new file mode 100644 index 0000000..e9a37f3 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcMessageService.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcConversation; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/1/4 + */ +public interface AigcMessageService extends IService { + + /** + * 获取会话列表 + */ + List conversations(String userId); + + /** + * 获取会话分页列表 + */ + IPage conversationPages(AigcConversation data, QueryPage queryPage); + + /** + * 新增会话 + */ + AigcConversation addConversation(AigcConversation conversation); + + /** + * 修改会话 + */ + void updateConversation(AigcConversation conversation); + + /** + * 删除会话 + */ + void delConversation(String conversationId); + + AigcMessage addMessage(AigcMessage message); + + void clearMessage(String conversationId); + + List getMessages(String conversationId); + + List getMessages(String conversationId, String userId); +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcModelService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcModelService.java new file mode 100644 index 0000000..1bd4100 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcModelService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/1/19 + */ +public interface AigcModelService extends IService { + + List getChatModels(); + + List getImageModels(); + + List getEmbeddingModels(); + + List list(AigcModel data); + + Page page(AigcModel data, QueryPage queryPage); + + AigcModel selectById(String id); +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcOssService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcOssService.java new file mode 100644 index 0000000..a323015 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/AigcOssService.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc; + +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author tycoding + * @since 2024/1/4 + */ +public interface AigcOssService extends IService { + + AigcOss upload(MultipartFile file, String userId); +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppApiServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppApiServiceImpl.java new file mode 100644 index 0000000..409f6f3 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppApiServiceImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.iocoder.yudao.module.langchat.entity.AigcAppApi; +import cn.iocoder.yudao.module.langchat.mapper.AigcAppApiMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppApiService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@Service +public class AigcAppApiServiceImpl extends ServiceImpl implements AigcAppApiService { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppServiceImpl.java new file mode 100644 index 0000000..08707a4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcAppServiceImpl.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.mapper.AigcAppMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author tycoding + * @since 2024/7/26 + */ +@RequiredArgsConstructor +@Service +public class AigcAppServiceImpl extends ServiceImpl implements AigcAppService { + + private final AigcModelService aigcModelService; + private final AigcKnowledgeService aigcKnowledgeService; + + @Override + public List list(AigcApp data) { + List list = baseMapper.selectList(Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(data.getName()), AigcApp::getName, data.getName())); + + Map> modelMap = aigcModelService.list(new AigcModel()).stream().collect(Collectors.groupingBy(AigcModel::getId)); + Map> knowledgeMap = aigcKnowledgeService.list().stream().collect(Collectors.groupingBy(AigcKnowledge::getId)); + list.forEach(i -> { + List models = modelMap.get(i.getModelId()); + if (models != null) { + i.setModel(models.get(0)); + } + if (i.getKnowledgeIds() != null) { + List knowledges = new ArrayList<>(); + i.getKnowledgeIds().forEach(k -> { + List items = knowledgeMap.get(k); + if (items != null) { + knowledges.add(items.get(0)); + } + }); + i.setKnowledges(knowledges); + } + }); + return list; + } + + @Override + public AigcApp getById(String id) { + AigcApp app = baseMapper.selectById(id); + if (app != null) { + String modelId = app.getModelId(); + if (modelId != null) { + app.setModel(aigcModelService.selectById(modelId)); + } + List knowledgeIds = app.getKnowledgeIds(); + if (knowledgeIds != null && !knowledgeIds.isEmpty()) { + app.setKnowledges(aigcKnowledgeService.list(Wrappers.lambdaQuery().in(AigcKnowledge::getId, knowledgeIds))); + } + } + return app; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcEmbedStoreServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcEmbedStoreServiceImpl.java new file mode 100644 index 0000000..0b910f5 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcEmbedStoreServiceImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.iocoder.yudao.module.langchat.entity.AigcEmbedStore; +import cn.iocoder.yudao.module.langchat.mapper.AigcEmbedStoreMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcEmbedStoreService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + * @author tycoding + * @since 2024/10/28 + */ +@Service +public class AigcEmbedStoreServiceImpl extends ServiceImpl implements AigcEmbedStoreService { + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcKnowledgeServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcKnowledgeServiceImpl.java new file mode 100644 index 0000000..af6ddef --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcKnowledgeServiceImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import cn.iocoder.yudao.module.langchat.entity.AigcKnowledge; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsSliceMapper; +import cn.iocoder.yudao.module.langchat.mapper.AigcKnowledgeMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author tycoding + * @since 2024/4/15 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AigcKnowledgeServiceImpl extends ServiceImpl implements AigcKnowledgeService { + + private final AigcDocsMapper aigcDocsMapper; + private final AigcDocsSliceMapper aigcDocsSliceMapper; + + @Override + @Transactional + public void addDocs(AigcDocs data) { + data.setCreateTime(new Date()); + aigcDocsMapper.insert(data); + } + + @Override + @Transactional + public void updateDocs(AigcDocs data) { + aigcDocsMapper.updateById(data); + } + + @Override + @Transactional + public void addDocsSlice(AigcDocsSlice data) { + data.setCreateTime(new Date()) + .setWordNum(data.getContent().length()) + .setStatus(true) + ; + aigcDocsSliceMapper.insert(data); + } + + @Override + @Transactional + public void updateDocsSlice(AigcDocsSlice data) { + aigcDocsSliceMapper.updateById(data); + } + + @Override + public List listSliceVectorIdsOfDoc(String docsId) { + LambdaQueryWrapper selectWrapper = Wrappers.lambdaQuery() + .select(AigcDocsSlice::getVectorId) + .eq(AigcDocsSlice::getDocsId, docsId); + List vectorIds = aigcDocsSliceMapper.selectList(selectWrapper) + .stream() + .map(AigcDocsSlice::getVectorId) + .collect(Collectors.toList()); + log.debug("slices of doc: [{}], count: [{}]", docsId, vectorIds.size()); + return vectorIds; + } + + @Override + public List getDocsByKb(String knowledgeId) { + return aigcDocsMapper.selectList(Wrappers.lambdaQuery() + .eq(AigcDocs::getKnowledgeId, knowledgeId)); + } + + @Override + @Transactional + public void removeKnowledge(String knowledgeId) { + baseMapper.deleteById(knowledgeId); + // del docs & docsSlice + List docsIds = getDocsByKb(knowledgeId).stream().map(AigcDocs::getId).toList(); + docsIds.forEach(this::removeSlicesOfDoc); + } + + @Override + @Transactional + public void removeSlicesOfDoc(String docsId) { + LambdaQueryWrapper deleteWrapper = Wrappers.lambdaQuery() + .eq(AigcDocsSlice::getDocsId, docsId); + int count = aigcDocsSliceMapper.delete(deleteWrapper); + log.debug("remove all slices of doc: [{}], count: [{}]", docsId, count); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcMessageServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcMessageServiceImpl.java new file mode 100644 index 0000000..c27b829 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcMessageServiceImpl.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.entity.AigcConversation; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import cn.iocoder.yudao.module.langchat.entity.SysUser; +import cn.iocoder.yudao.module.langchat.mapper.AigcConversationMapper; +import cn.iocoder.yudao.module.langchat.mapper.AigcMessageMapper; +import cn.iocoder.yudao.module.langchat.mapper.SysUserMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Service +@RequiredArgsConstructor +public class AigcMessageServiceImpl extends ServiceImpl implements + AigcMessageService { + private final AigcConversationMapper aigcConversationMapper; + private final SysUserMapper userMapper; + + @Override + public List conversations(String userId) { + return aigcConversationMapper.selectList( + Wrappers.lambdaQuery() + .eq(AigcConversation::getUserId, userId) + .orderByDesc(AigcConversation::getCreateTime)); + } + + @Override + public IPage conversationPages(AigcConversation data, QueryPage queryPage) { + Page page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize()); + Page iPage = aigcConversationMapper.selectPage(page, Wrappers.lambdaQuery() + .like(!StrUtil.isBlank(data.getTitle()), AigcConversation::getTitle, data.getTitle()) + .orderByDesc(AigcConversation::getCreateTime)); + + if (!iPage.getRecords().isEmpty()) { + Map> map = userMapper.selectList(Wrappers.lambdaQuery()).stream().collect(Collectors.groupingBy(SysUser::getId)); + Set ids = iPage.getRecords().stream().map(AigcConversation::getId).collect(Collectors.toSet()); + List messages = baseMapper.selectList(Wrappers.lambdaQuery() + .in(AigcMessage::getConversationId, ids) + .orderByDesc(AigcMessage::getCreateTime)); + + iPage.getRecords().forEach(i -> { + List list = map.get(i.getUserId()); + if (list != null && !list.isEmpty()) { + i.setUsername(list.get(0).getUsername()); + } + + List messageList = messages.stream().filter(m -> m.getConversationId() != null && m.getConversationId().equals(i.getId())).toList(); + if (!messageList.isEmpty()) { + i.setChatTotal(messageList.size()); + i.setEndTime(messageList.get(0).getCreateTime()); + i.setTokenUsed(messageList.stream().filter(m -> m.getTokens() != null).mapToInt(AigcMessage::getTokens).sum()); + } + }); + } + return iPage; + } + + @Override + @Transactional + public AigcConversation addConversation(AigcConversation conversation) { + conversation.setCreateTime(new Date()); + aigcConversationMapper.insert(conversation); + return conversation; + } + + @Override + @Transactional + public void updateConversation(AigcConversation conversation) { + aigcConversationMapper.updateById( + new AigcConversation().setId(conversation.getId()) + .setTitle(conversation.getTitle())); + } + + @Override + @Transactional + public void delConversation(String conversationId) { + aigcConversationMapper.deleteById(conversationId); + baseMapper.delete( + Wrappers.lambdaQuery() + .eq(AigcMessage::getConversationId, conversationId)); + } + + @Override + @Transactional + public AigcMessage addMessage(AigcMessage message) { + message.setCreateTime(new Date()); + baseMapper.insert(message); + return message; + } + + @Override + @Transactional + public void clearMessage(String conversationId) { + baseMapper.delete( + Wrappers.lambdaQuery() + .eq(AigcMessage::getConversationId, conversationId)); + } + + @Override + public List getMessages(String conversationId) { + // 避免页面渲染压力大,只截取最新的20条数据 + return baseMapper.selectPage(new Page<>(0, 20), Wrappers.lambdaQuery() + .eq(AigcMessage::getConversationId, conversationId) + .orderByAsc(AigcMessage::getCreateTime) + ).getRecords(); + } + + @Override + public List getMessages(String conversationId, String userId) { + // 避免页面渲染压力大,只截取最新的100条数据 + return baseMapper.selectPage(new Page<>(0, 100), Wrappers.lambdaQuery() + .eq(AigcMessage::getConversationId, conversationId) + .eq(AigcMessage::getUserId, userId) + .orderByAsc(AigcMessage::getCreateTime) + ).getRecords(); + } + +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcModelServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcModelServiceImpl.java new file mode 100644 index 0000000..4278f3f --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcModelServiceImpl.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.aigc.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.component.ModelTypeEnum; +import cn.iocoder.yudao.module.langchat.entity.AigcModel; +import cn.iocoder.yudao.module.langchat.mapper.AigcModelMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcModelService; +import cn.iocoder.yudao.module.langchat.utils.QueryPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/1/19 + */ +@Service +@RequiredArgsConstructor +public class AigcModelServiceImpl extends ServiceImpl implements AigcModelService { + + @Override + public List getChatModels() { + List list = baseMapper.selectList(Wrappers.lambdaQuery() + .eq(AigcModel::getType, ModelTypeEnum.CHAT.name())); + list.forEach(this::hide); + return list; + } + + @Override + public List getImageModels() { + List list = baseMapper.selectList(Wrappers.lambdaQuery() + .eq(AigcModel::getType, ModelTypeEnum.TEXT_IMAGE.name())); + list.forEach(this::hide); + return list; + } + + @Override + public List getEmbeddingModels() { + List list = baseMapper.selectList(Wrappers.lambdaQuery() + .eq(AigcModel::getType, ModelTypeEnum.EMBEDDING.name())); + list.forEach(this::hide); + return list; + } + + @Override + public List list(AigcModel data) { + List list = this.list(Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(data.getType()), AigcModel::getType, data.getType()) + .eq(StrUtil.isNotBlank(data.getProvider()), AigcModel::getProvider, data.getProvider())); + list.forEach(this::hide); + return list; + } + + @Override + public Page page(AigcModel data, QueryPage queryPage) { + Page page = new Page<>(queryPage.getPageNo(), queryPage.getPageSize()); + Page iPage = this.page(page, Wrappers.lambdaQuery().eq(AigcModel::getProvider, data.getProvider())); + iPage.getRecords().forEach(this::hide); + return iPage; + } + + @Override + public AigcModel selectById(String id) { + AigcModel model = this.getById(id); + hide(model); + return model; + } + + private void hide(AigcModel model) { + if (model == null || StrUtil.isBlank(model.getApiKey())) { + return; + } + String key = StrUtil.hide(model.getApiKey(), 3, model.getApiKey().length() - 4); + model.setApiKey(key); + + if (StrUtil.isBlank(model.getSecretKey())) { + return; + } + String sec = StrUtil.hide(model.getSecretKey(), 3, model.getSecretKey().length() - 4); + model.setSecretKey(sec); + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcOssServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcOssServiceImpl.java new file mode 100644 index 0000000..49ae40b --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/aigc/impl/AigcOssServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.oss.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import cn.iocoder.yudao.module.langchat.mapper.AigcOssMapper; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcOssService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.x.file.storage.core.FileInfo; +import org.dromara.x.file.storage.core.FileStorageService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Date; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AigcOssServiceImpl extends ServiceImpl implements AigcOssService { + + private final FileStorageService fileStorageService; + + @Override + public AigcOss upload(MultipartFile file, String userId) { + log.info(">>>>>>>>>>>>>> OSS文件上传开始: {}", file.getOriginalFilename()); + FileInfo info = fileStorageService.of(file) + .setPath(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN)) + .upload(); + log.info(">>>>>>>>>>>>>> OSS文件上传结束: {} - {}", info.getFilename(), info.getUrl()); + AigcOss oss = BeanUtil.copyProperties(info, AigcOss.class); + oss.setOssId(info.getId()); + oss.setUserId(userId); + this.save(oss); + return oss; + } +} + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/Agent.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/Agent.java new file mode 100644 index 0000000..cc501cd --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/Agent.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core; + +import dev.langchain4j.service.MemoryId; +import dev.langchain4j.service.TokenStream; +import dev.langchain4j.service.UserMessage; + +/** + * @author tycoding + * @since 2024/3/8 + */ +public interface Agent { + + TokenStream stream(@MemoryId String id, @UserMessage String message); + + String text(@MemoryId String id, @UserMessage String message); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/ChatService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/ChatService.java new file mode 100644 index 0000000..de253f7 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/ChatService.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core; + + +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.ImageR; +import cn.iocoder.yudao.module.langchat.entity.AigcOss; + +/** + * @author tycoding + * @since 2024/1/4 + */ +public interface ChatService { + + void chat(ChatReq req); + + + /** + * 文本请求 + */ + String text(ChatReq req); + + /** + * 文生图 + */ + AigcOss image(ImageR req); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/EmbeddingService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/EmbeddingService.java new file mode 100644 index 0000000..092be3d --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/EmbeddingService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core; + + +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; + +import java.util.List; +import java.util.Map; + +/** + * @author tycoding + * @since 2024/6/6 + */ +public interface EmbeddingService { + + void clearDocSlices(String docsId); + + void embedDocsSlice(AigcDocs data, String url); + + List> search(AigcDocs data); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangChatService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangChatService.java new file mode 100644 index 0000000..c765a21 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangChatService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core; + +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.ImageR; +import dev.langchain4j.data.image.Image; +import dev.langchain4j.model.output.Response; +import dev.langchain4j.service.TokenStream; + +/** + * @author tycoding + * @since 2024/3/8 + */ +public interface LangChatService { + + TokenStream chat(ChatReq req); + + TokenStream singleChat(ChatReq req); + + String text(ChatReq req); + + Response image(ImageR req); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangEmbeddingService.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangEmbeddingService.java new file mode 100644 index 0000000..865fb48 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/LangEmbeddingService.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core; + + +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR; + +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/4 + */ +public interface LangEmbeddingService { + + EmbeddingR embeddingText(ChatReq req); + + List embeddingDocs(ChatReq req); +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/ChatServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/ChatServiceImpl.java new file mode 100644 index 0000000..5d13fb4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/ChatServiceImpl.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatRes; +import cn.iocoder.yudao.module.langchat.dal.dto.ImageR; +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import cn.iocoder.yudao.module.langchat.entity.AigcMessage; +import cn.iocoder.yudao.module.langchat.entity.AigcOss; +import cn.iocoder.yudao.module.langchat.enums.RoleEnum; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcMessageService; +import cn.iocoder.yudao.module.langchat.service.core.ChatService; +import cn.iocoder.yudao.module.langchat.service.core.LangChatService; +import cn.iocoder.yudao.module.langchat.store.AppStore; +import cn.iocoder.yudao.module.langchat.utils.ServletUtil; +import cn.iocoder.yudao.module.langchat.utils.StreamEmitter; +import dev.langchain4j.data.image.Image; +import dev.langchain4j.model.output.Response; +import dev.langchain4j.model.output.TokenUsage; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +/** + * @author tycoding + * @since 2024/1/4 + */ +@Slf4j +@Service +@AllArgsConstructor +public class ChatServiceImpl implements ChatService { + + private final LangChatService langChatService; + private final AigcMessageService aigcMessageService; + private final AppStore appStore; + + @Override + public void chat(ChatReq req) { + StreamEmitter emitter = req.getEmitter(); + long startTime = System.currentTimeMillis(); + StringBuilder text = new StringBuilder(); + + if (StrUtil.isNotBlank(req.getAppId())) { + AigcApp app = appStore.get(req.getAppId()); + if (app != null) { + req.setModelId(app.getModelId()); + req.setPromptText(app.getPrompt()); + req.setKnowledgeIds(app.getKnowledgeIds()); + } + } + + // save user message + req.setRole(RoleEnum.USER.getName()); + saveMessage(req, 0, 0); + + try { + langChatService + .chat(req) + .onNext(e -> { + text.append(e); + emitter.send(new ChatRes(e)); + }) + .onComplete((e) -> { + TokenUsage tokenUsage = e.tokenUsage(); + ChatRes res = new ChatRes(tokenUsage.totalTokenCount(), startTime); + emitter.send(res); + emitter.complete(); + + // save assistant message + req.setMessage(text.toString()); + req.setRole(RoleEnum.ASSISTANT.getName()); + saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount()); + }) + .onError((e) -> { + emitter.error(e.getMessage()); + throw new RuntimeException(e.getMessage()); + }) + .start(); + } catch (Exception e) { + e.printStackTrace(); + emitter.error(e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + + private void saveMessage(ChatReq req, Integer inputToken, Integer outputToken) { + if (req.getConversationId() != null) { + AigcMessage message = new AigcMessage(); + BeanUtils.copyProperties(req, message); + message.setIp(ServletUtil.getIpAddr()); + message.setPromptTokens(inputToken); + message.setTokens(outputToken); + aigcMessageService.addMessage(message); + } + } + + @Override + public String text(ChatReq req) { + String text; + try { + text = langChatService.text(req); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + return text; + } + + @Override + public AigcOss image(ImageR req) { + Response res = langChatService.image(req); + + String path = res.content().url().toString(); + AigcOss oss = new AigcOss(); + oss.setUrl(path); + return oss; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/EmbeddingServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/EmbeddingServiceImpl.java new file mode 100644 index 0000000..485a3df --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/EmbeddingServiceImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR; +import cn.iocoder.yudao.module.langchat.enums.EmbedConst; +import cn.iocoder.yudao.module.langchat.entity.AigcDocs; +import cn.iocoder.yudao.module.langchat.entity.AigcDocsSlice; +import cn.iocoder.yudao.module.langchat.mapper.AigcDocsMapper; +import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcKnowledgeService; +import cn.iocoder.yudao.module.langchat.service.core.EmbeddingService; +import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService; +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingSearchRequest; +import dev.langchain4j.store.embedding.EmbeddingSearchResult; +import dev.langchain4j.store.embedding.EmbeddingStore; +import dev.langchain4j.store.embedding.filter.Filter; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey; + + +/** + * @author tycoding + * @since 2024/6/6 + */ +@Slf4j +@Service +@AllArgsConstructor +public class EmbeddingServiceImpl implements EmbeddingService { + + private final EmbeddingProvider embeddingProvider; + private final LangEmbeddingService langEmbeddingService; + private final AigcKnowledgeService aigcKnowledgeService; + private final AigcDocsMapper aigcDocsMapper; + + @Override + @Transactional + public void clearDocSlices(String docsId) { + if (StrUtil.isBlank(docsId)) { + return; + } + // remove from embedding store + List vectorIds = aigcKnowledgeService.listSliceVectorIdsOfDoc(docsId); + if (vectorIds.isEmpty()) { + return; + } + AigcDocs docs = aigcDocsMapper.selectById(docsId); + EmbeddingStore embeddingStore = embeddingProvider.getEmbeddingStore(docs.getKnowledgeId()); + embeddingStore.removeAll(vectorIds); + // remove from docSlice + aigcKnowledgeService.removeSlicesOfDoc(docsId); + } + + @Override + public void embedDocsSlice(AigcDocs data, String url) { + List list = langEmbeddingService.embeddingDocs( + new ChatReq() + .setDocsName(data.getName()) + .setKnowledgeId(data.getKnowledgeId()) + .setUrl(url)); + list.forEach(i -> { + aigcKnowledgeService.addDocsSlice(new AigcDocsSlice() + .setKnowledgeId(data.getKnowledgeId()) + .setDocsId(data.getId()) + .setVectorId(i.getVectorId()) + .setName(data.getName()) + .setContent(i.getText()) + ); + }); + + aigcKnowledgeService.updateDocs(new AigcDocs().setId(data.getId()).setSliceStatus(true).setSliceNum(list.size())); + } + + @Override + public List> search(AigcDocs data) { + if (StrUtil.isBlank(data.getKnowledgeId()) || StrUtil.isBlank(data.getContent())) { + return List.of(); + } + + EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(data.getKnowledgeId()); + EmbeddingStore embeddingStore = embeddingProvider.getEmbeddingStore(data.getKnowledgeId()); + Embedding queryEmbedding = embeddingModel.embed(data.getContent()).content(); + Filter filter = metadataKey(EmbedConst.KNOWLEDGE).isEqualTo(data.getKnowledgeId()); + EmbeddingSearchResult list = embeddingStore.search(EmbeddingSearchRequest + .builder() + .queryEmbedding(queryEmbedding) + .filter(filter) + .build()); + + List> result = new ArrayList<>(); + list.matches().forEach(i -> { + TextSegment embedded = i.embedded(); + Map map = embedded.metadata().toMap(); + map.put("text", embedded.text()); + result.add(map); + }); + return result; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangChatServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangChatServiceImpl.java new file mode 100644 index 0000000..c4593a1 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangChatServiceImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.ImageR; +import cn.iocoder.yudao.module.langchat.properties.ChatProps; +import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider; +import cn.iocoder.yudao.module.langchat.provider.ModelProvider; +import cn.iocoder.yudao.module.langchat.service.core.Agent; +import cn.iocoder.yudao.module.langchat.service.core.LangChatService; +import cn.iocoder.yudao.module.langchat.utils.PromptUtil; +import dev.langchain4j.data.image.Image; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; +import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; +import dev.langchain4j.model.image.ImageModel; +import dev.langchain4j.model.output.Response; +import dev.langchain4j.rag.DefaultRetrievalAugmentor; +import dev.langchain4j.rag.content.retriever.ContentRetriever; +import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; +import dev.langchain4j.rag.query.Query; +import dev.langchain4j.service.AiServices; +import dev.langchain4j.service.TokenStream; +import dev.langchain4j.store.embedding.filter.Filter; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.function.Function; + +import static cn.iocoder.yudao.module.langchat.enums.EmbedConst.KNOWLEDGE; +import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey; + +/** + * @author tycoding + * @since 2024/3/8 + */ +@Slf4j +@Service +@AllArgsConstructor +public class LangChatServiceImpl implements LangChatService { + + private final ModelProvider provider; + private final EmbeddingProvider embeddingProvider; + private final ChatProps chatProps; + + private AiServices build(StreamingChatLanguageModel streamModel, ChatLanguageModel model, ChatReq req) { + AiServices aiServices = AiServices.builder(Agent.class) + .chatMemoryProvider(memoryId -> MessageWindowChatMemory.builder() + .id(req.getConversationId()) + .chatMemoryStore(new PersistentChatMemoryStore()) + .maxMessages(chatProps.getMemoryMaxMessage()) + .build()); + if (StrUtil.isNotBlank(req.getPromptText())) { + aiServices.systemMessageProvider(memoryId -> req.getPromptText()); + } + if (streamModel != null) { + aiServices.streamingChatLanguageModel(streamModel); + } + if (model != null) { + aiServices.chatLanguageModel(model); + } + return aiServices; + } + + @Override + public TokenStream chat(ChatReq req) { + StreamingChatLanguageModel model = provider.stream(req.getModelId()); + if (StrUtil.isBlank(req.getConversationId())) { + req.setConversationId(IdUtil.simpleUUID()); + } + + AiServices aiServices = build(model, null, req); + + if (StrUtil.isNotBlank(req.getKnowledgeId())) { + req.getKnowledgeIds().add(req.getKnowledgeId()); + } + + if (req.getKnowledgeIds() != null && !req.getKnowledgeIds().isEmpty()) { + Function filter = (query) -> metadataKey(KNOWLEDGE).isIn(req.getKnowledgeIds()); + ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder() + .embeddingStore(embeddingProvider.getEmbeddingStore(req.getKnowledgeIds())) + .embeddingModel(embeddingProvider.getEmbeddingModel(req.getKnowledgeIds())) + .dynamicFilter(filter) + .build(); + aiServices.retrievalAugmentor(DefaultRetrievalAugmentor + .builder() + .contentRetriever(contentRetriever) + .build()); + } + Agent agent = aiServices.build(); + return agent.stream(req.getConversationId(), req.getMessage()); + } + + @Override + public TokenStream singleChat(ChatReq req) { + StreamingChatLanguageModel model = provider.stream(req.getModelId()); + if (StrUtil.isBlank(req.getConversationId())) { + req.setConversationId(IdUtil.simpleUUID()); + } + + Agent agent = build(model, null, req).build(); + if (req.getPrompt() == null) { + req.setPrompt(PromptUtil.build(req.getMessage(), req.getPromptText())); + } + return agent.stream(req.getConversationId(), req.getPrompt().text()); + } + + @Override + public String text(ChatReq req) { + if (StrUtil.isBlank(req.getConversationId())) { + req.setConversationId(IdUtil.simpleUUID()); + } + + try { + ChatLanguageModel model = provider.text(req.getModelId()); + Agent agent = build(null, model, req).build(); + String text = agent.text(req.getConversationId(), req.getMessage()); + return text; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public Response image(ImageR req) { + try { + ImageModel model = provider.image(req.getModelId()); + return model.generate(req.getPrompt().text()); + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "图片生成失败"); + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangEmbeddingServiceImpl.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangEmbeddingServiceImpl.java new file mode 100644 index 0000000..af38eb8 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/LangEmbeddingServiceImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core.impl; + +import cn.iocoder.yudao.module.langchat.dal.dto.ChatReq; +import cn.iocoder.yudao.module.langchat.dal.dto.EmbeddingR; +import cn.iocoder.yudao.module.langchat.enums.EmbedConst; +import cn.iocoder.yudao.module.langchat.provider.EmbeddingProvider; +import cn.iocoder.yudao.module.langchat.service.core.LangEmbeddingService; +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.DocumentSplitter; +import dev.langchain4j.data.document.Metadata; +import dev.langchain4j.data.document.loader.UrlDocumentLoader; + +import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser; +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingStore; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author tycoding + * @since 2024/4/4 + */ +@Slf4j +@Service +@AllArgsConstructor +public class LangEmbeddingServiceImpl implements LangEmbeddingService { + + private final EmbeddingProvider embeddingProvider; + + @Override + public EmbeddingR embeddingText(ChatReq req) { + log.info(">>>>>>>>>>>>>> Text文本向量解析开始,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName()); + TextSegment segment = TextSegment.from(req.getMessage(), + Metadata.metadata(EmbedConst.KNOWLEDGE, req.getKnowledgeId()).put(EmbedConst.FILENAME, req.getDocsName())); + + EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(req.getKnowledgeId()); + EmbeddingStore embeddingStore = embeddingProvider.getEmbeddingStore(req.getKnowledgeId()); + Embedding embedding = embeddingModel.embed(segment).content(); + String id = embeddingStore.add(embedding, segment); + + log.info(">>>>>>>>>>>>>> Text文本向量解析结束,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName()); + return new EmbeddingR().setVectorId(id).setText(segment.text()); + } + + @Override + public List embeddingDocs(ChatReq req) { + log.info(">>>>>>>>>>>>>> Docs文档向量解析开始,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName()); + Document document = UrlDocumentLoader.load(req.getUrl(), new ApacheTikaDocumentParser()); + document.metadata().put(EmbedConst.KNOWLEDGE, req.getKnowledgeId()).put(EmbedConst.FILENAME, req.getDocsName()); + + List list = new ArrayList<>(); + try { + DocumentSplitter splitter = EmbeddingProvider.splitter(); + List segments = splitter.split(document); + + EmbeddingModel embeddingModel = embeddingProvider.getEmbeddingModel(req.getKnowledgeId()); + EmbeddingStore embeddingStore = embeddingProvider.getEmbeddingStore(req.getKnowledgeId()); + List embeddings = embeddingModel.embedAll(segments).content(); + List ids = embeddingStore.addAll(embeddings, segments); + + for (int i = 0; i < ids.size(); i++) { + list.add(new EmbeddingR().setVectorId(ids.get(i)).setText(segments.get(i).text())); + } + } catch (Exception e) { + e.printStackTrace(); + } + + log.info(">>>>>>>>>>>>>> Docs文档向量解析结束,KnowledgeId={}, DocsName={}", req.getKnowledgeId(), req.getDocsName()); + return list; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/PersistentChatMemoryStore.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/PersistentChatMemoryStore.java new file mode 100644 index 0000000..2b7091d --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/service/core/impl/PersistentChatMemoryStore.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.service.core.impl; + +import dev.langchain4j.data.message.ChatMessage; +import dev.langchain4j.data.message.SystemMessage; +import dev.langchain4j.store.memory.chat.ChatMemoryStore; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author tycoding + * @since 2024/8/15 + */ +@Slf4j +public class PersistentChatMemoryStore implements ChatMemoryStore { + + private static final Map> store = new HashMap<>(); + private static final Map initSystemMessageStore = new HashMap<>(); + private static final Map initMessageStore = new HashMap<>(); + + public static void clean(Object memoryId) { + log.info("clean message memory store to: {}", memoryId); + store.remove(memoryId); + } + + public static void init(Object memoryId, SystemMessage message) { + Boolean isInitSystemMessage = initSystemMessageStore.get(memoryId); + if (isInitSystemMessage != null && isInitSystemMessage) { + return; + } + + List list = store.get(memoryId); + if (list == null) { + store.put(memoryId, new ArrayList<>(List.of(message))); + } else { + list.add(message); + } + initSystemMessageStore.put(memoryId, true); + } + + public static void init(Object memoryId, List messages) { + log.info("initialize message memory store to: {}", memoryId); + + Boolean isInitMessage = initMessageStore.get(memoryId); + if (isInitMessage != null && isInitMessage) { + return; + } + + List list = store.get(memoryId); + if (list == null) { + store.put(memoryId, messages); + } else { + list.addAll(messages); + } + initMessageStore.put(memoryId, true); + } + + @Override + public List getMessages(Object memoryId) { + List list = store.get(memoryId); + if (list == null) { + return new ArrayList<>(); + } + return list; + } + + @Override + public void updateMessages(Object memoryId, List messages) { + store.put(memoryId, messages); + } + + @Override + public void deleteMessages(Object memoryId) { + store.remove(memoryId); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/store/AppStore.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/store/AppStore.java new file mode 100644 index 0000000..1456cb6 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/store/AppStore.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.store; + +import cn.iocoder.yudao.module.langchat.entity.AigcApp; +import cn.iocoder.yudao.module.langchat.service.aigc.AigcAppService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author tycoding + * @since 2024/8/8 + */ +@Slf4j +@Component +@AllArgsConstructor +public class AppStore { + + private static final Map appMap = new HashMap<>(); + private final AigcAppService aigcAppService; + + @PostConstruct + public void init() { + log.info("initialize app config list..."); + List list = aigcAppService.list(); + list.forEach(i -> appMap.put(i.getId(), i)); + } + + public AigcApp get(String appId) { + return appMap.get(appId); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/AnalysisThreadPool.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/AnalysisThreadPool.java new file mode 100644 index 0000000..bac8074 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/AnalysisThreadPool.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.task; + +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author GB + * @since 2024-08-22 + */ +public class AnalysisThreadPool { + /** + * 根据cpu 数量动态配置核心线程数和最大线程数 + */ + private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); + /** + * 核心线程数 + */ + private static final int CORE_PO0L_SIZE = CPU_COUNT + 1; + /** + * 最大线程数 + */ + private static final int MAX_POOL_SIZE = 2 * CPU_COUNT + 1; + /** + * 线程队列 + */ + private static final int MAX_LIMIT_JOB_SIZE = 1000; + /** + * 非核心线程存活时间1s + */ + private static final int KEEP_ALIVE = 1; + volatile private static ThreadPoolExecutor EXECUTOR = null; + + public static ThreadPoolExecutor getThreadPool() { + if (null == EXECUTOR) { + synchronized (AnalysisThreadPool.class) { + if (null == EXECUTOR) { + EXECUTOR = new ThreadPoolExecutor( + CORE_PO0L_SIZE, + MAX_POOL_SIZE, + KEEP_ALIVE, + TimeUnit.MICROSECONDS, + new LinkedBlockingDeque<>(MAX_LIMIT_JOB_SIZE), + Executors.defaultThreadFactory(), + new ThreadPoolExecutor.AbortPolicy() { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + super.rejectedExecution(r, e); + } + } + ); + } + } + } + return EXECUTOR; + } + + public static void execute(Runnable runable) { + getThreadPool().execute(runable); + + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/TaskManager.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/TaskManager.java new file mode 100644 index 0000000..767e9f3 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/task/TaskManager.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.task; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; + + +/** + * @author GB + * @since 2024-08-22 + */ +public class TaskManager { + private static final ConcurrentHashMap>> TASK_MAP = new ConcurrentHashMap<>(); + + /** + * 提交任务 + */ + public static void submitTask(String id, Callable function) { + Future future = AnalysisThreadPool.getThreadPool().submit(function); + List> orDefault = TASK_MAP.getOrDefault(id, new ArrayList<>()); + orDefault.add(future); + TASK_MAP.put(id, orDefault); + } + + /** + * 弹出任务 + */ + public void popTaskResult(String id) { + TASK_MAP.remove(id); + } + + public int getCount(String id) { + if (TASK_MAP.containsKey(id)) { + Collection collection = TASK_MAP.get(id); + return collection != null ? collection.size() : 0; + } + return 0; + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/MybatisUtil.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/MybatisUtil.java new file mode 100644 index 0000000..215fc2a --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/MybatisUtil.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.utils; + +import cn.hutool.core.lang.Dict; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +/** + * @author tycoding + * @since 2024/1/2 + */ +public class MybatisUtil { + + /** + * 分页查询:格式化响应数据结构 + * + * @param page 分页数据 + * @return 格式化后的Map对象 + */ + public static Dict getData(IPage page) { + return Dict.create().set("list", page.getRecords()).set("total", (int) page.getTotal()); + } + + /** + * QueryPage对象转换为Page对象 + */ + public static IPage wrap(T t, QueryPage query) { + return new Page(query.getPageNo(), query.getPageSize()); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/PromptUtil.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/PromptUtil.java new file mode 100644 index 0000000..e18e120 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/PromptUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.utils; + +import cn.hutool.core.bean.BeanUtil; +import cn.iocoder.yudao.module.langchat.dal.dto.PromptConst; +import dev.langchain4j.model.input.Prompt; +import dev.langchain4j.model.input.PromptTemplate; + +import java.util.Map; + +/** + * @author tycoding + * @since 2024/3/1 + */ +public class PromptUtil { + + public static Prompt build(String message) { + return new Prompt(message); + } + + public static Prompt build(String message, String promptText) { + return new PromptTemplate(promptText + PromptConst.EMPTY).apply(Map.of(PromptConst.QUESTION, message)); + } + + public static Prompt build(String message, String promptText, Object param) { + Map params = BeanUtil.beanToMap(param, false, true); + params.put(PromptConst.QUESTION, message); + return new PromptTemplate(promptText).apply(params); + } + + public static Prompt buildDocs(String message) { + return new PromptTemplate(PromptConst.DOCUMENT).apply(Map.of(PromptConst.QUESTION, message)); + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/QueryPage.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/QueryPage.java new file mode 100644 index 0000000..2e5e3b1 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/QueryPage.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author tycoding + * @since 2024/1/2 + */ +@Data +@AllArgsConstructor +public class QueryPage implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 当前页 + */ + private int pageNo = 1; + + /** + * 每页的记录数 + */ + private int pageSize = 10; +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/ServletUtil.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/ServletUtil.java new file mode 100644 index 0000000..79a77d4 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/ServletUtil.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.utils; + +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.langchat.constant.CommonConst; +import lombok.SneakyThrows; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author tycoding + * @since 2024/1/2 + */ +public class ServletUtil { + + @SneakyThrows + public static void write(HttpServletResponse response, CommonResult data) { + response.setStatus(data.getCode()); + response.setHeader("Content-type", "application/json;charset=" + CommonConst.UTF_8); + response.setCharacterEncoding(CommonConst.UTF_8); + response.getWriter().write(JSONUtil.toJsonStr(data)); + } + + @SneakyThrows + public static void write(HttpServletResponse response, int status, CommonResult data) { + response.setStatus(status); + response.setHeader("Content-type", "application/json;charset=" + CommonConst.UTF_8); + response.setCharacterEncoding(CommonConst.UTF_8); + response.getWriter().write(JSONUtil.toJsonStr(data)); + } + + public static HttpServletRequest getRequest() { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (servletRequestAttributes != null) { + return servletRequestAttributes.getRequest(); + } + return null; + } + + public static String getAuthorizationToken() { + String token = getRequest().getHeader("Authorization"); + if (token != null && token.toLowerCase().startsWith("bearer")) { + return token.toLowerCase().replace("bearer", "").trim(); + } + return null; + } + + public static String getToken(String token) { + if (token != null && token.toLowerCase().startsWith("bearer")) { + return token.replace("bearer", "").trim(); + } + return token; + } + + public static String getIpAddr() { + HttpServletRequest request = getRequest(); + if (request == null) { + return "unknown"; + } else { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + if ("0:0:0:0:0:0:0:1".equals(ip)) { + ip = "127.0.0.1"; + } + + if (ip.contains(",")) { + ip = ip.split(",")[0]; + } + return ip; + } + } +} \ No newline at end of file diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/StreamEmitter.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/StreamEmitter.java new file mode 100644 index 0000000..4da7e5d --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/utils/StreamEmitter.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.utils; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.concurrent.ExecutorService; + +/** + * @author tycoding + * @since 2024/1/30 + */ +public class StreamEmitter { + + private final SseEmitter emitter; + + public StreamEmitter() { + emitter = new SseEmitter(5 * 60 * 1000L); + } + + public SseEmitter get() { + return emitter; + } + + public SseEmitter streaming(final ExecutorService executor, Runnable func) { +// ExecutorService executor = Executors.newSingleThreadExecutor(); + + emitter.onCompletion(() -> { + System.out.println("SseEmitter 完成"); + executor.shutdownNow(); + }); + + emitter.onError((e) -> { + System.out.println("SseEmitter 出现错误: " + e.getMessage()); + executor.shutdownNow(); + }); + + emitter.onTimeout(() -> { + System.out.println("SseEmitter 超时"); + emitter.complete(); + executor.shutdownNow(); + }); + executor.execute(() -> { + try { + func.run(); + } catch (Exception e) { + System.out.println("捕获到异常: " + e.getMessage()); + emitter.completeWithError(e); + Thread.currentThread().interrupt(); + } finally { + if (!executor.isShutdown()) { + executor.shutdownNow(); + } + } + }); + return emitter; + } + + public void send(Object obj) { + try { + emitter.send(obj); + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + } + + public void complete() { + emitter.complete(); + } + + public void error(String message) { + try { + emitter.send("Error: " + message); + emitter.complete(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapper.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapper.java new file mode 100644 index 0000000..6474a00 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapper.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.wrapper; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.dromara.x.file.storage.core.exception.FileStorageRuntimeException; +import org.dromara.x.file.storage.core.file.FileWrapper; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * MultipartFile 文件包装类 + */ +@Getter +@Setter +@NoArgsConstructor +public class MultipartFileWrapper implements FileWrapper { + private MultipartFile file; + private String name; + private String contentType; + private InputStream inputStream; + private Long size; + + public MultipartFileWrapper(MultipartFile file, String name, String contentType, Long size) { + this.file = file; + this.name = name; + this.contentType = contentType; + this.size = size; + } + + @Override + public InputStream getInputStream() throws IOException { + if (inputStream == null) { + inputStream = new BufferedInputStream(file.getInputStream()); + } + return inputStream; + } + + @Override + public void transferTo(File dest) { + // 在某些 SpringBoot 版本中,例如 2.4.6,此方法会调用失败, + // 此时尝试手动将 InputStream 写入指定文件, + // 根据文档来看 MultipartFile 最终都会由框架从临时目录中删除 + try { + file.transferTo(dest); + IoUtil.close(inputStream); + } catch (Exception ignored) { + try { + FileUtil.writeFromStream(getInputStream(), dest); + } catch (Exception e) { + throw new FileStorageRuntimeException("文件移动失败", e); + } + } + } + + @Override + public boolean supportTransfer() { + return true; + } + + public void setSize(Long size) { + this.size = size; + } + + public void setName(String name) { + this.name = name; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapperAdapter.java b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapperAdapter.java new file mode 100644 index 0000000..d08b70c --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/java/cn/iocoder/yudao/module/langchat/wrapper/MultipartFileWrapperAdapter.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 LangChat. TyCoding All Rights Reserved. + * + * Licensed under the GNU Affero General Public License, Version 3 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.gnu.org/licenses/agpl-3.0.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.iocoder.yudao.module.langchat.wrapper; + +import lombok.Getter; +import lombok.Setter; +import org.dromara.x.file.storage.core.file.FileWrapper; +import org.dromara.x.file.storage.core.file.FileWrapperAdapter; +import org.springframework.web.multipart.MultipartFile; + +/** + * MultipartFile 文件包装适配器 + */ +@Getter +@Setter +public class MultipartFileWrapperAdapter implements FileWrapperAdapter { + + @Override + public boolean isSupport(Object source) { + return source instanceof MultipartFile || source instanceof MultipartFileWrapper; + } + + @Override + public FileWrapper getFileWrapper(Object source, String name, String contentType, Long size) { + if (source instanceof MultipartFileWrapper) { + return updateFileWrapper((MultipartFileWrapper) source, name, contentType, size); + } else { + MultipartFile file = (MultipartFile) source; + if (name == null) name = file.getOriginalFilename(); + if (contentType == null) contentType = file.getContentType(); + if (size == null) size = file.getSize(); + return new MultipartFileWrapper(file, name, contentType, size); + } + } +} diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-dev.yaml b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..b55cc1e --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-dev.yaml @@ -0,0 +1,214 @@ +--- #################### 注册中心 + 配置中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 192.168.0.15:8848 # Nacos 服务器地址 + username: nacos # Nacos 账号 + password: nacos # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: root + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: root + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 192.168.0.21 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 10.31.0.128 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.system.dal.mysql: debug + cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的) + secret: 2a7b3b20c537e52e74afd395eb85f61f + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) + # secret: 333ae72f41552af1e998fe1f54e1584a + # appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + # secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b + appid: wx66186af0759f47c9 # 测试号(puhui 提供的) + secret: 3218bcbd112cbc614c7264ceb20144ac + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + env: # 多环境的配置项 + tag: ${HOSTNAME} + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false + wxa-code: + env-version: develop # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop" + wxa-subscribe-message: + miniprogram-state: developer # 跳转小程序类型:开发版为 “developer”;体验版为 “trial”为;正式版为 “formal” + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-local.yaml b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-local.yaml new file mode 100644 index 0000000..f541324 --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-local.yaml @@ -0,0 +1,233 @@ +--- #################### 注册中心 + 配置中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 192.168.0.15:8848 # Nacos 服务器地址 + username: nacos # Nacos 账号 + password: nacos # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: yudao # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: yudao # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: root + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: root + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 10.31.0.128 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.langchat.server.mapper: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的) + secret: 2a7b3b20c537e52e74afd395eb85f61f + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) + # secret: 333ae72f41552af1e998fe1f54e1584a + # appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + # secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b + appid: wx66186af0759f47c9 # 测试号(puhui 提供的) + secret: 3218bcbd112cbc614c7264ceb20144ac + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + env: # 多环境的配置项 + tag: ${HOSTNAME} + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false + wxa-code: + env-version: develop # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop" + wxa-subscribe-message: + miniprogram-state: developer # 跳转小程序类型:开发版为 “developer”;体验版为 “trial”为;正式版为 “formal” + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 +langchat: + oss: + default-platform: local + qiniu-kodo: + - platform: qiniu # 存储平台标识,七牛:qiniu、阿里OSS:aliyun-oss、腾讯OSS:tencent-cos + enable-storage: true # 启用存储 + access-key: + secret-key: + bucket-name: + domain: / # 访问域名,注意“/”结尾,例如:http://abc.hn-bkt.clouddn.com/ + base-path: langchat/ # 基础路径 + local-plus: + - platform: local # 存储平台标识 + enable-storage: true #启用存储 + enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高) + domain: http://127.0.0.1:8100/ # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 + base-path: langchat/ # 基础路径 + path-patterns: /static/** # 访问路径(默认本地target目录),注意:如果使用本地nginx容器,此路径要修改为nginx存储路径 + storage-path: ./static/ # 存储路径(默认本地target目录),注意:如果使用本地nginx容器,此路径要修改为nginx存储路径 + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-prd.yaml b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-prd.yaml new file mode 100644 index 0000000..b55cc1e --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application-prd.yaml @@ -0,0 +1,214 @@ +--- #################### 注册中心 + 配置中心相关配置 #################### + +spring: + cloud: + nacos: + server-addr: 192.168.0.15:8848 # Nacos 服务器地址 + username: nacos # Nacos 账号 + password: nacos # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: dev # 命名空间。这里使用 dev 开发环境 + group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源 + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 + # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 + # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 + # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 + username: root + password: root + # username: sa # SQL Server 连接的示例 + # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 + # username: SYSDBA # DM 连接的示例 + # password: SYSDBA # DM 连接的示例 + slave: # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: root + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + redis: + host: 192.168.0.21 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 10.31.0.128 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: guest # RabbitMQ 服务的账号 + password: guest # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.system.dal.mysql: debug + cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # 配置 SmsChannelMapper 的日志级别为 info + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 + app-id: wx5b23ba7a5589ecbb # 测试号(自己的) + secret: 2a7b3b20c537e52e74afd395eb85f61f + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # 存储配置,解决 AccessToken 的跨节点的共享 + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) + # secret: 333ae72f41552af1e998fe1f54e1584a + # appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + # secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b + appid: wx66186af0759f47c9 # 测试号(puhui 提供的) + secret: 3218bcbd112cbc614c7264ceb20144ac + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + env: # 多环境的配置项 + tag: ${HOSTNAME} + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试 + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false + wxa-code: + env-version: develop # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop" + wxa-subscribe-message: + miniprogram-state: developer # 跳转小程序类型:开发版为 “developer”;体验版为 “trial”为;正式版为 “formal” + +justauth: + enabled: true + type: + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + # noinspection SpringBootApplicationYaml + WECHAT_MINI_APP: # 微信小程序 + client-id: ${wx.miniapp.appid} + client-secret: ${wx.miniapp.secret} + ignore-check-redirect-uri: true + ignore-check-state: true # 微信小程序,不会使用到 state,所以不进行校验 + WECHAT_MP: # 微信公众号 + client-id: ${wx.mp.app-id} + client-secret: ${wx.mp.secret} + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application.yaml b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application.yaml new file mode 100644 index 0000000..358afec --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/application.yaml @@ -0,0 +1,125 @@ +spring: + application: + name: langchat-server + + profiles: + active: local + + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + port: 48091 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面 + setting: + language: zh_cn + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${yudao.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋道相关配置 #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao.module.langchat + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + swagger: + title: 管理后台 + description: 提供管理员管理的所有功能 + version: ${yudao.info.version} + tenant: # 多租户相关配置项 + enable: true + ignore-urls: + - /admin-api/pay/notify/** # 支付回调通知,不携带租户编号 + +debug: false diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/banner.txt b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/banner.txt new file mode 100644 index 0000000..9a7704e --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/banner.txt @@ -0,0 +1,12 @@ + __ ________ __ + / / ____ _____ ____ _ / ____/ /_ ____ _/ /_ + / / / __ `/ __ \/ __ `/ / / / __ \/ __ `/ __/ + / /___/ /_/ / / / / /_/ / / /___/ / / / /_/ / /_ +/_____/\__,_/_/ /_/\__, / \____/_/ /_/\__,_/\__/ + /____/ + + Author: TyCoding + Home: https://langchat.cn + Blog: https://tycoding.cn + Github: https://github.com/tycoding + Email: langchat@outlook.com diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/logback-spring.xml b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..b1b9f3f --- /dev/null +++ b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/static/favicon.ico b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..57ee606 Binary files /dev/null and b/yudao-module-langchat/yudao-module-langchat-biz/src/main/resources/static/favicon.ico differ diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml index 0f90d49..72ea42a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml +++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml @@ -3,7 +3,7 @@ spring: cloud: nacos: - server-addr: 10.31.0.128:8848 # Nacos 服务器地址 + server-addr: 192.168.0.17:8848 # Nacos 服务器地址 username: nacos # Nacos 账号 password: nacos # Nacos 密码 discovery: # 【配置中心】配置项 @@ -57,30 +57,30 @@ spring: primary: master datasource: master: - url: jdbc:mysql://10.31.0.128:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例 # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 username: root - password: chinasoft@123 + password: root # username: sa # SQL Server 连接的示例 # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 # password: SYSDBA # DM 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://10.31.0.128:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + url: jdbc:mysql://192.168.0.17:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true username: root - password: chinasoft@123 + password: root # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 redis: - host: 10.31.0.128 # 地址 + host: 192.168.0.17 # 地址 port: 6379 # 端口 database: 0 # 数据库索引 -# password: 123456 # 密码,建议生产环境开启 + password: 123456 # 密码,建议生产环境开启 --- #################### MQ 消息队列相关配置 ####################