Browse Source

初始化

leichangchun2022@163.com 2 years ago
parent
commit
c4543dea00
100 changed files with 5987 additions and 0 deletions
  1. 33 0
      .classpath
  2. 34 0
      .project
  3. 316 0
      mvnw
  4. 188 0
      mvnw.cmd
  5. 73 0
      pom.xml
  6. 138 0
      src/engine/java/xbc/formula/engine/exp/XbcExpCalculator.java
  7. 12 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpKind.java
  8. 51 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpOpKind.java
  9. 72 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpTree.java
  10. 12 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpVarKind.java
  11. 140 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpToken.java
  12. 16 0
      src/engine/java/xbc/formula/engine/exp/datatype/XbcExpTokenAnnotation.java
  13. 20 0
      src/engine/java/xbc/formula/engine/exp/exception/XbcExpAbstractException.java
  14. 20 0
      src/engine/java/xbc/formula/engine/exp/exception/XbcExpExecException.java
  15. 20 0
      src/engine/java/xbc/formula/engine/exp/exception/XbcExpParamException.java
  16. 20 0
      src/engine/java/xbc/formula/engine/exp/exception/XbcExpParseException.java
  17. 20 0
      src/engine/java/xbc/formula/engine/exp/exception/XbcExpScanException.java
  18. 308 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculateExp.java
  19. 487 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExp2Op.java
  20. 119 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFunc.java
  21. 154 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFuncDate.java
  22. 93 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFuncRound.java
  23. 149 0
      src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpItv.java
  24. 14 0
      src/engine/java/xbc/formula/engine/exp/ext/XbcExpExtFunc.java
  25. 44 0
      src/engine/java/xbc/formula/engine/exp/ext/XbcExpExtFuncManager.java
  26. 556 0
      src/engine/java/xbc/formula/engine/exp/parser/XbcExpParser.java
  27. 631 0
      src/engine/java/xbc/formula/engine/exp/scanner/XbcExpScanner.java
  28. 198 0
      src/engine/java/xbc/formula/engine/exp/util/XbcExpDateUtil.java
  29. 30 0
      src/engine/java/xbc/formula/engine/exp/util/XbcExpNumericUtil.java
  30. 13 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorExp2OpVo.java
  31. 29 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorExpFuncDateTtcVo.java
  32. 20 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorReturnVo.java
  33. 59 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpDrawnInfoVo.java
  34. 25 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpIntervalVo.java
  35. 5 0
      src/engine/java/xbc/formula/engine/exp/vo/XbcExpResultVo.java
  36. 160 0
      src/engine/java/xbc/formula/engine/fml/XbcFmlCalculator.java
  37. 88 0
      src/engine/java/xbc/formula/engine/fml/vo/XbcFmlFormulaIndicatorVo.java
  38. 61 0
      src/engine/java/xbc/formula/engine/fml/vo/XbcFmlFormulaVo.java
  39. 17 0
      src/main/java/xbc/formula/CalculateManagerApplication.java
  40. 34 0
      src/main/java/xbc/formula/calcmgr/controller/CalculateManagerController.java
  41. 8 0
      src/main/java/xbc/formula/calcmgr/dao/CalcFormulaDao.java
  42. 14 0
      src/main/java/xbc/formula/calcmgr/dao/CalcIndicatorResultDao.java
  43. 12 0
      src/main/java/xbc/formula/calcmgr/dao/CalcNoManagerDao.java
  44. 12 0
      src/main/java/xbc/formula/calcmgr/dao/CalcResultDao.java
  45. 90 0
      src/main/java/xbc/formula/calcmgr/dao/entity/CalcIndicatorResult.java
  46. 76 0
      src/main/java/xbc/formula/calcmgr/dao/entity/CalcResult.java
  47. 9 0
      src/main/java/xbc/formula/calcmgr/logic/CalculateManagerLogic.java
  48. 8 0
      src/main/java/xbc/formula/calcmgr/logic/QueryCalcResultLogic.java
  49. 10 0
      src/main/java/xbc/formula/calcmgr/logic/SaveResultLogic.java
  50. 49 0
      src/main/java/xbc/formula/calcmgr/logic/impl/CalculateManagerLogicImpl.java
  51. 68 0
      src/main/java/xbc/formula/calcmgr/logic/impl/QueryCalcResultLogicImpl.java
  52. 181 0
      src/main/java/xbc/formula/calcmgr/logic/impl/SaveResultLogicImpl.java
  53. 12 0
      src/main/java/xbc/formula/calcmgr/service/CalculateManagerService.java
  54. 34 0
      src/main/java/xbc/formula/calcmgr/service/impl/CalculateManagerServiceImpl.java
  55. 53 0
      src/main/java/xbc/formula/calcmgr/vo/CalcResultDescVo.java
  56. 41 0
      src/main/java/xbc/formula/calcmgr/vo/CalculateRequestVo.java
  57. 25 0
      src/main/java/xbc/formula/calcmgr/vo/CalculateResponseVo.java
  58. 24 0
      src/main/java/xbc/formula/fmlmgr/controller/FormulaController.java
  59. 14 0
      src/main/java/xbc/formula/fmlmgr/dao/FormulaDao.java
  60. 10 0
      src/main/java/xbc/formula/fmlmgr/dao/IndicatorDao.java
  61. 8 0
      src/main/java/xbc/formula/fmlmgr/logic/FormulaLogic.java
  62. 81 0
      src/main/java/xbc/formula/fmlmgr/logic/impl/FormulaLogicImpl.java
  63. 8 0
      src/main/java/xbc/formula/fmlmgr/service/FormulaService.java
  64. 22 0
      src/main/java/xbc/formula/fmlmgr/service/impl/FormulaServiceImpl.java
  65. 15 0
      src/main/java/xbc/formula/framework/exception/FormulaException.java
  66. 22 0
      src/main/java/xbc/formula/framework/interceptor/ExceptionResponseHandler.java
  67. 46 0
      src/main/java/xbc/formula/framework/interceptor/NormalResponseHandler.java
  68. 40 0
      src/main/java/xbc/formula/framework/interceptor/TransactionAdviceConfig.java
  69. 12 0
      src/main/java/xbc/formula/framework/interceptor/TransferToResponseVoAnnotation.java
  70. 38 0
      src/main/java/xbc/formula/framework/vo/ResponseVo.java
  71. 8 0
      src/main/resources/application.properties
  72. 14 0
      src/main/resources/mapper/CalcFormulaMapper.xml
  73. 45 0
      src/main/resources/mapper/CalcIndicatorResultMapper.xml
  74. 23 0
      src/main/resources/mapper/CalcNoManagerMapper.xml
  75. 37 0
      src/main/resources/mapper/CalcResultMapper.xml
  76. 29 0
      src/main/resources/mapper/FormulaMapper.xml
  77. 17 0
      src/main/resources/mapper/IndicatorMapper.xml
  78. 29 0
      table/reset_ddl.txt
  79. 91 0
      table/table_dml.txt
  80. 8 0
      target/classes/application.properties
  81. 14 0
      target/classes/mapper/CalcFormulaMapper.xml
  82. 45 0
      target/classes/mapper/CalcIndicatorResultMapper.xml
  83. 23 0
      target/classes/mapper/CalcNoManagerMapper.xml
  84. 37 0
      target/classes/mapper/CalcResultMapper.xml
  85. 29 0
      target/classes/mapper/FormulaMapper.xml
  86. 17 0
      target/classes/mapper/IndicatorMapper.xml
  87. BIN
      target/classes/xbc/formula/CalculateManagerApplication.class
  88. BIN
      target/classes/xbc/formula/calcmgr/controller/CalculateManagerController.class
  89. BIN
      target/classes/xbc/formula/calcmgr/dao/CalcFormulaDao.class
  90. BIN
      target/classes/xbc/formula/calcmgr/dao/CalcIndicatorResultDao.class
  91. BIN
      target/classes/xbc/formula/calcmgr/dao/CalcNoManagerDao.class
  92. BIN
      target/classes/xbc/formula/calcmgr/dao/CalcResultDao.class
  93. BIN
      target/classes/xbc/formula/calcmgr/dao/entity/CalcIndicatorResult.class
  94. BIN
      target/classes/xbc/formula/calcmgr/dao/entity/CalcResult.class
  95. BIN
      target/classes/xbc/formula/calcmgr/logic/CalculateManagerLogic.class
  96. BIN
      target/classes/xbc/formula/calcmgr/logic/QueryCalcResultLogic.class
  97. BIN
      target/classes/xbc/formula/calcmgr/logic/SaveResultLogic.class
  98. BIN
      target/classes/xbc/formula/calcmgr/logic/impl/CalculateManagerLogicImpl.class
  99. BIN
      target/classes/xbc/formula/calcmgr/logic/impl/QueryCalcResultLogicImpl.class
  100. BIN
      target/classes/xbc/formula/calcmgr/logic/impl/SaveResultLogicImpl$RetrieveIndicatorVo.class

+ 33 - 0
.classpath

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/engine/java"/>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="test" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 34 - 0
.project

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>xbc-cm</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.springframework.ide.eclipse.core.springbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.springframework.ide.eclipse.core.springnature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

+ 316 - 0
mvnw

@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF 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
+#
+#    https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 188 - 0
mvnw.cmd

@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%

+ 73 - 0
pom.xml

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.7.2</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>xbc.formula</groupId>
+	<artifactId>calculate-manager</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>calculate-manager</name>
+	<description>Demo project for Spring Boot</description>
+	<properties>
+		<java.version>1.8</java.version>
+		<spring-cloud.version>2021.0.3</spring-cloud.version>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-aop</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.mybatis.spring.boot</groupId>
+			<artifactId>mybatis-spring-boot-starter</artifactId>
+			<version>2.2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.springframework.cloud</groupId>
+				<artifactId>spring-cloud-dependencies</artifactId>
+				<version>${spring-cloud.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+</project>

+ 138 - 0
src/engine/java/xbc/formula/engine/exp/XbcExpCalculator.java

@@ -0,0 +1,138 @@
+package xbc.formula.engine.exp;
+
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpParamException;
+import xbc.formula.engine.exp.exec.XbcExpCalculateExp;
+import xbc.formula.engine.exp.parser.XbcExpParser;
+import xbc.formula.engine.exp.vo.XbcExpCalculatorReturnVo;
+import xbc.formula.engine.exp.vo.XbcExpDrawnInfoVo;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 表达式计算工具
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculator {
+	
+	// 默认的大数计算保留小数位数
+	private static final int DEFAULT_BAOLIU_WEISHU = 12;
+	
+	// 默认的取整模式: 四舍五入
+	private static final RoundingMode DEFAULT_ROUND_MODE = RoundingMode.HALF_UP;
+
+	/**
+	 * 获取表达式里面的指标
+	 * @param expStr 表达式字符串
+	 * @return 指标列表
+	 */
+	public static List<String> getParamters(String expStr) {
+		if(expStr == null) {
+			throw new XbcExpParamException("请传入一个表达式。");
+		}
+		XbcExpParser ep = new XbcExpParser(expStr);
+		ep.parse();
+		Set<String> paramSet = ep.getParamSet();
+		List<String> params = new ArrayList<>();
+		for(String param : paramSet) {
+			params.add(param);
+		}
+		return params;
+	}
+
+	public static List<XbcExpDrawnInfoVo> getDrawnInfoVos(String expStr) {
+		if(expStr == null) {
+			throw new XbcExpParamException("请传入一个表达式。");
+		}
+		XbcExpParser ep = new XbcExpParser(expStr, true);
+		ep.parse();
+		List<XbcExpDrawnInfoVo> divs = ep.getDrawnInfoVos();
+		return divs;
+	}
+	
+	/**
+	 * 给定参数,计算表达式的值
+	 * @param expStr 表达式字符串
+	 * @param paramMap 指标Map
+	 * @param formulaParams 表达式计算过程中,可以使用的参数(@XbcExpExtFunc)
+	 * @return 表达式的计算结果
+	 */
+	public static Object calculate(String expStr, Map<String, Object> paramMap, Object[] formulaParams) {
+		return calculate(DEFAULT_BAOLIU_WEISHU, DEFAULT_ROUND_MODE, expStr, paramMap, formulaParams);
+	}
+	
+	/**
+	 * 给定参数,计算表达式的值
+	 * @param weishu 保留的小数位数
+	 * @param roundMode 保留小数时的取舍方式
+	 * @param expStr 表达式字符串
+	 * @param paramMap 指标Map
+	 * @param formulaParams 表达式计算过程中,可以使用的参数(@XbcExpExtFunc)
+	 * @return 表达式的计算结果
+	 */
+	public static Object calculate(int weishu, RoundingMode roundMode, String expStr, Map<String, Object> paramMap, Object[] formulaParams) {
+		Map<String, XbcExpResultVo> piMap = new HashMap<>();
+		return calculateExp(weishu, roundMode, expStr, paramMap, piMap, formulaParams);
+	}
+	
+	public static XbcExpCalculatorReturnVo calculateEx(String expStr, Map<String, Object> paramMap, Object[] formulaParams) {
+		return calculateEx(DEFAULT_BAOLIU_WEISHU, DEFAULT_ROUND_MODE, expStr, paramMap, formulaParams);
+	}
+
+	public static XbcExpCalculatorReturnVo calculateEx(int weishu, RoundingMode roundMode, String expStr, Map<String, Object> paramMap, Object[] formulaParams) {
+		Map<String, XbcExpResultVo> piMap = new HashMap<>();
+		Object retObj = calculateExp(weishu, roundMode, expStr, paramMap, piMap, formulaParams);
+		XbcExpCalculatorReturnVo crv = new XbcExpCalculatorReturnVo();
+		crv.setResult(retObj);
+		Map<String, Object> retParamMap = new HashMap<>();
+		crv.setParamMap(retParamMap);
+		for(String key : piMap.keySet()) {
+			XbcExpResultVo result = piMap.get(key);
+			retParamMap.put(key, result.value);
+		}
+		return crv;
+	}
+
+	private static Object calculateExp(int weishu, RoundingMode roundMode, String expStr, Map<String, Object> paramMap, Map<String, XbcExpResultVo> piMap, Object[] formulaParams) {
+		if(expStr == null) {
+			throw new XbcExpParamException("请传入一个表达式。");
+		}
+		if(paramMap == null) {
+			paramMap = new HashMap<>();
+		}
+		XbcExpParser ep = new XbcExpParser(expStr);
+		XbcExpExpTree exp = ep.parse();
+		Set<String> paramSet = ep.getParamSet();
+		List<String> notExistedParams = new ArrayList<>();
+		for(String param : paramSet) {
+			if(paramMap.get(param) == null) {
+				notExistedParams.add(param);
+			}
+		}
+		if(notExistedParams.size() > 0) {
+			String msg = String.join(",", notExistedParams);
+			throw new XbcExpParamException("需要传递以下参数: " + msg + ".公式: " + expStr);
+		}
+		
+		for(String key : paramMap.keySet()) {
+			XbcExpResultVo result = new XbcExpResultVo();
+			Object val = paramMap.get(key);
+			if(val == null) {
+				val = 0;
+			}
+			result.value = val;
+			piMap.put(key, result);
+		}
+
+		XbcExpCalculateExp calculateExp = new XbcExpCalculateExp(weishu, roundMode, expStr, piMap, formulaParams);
+		Object ret = calculateExp.calculate(exp);
+		return ret;
+	}
+}

+ 12 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpKind.java

@@ -0,0 +1,12 @@
+package xbc.formula.engine.exp.datatype;
+
+/**
+ * 表达式树类型常量
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExpKind {
+	public static final int OP_EK  = 1;  /* + - * / ..... */
+	public static final int CST_EK = 2;  /* constant      */
+	public static final int ID_EK  = 3;  /* variable      */
+}

+ 51 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpOpKind.java

@@ -0,0 +1,51 @@
+package xbc.formula.engine.exp.datatype;
+
+/**
+ * 运算类型表达式树的运算类型
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExpOpKind {
+    public static final int ASN_OK      = 1;     /* =                           */
+    public static final int MPTASN_OK   = 2;     /* *=                          */
+    public static final int DIVASN_OK   = 3;     /* /=                          */
+    public static final int MODASN_OK   = 4;     /* %=                          */
+    public static final int PLSASN_OK   = 5;     /* +=                          */
+    public static final int MNSASN_OK   = 6;     /* -=                          */
+    public static final int S2LASN_OK   = 7;     /* <<=                         */
+    public static final int S2RASN_OK   = 8;     /* >>=                         */
+    public static final int BANDASN_OK  = 10;    /* &=                          */
+    public static final int BXORASN_OK  = 11;    /* ^=                          */
+    public static final int BORASN_OK   = 12;    /* |=                          */
+    public static final int MPT_OK      = 13;    /* *                           */
+    public static final int DIV_OK      = 14;    /* /                           */
+    public static final int MOD_OK      = 15;    /* %                           */
+    public static final int PLS_OK      = 16;    /* +                           */
+    public static final int MNS_OK      = 17;    /* -                           */
+    public static final int S2L_OK      = 18;    /* <<                          */
+    public static final int S2R_OK      = 19;    /* >>                          */
+    public static final int BAND_OK     = 21;    /* &                           */
+    public static final int BXOR_OK     = 22;    /* ^                           */
+    public static final int BOR_OK      = 23;    /* |                           */
+    public static final int BNOT_OK     = 24;    /* ~                           */
+    public static final int QRY_OK      = 25;    /* ?                           */
+    public static final int OR_OK       = 26;    /* ||                          */
+    public static final int AND_OK      = 27;    /* &&                          */
+    public static final int EQ_OK       = 28;    /* ==                          */
+    public static final int NEQ_OK      = 29;    /* !=                          */
+    public static final int LT_OK       = 30;    /* <                           */
+    public static final int GT_OK       = 31;    /* >                           */
+    public static final int LEQ_OK      = 32;    /* <=                          */
+    public static final int GEQ_OK      = 33;    /* >=                          */
+    public static final int NOT_OK      = 34;    /* !                           */
+    public static final int PRD_OK      = 35;    /* .                           */
+    public static final int FUNC_OK     = 37;    /* () function                 */
+    public static final int SQB_OK      = 38;    /* {                           */
+    public static final int CMM_OK      = 39;    /*                             */
+    public static final int PREINC_OK   = 40;    /* ++ var                      */
+    public static final int AFTINC_OK   = 41;    /* var ++                      */
+    public static final int PREDEC_OK   = 42;    /* -- var                      */
+    public static final int AFTDEC_OK   = 43;    /* var --                      */
+    public static final int ITV_OK      = 44;    /* 层差计算                     */
+    public static final int NULL_OK     = 99;    /* null op                     */
+}

+ 72 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpTree.java

@@ -0,0 +1,72 @@
+package xbc.formula.engine.exp.datatype;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import xbc.formula.engine.exp.exception.XbcExpParseException;
+import xbc.formula.engine.exp.vo.XbcExpIntervalVo;
+
+/**
+ * 表达式树
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExpTree {
+	public int ek;   // ExpKind
+	public int ok;   // OpKind
+	public XbcExpExpTree lchild;
+	public XbcExpExpTree rchild;
+	public Object value;
+	public List<XbcExpExpTree> params;
+
+	public List<XbcExpIntervalVo> itvs; // 层差计算用
+
+	public XbcExpExpTree() {
+	}
+	
+	public XbcExpExpTree(int ok) {
+		this.ek = XbcExpExpKind.OP_EK;
+		this.ok = ok;
+	}
+	
+	public static XbcExpExpTree newConstIntExptree(String tokenContent, String expStr) {
+		XbcExpExpTree exp = new XbcExpExpTree();
+		exp.ek = XbcExpExpKind.CST_EK;
+		exp.ok = XbcExpExpVarKind.INT_VK;
+		try {
+			BigDecimal bv = new BigDecimal(tokenContent);
+			exp.value = bv;
+		} catch(NumberFormatException nx) {
+			throw new XbcExpParseException("无法将【" + tokenContent + "】转换成整数。" + " 公式: " + expStr);
+		}
+		return exp;
+	}
+
+	public static XbcExpExpTree newConstDblExptree(String tokenContent, String expStr) {
+		XbcExpExpTree exp = new XbcExpExpTree();
+		exp.ek = XbcExpExpKind.CST_EK;
+		exp.ok = XbcExpExpVarKind.DBL_VK;
+		try {
+			BigDecimal bv = new BigDecimal(tokenContent);
+			exp.value = bv;
+		} catch(NumberFormatException nx) {
+			throw new XbcExpParseException("无法将【" + tokenContent + "】转换成浮点数。" + " 公式: " + expStr);
+		}
+		return exp;
+	}
+
+	public static XbcExpExpTree newConstStrExptree(String tokenContent) {
+		XbcExpExpTree exp = new XbcExpExpTree();
+		exp.ek = XbcExpExpKind.CST_EK;
+		exp.ok = XbcExpExpVarKind.STR_VK;
+		exp.value = tokenContent;
+		return exp;
+	}
+	
+	public static XbcExpExpTree newIdExptree(String tokenContent) {
+		XbcExpExpTree exp = new XbcExpExpTree();
+		exp.ek = XbcExpExpKind.ID_EK;
+		exp.value = tokenContent;
+		return exp;
+	}
+}

+ 12 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpExpVarKind.java

@@ -0,0 +1,12 @@
+package xbc.formula.engine.exp.datatype;
+
+/**
+ * 变量类型常量
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExpVarKind {
+	public static final int INT_VK = 100;
+	public static final int STR_VK = 101;
+	public static final int DBL_VK = 102;
+}

+ 140 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpToken.java

@@ -0,0 +1,140 @@
+package xbc.formula.engine.exp.datatype;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Token定义
+ * 
+ * @author leichangchun
+ */
+public class XbcExpToken {
+	@XbcExpTokenAnnotation(id="var", name="变量", value="var")
+	public static final int EXPNAME_TT = 1;
+	@XbcExpTokenAnnotation(id="int", name="整数", value="int")
+	public static final int INT_TT = 2;
+	@XbcExpTokenAnnotation(id="double", name="浮点数", value="double")
+	public static final int DBL_TT = 3;
+	@XbcExpTokenAnnotation(id="string", name="字符串", value="string")
+	public static final int STR_TT = 4;
+	@XbcExpTokenAnnotation(id="asn", name="等号", value="=")
+	public static final int ASN_TT = 5;
+	@XbcExpTokenAnnotation(id="mptasn", name="乘等号", value="*=")
+	public static final int MPTASN_TT = 6;
+	@XbcExpTokenAnnotation(id="divasn", name="除等号", value="/=")
+	public static final int DIVASN_TT = 7;
+	@XbcExpTokenAnnotation(id="modasn", name="取模等号", value="%=")
+	public static final int MODASN_TT = 8;
+	@XbcExpTokenAnnotation(id="plsasn", name="加等号", value="+=")
+	public static final int PLSASN_TT = 9;
+	@XbcExpTokenAnnotation(id="mnsasn", name="减等号", value="-=")
+	public static final int MNSASN_TT = 10;
+	@XbcExpTokenAnnotation(id="s2lasn", name="左移位等号", value="<<=")
+	public static final int S2LASN_TT = 11;
+	@XbcExpTokenAnnotation(id="s2rasn", name="右移位等号", value=">>=")
+	public static final int S2RASN_TT = 12;
+	@XbcExpTokenAnnotation(id="bandasn", name="与等号", value="&=")
+	public static final int BANDASN_TT = 13;
+	@XbcExpTokenAnnotation(id="bxorasn", name="异或等号", value="^=")
+	public static final int BXORASN_TT = 14;
+	@XbcExpTokenAnnotation(id="borasn", name="或等号", value="|=")
+	public static final int BORASN_TT = 15;
+	@XbcExpTokenAnnotation(id="mpt", name="乘号", value="*")
+	public static final int MPT_TT = 16;
+	@XbcExpTokenAnnotation(id="div", name="除号", value="/")
+	public static final int DIV_TT = 17;
+	@XbcExpTokenAnnotation(id="mod", name="取模", value="%")
+	public static final int MOD_TT = 18;
+	@XbcExpTokenAnnotation(id="pls", name="加号", value="+")
+	public static final int PLS_TT = 19;
+	@XbcExpTokenAnnotation(id="mns", name="减号", value="-")
+	public static final int MNS_TT = 20;
+	@XbcExpTokenAnnotation(id="s2l", name="左移位", value="<<")
+	public static final int S2L_TT = 21;
+	@XbcExpTokenAnnotation(id="s2r", name="右移位", value=">>")
+	public static final int S2R_TT = 22;
+	@XbcExpTokenAnnotation(id="band", name="与号", value="&")
+	public static final int BAND_TT = 23;
+	@XbcExpTokenAnnotation(id="bor", name="或", value="|")
+	public static final int BOR_TT = 24;
+	@XbcExpTokenAnnotation(id="bxor", name="异或", value="^")
+	public static final int BXOR_TT = 25;
+	@XbcExpTokenAnnotation(id="bnot", name="非", value="~")
+	public static final int BNOT_TT = 26;
+	@XbcExpTokenAnnotation(id="selfmns", name="自减", value="--")
+	public static final int SELFMNS_TT = 27;
+	@XbcExpTokenAnnotation(id="selfpls", name="自增", value="++")
+	public static final int SELFPLS_TT = 28;
+	@XbcExpTokenAnnotation(id="qry", name="问号", value="?")
+	public static final int QRY_TT = 29;
+	@XbcExpTokenAnnotation(id="cln", name="冒号", value=":")
+	public static final int CLN_TT = 30;
+	@XbcExpTokenAnnotation(id="cmm", name="逗号", value=",")
+	public static final int CMM_TT = 31;
+	@XbcExpTokenAnnotation(id="lprn", name="左括号", value="(")
+	public static final int LPRN_TT = 32;
+	@XbcExpTokenAnnotation(id="rprn", name="右括号", value=")")
+	public static final int RPRN_TT = 33;
+	@XbcExpTokenAnnotation(id="or", name="条件或", value="||")
+	public static final int OR_TT = 34;
+	@XbcExpTokenAnnotation(id="and", name="条件与", value="&&")
+	public static final int AND_TT = 35;
+	@XbcExpTokenAnnotation(id="eq", name="条件等", value="==")
+	public static final int EQ_TT = 36;
+	@XbcExpTokenAnnotation(id="neq", name="条件不等", value="!=")
+	public static final int NEQ_TT = 37;
+	@XbcExpTokenAnnotation(id="lt", name="小于", value="<")
+	public static final int LT_TT = 38;
+	@XbcExpTokenAnnotation(id="gt", name="大于", value=">")
+	public static final int GT_TT = 39;
+	@XbcExpTokenAnnotation(id="leq", name="小于等于", value="<=")
+	public static final int LEQ_TT = 40;
+	@XbcExpTokenAnnotation(id="geq", name="大于等于", value=">=")
+	public static final int GEQ_TT = 41;
+	@XbcExpTokenAnnotation(id="not", name="条件非", value="!")
+	public static final int NOT_TT = 42;
+	@XbcExpTokenAnnotation(id="lsqb", name="左大括号", value="{")
+	public static final int LSQB_TT = 43;
+	@XbcExpTokenAnnotation(id="rsqb", name="右大括号", value="}")
+	public static final int RSQB_TT = 44;
+	@XbcExpTokenAnnotation(id="lbkt", name="左中括号", value="[")
+	public static final int LBKT_TT = 45;
+	@XbcExpTokenAnnotation(id="rbkt", name="右中括号", value="]")
+	public static final int RBKT_TT = 46;
+	@XbcExpTokenAnnotation(id="eof", name="结束符", value="eof")
+	public static final int EOF_TT = 99;
+	
+	private static Map<Integer, XbcExpTokenAnnotation> tokenMap = null;
+	
+	public static String getTokenName(int token) {
+		if(tokenMap == null) {
+			initTokenMap();
+		}
+		XbcExpTokenAnnotation a = tokenMap.get(token);
+		if(a != null) {
+			return a.name();
+		}
+		return "未知";
+	}
+	
+	private synchronized static void initTokenMap() {
+		if(tokenMap != null) {
+			return;
+		}
+		tokenMap = new HashMap<>();
+		Field[] fields = XbcExpToken.class.getDeclaredFields();
+		for(Field field : fields) {
+			XbcExpTokenAnnotation a = field.getAnnotation(XbcExpTokenAnnotation.class);
+			if(a == null) {
+				continue;
+			}
+			try {
+				int t = field.getInt(null);
+				tokenMap.put(t, a);
+			} catch (IllegalArgumentException | IllegalAccessException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+}

+ 16 - 0
src/engine/java/xbc/formula/engine/exp/datatype/XbcExpTokenAnnotation.java

@@ -0,0 +1,16 @@
+package xbc.formula.engine.exp.datatype;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Token描述信息
+ * 
+ * @author leichangchun
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface XbcExpTokenAnnotation {
+	String id();
+	String name();
+	String value();
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/exception/XbcExpAbstractException.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.exception;
+
+/**
+ * 计算参数校验、表达式此法分析、语法分析、执行过程中出现的异常的抽象定义
+ * 
+ * @author leichangchun
+ */
+public abstract class XbcExpAbstractException extends RuntimeException {
+
+	private static final long serialVersionUID = -7287027670481134776L;
+
+    public XbcExpAbstractException(String message) {
+        super(message);
+    }
+    
+    public XbcExpAbstractException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/exception/XbcExpExecException.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.exception;
+
+/**
+ * 表达式执行过程中的异常定义
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExecException extends XbcExpAbstractException {
+
+	private static final long serialVersionUID = 5533722714505458134L;
+
+    public XbcExpExecException(String message) {
+        super(message);
+    }
+    
+    public XbcExpExecException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/exception/XbcExpParamException.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.exception;
+
+/**
+ * 表达式参数异常定义
+ * 
+ * @author leichangchun
+ */
+public class XbcExpParamException extends XbcExpAbstractException {
+
+	private static final long serialVersionUID = 5412864567601819570L;
+
+    public XbcExpParamException(String message) {
+        super(message);
+    }
+    
+    public XbcExpParamException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/exception/XbcExpParseException.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.exception;
+
+/**
+ * 语法解析异常定义
+ * 
+ * @author leichangchun
+ */
+public class XbcExpParseException extends XbcExpAbstractException {
+
+	private static final long serialVersionUID = 2582634615112743627L;
+
+    public XbcExpParseException(String message) {
+        super(message);
+    }
+    
+    public XbcExpParseException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/exception/XbcExpScanException.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.exception;
+
+/**
+ * 此法分析异常定义
+ * 
+ * @author leichangchun
+ */
+public class XbcExpScanException extends XbcExpAbstractException {
+	
+	private static final long serialVersionUID = 7153634617398901937L;
+
+    public XbcExpScanException(String message) {
+        super(message);
+    }
+    
+    public XbcExpScanException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 308 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculateExp.java

@@ -0,0 +1,308 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.RoundingMode;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpKind;
+import xbc.formula.engine.exp.datatype.XbcExpExpOpKind;
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 表达式执行类
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculateExp {
+
+	private String expStr;
+	
+	private Map<String, XbcExpResultVo> indicatorMap;
+	
+	private XbcExpCalculatorExp2Op calculatorExp2Op;
+	
+	private XbcExpCalculatorExpItv calculatorExpItv;
+	
+	private XbcExpCalculatorExpFunc calculatorExpFunc;
+	
+	public XbcExpCalculateExp(int weishu, RoundingMode roundMode, String expStr, Map<String, XbcExpResultVo> indicatorMap, Object[] formulaParams) {
+		if(indicatorMap == null) {
+			indicatorMap = new HashMap<>();
+		}
+		this.expStr = expStr;
+		this.indicatorMap = indicatorMap;
+		calculatorExp2Op = new XbcExpCalculatorExp2Op(this, weishu, roundMode, expStr);
+		calculatorExpItv = new XbcExpCalculatorExpItv(this, weishu, roundMode, expStr);
+		calculatorExpFunc = new XbcExpCalculatorExpFunc(this, weishu, roundMode, expStr, formulaParams);
+	}
+
+	public Object calculate(XbcExpExpTree exp) {
+		if(exp == null) {
+			return null;
+		}
+		XbcExpResultVo result = new XbcExpResultVo();
+		calcExp(exp, result);
+		return result.value;
+	}
+	
+	protected void calcExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		if(exp.ek == XbcExpExpKind.OP_EK) {
+			calcOpExp(exp, result);
+			return;
+		}
+		if(exp.ek == XbcExpExpKind.ID_EK) {
+			String key = (String)exp.value;
+			XbcExpResultVo pv = indicatorMap.get(key);
+			if(pv == null) {
+				throw new XbcExpExecException("需要传入一个名为【" + key + "】的参数。公式: " + expStr);
+			}
+			result.value = pv.value;
+			return;
+		}
+		result.value = exp.value;
+		return;
+	}
+
+	private static int isAsnExOk(int ok) {
+		switch (ok) {
+		case XbcExpExpOpKind.MPTASN_OK:
+			return XbcExpExpOpKind.MPT_OK;
+		case XbcExpExpOpKind.DIVASN_OK:
+			return XbcExpExpOpKind.DIV_OK;
+		case XbcExpExpOpKind.MODASN_OK:
+			return XbcExpExpOpKind.MOD_OK;
+		case XbcExpExpOpKind.PLSASN_OK:
+			return XbcExpExpOpKind.PLS_OK;
+		case XbcExpExpOpKind.MNSASN_OK:
+			return XbcExpExpOpKind.MNS_OK;
+		case XbcExpExpOpKind.S2LASN_OK:
+			return XbcExpExpOpKind.S2L_OK;
+		case XbcExpExpOpKind.S2RASN_OK:
+			return XbcExpExpOpKind.S2R_OK;
+		case XbcExpExpOpKind.BANDASN_OK:
+			return XbcExpExpOpKind.BAND_OK;
+		case XbcExpExpOpKind.BXORASN_OK:
+			return XbcExpExpOpKind.BXOR_OK;
+		case XbcExpExpOpKind.BORASN_OK:
+			return XbcExpExpOpKind.BOR_OK;
+		}
+		return 0;
+	}
+
+	private boolean calcAsnExExp1(XbcExpExpTree exp, XbcExpResultVo result) {
+		int asnEx = isAsnExOk(exp.ok);
+		if(asnEx > 0) {
+			calcAsnExExp(exp, result, asnEx);
+		}
+		return false;
+	}
+	
+	private void calcOpExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		if(calcAsnExExp1(exp, result)) {
+			return;
+		}
+		switch(exp.ok) {
+		case XbcExpExpOpKind.ASN_OK:
+			calcAsnExp(exp, result);
+			break;
+		case XbcExpExpOpKind.QRY_OK:
+			calcQryExp(exp, result);
+			break;
+	    case XbcExpExpOpKind.MPT_OK:
+	    case XbcExpExpOpKind.DIV_OK:
+	    case XbcExpExpOpKind.MOD_OK:
+	    case XbcExpExpOpKind.PLS_OK:
+	    case XbcExpExpOpKind.MNS_OK:
+	    case XbcExpExpOpKind.S2L_OK:
+	    case XbcExpExpOpKind.S2R_OK:
+	    case XbcExpExpOpKind.BAND_OK:
+	    case XbcExpExpOpKind.BXOR_OK:
+	    case XbcExpExpOpKind.BOR_OK:
+	    case XbcExpExpOpKind.EQ_OK:
+	    case XbcExpExpOpKind.NEQ_OK:
+	    case XbcExpExpOpKind.LT_OK:
+	    case XbcExpExpOpKind.GT_OK:
+	    case XbcExpExpOpKind.LEQ_OK:
+	    case XbcExpExpOpKind.GEQ_OK:
+	    	calculatorExp2Op.calc2OperandsExp(exp, result);
+	    	break;
+	    
+	    case XbcExpExpOpKind.OR_OK:
+	    	calcOrExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.AND_OK:
+	    	calcAndExp(exp, result);
+	    	break;
+	    
+	    case XbcExpExpOpKind.BNOT_OK:
+	    	calcBnotExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.NOT_OK:
+	    	calcNotExp(exp, result);
+	    	break;
+
+	    case XbcExpExpOpKind.PREINC_OK:
+	    	calcPreincExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.AFTINC_OK:
+	    	calcAftincExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.PREDEC_OK:
+	    	calcPredecExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.AFTDEC_OK:
+	    	calcAftdecExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.FUNC_OK:
+	    	calculatorExpFunc.calcFuncExp(exp, result);
+	    	break;
+	    case XbcExpExpOpKind.ITV_OK:
+	    	calculatorExpItv.calcItvExp(exp, result);
+	    	break;
+		}
+	}
+
+	private void calcAsnExExp(XbcExpExpTree exp, XbcExpResultVo result, int asnEx) {
+		XbcExpExpTree expEx = new XbcExpExpTree(XbcExpExpOpKind.ASN_OK);
+		expEx.lchild = exp.lchild;
+		expEx.rchild = new XbcExpExpTree(asnEx);
+		expEx.rchild.lchild = exp.lchild;
+		expEx.rchild.rchild = exp.rchild;
+		calcAsnExp(expEx, result);
+	}
+	
+	private XbcExpResultVo getUnaryVn(XbcExpExpTree exp) {
+		XbcExpResultVo result = null;
+		if(exp.ek == XbcExpExpKind.ID_EK) {
+			result = indicatorMap.get((String)exp.value);
+			if(result == null) {
+				throw new XbcExpExecException("需要传入一个名为【" + (String)exp.value + "】的参数。公式: " + expStr);
+			}
+			return result;
+		}
+		if(exp.ek == XbcExpExpKind.CST_EK) {
+			throw new XbcExpExecException("不能为常量赋值。公式: " + expStr);
+		} else {
+			throw new XbcExpExecException("暂不支持为表达式赋值。公式: " + expStr);
+		}
+	}
+
+	private void calcAsnExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = getUnaryVn(exp.lchild);
+		XbcExpResultVo rr = new XbcExpResultVo();
+		calcExp(exp.rchild, rr);
+		lr.value = rr.value;
+		result.value = rr.value;
+	}
+
+	private void calcQryExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		boolean bv = calculatorExp2Op.testExpVal(exp.lchild);
+		if(bv) {
+			calcExp(exp.rchild.lchild, result);
+		} else {
+			calcExp(exp.rchild.rchild, result);
+		}
+	}
+
+	private void calcOrExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		boolean bv = calculatorExp2Op.testExpVal(exp.lchild);
+		if(bv) {
+			result.value = true;
+			return;
+		}
+		bv = calculatorExp2Op.testExpVal(exp.rchild);
+		result.value = bv;
+	}
+
+	private void calcAndExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		boolean bv = calculatorExp2Op.testExpVal(exp.lchild);
+		if(!bv) {
+			result.value = false;
+			return;
+		}
+		bv = calculatorExp2Op.testExpVal(exp.rchild);
+		result.value = bv;
+	}
+
+	private void calcBnotExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = new XbcExpResultVo();
+		calcExp(exp, lr);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)做按位或操作。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Integer.class && lr.value.getClass() != int.class) {
+			throw new XbcExpExecException("不能对非整数做按位或操作。公式: " + expStr);
+		}
+		int iv = (int)lr.value;
+		result.value = ~iv;
+	}
+
+	private void calcNotExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = new XbcExpResultVo();
+		calcExp(exp, lr);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)求非。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Boolean.class && lr.value.getClass() != boolean.class) {
+			throw new XbcExpExecException("不能对非布尔值求非。公式: " + expStr);
+		}
+		boolean bv = (boolean)lr.value;
+		result.value = !bv;
+	}
+
+	private void calcPreincExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = getUnaryVn(exp.lchild);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)做自增操作。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Integer.class && lr.value.getClass() != int.class) {
+			throw new XbcExpExecException("不能对非整数做自增操作。公式: " + expStr);
+		}
+		int iv = (int)lr.value;
+		result.value = iv + 1;
+		lr.value = iv + 1;
+	}
+
+	private void calcAftincExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = getUnaryVn(exp.lchild);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)做自增操作。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Integer.class && lr.value.getClass() != int.class) {
+			throw new XbcExpExecException("不能对非整数做自增操作。公式: " + expStr);
+		}
+		int iv = (int)lr.value;
+		result.value = iv;
+		lr.value = iv + 1;
+	}
+
+	private void calcPredecExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = getUnaryVn(exp.lchild);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)做自减操作。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Integer.class && lr.value.getClass() != int.class) {
+			throw new XbcExpExecException("不能对非整数做自减操作。公式: " + expStr);
+		}
+		int iv = (int)lr.value;
+		result.value = iv - 1;
+		lr.value = iv - 1;
+	}
+
+	private void calcAftdecExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = getUnaryVn(exp.lchild);
+		if(lr.value == null) {
+			throw new XbcExpExecException("不能对空值(null)做自减操作。公式: " + expStr);
+		}
+		if(lr.value.getClass() != Integer.class && lr.value.getClass() != int.class) {
+			throw new XbcExpExecException("不能对非整数做自减操作。公式: " + expStr);
+		}
+		int iv = (int)lr.value;
+		result.value = iv;
+		lr.value = iv - 1;
+	}
+	
+}

+ 487 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExp2Op.java

@@ -0,0 +1,487 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpOpKind;
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.util.XbcExpDateUtil;
+import xbc.formula.engine.exp.util.XbcExpNumericUtil;
+import xbc.formula.engine.exp.vo.XbcExpCalculatorExp2OpVo;
+import xbc.formula.engine.exp.vo.XbcExpCalculatorExpFuncDateTtcVo;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 二元表达式执行类
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExp2Op {
+
+	private XbcExpCalculateExp calculateExp;
+	
+	private int weishu;
+	
+	private RoundingMode roundMode;
+
+	private String expStr;
+	
+	public XbcExpCalculatorExp2Op(XbcExpCalculateExp calculateExp, int weishu, RoundingMode roundMode, String expStr) {
+		this.calculateExp = calculateExp;
+		this.weishu = weishu;
+		this.roundMode = roundMode;
+		this.expStr = expStr;
+	}
+
+	protected boolean testExpVal(XbcExpExpTree exp) {
+		XbcExpResultVo er = new XbcExpResultVo();
+		calculateExp.calcExp(exp, er);
+		if (er.value == null) {
+			return false;
+		}
+		if (er.value.getClass() == Boolean.class || er.value.getClass() == boolean.class) {
+			return (boolean) er.value;
+		}
+		if (er.value.getClass() == Integer.class || er.value.getClass() == int.class) {
+			return (int) er.value != 0;
+		}
+		throw new XbcExpExecException(er.getClass().getName() + " 不能作为布尔值使用。公式: " + expStr);
+	}
+
+	protected void calc2OperandsExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		XbcExpResultVo lr = new XbcExpResultVo();
+		XbcExpResultVo rr = new XbcExpResultVo();
+		calculateExp.calcExp(exp.lchild, lr);
+		calculateExp.calcExp(exp.rchild, rr);
+		if (lr.value == null) {
+			lr.value = 0;
+		}
+		if (rr.value == null) {
+			rr.value = 0;
+		}
+		switch (exp.ok) {
+		case XbcExpExpOpKind.MPT_OK:
+			calcMptExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.DIV_OK:
+			calcDivExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.MOD_OK:
+			calcModExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.PLS_OK:
+			calcPlsExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.MNS_OK:
+			calcMnsExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.S2L_OK:
+			calcS2lExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.S2R_OK:
+			calcS2rExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.BAND_OK:
+			calcBandExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.BXOR_OK:
+			calcBxorExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.BOR_OK:
+			calcBorExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.EQ_OK:
+			calcEqExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.NEQ_OK:
+			calcNeqExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.LT_OK:
+			calcLtExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.GT_OK:
+			calcGtExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.LEQ_OK:
+			calcLeqExp(lr, rr, result);
+			break;
+		case XbcExpExpOpKind.GEQ_OK:
+			calcGeqExp(lr, rr, result);
+			break;
+		}
+	}
+
+	private static final int BOOL_TYPE_GEQ = 1;
+	private static final int BOOL_TYPE_LEQ = 2;
+	private static final int BOOL_TYPE_GT = 3;
+	private static final int BOOL_TYPE_LT = 4;
+	private static final int BOOL_TYPE_NEQ = 5;
+	private static final int BOOL_TYPE_EQ = 6;
+
+	private void calcBoolExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result, int type) {
+		if(isNumericType(lr.value.getClass()) && isNumericType(rr.value.getClass())) {
+			XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+			result.value = false;
+			switch(type) {
+				case BOOL_TYPE_GEQ:
+					if(ov.lb.compareTo(ov.rb) >= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LEQ:
+					if(ov.lb.compareTo(ov.rb) <= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_GT:
+					if(ov.lb.compareTo(ov.rb) > 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LT:
+					if(ov.lb.compareTo(ov.rb) < 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_NEQ:
+					if(ov.lb.compareTo(ov.rb) != 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_EQ:
+					if(ov.lb.compareTo(ov.rb) == 0) {
+						result.value = true;
+					}
+					break;
+			}
+			return;
+		}
+		if(isNumericType(lr.value.getClass()) && rr.value.getClass() == String.class) {
+			if(BOOL_TYPE_NEQ == type || BOOL_TYPE_EQ == type) {
+				BigDecimal rb = null;
+				try {
+					rb = new BigDecimal((String)rr.value);
+				} catch(NumberFormatException nx) {
+					result.value = true;
+					return;
+				}
+				BigDecimal lb = XbcExpNumericUtil.translate2BigDecimal(lr.value, weishu, roundMode, expStr);
+				result.value = false;
+				if(BOOL_TYPE_NEQ == type) {
+					if(lb.compareTo(rb) != 0) {
+						result.value = true;
+					}
+				} else {
+					if(lb.compareTo(rb) == 0) {
+						result.value = true;
+					}
+				}
+				return;
+			}
+			BigDecimal lb = XbcExpNumericUtil.translate2BigDecimal(lr.value, weishu, roundMode, expStr);
+			String ls = lb.toEngineeringString();
+			String rs = (String)rr.value;
+			result.value = false;
+			switch(type) {
+				case BOOL_TYPE_GEQ:
+					if(ls.compareTo(rs) >= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LEQ:
+					if(ls.compareTo(rs) <= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_GT:
+					if(ls.compareTo(rs) > 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LT:
+					if(ls.compareTo(rs) < 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_EQ:
+					break;
+			}
+			return;
+		}
+		if(lr.value.getClass() == String.class && isNumericType(rr.value.getClass())) {
+			if(BOOL_TYPE_NEQ == type || BOOL_TYPE_EQ == type) {
+				BigDecimal lb = null;
+				try {
+					lb = new BigDecimal((String)lr.value);
+				} catch(NumberFormatException nx) {
+					result.value = true;
+					return;
+				}
+				BigDecimal rb = XbcExpNumericUtil.translate2BigDecimal(rr.value, weishu, roundMode, expStr);
+				result.value = false;
+				if(BOOL_TYPE_NEQ == type) {
+					if(lb.compareTo(rb) != 0) {
+						result.value = true;
+					}
+				} else {
+					if(lb.compareTo(rb) == 0) {
+						result.value = true;
+					}
+				}
+
+				return;
+			}
+			String ls = (String)lr.value;
+			BigDecimal rb = XbcExpNumericUtil.translate2BigDecimal(rr.value, weishu, roundMode, expStr);
+			String rs = rb.toEngineeringString();
+			result.value = false;
+			switch(type) {
+				case BOOL_TYPE_GEQ:
+					if(ls.compareTo(rs) >= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LEQ:
+					if(ls.compareTo(rs) <= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_GT:
+					if(ls.compareTo(rs) > 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LT:
+					if(ls.compareTo(rs) < 0) {
+						result.value = true;
+					}
+					break;
+			}
+			return;
+		}
+		if(lr.value.getClass() == String.class && rr.value.getClass() == String.class) {
+			String ls = (String)lr.value;
+			String rs = (String)rr.value;
+			result.value = false;
+			switch(type) {
+				case BOOL_TYPE_GEQ:
+					if(ls.compareTo(rs) >= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LEQ:
+					if(ls.compareTo(rs) <= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_GT:
+					if(ls.compareTo(rs) > 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LT:
+					if(ls.compareTo(rs) < 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_NEQ:
+					if(!lr.value.equals(rr.value)) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_EQ:
+					if(lr.value.equals(rr.value)) {
+						result.value = true;
+					}
+					break;
+			}
+			return;
+		}
+		if(XbcExpDateUtil.isDateType(lr.value) && XbcExpDateUtil.isDateType(rr.value) ||
+				XbcExpDateUtil.isDateType(lr.value) && rr.value.getClass() == String.class ||
+				lr.value.getClass() == String.class && XbcExpDateUtil.isDateType(rr.value)) {
+			XbcExpCalculatorExpFuncDateTtcVo tcvl = XbcExpDateUtil.translateToCalendar("", "", lr.value);
+			XbcExpCalculatorExpFuncDateTtcVo tcvr = XbcExpDateUtil.translateToCalendar("", "", rr.value);
+			int rc = tcvl.getCv().compareTo(tcvr.getCv());
+			result.value = false;
+			switch(type) {
+				case BOOL_TYPE_GEQ:
+					if(rc >= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LEQ:
+					if(rc <= 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_GT:
+					if(rc > 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_LT:
+					if(rc < 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_NEQ:
+					if(rc != 0) {
+						result.value = true;
+					}
+					break;
+				case BOOL_TYPE_EQ:
+					if(rc == 0) {
+						result.value = true;
+					}
+					break;
+			}
+			return;
+		}
+		throw new XbcExpExecException(lr.value.getClass() + " 与 " + rr.value.getClass() + " 不能进行对比操作。公式: " + expStr);
+	}
+
+	private void calcGeqExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_GEQ);
+	}
+
+	private void calcLeqExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_LEQ);
+	}
+
+	private void calcGtExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_GT);
+	}
+
+	private void calcLtExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_LT);
+	}
+
+	private void calcNeqExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_NEQ);
+	}
+
+	private void calcEqExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		calcBoolExp(lr, rr, result, BOOL_TYPE_EQ);
+	}
+
+	private void calcBorExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li | ri;
+	}
+
+	private void calcBxorExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li ^ ri;
+	}
+
+	private void calcBandExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li & ri;
+	}
+
+	private void calcS2rExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li >> ri;
+	}
+	
+	private void calcS2lExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li << ri;
+	}
+	
+	private void calcMnsExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		BigDecimal bv = ov.lb.subtract(ov.rb);
+		result.value = bv;
+	}
+	
+	private void calcPlsExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		if(lr.value.getClass() == String.class) {
+			String rs = null;
+			if(rr.value.getClass() == BigDecimal.class) {
+				rs = ((BigDecimal)(rr.value)).toPlainString();
+			} else {
+				rs = rr.value.toString();
+			}
+			result.value = ((String)(lr.value)) + rs;
+			return;
+		}
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		BigDecimal bv = ov.lb.add(ov.rb);
+		result.value = bv;
+	}
+	
+	private void calcModExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		int li = ov.lb.intValue();
+		int ri = ov.rb.intValue();
+		result.value = li % ri;
+	}
+
+	private void calcDivExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		if(ov.rb.compareTo(BigDecimal.ZERO) == 0) {
+			throw new XbcExpExecException("0不能作为除数。公式: " + expStr);
+		}
+		BigDecimal bv = ov.lb.divide(ov.rb, weishu, roundMode);
+		result.value = bv;
+	}
+
+	private void calcMptExp(XbcExpResultVo lr, XbcExpResultVo rr, XbcExpResultVo result) {
+		invalidSsOperands(lr, rr);
+		XbcExpCalculatorExp2OpVo ov = get2OpVo(lr, rr);
+		BigDecimal bv = ov.lb.multiply(ov.rb);
+		bv = bv.setScale(weishu, roundMode);
+		result.value = bv;
+	}
+	
+	private XbcExpCalculatorExp2OpVo get2OpVo(XbcExpResultVo lr, XbcExpResultVo rr) {
+		XbcExpCalculatorExp2OpVo ov = new XbcExpCalculatorExp2OpVo();
+		BigDecimal lb = XbcExpNumericUtil.translate2BigDecimal(lr.value, weishu, roundMode, expStr);
+		BigDecimal rb = XbcExpNumericUtil.translate2BigDecimal(rr.value, weishu, roundMode, expStr);
+		ov.lb = lb;
+		ov.rb = rb;
+		return ov;
+	}
+	
+	private static boolean isNumericType(Class<?> clazz) {
+		if (clazz != Integer.class && clazz != int.class
+				&& clazz != Long.class && clazz != long.class
+				&& clazz != Double.class && clazz != double.class
+				&& clazz != BigDecimal.class) {
+			return false;
+		}
+		return true;
+	}
+
+	private void invalidSsOperands(XbcExpResultVo lr, XbcExpResultVo rr) {
+		if (!isNumericType(lr.value.getClass())) {
+			throw new XbcExpExecException("运算不支持的数据类型: " + lr.value.getClass().getName() + "。公式: " + expStr);
+		}
+		if (!isNumericType(rr.value.getClass())) {
+			throw new XbcExpExecException("运算不支持的数据类型: " + rr.value.getClass().getName() + "。公式: " + expStr);
+		}
+	}
+	
+}

+ 119 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFunc.java

@@ -0,0 +1,119 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.ext.XbcExpExtFuncManager;
+import xbc.formula.engine.exp.util.XbcExpNumericUtil;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 表达式计算引擎自带函数执行类
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExpFunc {
+
+	private XbcExpCalculateExp calculateExp;
+
+	private int weishu;
+	
+	private RoundingMode roundMode;
+
+	private String expStr;
+	
+	private Object[] formulaParams;
+	
+	private XbcExpCalculatorExpFuncRound calculatorExpFuncRound;
+	
+	private XbcExpCalculatorExpFuncDate calculatorExpFuncDate;
+	
+	public XbcExpCalculatorExpFunc(XbcExpCalculateExp calculateExp, int weishu, RoundingMode roundMode, String expStr, Object[] formulaParams) {
+		this.calculateExp = calculateExp;
+		this.weishu = weishu;
+		this.roundMode = roundMode;
+		this.expStr = expStr;
+		this.formulaParams = formulaParams;
+		calculatorExpFuncRound = new XbcExpCalculatorExpFuncRound(this, weishu, roundMode, expStr);
+		calculatorExpFuncDate = new XbcExpCalculatorExpFuncDate(this, expStr);
+	}
+
+	public void calcFuncExp(XbcExpExpTree exp, XbcExpResultVo result) {
+		String funcName = (String)exp.lchild.value;
+		if(funcName.equals("min")) {
+			calcMinFunc(exp, result);
+		} else if(funcName.equals("max")) {
+			calcMaxFunc(exp, result);
+		} else if(funcName.equals("roundUp")) {
+			calculatorExpFuncRound.calcRoundUpFunc(exp, result);
+		} else if(funcName.equals("roundHalfUp")) {
+			calculatorExpFuncRound.calcRoundHalfUpFunc(exp, result);
+		} else if(funcName.equals("roundDown")) {
+			calculatorExpFuncRound.calcRoundDownFunc(exp, result);
+		} else if(funcName.equals("compareDate")) {
+			calculatorExpFuncDate.calcCompareDateFunc(exp, result);
+		} else if(funcName.equals("addDate")) {
+			calculatorExpFuncDate.calcAddDateFunc(exp, result);
+		} else if(funcName.equals("addMonth")) {
+			calculatorExpFuncDate.calcAddMonthFunc(exp, result);
+		} else if(funcName.equals("addHour")) {
+			calculatorExpFuncDate.calcAddHourFunc(exp, result);
+		} else if(funcName.equals("dateToString")) {
+			calculatorExpFuncDate.calcDateToStringFunc(exp, result);
+		} else if(funcName.equals("thisDay") || funcName.equals("today")) {
+			calculatorExpFuncDate.calcThisDayFunc(exp, result);
+		} else if(funcName.equals("now")) {
+			calculatorExpFuncDate.calcNowFunc(exp, result);
+		} else if(funcName.equals("diffDate")) {
+			calculatorExpFuncDate.calcDiffDateFunc(exp, result);
+		} else {
+			List<Object> pvs = getFuncParams(exp);
+			BigDecimal bv = XbcExpExtFuncManager.executeFunc(funcName, pvs, expStr, formulaParams);
+			result.value = bv;
+		}
+	}
+	
+	protected List<Object> getFuncParams(XbcExpExpTree exp) {
+		List<Object> pvs = new ArrayList<>();
+		XbcExpResultVo pr = new XbcExpResultVo();
+		for(XbcExpExpTree pe : exp.params) {
+			calculateExp.calcExp(pe, pr);
+			pvs.add(pr.value);
+		}
+		return pvs;
+	}
+	
+	private void calcMinFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+		if(exp.params.size() < 2) {
+			throw new XbcExpExecException("min函数至少需要两个参数。");
+		}
+		List<Object> pvs = getFuncParams(exp);
+		BigDecimal minBv = XbcExpNumericUtil.translate2BigDecimal(pvs.get(0), weishu, roundMode, expStr);
+		for(int k = 1;k < pvs.size();k ++) {
+			BigDecimal bv = XbcExpNumericUtil.translate2BigDecimal(pvs.get(k), weishu, roundMode, expStr);
+			if(minBv.compareTo(bv) > 0) {
+				minBv = bv;
+			}
+		}
+		result.value = minBv;
+	}
+	
+	private void calcMaxFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+		if(exp.params.size() < 2) {
+			throw new XbcExpExecException("max函数至少需要两个参数。");
+		}
+		List<Object> pvs = getFuncParams(exp);
+		BigDecimal maxBv = XbcExpNumericUtil.translate2BigDecimal(pvs.get(0), weishu, roundMode, expStr);
+		for(int k = 1;k < pvs.size();k ++) {
+			BigDecimal bv = XbcExpNumericUtil.translate2BigDecimal(pvs.get(k), weishu, roundMode, expStr);
+			if(maxBv.compareTo(bv) < 0) {
+				maxBv = bv;
+			}
+		}
+		result.value = maxBv;
+	}
+}

+ 154 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFuncDate.java

@@ -0,0 +1,154 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.List;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.util.XbcExpDateUtil;
+import xbc.formula.engine.exp.vo.XbcExpCalculatorExpFuncDateTtcVo;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 表达式计算引擎自带函数(日期类函数)执行类
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExpFuncDate {
+
+	private XbcExpCalculatorExpFunc calculatorExpFunc;
+
+	private String expStr;
+	
+	public XbcExpCalculatorExpFuncDate(XbcExpCalculatorExpFunc calculatorExpFunc, String expStr) {
+		this.calculatorExpFunc = calculatorExpFunc;
+		this.expStr = expStr;
+	}
+
+    protected void calcCompareDateFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        if(exp.params.size() < 2) {
+            throw new XbcExpExecException("compareDate函数需要两个日期类型的参数。公式: " + expStr);
+        }
+        List<Object> pvs = calculatorExpFunc.getFuncParams(exp);
+        if(pvs.get(0) == null || pvs.get(1) == null) {
+            throw new XbcExpExecException("compareDate函数的第一个参数和第二个参数都不能为空,且必须为日期类型的参数。公式: " + expStr);
+        }
+        XbcExpCalculatorExpFuncDateTtcVo ttcv1 = XbcExpDateUtil.translateToCalendar("compareDate", "一", pvs.get(0));
+        XbcExpCalculatorExpFuncDateTtcVo ttcv2 = XbcExpDateUtil.translateToCalendar("compareDate", "二", pvs.get(1));
+        int cr = ttcv1.getCv().compareTo(ttcv2.getCv());
+        if(cr == 0) {
+            result.value = 0;
+        } else if(cr > 0) {
+            result.value = 1;
+        } else {
+            result.value = -1;
+        }
+    }
+
+    protected void calcAddDateFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcCommonDateFunc("addDate", Calendar.DAY_OF_MONTH, exp, result);
+    }
+
+    protected void calcAddMonthFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcCommonDateFunc("addMonth", Calendar.MONTH, exp, result);
+    }
+
+    protected void calcAddHourFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcCommonDateFunc("addHour", Calendar.HOUR, exp, result);
+    }
+
+    protected void calcDateToStringFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        if(exp.params.size() == 0) {
+            throw new XbcExpExecException("dateToString函数需要一个或两个参数。公式: " + expStr);
+        } else if(exp.params.size() > 2) {
+            throw new XbcExpExecException("dateToString函数需要一个或两个参数。公式: " + expStr);
+        }
+        List<Object> pvs = calculatorExpFunc.getFuncParams(exp);
+        XbcExpCalculatorExpFuncDateTtcVo ttcv = XbcExpDateUtil.translateToCalendar("dateToString", "一", pvs.get(0));
+        String format = null;
+        if(pvs.size() == 1 || pvs.get(1) == null) {
+            format = "yyyy-MM-dd";
+        } else {
+            Object p2 = pvs.get(1);
+            if(p2.getClass() != String.class) {
+                throw new XbcExpExecException("dateToString函数的第二个参数应该是一个表示日期格式的字符串。公式: " + expStr);
+            }
+            format = (String)p2;
+        }
+        String ds = XbcExpDateUtil.calendarToString(ttcv.getCv(), format);
+        result.value = ds;
+    }
+
+    protected void calcThisDayFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        String ds = XbcExpDateUtil.thisDay();
+        result.value = ds;
+    }
+
+    protected void calcNowFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        String ds = XbcExpDateUtil.now();
+        result.value = ds;
+    }
+
+    protected void calcDiffDateFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        if(exp.params.size() < 2) {
+            throw new XbcExpExecException("diffDate函数需要两个日期类型的参数。公式: " + expStr);
+        }
+        List<Object> pvs = calculatorExpFunc.getFuncParams(exp);
+        if(pvs.get(0) == null || pvs.get(1) == null) {
+            throw new XbcExpExecException("diffDate函数的第一个参数和第二个参数都不能为空,且必须为日期类型的参数。公式: " + expStr);
+        }
+        XbcExpCalculatorExpFuncDateTtcVo ttcv1 = XbcExpDateUtil.translateToCalendar("compareDate", "一", pvs.get(0));
+        XbcExpCalculatorExpFuncDateTtcVo ttcv2 = XbcExpDateUtil.translateToCalendar("compareDate", "二", pvs.get(1));
+        int iv = XbcExpDateUtil.diffDate(ttcv1.getCv(), ttcv2.getCv());
+        result.value = iv;
+    }
+
+    private void calcCommonDateFunc(String funcName, int field, XbcExpExpTree exp, XbcExpResultVo result) {
+        if(exp.params.size() < 2) {
+            throw new XbcExpExecException(funcName + "函数需要两个参数。公式: " + expStr);
+        }
+        List<Object> pvs = calculatorExpFunc.getFuncParams(exp);
+        if(pvs.get(0) == null || pvs.get(1) == null) {
+            throw new XbcExpExecException(funcName + "函数的第一个参数和第二个参数都不能为空。公式: " + expStr);
+        }
+        XbcExpCalculatorExpFuncDateTtcVo ttcv = XbcExpDateUtil.translateToCalendar(funcName, "一", pvs.get(0));
+        int oi = translateToInt(funcName, "二", pvs.get(1));
+        Calendar cv = ttcv.getCv();
+        cv.add(field, oi);
+        Class<?> clazz = ttcv.getClazz();
+        Object retOv = null;
+        if(clazz == java.util.Date.class) {
+            java.util.Date dv = new java.util.Date();
+            dv.setTime(cv.getTimeInMillis());
+            retOv = dv;
+        } else if(clazz == java.sql.Date.class) {
+            java.sql.Date dv = new java.sql.Date(cv.getTimeInMillis());
+            retOv = dv;
+        } else if(clazz == Timestamp.class) {
+            Timestamp tv = new Timestamp(cv.getTimeInMillis());
+            retOv = tv;
+        } else if(clazz == Calendar.class) {
+            retOv = cv;
+        }
+        result.value = retOv;
+    }
+
+    private int translateToInt(String funcName, String pidx, Object ov) {
+        Class<?> clazz = ov.getClass();
+        if(clazz == int.class || clazz == Integer.class) {
+            int iv = (int)ov;
+            return iv;
+        }
+        if(clazz == long.class || clazz == Long.class) {
+            long lv = (long)ov;
+            return (int)lv;
+        }
+        if(clazz == BigDecimal.class) {
+            BigDecimal bv = (BigDecimal)ov;
+            return bv.intValue();
+        }
+        throw new XbcExpExecException(funcName + "函数的第" + pidx + "个参数必须为整型的参数。公式: " + expStr);
+    }
+}

+ 93 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpFuncRound.java

@@ -0,0 +1,93 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.util.XbcExpNumericUtil;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 表达式计算引擎自带函数(取舍类函数)执行类
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExpFuncRound {
+
+	private XbcExpCalculatorExpFunc calculatorExpFunc;
+
+	private int weishu;
+	
+	private RoundingMode roundMode;
+
+	private String expStr;
+	
+	public XbcExpCalculatorExpFuncRound(XbcExpCalculatorExpFunc calculatorExpFunc, int weishu, RoundingMode roundMode, String expStr) {
+		this.calculatorExpFunc = calculatorExpFunc;
+		this.weishu = weishu;
+		this.roundMode = roundMode;
+		this.expStr = expStr;
+	}
+
+    protected void calcRoundUpFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcRoundFunc("roundUp", RoundingMode.UP, exp, result);
+    }
+
+    protected void calcRoundHalfUpFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcRoundFunc("roundHalfUp", RoundingMode.HALF_UP, exp, result);
+    }
+
+    protected void calcRoundDownFunc(XbcExpExpTree exp, XbcExpResultVo result) {
+        calcRoundFunc("roundDown", RoundingMode.DOWN, exp, result);
+    }
+
+    private void calcRoundFunc(String funcName, RoundingMode roundMode, XbcExpExpTree exp, XbcExpResultVo result) {
+        if(exp.params.size() < 2) {
+            throw new XbcExpExecException(funcName + "函数需要两个参数。");
+        }
+        List<Object> pvs = calculatorExpFunc.getFuncParams(exp);
+        if(pvs.get(0) == null || pvs.get(1) == null) {
+            throw new XbcExpExecException(funcName + "函数的第一个参数和第二个参数都不能为空。");
+        }
+        BigDecimal bv1 = getBigDecimalParam(funcName, "一", pvs.get(0));
+        BigDecimal bv2 = getBigDecimalParam(funcName, "二", pvs.get(1));
+        double scale = bv2.doubleValue();
+        if(scale <= 0) {
+            throw new XbcExpExecException(funcName + "函数的第二个参数必须大于0");
+        }
+        BigDecimal bv = round(bv1, scale, roundMode);
+        result.value = bv;
+    }
+
+    private static BigDecimal round(BigDecimal bv, double scale, RoundingMode roundMode) {
+        int sc = 0;
+        if(scale < 1) {
+            while(scale < 1) {
+                sc ++;
+                scale *= 10;
+            }
+        } else if(scale >= 1 && scale < 10) {
+            sc = 0;
+        } else {
+            while(scale > 1) {
+                sc --;
+                scale /= 10;
+            }
+        }
+        BigDecimal bt = bv.setScale(sc, roundMode);
+        bt = new BigDecimal(bt.toPlainString());
+        return bt;
+    }
+    
+    private BigDecimal getBigDecimalParam(String funcName, String pidx, Object ov) {
+        BigDecimal bv = null;
+        try {
+            bv = XbcExpNumericUtil.translate2BigDecimal(ov, weishu, roundMode, expStr);
+        } catch(XbcExpExecException x) {
+            throw new XbcExpExecException(funcName + "函数的第" + pidx + "个参数不是数值类型的参数。");
+        }
+        return bv;
+    }
+}

+ 149 - 0
src/engine/java/xbc/formula/engine/exp/exec/XbcExpCalculatorExpItv.java

@@ -0,0 +1,149 @@
+package xbc.formula.engine.exp.exec;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.datatype.XbcExpToken;
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+import xbc.formula.engine.exp.util.XbcExpNumericUtil;
+import xbc.formula.engine.exp.vo.XbcExpIntervalVo;
+import xbc.formula.engine.exp.vo.XbcExpResultVo;
+
+/**
+ * 层差表达式执行类
+ * 
+ * 层差表达式, 例如:
+ *    exp { >= 0 : 1, (0, -10] : 0.9, (-10, -20] : 0.8, ...., < 100 : 0}
+ *    即: 当 exp 的值落在不同的区间时,取不同的值
+ *    
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExpItv {
+
+	private XbcExpCalculateExp calculateExp;
+
+	private int weishu;
+	
+	private RoundingMode roundMode;
+
+	private String expStr;
+	
+	public XbcExpCalculatorExpItv(XbcExpCalculateExp calculateExp, int weishu, RoundingMode roundMode, String expStr) {
+		this.calculateExp = calculateExp;
+		this.weishu = weishu;
+		this.roundMode = roundMode;
+		this.expStr = expStr;
+	}
+
+    public void calcItvExp(XbcExpExpTree exp, XbcExpResultVo result) {
+        XbcExpResultVo idr = new XbcExpResultVo();
+        calculateExp.calcExp(exp.lchild, idr);
+        BigDecimal ibv = null; // 指标值
+        try {
+            ibv = XbcExpNumericUtil.translate2BigDecimal(idr.value, weishu, roundMode, expStr);
+        } catch(XbcExpExecException x) {
+            throw new XbcExpExecException("层差计算的指标值不是数值类型: " + idr.value.getClass().getName());
+        }
+        List<XbcExpIntervalVo> itvs = exp.itvs;
+        if(itvs == null || itvs.size() == 0) {
+            throw new XbcExpExecException("层差计算没有设置区间。");
+        }
+        for(int k = 0;k < itvs.size();k ++) {
+            boolean matched = false;
+            XbcExpIntervalVo itv = itvs.get(k);
+            if(itv.token == XbcExpToken.LT_TT || itv.token == XbcExpToken.LEQ_TT || itv.token == XbcExpToken.GT_TT || itv.token == XbcExpToken.GEQ_TT) {
+                XbcExpResultVo sr = new XbcExpResultVo();
+                calculateExp.calcExp(itv.startExp, sr);
+                BigDecimal ebv = toBigDecimal(sr.value, k + 1, false);
+                int cr = ibv.compareTo(ebv);
+                if(cr < 0 && itv.token == XbcExpToken.LT_TT) {
+                    matched = true;
+                } else if(cr <= 0 && itv.token == XbcExpToken.LEQ_TT) {
+                    matched = true;
+                } else if(cr > 0 && itv.token == XbcExpToken.GT_TT) {
+                    matched = true;
+                } else if(cr >= 0 && itv.token == XbcExpToken.GEQ_TT) {
+                    matched = true;
+                }
+            } else if(itv.startExp == null && itv.endExp == null) {
+                throw new XbcExpExecException("层差表达式必须同时设置起点和终点");
+            } else {
+                XbcExpResultVo sr = new XbcExpResultVo();
+                calculateExp.calcExp(itv.startExp, sr);
+                BigDecimal sbv = toBigDecimal(sr.value, k + 1, true);
+                XbcExpResultVo er = new XbcExpResultVo();
+                calculateExp.calcExp(itv.endExp, er);
+                BigDecimal ebv = toBigDecimal(er.value, k + 1, false);
+                int scr = ibv.compareTo(sbv);
+                int ecr = ibv.compareTo(ebv);
+                if(sbv.compareTo(ebv) <= 0) { // 正向区间
+                    if(itv.startType == XbcExpIntervalVo.OPEN_INTERVAL && itv.endType == XbcExpIntervalVo.OPEN_INTERVAL) {
+                        if(scr > 0 && ecr < 0) {
+                            matched = true;
+                        }
+                    } else if(itv.startType == XbcExpIntervalVo.OPEN_INTERVAL && itv.endType == XbcExpIntervalVo.CLOSE_INTERVAL) {
+                        if(scr > 0 && ecr <= 0) {
+                            matched = true;
+                        }
+                    } else if(itv.startType == XbcExpIntervalVo.CLOSE_INTERVAL && itv.endType == XbcExpIntervalVo.OPEN_INTERVAL) {
+                        if(scr >= 0 && ecr < 0) {
+                            matched = true;
+                        }
+                    } else {
+                        if(scr >= 0 && ecr <= 0) {
+                            matched = true;
+                        }
+                    }
+                } else { // 反向区间
+                    if(itv.startType == XbcExpIntervalVo.OPEN_INTERVAL && itv.endType == XbcExpIntervalVo.OPEN_INTERVAL) {
+                        if(scr < 0 && ecr > 0) {
+                            matched = true;
+                        }
+                    } else if(itv.startType == XbcExpIntervalVo.OPEN_INTERVAL && itv.endType == XbcExpIntervalVo.CLOSE_INTERVAL) {
+                        if(scr < 0 && ecr >= 0) {
+                            matched = true;
+                        }
+                    } else if(itv.startType == XbcExpIntervalVo.CLOSE_INTERVAL && itv.endType == XbcExpIntervalVo.OPEN_INTERVAL) {
+                        if(scr <= 0 && ecr > 0) {
+                            matched = true;
+                        }
+                    } else {
+                        if(scr <= 0 && ecr >= 0) {
+                            matched = true;
+                        }
+                    }
+                }
+            }
+            if(matched) {
+            	calculateExp.calcExp(itv.valueExp, result);
+                return;
+            }
+        }
+        if(exp.rchild != null) {
+            // 如果没有落到任何区间,且指定了默认值,则使用默认值
+        	calculateExp.calcExp(exp.rchild, result);
+            return;
+        }
+        // 没有落到任何区间,且没有指定默认值,则返回 0
+        result.value = 0;
+    }
+
+    private BigDecimal toBigDecimal(Object ov, int index, boolean isS) {
+        BigDecimal bv = null;
+        try {
+            bv = XbcExpNumericUtil.translate2BigDecimal(ov, weishu, roundMode, expStr);
+        } catch(XbcExpExecException x) {
+            String err = "层差计算, 第" + index + "区间的";
+            if(isS) {
+                err = err + "起点值";
+            } else {
+                err = err + "终点值";
+            }
+            err = err + "不是数值类型: " + ov.getClass().getName();
+            throw new XbcExpExecException(err);
+        }
+        return bv;
+    }
+}

+ 14 - 0
src/engine/java/xbc/formula/engine/exp/ext/XbcExpExtFunc.java

@@ -0,0 +1,14 @@
+package xbc.formula.engine.exp.ext;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 扩展函数的接口
+ * 
+ * @author leichangchun
+ */
+public interface XbcExpExtFunc {
+
+	BigDecimal calculate(List<Object> params, Object[] formulaParams);
+}

+ 44 - 0
src/engine/java/xbc/formula/engine/exp/ext/XbcExpExtFuncManager.java

@@ -0,0 +1,44 @@
+package xbc.formula.engine.exp.ext;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+
+/**
+ * 扩展函数管理工具
+ * 
+ * @author leichangchun
+ */
+public class XbcExpExtFuncManager {
+
+	private static Map<String, XbcExpExtFunc> exFuncMap = new HashMap<>();
+	
+	/**
+	 * 向计算工具注册扩展函数
+	 * @param funcName 扩展函数名称 
+	 * @param func 扩展函数的实现类
+	 */
+	public synchronized static void registerFunc(String funcName, XbcExpExtFunc func) {
+		if(exFuncMap.containsKey(funcName)) {
+			return;
+		}
+		exFuncMap.put(funcName, func);
+	}
+	
+	public static XbcExpExtFunc getExtFunc(String funcName) {
+		return exFuncMap.get(funcName);
+	}
+	
+	public static BigDecimal executeFunc(String funcName, List<Object> pvs, String expStr, Object[] formulaParams) {
+		XbcExpExtFunc func = XbcExpExtFuncManager.getExtFunc(funcName);
+		if(func == null) {
+			throw new XbcExpExecException("不支持的函数: " + funcName + "。");
+		} else {
+			BigDecimal bv = func.calculate(pvs, formulaParams);
+			return bv;
+		}
+	}
+}

+ 556 - 0
src/engine/java/xbc/formula/engine/exp/parser/XbcExpParser.java

@@ -0,0 +1,556 @@
+package xbc.formula.engine.exp.parser;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpKind;
+import xbc.formula.engine.exp.datatype.XbcExpExpOpKind;
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+import xbc.formula.engine.exp.datatype.XbcExpExpVarKind;
+import xbc.formula.engine.exp.datatype.XbcExpToken;
+import xbc.formula.engine.exp.exception.XbcExpParseException;
+import xbc.formula.engine.exp.scanner.XbcExpScanner;
+import xbc.formula.engine.exp.vo.XbcExpDrawnInfoVo;
+import xbc.formula.engine.exp.vo.XbcExpIntervalVo;
+
+/**
+ * 语法分析,最总得到表达式树
+ * 
+ * @author leichangchun
+ */
+public class XbcExpParser {
+
+	private String expStr;
+	
+	private XbcExpScanner es;
+	
+	private int token;
+	
+	private Set<String> paramSet;
+
+	private boolean mkDiv;
+
+	public XbcExpParser(String expStr) {
+		this(expStr, false);
+	}
+	public XbcExpParser(String expStr, boolean mkDiv) {
+		this.expStr = expStr;
+		this.mkDiv = mkDiv;
+		paramSet = new HashSet<>();
+	}
+	
+	public Set<String> getParamSet() {
+		return paramSet;
+	}
+
+	public List<XbcExpDrawnInfoVo> getDrawnInfoVos() {
+		if(es == null) {
+			return null;
+		}
+		return es.getDrawnInfoVos();
+	}
+	
+	public XbcExpExpTree parse() {
+		es = new XbcExpScanner(expStr, mkDiv);
+		XbcExpExpTree exp = null;
+		token = es.readToken();
+		//while(token != XbcExpToken.EOF_TT) {
+			switch(token) {
+			case XbcExpToken.EXPNAME_TT: // variable-name
+			case XbcExpToken.INT_TT:     // integer
+			case XbcExpToken.DBL_TT:     // double
+			case XbcExpToken.STR_TT:     // string
+			case XbcExpToken.LPRN_TT:    // (
+			case XbcExpToken.SELFMNS_TT: // --
+			case XbcExpToken.SELFPLS_TT: // ++
+			case XbcExpToken.PLS_TT:     // +
+			case XbcExpToken.MNS_TT:     // -
+			case XbcExpToken.BNOT_TT:    // ~
+			case XbcExpToken.NOT_TT:     // !
+				exp = readExpTree();
+				break;
+			default:
+				throw new XbcExpParseException("未知的符号: " + XbcExpToken.getTokenName(token) + ", 公式: " + expStr);
+			}
+			token = es.readToken();
+			if(token != XbcExpToken.EOF_TT) {
+				throw new XbcExpParseException("多余的符号: " + XbcExpToken.getTokenName(token) + ", 公式: " + expStr);
+			}
+		//}
+		return exp;
+	}
+	
+	private void match(int expectedToken) {
+		if(expectedToken != token) {
+			throw new XbcExpParseException("希望符号【" + XbcExpToken.getTokenName(expectedToken) + "】, 实际符号【" + XbcExpToken.getTokenName(token) + "】。" + " 公式: " + expStr);
+		}
+		token = es.readToken();
+	}
+	
+	private XbcExpExpTree readExpTree() {
+		XbcExpExpTree exp = readAsnExp();
+		return exp;
+	}
+	
+	private int translateAsnEt(int t) {
+		switch(t) {
+	    case XbcExpToken.MODASN_TT:
+	        return XbcExpExpOpKind.MODASN_OK;
+	    case XbcExpToken.BANDASN_TT:
+	        return XbcExpExpOpKind.BANDASN_OK;
+	    case XbcExpToken.MPTASN_TT:
+	        return XbcExpExpOpKind.MPTASN_OK;
+	    case XbcExpToken.DIVASN_TT:
+	        return XbcExpExpOpKind.DIVASN_OK;
+	    case XbcExpToken.BXORASN_TT:
+	        return XbcExpExpOpKind.BXORASN_OK;
+	    case XbcExpToken.BORASN_TT:
+	        return XbcExpExpOpKind.BORASN_OK;
+	    case XbcExpToken.PLSASN_TT:
+	        return XbcExpExpOpKind.PLSASN_OK;
+	    case XbcExpToken.S2LASN_TT:
+	        return XbcExpExpOpKind.S2LASN_OK;
+	    case XbcExpToken.ASN_TT:
+	        return XbcExpExpOpKind.ASN_OK;
+	    case XbcExpToken.MNSASN_TT:
+	        return XbcExpExpOpKind.MNSASN_OK;
+	    case XbcExpToken.S2RASN_TT:
+	        return XbcExpExpOpKind.S2RASN_OK;
+		}
+		return 0;
+	}
+	// 最基础的表达式
+	private XbcExpExpTree readAsnExp() {
+		XbcExpExpTree exp = readCndExp();
+		int op = translateAsnEt(token);
+		if(op > 0) {
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			match(token);
+			exp1.lchild = exp;
+			exp1.rchild = readAsnExp();
+			exp = exp1;
+		}
+		// 读取层差表达式
+		if(token == XbcExpToken.LSQB_TT) {
+			XbcExpExpTree itvExp = readItvExp(exp);
+			exp = itvExp;
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readItvExp(XbcExpExpTree lchild) {
+		match(XbcExpToken.LSQB_TT);
+		XbcExpExpTree exp = new XbcExpExpTree(XbcExpExpOpKind.ITV_OK);
+		exp.lchild = lchild;
+		List<XbcExpIntervalVo> itvs = new ArrayList<>();
+		exp.itvs = itvs;
+		while(true) {   // 读取区间列表
+			XbcExpIntervalVo itv = new XbcExpIntervalVo();
+			itvs.add(itv);
+			if(token == XbcExpToken.CLN_TT) {
+				if(exp.rchild != null) {
+					throw new XbcExpParseException("层差表达式里面,只能设置一个默认值。 公式: " + expStr);
+				}
+				match(token);
+				exp.rchild = readAsnExp();
+				match(XbcExpToken.CMM_TT);
+			}
+			if(token == XbcExpToken.LT_TT || token == XbcExpToken.LEQ_TT || token == XbcExpToken.GT_TT || token == XbcExpToken.GEQ_TT) {
+				itv.token = token;
+				match(token);
+				itv.startExp = readAsnExp();
+			} else {
+				if(token == XbcExpToken.LPRN_TT) {  // ( 开区间
+					itv.startType = XbcExpIntervalVo.OPEN_INTERVAL;
+				} else if(token == XbcExpToken.LBKT_TT) {  // [ 闭区间
+					itv.startType = XbcExpIntervalVo.CLOSE_INTERVAL;
+				} else {  // , 无起点区间
+					throw new XbcExpParseException("希望符号【" + XbcExpToken.getTokenName(XbcExpToken.LPRN_TT) + "】或【" +
+							XbcExpToken.getTokenName(XbcExpToken.LBKT_TT) + "】, 实际符号【" + XbcExpToken.getTokenName(token) + "】。" + " 公式: " + expStr);
+				}
+				match(token);
+				if(token == XbcExpToken.CMM_TT) {
+					itv.startExp = null;
+				} else {
+					itv.startExp = readAsnExp();
+				}
+				match(XbcExpToken.CMM_TT);
+				if(token == XbcExpToken.RPRN_TT || token == XbcExpToken.RBKT_TT) {
+					if(itv.startExp == null) {
+						throw new XbcExpParseException("层差表达式的区间,不能起点和终点都为空。 公式: " + expStr);
+					}
+					itv.endExp = null;
+				} else {
+					itv.endExp = readAsnExp();
+				}
+				if(token == XbcExpToken.RPRN_TT) {
+					itv.endType = XbcExpIntervalVo.OPEN_INTERVAL;
+				} else if(token == XbcExpToken.RBKT_TT) {
+					itv.endType = XbcExpIntervalVo.CLOSE_INTERVAL;
+				} else {
+					throw new XbcExpParseException("希望符号【" + XbcExpToken.getTokenName(XbcExpToken.RPRN_TT) + "】或【" +
+							XbcExpToken.getTokenName(XbcExpToken.RBKT_TT) + "】, 实际符号【" + XbcExpToken.getTokenName(token) + "】。" + " 公式: " + expStr);
+				}
+				match(token);
+			}
+			match(XbcExpToken.CLN_TT);
+			itv.valueExp = readAsnExp();
+			if(token == XbcExpToken.RSQB_TT) {
+				match(token);
+				break;
+			}
+			if(token == XbcExpToken.CMM_TT) {
+				match(token);
+			} else {
+				throw new XbcExpParseException("希望符号【" + XbcExpToken.getTokenName(XbcExpToken.RSQB_TT) + "】或【" +
+						XbcExpToken.getTokenName(XbcExpToken.CMM_TT) + "】, 实际符号【" + XbcExpToken.getTokenName(token) + "】。" + " 公式: " + expStr);
+			}
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readCndExp() {
+		XbcExpExpTree exp = readOrExp();
+		if(token == XbcExpToken.QRY_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.QRY_OK);
+			exp1.lchild = exp;
+			exp1.rchild = new XbcExpExpTree(XbcExpExpOpKind.NULL_OK);
+			exp = exp1;
+			exp1 = exp1.rchild;
+			exp1.lchild = readAsnExp();
+			match(XbcExpToken.CLN_TT);
+			exp1.rchild = readAsnExp();
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readOrExp() {
+		XbcExpExpTree exp = readAndExp();
+		while(token == XbcExpToken.OR_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.OR_OK);
+			exp1.lchild = exp;
+			exp1.rchild = readAndExp();
+			exp = exp1;
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readAndExp() {
+		XbcExpExpTree exp = readBorExp();
+		while(token == XbcExpToken.AND_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.AND_OK);
+			exp1.lchild = exp;
+			exp1.rchild = readBorExp();
+			exp = exp1;
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readBorExp() {
+		XbcExpExpTree exp = readBxorExp();
+		while(token == XbcExpToken.BOR_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.BOR_OK);
+			exp1.lchild = exp;
+			exp1.rchild = readBxorExp();
+			exp = exp1;
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readBxorExp() {
+		XbcExpExpTree exp = readBandExp();
+		while(token == XbcExpToken.BXOR_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.BXOR_OK);
+			exp1.lchild = exp;
+			exp1.rchild = readBandExp();
+			exp = exp1;
+		}
+		return exp;
+	}
+
+	private XbcExpExpTree readBandExp() {
+		XbcExpExpTree exp = readEquExp();
+		while(token == XbcExpToken.BAND_TT) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.BAND_OK);
+			exp1.lchild = exp;
+			exp1.rchild = readEquExp();
+			exp = exp1;
+		}
+		return exp;
+	}
+
+	private int translateEquEt(int t) {
+		switch(t) {
+	    case XbcExpToken.EQ_TT:
+	        return XbcExpExpOpKind.EQ_OK;
+	    case XbcExpToken.NEQ_TT:
+	        return XbcExpExpOpKind.NEQ_OK;
+		}
+		return 0;
+	}
+	
+	private XbcExpExpTree readEquExp() {
+		XbcExpExpTree exp = readRelExp();
+		int op = translateEquEt(token);
+		while(op > 0) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			exp1.lchild = exp;
+			exp1.rchild = readRelExp();
+			exp = exp1;
+			op = translateEquEt(token);
+		}
+		return exp;
+	}
+
+	private int translateRelEt(int t) {
+		switch(t) {
+	    case XbcExpToken.LT_TT:
+	        return XbcExpExpOpKind.LT_OK;
+	    case XbcExpToken.LEQ_TT:
+	        return XbcExpExpOpKind.LEQ_OK;
+	    case XbcExpToken.GT_TT:
+	        return XbcExpExpOpKind.GT_OK;
+	    case XbcExpToken.GEQ_TT:
+	        return XbcExpExpOpKind.GEQ_OK;
+		}
+		return 0;
+	}
+	
+	private XbcExpExpTree readRelExp() {
+		XbcExpExpTree exp = readShiftExp();
+		int op = translateRelEt(token);
+		while(op > 0) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			exp1.lchild = exp;
+			exp1.rchild = readShiftExp();
+			exp = exp1;
+			op = translateRelEt(token);
+		}
+		return exp;
+	}
+
+	private int translateShiftEt(int t) {
+		switch(t) {
+	    case XbcExpToken.S2L_TT:
+	        return XbcExpExpOpKind.S2L_OK;
+	    case XbcExpToken.S2R_TT:
+	        return XbcExpExpOpKind.S2R_OK;
+		}
+		return 0;
+	}
+	
+	private XbcExpExpTree readShiftExp() {
+		XbcExpExpTree exp = readAddExp();
+		int op = translateShiftEt(token);
+		while(op > 0) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			exp1.lchild = exp;
+			exp1.rchild = readAddExp();
+			exp = exp1;
+			op = translateShiftEt(token);
+		}
+		return exp;
+	}
+
+	private int translateAddEt(int t) {
+		switch(t) {
+	    case XbcExpToken.PLS_TT:
+	        return XbcExpExpOpKind.PLS_OK;
+	    case XbcExpToken.MNS_TT:
+	        return XbcExpExpOpKind.MNS_OK;
+		}
+		return 0;
+	}
+	
+	private XbcExpExpTree readAddExp() {
+		XbcExpExpTree exp = readMulExp();
+		int op = translateAddEt(token);
+		while(op > 0) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			exp1.lchild = exp;
+			exp1.rchild = readMulExp();
+			exp = exp1;
+			op = translateAddEt(token);
+		}
+		return exp;
+	}
+
+	private int translateMulEt(int t) {
+		switch(t) {
+	    case XbcExpToken.MPT_TT:
+	        return XbcExpExpOpKind.MPT_OK;
+	    case XbcExpToken.DIV_TT:
+	        return XbcExpExpOpKind.DIV_OK;
+	    case XbcExpToken.MOD_TT:
+	        return XbcExpExpOpKind.MOD_OK;
+		}
+		return 0;
+	}
+	
+	private XbcExpExpTree readMulExp() {
+		XbcExpExpTree exp = readCastExp();
+		int op = translateMulEt(token);
+		while(op > 0) {
+			match(token);
+			XbcExpExpTree exp1 = new XbcExpExpTree(op);
+			exp1.lchild = exp;
+			exp1.rchild = readCastExp();
+			exp = exp1;
+			op = translateMulEt(token);
+		}
+		return exp;
+	}
+	
+	private XbcExpExpTree readCastExp() {
+		XbcExpExpTree exp = null;
+		switch(token) {
+		case XbcExpToken.SELFPLS_TT:
+			match(token);
+			exp = new XbcExpExpTree(XbcExpExpOpKind.PREINC_OK);
+			exp.lchild = readCastExp();
+			break;
+		case XbcExpToken.SELFMNS_TT:
+			match(token);
+			exp = new XbcExpExpTree(XbcExpExpOpKind.PREDEC_OK);
+			exp.lchild = readCastExp();
+			break;
+		case XbcExpToken.MNS_TT:
+			match(token);
+			XbcExpExpTree exp1 = readCastExp();
+			if(exp1.ek == XbcExpExpKind.CST_EK || (exp1.ok != XbcExpExpVarKind.INT_VK && exp1.ok != XbcExpExpVarKind.DBL_VK)) {
+				exp = new XbcExpExpTree(XbcExpExpOpKind.MNS_OK);
+				exp.lchild = XbcExpExpTree.newConstIntExptree("0", expStr);
+				exp.rchild = exp1;
+			} else {
+				if(exp1.ok == XbcExpExpVarKind.INT_VK) {
+					exp1.value = 0 - (int)exp1.value;
+				} else {
+					exp1.value = 0.0 - (double)exp1.value;
+				}
+			}
+			break;
+		case XbcExpToken.PLS_TT:
+			match(token);
+			exp = readCastExp();
+			break;
+		case XbcExpToken.BNOT_TT:
+			match(token);
+			exp = new XbcExpExpTree(XbcExpExpOpKind.BNOT_OK);
+			exp.lchild = readCastExp();
+			break;
+		case XbcExpToken.NOT_TT:
+			match(token);
+			exp = new XbcExpExpTree(XbcExpExpOpKind.NOT_OK);
+			exp.lchild = readCastExp();
+			break;
+		default:
+			exp = readPfExp();
+			break;
+		}
+		return exp;
+	}
+
+	private boolean isPfEt(int t) {
+		switch(t) {
+	    case XbcExpToken.LPRN_TT:
+	        return true;
+		}
+		return false;
+	}
+	
+	private void readFuncPlist(XbcExpExpTree exp, String fname) {
+		while(token != XbcExpToken.RPRN_TT) {
+			XbcExpExpTree pexp = readAsnExp();
+			exp.params.add(pexp);
+			if(token == XbcExpToken.CMM_TT) {
+				match(token);
+				if(token == XbcExpToken.RPRN_TT) {
+					throw new XbcExpParseException("解析函数【" + fname + "】的参数时出错。" + " 公式: " + expStr);
+				}
+			} else if(token != XbcExpToken.RPRN_TT) {
+				throw new XbcExpParseException("解析函数【" + fname + "】的参数时出错。" + " 公式: " + expStr);
+			}
+		}
+		match(XbcExpToken.RPRN_TT);
+	}
+	
+	private XbcExpExpTree readPfExp() {
+		XbcExpExpTree exp = readFactExp();
+		if(exp == null) {
+			return null;
+		}
+		if(exp.ek == XbcExpExpKind.CST_EK && exp.ok != XbcExpExpVarKind.STR_VK) {
+			return exp;
+		}
+		while(isPfEt(token)) {
+			switch(token) {
+			case XbcExpToken.LPRN_TT:
+				match(token);
+				XbcExpExpTree exp1 = new XbcExpExpTree(XbcExpExpOpKind.FUNC_OK);
+				exp1.lchild = exp;
+				exp1.params = new ArrayList<>();
+				paramSet.remove((String)exp.value);
+				es.setFunctionDiv((String)exp.value);
+				readFuncPlist(exp1, (String)exp.value);
+				exp = exp1;
+				break;
+			}
+		}
+		if(token == XbcExpToken.SELFMNS_TT || token == XbcExpToken.SELFPLS_TT) {
+			XbcExpExpTree exp1 = null;
+			if(token == XbcExpToken.SELFMNS_TT) {
+				exp1 = new XbcExpExpTree(XbcExpExpOpKind.AFTDEC_OK);
+			} else {
+				exp1 = new XbcExpExpTree(XbcExpExpOpKind.AFTINC_OK);
+			}
+			match(token);
+			exp1.lchild = exp;
+			exp = exp1;
+		}
+		return exp;
+	}
+	
+	private XbcExpExpTree readFactExp() {
+		XbcExpExpTree exp = null;
+		switch(token) {
+		case XbcExpToken.EXPNAME_TT:
+			String tokenContent = es.getTokenContent();
+			exp = XbcExpExpTree.newIdExptree(tokenContent);
+			paramSet.add(tokenContent);
+			match(token);
+			break;
+		case XbcExpToken.INT_TT:
+			exp = XbcExpExpTree.newConstIntExptree(es.getTokenContent(), expStr);
+			match(token);
+			break;
+		case XbcExpToken.DBL_TT:
+			exp = XbcExpExpTree.newConstDblExptree(es.getTokenContent(), expStr);
+			match(token);
+			break;
+		case XbcExpToken.STR_TT:
+			exp = XbcExpExpTree.newConstStrExptree(es.getTokenContent());
+			match(token);
+			break;
+		case XbcExpToken.LPRN_TT:
+			match(token);
+			exp = readExpTree();
+			match(XbcExpToken.RPRN_TT);
+			break;
+		default:
+			throw new XbcExpParseException("读取表达式出错: " + es.getTokenContent() + ", 公式: " + expStr);
+		}
+		return exp;
+	}
+}

+ 631 - 0
src/engine/java/xbc/formula/engine/exp/scanner/XbcExpScanner.java

@@ -0,0 +1,631 @@
+package xbc.formula.engine.exp.scanner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import xbc.formula.engine.exp.datatype.XbcExpToken;
+import xbc.formula.engine.exp.exception.XbcExpScanException;
+import xbc.formula.engine.exp.vo.XbcExpDrawnInfoVo;
+
+/**
+ * 从表达式字符串中提取token
+ * 
+ * @author leichangchun
+ */
+public class XbcExpScanner {
+
+	private static final char EOF = '\0';
+
+	private static final int SCAN_START_STATE = 0;      // start-state
+	private static final int SCAN_LC_STATE    = 1;      // line-comment-state
+	private static final int SCAN_C_STATE     = 2;      // comment-state
+	private static final int SCAN_EN_STATE    = 3;      // expname-state
+	//private static final int SCAN_EEN1_STATE  = 4;      // e-expname1-state
+	private static final int SCAN_EEN_STATE   = 5;      // e-expname-state
+	private static final int SCAN_INT1_STATE  = 6;      // integer1-state
+	private static final int SCAN_INT_STATE   = 7;      // integer-state
+	private static final int SCAN_OCT_STATE   = 8;      // octal-state
+	private static final int SCAN_HEX_STATE   = 9;      // hex-state
+	private static final int SCAN_DBL_STATE   = 10;     // double-state
+	private static final int SCAN_EDBL1_STATE = 11;     // e-double-state
+	private static final int SCAN_EDBL_STATE  = 12;     // e-double-state
+	private static final int SCAN_STR_STATE   = 13;     // string-state
+	private static final int SCAN_DONE_STATE  = 20;     // finished-state
+
+//	private static final int SCANF_INT  = 1;
+//	private static final int SCANF_OCT  = 2;
+//	private static final int SCANF_HEX  = 3;
+//	private static final int SCANF_DBL  = 4;
+//	private static final int SCANF_EDBL = 5;
+
+	private String expStr;
+
+	private int pos;
+
+	private StringBuilder tokenContent;
+
+	private int token;
+
+	private boolean mkDiv;
+
+	private List<XbcExpDrawnInfoVo> divs;
+
+	private StringBuilder divContent;
+
+	public XbcExpScanner(String expStr) {
+		this(expStr, false);
+	}
+
+	public XbcExpScanner(String expStr, boolean mkDiv) {
+		this.expStr = expStr;
+		this.mkDiv = mkDiv;
+		pos = 0;
+		tokenContent = new StringBuilder();
+		if(mkDiv) {
+			divs = new ArrayList<>();
+		}
+	}
+
+	public String getTokenContent() {
+		return tokenContent.toString();
+	}
+
+	public List<XbcExpDrawnInfoVo> getDrawnInfoVos() {
+		return divs;
+	}
+
+	public void setFunctionDiv(String funcName) {
+		if(funcName == null) {
+			return;
+		}
+		if(divs != null && divs.size() > 0) {
+			for(XbcExpDrawnInfoVo div : divs) {
+				if(div.getType() == XbcExpDrawnInfoVo.VARIABLE_TYPE && funcName.equals(div.getText())) {
+					div.setType(XbcExpDrawnInfoVo.FUNCTION_TYPE);
+				}
+			}
+		}
+	}
+
+	private void addDiv(int type) {
+		if(!mkDiv) {
+			return;
+		}
+		if(divContent.length() == 0) {
+			return;
+		}
+		int dspos = 0;
+		if(divs.size() > 0) {
+			dspos = divs.get(divs.size() - 1).getEnd() + 1;
+		}
+		XbcExpDrawnInfoVo div = new XbcExpDrawnInfoVo();
+		div.setType(type);
+		div.setStart(dspos);
+		int end = dspos + divContent.length() - 1;
+		div.setEnd(end);
+		div.setText(divContent.toString());
+		divs.add(div);
+		divContent = new StringBuilder();
+	}
+
+	private void appendDivChar(char c) {
+		if(mkDiv) {
+			divContent.append(c);
+		}
+	}
+
+	public int readToken() {
+		tokenContent = new StringBuilder();
+		if(mkDiv) {
+			divContent = new StringBuilder();
+		}
+		char c = EOF, lastC, strC = EOF, nextC;
+		int state = SCAN_START_STATE;
+		//int nFlag;
+		while(state != SCAN_DONE_STATE) {
+			lastC = c;
+			c = getNextChar();
+			switch(state) {
+			case SCAN_START_STATE:
+				if(isSpaceChar(c)) {
+					appendDivChar(c);
+					break;
+				}
+				if(mkDiv && divContent.length() > 0) {
+					addDiv(XbcExpDrawnInfoVo.SPACE_TYPE);
+				}
+				if(Character.isAlphabetic(c) || c == '_') {
+					state = SCAN_EN_STATE;
+					ungetNextChar();
+					break;
+				}
+	            if(c > '0'&&c <= '9') {
+	                tokenContent.append(c);
+					appendDivChar(c);
+	                state = SCAN_INT_STATE;
+	                break;
+	            }
+	            switch(c) {
+	            case '0':
+					appendDivChar(c);
+	            	state = SCAN_INT1_STATE;
+	                break;
+	            case '\'':
+					appendDivChar(c);
+	                strC = '\'';
+	                state = SCAN_STR_STATE;
+	                break;
+	            case '"':
+					appendDivChar(c);
+	            	strC = '"';
+	                state = SCAN_STR_STATE;
+	                break;
+	            case '#':
+					appendDivChar(c);
+	                state = SCAN_LC_STATE;
+	                break;
+	            case '/':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '/') {
+						appendDivChar(nextC);
+	                    state = SCAN_LC_STATE;
+	                }   else if(nextC == '*') {
+						appendDivChar(nextC);
+	                    lastC = 0;
+	                    state  = SCAN_C_STATE;
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+						addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                    token = XbcExpToken.DIVASN_TT;
+	                    state = SCAN_DONE_STATE;
+	                }   else {
+						addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                	token = XbcExpToken.DIV_TT;
+	                    state = SCAN_DONE_STATE;
+	                    ungetNextChar();
+	                }
+	                break;
+	            case '-':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '-') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.SELFMNS_TT;
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.MNSASN_TT;
+	                }   else {
+	                    token = XbcExpToken.MNS_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '!':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.NEQ_TT;
+	                }   else {
+	                    token = XbcExpToken.NOT_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '%':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.MODASN_TT;
+	                }   else {
+	                    token = XbcExpToken.MOD_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '&':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '&') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.AND_TT;
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.BANDASN_TT;
+	                }   else {
+	                    token = XbcExpToken.BAND_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '(':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.LPRN_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case ')':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.RPRN_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case '*':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.MPTASN_TT;
+	                }   else {
+	                    token = XbcExpToken.MPT_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case ',':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.CMM_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case ':':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.CLN_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case '?':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.QRY_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case '^':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.BXORASN_TT;
+	                }   else {
+	                    token = XbcExpToken.BXOR_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '|':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '|') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.OR_TT;
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.BORASN_TT;
+	                }   else {
+	                    token = XbcExpToken.BOR_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '~':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+                    token = XbcExpToken.BNOT_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            case '+':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '+') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.SELFPLS_TT;
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.PLSASN_TT;
+	                }   else {
+	                    token = XbcExpToken.PLS_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '<':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '<') {
+						appendDivChar(nextC);
+	                    c = getNextChar();
+	                    if(c == '=') {
+							appendDivChar(c);
+		                    token = XbcExpToken.S2LASN_TT;
+	                    } else {
+		                    token = XbcExpToken.S2L_TT;
+	                        ungetNextChar();
+	                    }
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.LEQ_TT;
+	                }   else {
+	                    token = XbcExpToken.LT_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '=':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.EQ_TT;
+	                }   else {
+	                    token = XbcExpToken.ASN_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '>':
+					appendDivChar(c);
+	            	nextC = getNextChar();
+	                if(nextC == '>') {
+						appendDivChar(nextC);
+	                    c = getNextChar();
+	                    if(c == '=') {
+							appendDivChar(c);
+		                    token = XbcExpToken.S2RASN_TT;
+	                    } else {
+		                    token = XbcExpToken.S2R_TT;
+	                        ungetNextChar();
+	                    }
+	                }   else if(nextC == '=') {
+						appendDivChar(nextC);
+	                    token = XbcExpToken.GEQ_TT;
+	                }   else {
+	                    token = XbcExpToken.GT_TT;
+	                    ungetNextChar();
+	                }
+					state = SCAN_DONE_STATE;
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	                break;
+	            case '{':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	            	token = XbcExpToken.LSQB_TT;
+					state = SCAN_DONE_STATE;
+					break;
+	            case '}':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	            	token = XbcExpToken.RSQB_TT;
+					state = SCAN_DONE_STATE;
+					break;
+	            case '[':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+	            	token = XbcExpToken.LBKT_TT;
+					state = SCAN_DONE_STATE;
+					break;
+	            case ']':
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.OPERATOR_TYPE);
+					token = XbcExpToken.RBKT_TT;
+					state = SCAN_DONE_STATE;
+					break;
+	            case EOF:
+                    token = XbcExpToken.EOF_TT;
+	                state = SCAN_DONE_STATE;
+	                break;
+	            default:
+	            	throw new XbcExpScanException(c + " 无法识别的符号。" + " 公式: " + expStr);
+	            }
+				break;
+			case SCAN_LC_STATE:    // line-comment-state
+				if(c == EOF) {
+					addDiv(XbcExpDrawnInfoVo.COMMENT_TYPE);
+					token = XbcExpToken.EOF_TT;
+	                state = SCAN_DONE_STATE;
+				} else if(c == 10) {
+					appendDivChar(c);
+					state = SCAN_START_STATE;
+				}
+				break;
+			case SCAN_C_STATE:     // comment-state
+				appendDivChar(c);
+				if(c == EOF) {
+					throw new XbcExpScanException("【/*】注释没有结束符【*/】。" + " 公式: " + expStr);
+				}   else if(lastC == '*'&&c == '/') {
+					addDiv(XbcExpDrawnInfoVo.COMMENT_TYPE);
+	                state = SCAN_START_STATE;
+	            }
+				break;
+			case SCAN_EN_STATE:    // expname-state
+	            if(Character.isAlphabetic(c) || c == '_' || (c >= '0' && c <= '9')) {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else {
+					addDiv(XbcExpDrawnInfoVo.VARIABLE_TYPE);
+					token = XbcExpToken.EXPNAME_TT;
+                    state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_EEN_STATE:   // e-expname-state
+	            if(c >= '0' && c <= '9') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else {
+					addDiv(XbcExpDrawnInfoVo.VARIABLE_TYPE);
+					token = XbcExpToken.EXPNAME_TT;
+                    state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_INT1_STATE:  // integer1-state
+	            if(c == 'x'||c == 'X') {
+					appendDivChar(c);
+	            	state = SCAN_HEX_STATE;
+				} else if(c >= '0'&&c <= '7') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	                state = SCAN_OCT_STATE;
+	            }   else if(c == '.') {
+					appendDivChar(c);
+	            	tokenContent.append('0');
+	            	tokenContent.append('.');
+	                state = SCAN_DBL_STATE;
+	            }   else {
+	                //nFlag = SCANF_INT;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.INT_TT;
+	                state = SCAN_DONE_STATE;
+	            	tokenContent.append('0');
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_INT_STATE:   // integer-state
+	            if(c >= '0' && c <= '9') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else if(c == '.') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	                state = SCAN_DBL_STATE;
+	            }   else if(c == 'e'||c == 'E') {
+					appendDivChar(c);
+	            	tokenContent.append('E');
+	                state = SCAN_EDBL1_STATE;
+	            }   else if(Character.isAlphabetic(c) || c == '"') {
+	            	throw new XbcExpScanException("整数【" + tokenContent.toString() + "】后面不能接字符: " + c + ", 公式: " + expStr);
+	            }   else {
+	                //nFlag = SCANF_INT;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.INT_TT;
+	                state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_OCT_STATE:   // octal-state
+	            if(c >= '0'&&c <= '7') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else if(Character.isAlphabetic(c)||c == '8'||c == '9'||c == '"') {
+	            	throw new XbcExpScanException("八进制整数【" + tokenContent.toString() + "】后面不能接字符: " + c + ", 公式: " + expStr);
+	            }   else {
+	                //nFlag = SCANF_OCT;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.INT_TT;
+	                state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_HEX_STATE:   // hex-state
+	            if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else if(Character.isAlphabetic(c) ||c == '"') {
+	            	throw new XbcExpScanException("十六进制整数【" + tokenContent.toString() + "】后面不能接字符: " + c + ", 公式: " + expStr);
+	            }   else {
+	                //nFlag = SCANF_HEX;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.INT_TT;
+	                state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_DBL_STATE:   // double-state
+	            if(c >= '0' && c <= '9') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else if(c == 'e'||c == 'E') {
+					appendDivChar(c);
+	            	tokenContent.append('E');
+	                state = SCAN_EDBL1_STATE;
+	            } else if(Character.isAlphabetic(c) ||c == '"') {
+	            	throw new XbcExpScanException("浮点数【" + tokenContent.toString() + "】后面不能接字符: " + c + ", 公式: " + expStr);
+	            }   else {
+	                //nFlag = SCANF_DBL;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.DBL_TT;
+	                state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_EDBL1_STATE:  // e-double-state
+				appendDivChar(c);
+	        	tokenContent.append(c);
+	            if(c == '+'||c == '-') {
+	                c = getNextChar();
+					appendDivChar(c);
+	                tokenContent.append(c);
+	                if(c >= '0' && c <= '9') {
+	                    state = SCAN_EDBL_STATE;
+	                }   else {
+	                	throw new XbcExpScanException("+-符号后面应该跟数字" + ", 公式: " + expStr);
+	                }
+	            }   else if(c >= '0' && c <= '9') {
+	                state = SCAN_EDBL_STATE;
+	            }   else {
+	            	throw new XbcExpScanException("科学计数法常数不正确。" + ", 公式: " + expStr);
+	            }
+	            break;
+	        case SCAN_EDBL_STATE:  // e-double-state
+	            if(c >= '0' && c <= '9') {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            } else {
+	                //nFlag = SCANF_EDBL;
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_NUMERIC_TYPE);
+					token = XbcExpToken.DBL_TT;
+	                state = SCAN_DONE_STATE;
+                    ungetNextChar();
+	            }
+	            break;
+	        case SCAN_STR_STATE:   // string-state
+	            if(c == EOF||c == 13||c == 10||c == 12) {
+	            	throw new XbcExpScanException("字符串常数没有结束【" + tokenContent.toString() + "】" + ", 公式: " + expStr);
+	            }   else if(c == strC) {
+					appendDivChar(c);
+					addDiv(XbcExpDrawnInfoVo.CONSTANT_STRING_TYPE);
+					token = XbcExpToken.STR_TT;
+	                state = SCAN_DONE_STATE;
+	            }   else {
+					appendDivChar(c);
+	            	tokenContent.append(c);
+	            }
+	            break;
+			}
+		}
+		return token;
+	}
+
+	private void ungetNextChar() {
+		pos --;
+	}
+
+	private char getNextChar() {
+		if(pos >= expStr.length()) {
+			pos ++;
+			return EOF;
+		}
+		char c = expStr.charAt(pos);
+		pos ++;
+		return c;
+	}
+	
+	private boolean isSpaceChar(char c) {
+		if(c == ' '||c == 10||c == 13||c == 12||c == 9) {
+			return true;
+		}
+		return false;
+	}
+}

+ 198 - 0
src/engine/java/xbc/formula/engine/exp/util/XbcExpDateUtil.java

@@ -0,0 +1,198 @@
+package xbc.formula.engine.exp.util;
+
+import java.sql.Date;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import xbc.formula.engine.exp.exception.XbcExpParamException;
+import xbc.formula.engine.exp.vo.XbcExpCalculatorExpFuncDateTtcVo;
+
+public class XbcExpDateUtil {
+
+    /**
+     * 将字符串转换成Date
+     *
+     * @param dateStr
+     * @return 失败时返回null。
+     */
+    public static Date dateFromString(String dateStr) {
+        if (dateStr == null || dateStr.isEmpty()) {
+            return null;
+        }
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            return new Date(formatter.parse(dateStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy/MM/dd");
+        try {
+            return new Date(formatter.parse(dateStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 将字符串转换成Timestamp
+     *
+     * @param tsStr
+     * @return 失败时返回null。
+     */
+    public static Timestamp timestampFromString(String tsStr) {
+        if (tsStr == null || tsStr.isEmpty()) {
+            return null;
+        }
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy-MM-dd");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("yyyy/MM/dd");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        formatter = new SimpleDateFormat("HH:mm:ss");
+        try {
+            return new Timestamp(formatter.parse(tsStr).getTime());
+        } catch (ParseException e) {
+            // e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static String thisDay() {
+        Calendar c = Calendar.getInstance();
+        Timestamp tv = new Timestamp(c.getTimeInMillis());
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        String ds = formatter.format(tv);
+        return ds;
+    }
+
+    public static String now() {
+        Calendar c = Calendar.getInstance();
+        Timestamp tv = new Timestamp(c.getTimeInMillis());
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+        String ds = formatter.format(tv);
+        return ds;
+    }
+
+    public static String calendarToString(Calendar c, String format) {
+        Timestamp tv = new Timestamp(c.getTimeInMillis());
+        SimpleDateFormat formatter = new SimpleDateFormat(format);
+        String ds = formatter.format(tv);
+        return ds;
+    }
+
+    public static int diffDate(Calendar c1, Calendar c2) {
+        // 将时分秒毫秒 设置为0
+        c1.set(Calendar.HOUR_OF_DAY, 0);
+        c1.set(Calendar.MINUTE, 0);
+        c1.set(Calendar.SECOND, 0);
+        c1.set(Calendar.MILLISECOND, 0);
+        c2.set(Calendar.HOUR_OF_DAY, 0);
+        c2.set(Calendar.MINUTE, 0);
+        c2.set(Calendar.SECOND, 0);
+        c2.set(Calendar.MILLISECOND, 0);
+        long dl = c1.getTimeInMillis() - c2.getTimeInMillis();
+        int iv = (int) (dl / (1000 * 60 * 60 * 24));
+        return iv;
+    }
+
+    public static Calendar calendarFromString(String ds) {
+        Date dv = dateFromString(ds);
+        Calendar c = Calendar.getInstance();
+        c.setTime(dv);
+        return c;
+    }
+
+    public static boolean isDateType(Object ov) {
+        if(ov == null) {
+            return false;
+        }
+        Class<?> clazz = ov.getClass();
+        if(clazz == java.util.Date.class || clazz == java.sql.Date.class || clazz == Timestamp.class || clazz == Calendar.class) {
+            return true;
+        }
+        if(clazz == String.class) {
+            Timestamp tv = timestampFromString((String) ov);
+            if (tv == null) {
+                java.sql.Date dv = dateFromString((String) ov);
+                if (dv == null) {
+                    return false;
+                }
+                return true;
+            } else {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static XbcExpCalculatorExpFuncDateTtcVo translateToCalendar(String funcName, String pidx, Object ov) {
+        Calendar c = Calendar.getInstance();
+        Class<?> clazz = ov.getClass();
+        if(clazz == java.util.Date.class) {
+            java.util.Date dv = (java.util.Date)ov;
+            c.setTimeInMillis(dv.getTime());
+        } else if(clazz == java.sql.Date.class) {
+            java.sql.Date dv = (java.sql.Date)ov;
+            c.setTimeInMillis(dv.getTime());
+        } else if(clazz == Timestamp.class) {
+            Timestamp tv = (Timestamp)ov;
+            c.setTimeInMillis(tv.getTime());
+        } else if(clazz == Calendar.class) {
+            Calendar cv = (Calendar) ov;
+            c.setTimeInMillis(cv.getTimeInMillis());
+        } else if(clazz == String.class) {
+            Timestamp tv = timestampFromString((String)ov);
+            if(tv == null) {
+                java.sql.Date dv = dateFromString((String)ov);
+                if(dv == null) {
+                    throw new XbcExpParamException(funcName + "函数的第" + pidx + "个参数无法转换成日期类型: " + (String)ov);
+                }
+                clazz = java.sql.Date.class;
+                c.setTimeInMillis(dv.getTime());
+            } else {
+                clazz = Timestamp.class;
+                c.setTimeInMillis(tv.getTime());
+            }
+        } else {
+            throw new XbcExpParamException(funcName + "函数的第" + pidx + "个参数必须为日期类型的参数。");
+        }
+        XbcExpCalculatorExpFuncDateTtcVo ttcVo = new XbcExpCalculatorExpFuncDateTtcVo();
+        ttcVo.setClazz(clazz);
+        ttcVo.setCv(c);
+        return ttcVo;
+    }
+}

+ 30 - 0
src/engine/java/xbc/formula/engine/exp/util/XbcExpNumericUtil.java

@@ -0,0 +1,30 @@
+package xbc.formula.engine.exp.util;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import xbc.formula.engine.exp.exception.XbcExpExecException;
+
+public class XbcExpNumericUtil {
+
+	public static BigDecimal translate2BigDecimal(Object value, int weishu, RoundingMode roundMode, String expStr) {
+		if(value == null) {
+			return BigDecimal.ZERO;
+		}
+		if(value.getClass() == BigDecimal.class) {
+			return (BigDecimal)value;
+		}
+		if(value.getClass() == Integer.class || value.getClass() == int.class) {
+			return new BigDecimal((int)value);
+		}
+		if(value.getClass() == Long.class || value.getClass() == long.class) {
+			return new BigDecimal((long)value);
+		}
+		if(value.getClass() == Double.class || value.getClass() == double.class) {
+			BigDecimal bv = new BigDecimal((double)value);
+			bv = bv.setScale(weishu, roundMode);
+			return bv;
+		}
+		throw new XbcExpExecException("BigDecimal类型转换,不支持的数据类型: " + value.getClass().getName() + "。公式: " + expStr);
+	}
+}

+ 13 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorExp2OpVo.java

@@ -0,0 +1,13 @@
+package xbc.formula.engine.exp.vo;
+
+import java.math.BigDecimal;
+
+/**
+ * 二元表达式执行过程中的辅助数据对象
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExp2OpVo {
+	public BigDecimal lb;
+	public BigDecimal rb;
+}

+ 29 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorExpFuncDateTtcVo.java

@@ -0,0 +1,29 @@
+package xbc.formula.engine.exp.vo;
+
+import java.util.Calendar;
+
+/**
+ * 自带日期类函数执行过程中的辅助数据对象
+ * 
+ * @author leichangchun
+ */
+public class XbcExpCalculatorExpFuncDateTtcVo {
+    private Class<?> clazz;
+    private Calendar cv;
+
+    public Class<?> getClazz() {
+        return clazz;
+    }
+
+    public void setClazz(Class<?> clazz) {
+        this.clazz = clazz;
+    }
+
+    public Calendar getCv() {
+        return cv;
+    }
+
+    public void setCv(Calendar cv) {
+        this.cv = cv;
+    }
+}

+ 20 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpCalculatorReturnVo.java

@@ -0,0 +1,20 @@
+package xbc.formula.engine.exp.vo;
+
+import java.util.Map;
+
+public class XbcExpCalculatorReturnVo {
+	private Object result;
+	private Map<String, Object> paramMap;
+	public Object getResult() {
+		return result;
+	}
+	public void setResult(Object result) {
+		this.result = result;
+	}
+	public Map<String, Object> getParamMap() {
+		return paramMap;
+	}
+	public void setParamMap(Map<String, Object> paramMap) {
+		this.paramMap = paramMap;
+	}
+}

+ 59 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpDrawnInfoVo.java

@@ -0,0 +1,59 @@
+package xbc.formula.engine.exp.vo;
+
+public class XbcExpDrawnInfoVo {
+    public static final int VARIABLE_TYPE = 1;
+    public static final int CONSTANT_NUMERIC_TYPE = 2;
+    public static final int CONSTANT_STRING_TYPE = 3;
+    public static final int OPERATOR_TYPE = 4;
+    public static final int FUNCTION_TYPE = 5;
+    public static final int SPACE_TYPE = 8;
+    public static final int COMMENT_TYPE = 9;
+    /**
+     * 开始位置, 0 为起点
+     */
+    private int start;
+    /**
+     * 结束位置
+     */
+    private int end;
+    /**
+     * 内容
+     */
+    private String text;
+    /**
+     * 类型: 变量,常量,运算符,空白,注释
+     */
+    private int type;
+
+    public int getStart() {
+        return start;
+    }
+
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    public int getEnd() {
+        return end;
+    }
+
+    public void setEnd(int end) {
+        this.end = end;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+}

+ 25 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpIntervalVo.java

@@ -0,0 +1,25 @@
+package xbc.formula.engine.exp.vo;
+
+import xbc.formula.engine.exp.datatype.XbcExpExpTree;
+
+
+/**
+ * 用于层差表达式计算的数据对象
+ * 
+ * @author leichangchun
+ */
+public class XbcExpIntervalVo {
+
+    public static final int OPEN_INTERVAL = 1; // 开区间
+    public static final int CLOSE_INTERVAL = 2; // 闭区间
+
+    public int startType; // 起点的区间类型
+    public int endType; // 终点的区间类型
+
+    public XbcExpExpTree startExp; // 起点表达式
+    public XbcExpExpTree endExp; // 终点表达式
+
+    public XbcExpExpTree valueExp; // 取值表达式
+
+    public int token; // > >= < <=
+}

+ 5 - 0
src/engine/java/xbc/formula/engine/exp/vo/XbcExpResultVo.java

@@ -0,0 +1,5 @@
+package xbc.formula.engine.exp.vo;
+
+public class XbcExpResultVo {
+	public Object value = 0;
+}

+ 160 - 0
src/engine/java/xbc/formula/engine/fml/XbcFmlCalculator.java

@@ -0,0 +1,160 @@
+package xbc.formula.engine.fml;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import xbc.formula.engine.exp.XbcExpCalculator;
+import xbc.formula.engine.exp.exception.XbcExpParamException;
+import xbc.formula.engine.exp.ext.XbcExpExtFuncManager;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+/**
+ * 公式计算类
+ * 
+ * @author leichangchun
+ */
+public class XbcFmlCalculator {
+
+	/**
+	 * 获取公式里面包含的指标的名称列表。
+	 * 
+	 * @param expStr 公式的表达式
+	 * @return 指标名称列表
+	 */
+	public static List<String> getIndicators(String expStr) {
+		List<String> ids = XbcExpCalculator.getParamters(expStr);
+		return ids;
+	}
+
+	/**
+	 * 执行公式的计算
+	 *  没有需要脚本计算出指标值的情况,请使用这个方法
+	 * 
+	 * @param fv 公式
+	 * @param params 传递给公式中包含的脚本类指标的脚本方法的参数,或Map类指标的取值源
+	 * @return 公式的计算结果
+	 */
+	public static Object calculte(XbcFmlFormulaVo fv, Object... formulaParams) {
+		Set<Long> formulaIdSet = new HashSet<>(); // 防止公式循环调用
+		Object ov = calculte(fv, formulaIdSet, formulaParams);
+		return ov;
+	}
+	
+	private static Object calculte(XbcFmlFormulaVo fv, Set<Long> formulaIdSet, Object[] formulaParams) {
+		if(fv == null) {
+			throw new XbcExpParamException("需要计算的公式是空值。");
+		}
+		Long formulaId = fv.getKeyId();
+		if(formulaIdSet.contains(formulaId)) {
+			throw new XbcExpParamException("公式不能循环调用,公式id: " + formulaId + ", 公式名称: " + fv.getFormulaName());
+		}
+		formulaIdSet.add(formulaId);
+		Object ret = executeExp(fv, formulaIdSet, formulaParams);
+		fv.setResult(ret);
+		formulaIdSet.remove(formulaId);
+		return ret;
+	}
+	
+	private static Object executeExp(XbcFmlFormulaVo fv, Set<Long> formulaIdSet, Object[] formulaParams) {
+		Map<String, Object> pm = new HashMap<>();
+		List<XbcFmlFormulaIndicatorVo> ivs = fv.getIndicatorVos();
+		if(ivs != null && ivs.size() > 0) {
+			for(XbcFmlFormulaIndicatorVo iv : ivs) {
+				String key = iv.getIndicatorName();
+				Object val = null;
+				int qt = iv.getIndicatorType();
+				if(qt == XbcFmlFormulaIndicatorVo.SPECIFIED_TYPE) {
+					try {
+						val = new BigDecimal(iv.getSpecifiedVal());
+					} catch(NumberFormatException nx) {
+						val = iv.getSpecifiedVal();
+					}
+				} else if(qt == XbcFmlFormulaIndicatorVo.SCRIPT_TYPE) {
+					val = iv.getResult();
+				} else if(qt == XbcFmlFormulaIndicatorVo.FORMULA_TYPE) {
+					val = calculte(iv.getFormulaVo(), formulaIdSet, formulaParams);
+					iv.setResult(val);
+				} else if(qt == XbcFmlFormulaIndicatorVo.PARAM_TYPE) {
+					val = getValueFromParams(iv, formulaParams);
+					iv.setResult(val);
+				} else if(qt == XbcFmlFormulaIndicatorVo.FUNC_TYPE) {
+					val = getValueFromFunc(iv, fv.getFormulaValue(), formulaParams);
+					iv.setResult(val);
+				} else {
+					throw new XbcExpParamException("不支持的指标类型: " + qt + ", 指标keyid: " + iv.getKeyId() + ", 指标名称: " + iv.getIndicatorName());
+				}
+				pm.put(key, val);
+			}
+		}
+		String expStr = fv.getFormulaValue();
+		Object retObj = XbcExpCalculator.calculate(expStr, pm, formulaParams);
+		return retObj;
+	}
+	
+	private static Object getValueFromFunc(XbcFmlFormulaIndicatorVo iv, String expStr, Object[] formulaParams) {
+		String funcName = iv.getSpecifiedVal();
+		List<Object> pvs = new ArrayList<>();
+		pvs.add(iv);
+		BigDecimal bv = null;
+		bv = XbcExpExtFuncManager.executeFunc(funcName, pvs, expStr, formulaParams);
+		return bv;
+	}
+
+	@SuppressWarnings("rawtypes")
+	private static Object getValueFromParams(XbcFmlFormulaIndicatorVo iv, Object[] formulaParams) {
+		if(formulaParams == null) {
+			return null;
+		}
+		// 查找传入的参数里面,是否有Map
+		Map map = null;
+		for(Object param : formulaParams) {
+			if(param != null && Map.class.isAssignableFrom(param.getClass())) {
+				map = (Map)param;
+				break;
+			}
+		}
+		if(map != null) {
+			if(map.containsKey(iv.getSpecifiedVal())) {
+				return map.get(iv.getSpecifiedVal());
+			}
+		}
+		// 查找传入的参数里面,是否有包含 iv.getScriptFieldId() 的对象
+		Field field = null;
+		Object inst = null;
+		for(Object param : formulaParams) {
+			if(param != null) {
+				field = getFieldByName(param.getClass(), iv.getSpecifiedVal());
+				if(field != null) {
+					inst = param;
+					break;
+				}
+			}
+		}
+		if(field != null) {
+			field.setAccessible(true);
+			try {
+				return field.get(inst);
+			} catch (IllegalArgumentException | IllegalAccessException e) {
+				throw new XbcExpParamException(e, "从参数的对象[" + inst.getClass() + "]获取字段[" + iv.getSpecifiedVal() + "]时出错。指标keyid: " + iv.getKeyId());
+			}
+		}
+		throw new XbcExpParamException("传入的参数中没有找到指标(参数名: " + iv.getSpecifiedVal() + ")需要的值。指标keyid: " + iv.getKeyId() + ", 指标名称: " + iv.getIndicatorName());
+	}
+
+	private static Field getFieldByName(Class<?> clazz, String name) {
+		Field field = null;
+		try {
+			field = clazz.getDeclaredField(name);
+		} catch (NoSuchFieldException | SecurityException e) {
+			//
+		}
+		return field;
+	}
+}

+ 88 - 0
src/engine/java/xbc/formula/engine/fml/vo/XbcFmlFormulaIndicatorVo.java

@@ -0,0 +1,88 @@
+package xbc.formula.engine.fml.vo;
+
+/**
+ * 指标数据对象
+ * 
+ * @author leichangchun
+ */
+public class XbcFmlFormulaIndicatorVo {
+    // 指标类型
+    public static final int SPECIFIED_TYPE = 1; // 指定值
+    public static final int SCRIPT_TYPE = 2;    // 脚本
+    public static final int FORMULA_TYPE = 3;   // 公式
+    public static final int PARAM_TYPE = 4;     // 参数
+    public static final int FUNC_TYPE = 5;      // 函数
+
+    private long keyId;
+    private int indicatorType;
+    private String indicatorName;
+    private String indicatorCode;
+    private String specifiedVal;
+    private String indicatorComment;
+    private XbcFmlFormulaVo formulaVo;
+    private Object result;
+    
+	public long getKeyId() {
+		return keyId;
+	}
+	public void setKeyId(long keyId) {
+		this.keyId = keyId;
+	}
+	public int getIndicatorType() {
+		return indicatorType;
+	}
+	public void setIndicatorType(int indicatorType) {
+		this.indicatorType = indicatorType;
+	}
+	public String getIndicatorName() {
+		return indicatorName;
+	}
+	public void setIndicatorName(String indicatorName) {
+		this.indicatorName = indicatorName;
+	}
+	public String getIndicatorCode() {
+		return indicatorCode;
+	}
+	public void setIndicatorCode(String indicatorCode) {
+		this.indicatorCode = indicatorCode;
+	}
+	public String getSpecifiedVal() {
+		return specifiedVal;
+	}
+	public void setSpecifiedVal(String specifiedVal) {
+		this.specifiedVal = specifiedVal;
+	}
+	public String getIndicatorComment() {
+		return indicatorComment;
+	}
+	public void setIndicatorComment(String indicatorComment) {
+		this.indicatorComment = indicatorComment;
+	}
+	public XbcFmlFormulaVo getFormulaVo() {
+		return formulaVo;
+	}
+	public void setFormulaVo(XbcFmlFormulaVo formulaVo) {
+		this.formulaVo = formulaVo;
+	}
+	public static int getSpecifiedType() {
+		return SPECIFIED_TYPE;
+	}
+	public static int getScriptType() {
+		return SCRIPT_TYPE;
+	}
+	public static int getFormulaType() {
+		return FORMULA_TYPE;
+	}
+	public static int getParamType() {
+		return PARAM_TYPE;
+	}
+	public static int getFuncType() {
+		return FUNC_TYPE;
+	}
+	public Object getResult() {
+		return result;
+	}
+	public void setResult(Object result) {
+		this.result = result;
+	}
+}

+ 61 - 0
src/engine/java/xbc/formula/engine/fml/vo/XbcFmlFormulaVo.java

@@ -0,0 +1,61 @@
+package xbc.formula.engine.fml.vo;
+
+import java.util.List;
+
+/**
+ * 公式数据对象
+ * 
+ * @author leichangchun
+ */
+public class XbcFmlFormulaVo {
+    private long keyId;
+    private String formulaName;
+    private String formulaCode;
+    private String formulaValue;
+    private String formulaDesc;
+    private List<XbcFmlFormulaIndicatorVo> indicatorVos;
+    private Object result;
+    
+	public long getKeyId() {
+		return keyId;
+	}
+	public void setKeyId(long keyId) {
+		this.keyId = keyId;
+	}
+	public String getFormulaName() {
+		return formulaName;
+	}
+	public void setFormulaName(String formulaName) {
+		this.formulaName = formulaName;
+	}
+	public String getFormulaCode() {
+		return formulaCode;
+	}
+	public void setFormulaCode(String formulaCode) {
+		this.formulaCode = formulaCode;
+	}
+	public String getFormulaValue() {
+		return formulaValue;
+	}
+	public void setFormulaValue(String formulaValue) {
+		this.formulaValue = formulaValue;
+	}
+	public String getFormulaDesc() {
+		return formulaDesc;
+	}
+	public void setFormulaDesc(String formulaDesc) {
+		this.formulaDesc = formulaDesc;
+	}
+	public List<XbcFmlFormulaIndicatorVo> getIndicatorVos() {
+		return indicatorVos;
+	}
+	public void setIndicatorVos(List<XbcFmlFormulaIndicatorVo> indicatorVos) {
+		this.indicatorVos = indicatorVos;
+	}
+	public Object getResult() {
+		return result;
+	}
+	public void setResult(Object result) {
+		this.result = result;
+	}
+}

+ 17 - 0
src/main/java/xbc/formula/CalculateManagerApplication.java

@@ -0,0 +1,17 @@
+package xbc.formula;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@SpringBootApplication
+@MapperScan("xbc.formula.*.dao")
+public class CalculateManagerApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(CalculateManagerApplication.class, args);
+	}
+
+}

+ 34 - 0
src/main/java/xbc/formula/calcmgr/controller/CalculateManagerController.java

@@ -0,0 +1,34 @@
+package xbc.formula.calcmgr.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import xbc.formula.calcmgr.service.CalculateManagerService;
+import xbc.formula.calcmgr.vo.CalcResultDescVo;
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+
+@RestController
+@RequestMapping("/calculatemanager")
+public class CalculateManagerController {
+	
+	@Autowired
+	private CalculateManagerService calculateManagerService;
+
+    @PostMapping("/calculate")
+	public CalculateResponseVo calculate(@RequestBody CalculateRequestVo requestVo) {
+    	CalculateResponseVo responseVo = calculateManagerService.calculate(requestVo);
+    	return responseVo;
+    }
+
+    @GetMapping("/desc")
+    public CalcResultDescVo getCalcResultDesc(@RequestParam("calcResultId") long calcResultId) {
+    	CalcResultDescVo crdv = calculateManagerService.getCalcResultDesc(calcResultId);
+    	return crdv;
+    }
+}

+ 8 - 0
src/main/java/xbc/formula/calcmgr/dao/CalcFormulaDao.java

@@ -0,0 +1,8 @@
+package xbc.formula.calcmgr.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface CalcFormulaDao {
+	
+	String getFormulaCode(@Param("productId") long productId, @Param("bizType") String bizType);
+}

+ 14 - 0
src/main/java/xbc/formula/calcmgr/dao/CalcIndicatorResultDao.java

@@ -0,0 +1,14 @@
+package xbc.formula.calcmgr.dao;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+import xbc.formula.calcmgr.dao.entity.CalcIndicatorResult;
+
+public interface CalcIndicatorResultDao {
+	
+	void saveBatch(@Param("pos") List<CalcIndicatorResult> cirs);
+	
+	List<CalcIndicatorResult> getByCalcResultId(@Param("calcResultId") long calcResultId);
+}

+ 12 - 0
src/main/java/xbc/formula/calcmgr/dao/CalcNoManagerDao.java

@@ -0,0 +1,12 @@
+package xbc.formula.calcmgr.dao;
+
+import java.sql.Timestamp;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface CalcNoManagerDao {
+
+	long getCurrentNoLock(@Param("resultType") int resultType);
+	
+	void updateCurrentNo(@Param("resultType") int resultType, @Param("noValue") long noValue, @Param("thistime") Timestamp thistime);
+}

+ 12 - 0
src/main/java/xbc/formula/calcmgr/dao/CalcResultDao.java

@@ -0,0 +1,12 @@
+package xbc.formula.calcmgr.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+import xbc.formula.calcmgr.dao.entity.CalcResult;
+
+public interface CalcResultDao {
+	
+	void save(CalcResult cr);
+	
+	CalcResult getByKeyId(@Param("keyId") long keyId);
+}

+ 90 - 0
src/main/java/xbc/formula/calcmgr/dao/entity/CalcIndicatorResult.java

@@ -0,0 +1,90 @@
+package xbc.formula.calcmgr.dao.entity;
+
+import java.sql.Timestamp;
+
+public class CalcIndicatorResult {
+	private long keyId;
+	private long calcResultId;
+	private long indicatorId;
+	private int indicatorType;
+	private String indicatorName;
+	private String specifiedVal;
+	private String calcResultType;
+	private String calcResult;
+	private long parentId;
+	private String nodeCode;
+	private String formulaValue;
+	private Timestamp createTime;
+	public long getKeyId() {
+		return keyId;
+	}
+	public void setKeyId(long keyId) {
+		this.keyId = keyId;
+	}
+	public long getCalcResultId() {
+		return calcResultId;
+	}
+	public void setCalcResultId(long calcResultId) {
+		this.calcResultId = calcResultId;
+	}
+	public long getIndicatorId() {
+		return indicatorId;
+	}
+	public void setIndicatorId(long indicatorId) {
+		this.indicatorId = indicatorId;
+	}
+	public int getIndicatorType() {
+		return indicatorType;
+	}
+	public void setIndicatorType(int indicatorType) {
+		this.indicatorType = indicatorType;
+	}
+	public String getIndicatorName() {
+		return indicatorName;
+	}
+	public void setIndicatorName(String indicatorName) {
+		this.indicatorName = indicatorName;
+	}
+	public String getSpecifiedVal() {
+		return specifiedVal;
+	}
+	public void setSpecifiedVal(String specifiedVal) {
+		this.specifiedVal = specifiedVal;
+	}
+	public String getCalcResultType() {
+		return calcResultType;
+	}
+	public void setCalcResultType(String calcResultType) {
+		this.calcResultType = calcResultType;
+	}
+	public String getCalcResult() {
+		return calcResult;
+	}
+	public void setCalcResult(String calcResult) {
+		this.calcResult = calcResult;
+	}
+	public long getParentId() {
+		return parentId;
+	}
+	public void setParentId(long parentId) {
+		this.parentId = parentId;
+	}
+	public String getNodeCode() {
+		return nodeCode;
+	}
+	public void setNodeCode(String nodeCode) {
+		this.nodeCode = nodeCode;
+	}
+	public String getFormulaValue() {
+		return formulaValue;
+	}
+	public void setFormulaValue(String formulaValue) {
+		this.formulaValue = formulaValue;
+	}
+	public Timestamp getCreateTime() {
+		return createTime;
+	}
+	public void setCreateTime(Timestamp createTime) {
+		this.createTime = createTime;
+	}
+}

+ 76 - 0
src/main/java/xbc/formula/calcmgr/dao/entity/CalcResult.java

@@ -0,0 +1,76 @@
+package xbc.formula.calcmgr.dao.entity;
+
+import java.sql.Timestamp;
+
+public class CalcResult {
+	private long keyId;
+	private long productId;
+	private String bizType;
+	private String bizCode;
+	private String formulaCode;
+	private String calcResultType;
+	private String calcResult;
+	private String formulaName;
+	private String formulaValue;
+	private Timestamp createTime;
+	public long getKeyId() {
+		return keyId;
+	}
+	public void setKeyId(long keyId) {
+		this.keyId = keyId;
+	}
+	public long getProductId() {
+		return productId;
+	}
+	public void setProductId(long productId) {
+		this.productId = productId;
+	}
+	public String getBizType() {
+		return bizType;
+	}
+	public void setBizType(String bizType) {
+		this.bizType = bizType;
+	}
+	public String getBizCode() {
+		return bizCode;
+	}
+	public void setBizCode(String bizCode) {
+		this.bizCode = bizCode;
+	}
+	public String getFormulaCode() {
+		return formulaCode;
+	}
+	public void setFormulaCode(String formulaCode) {
+		this.formulaCode = formulaCode;
+	}
+	public String getCalcResultType() {
+		return calcResultType;
+	}
+	public void setCalcResultType(String calcResultType) {
+		this.calcResultType = calcResultType;
+	}
+	public String getCalcResult() {
+		return calcResult;
+	}
+	public void setCalcResult(String calcResult) {
+		this.calcResult = calcResult;
+	}
+	public String getFormulaName() {
+		return formulaName;
+	}
+	public void setFormulaName(String formulaName) {
+		this.formulaName = formulaName;
+	}
+	public String getFormulaValue() {
+		return formulaValue;
+	}
+	public void setFormulaValue(String formulaValue) {
+		this.formulaValue = formulaValue;
+	}
+	public Timestamp getCreateTime() {
+		return createTime;
+	}
+	public void setCreateTime(Timestamp createTime) {
+		this.createTime = createTime;
+	}
+}

+ 9 - 0
src/main/java/xbc/formula/calcmgr/logic/CalculateManagerLogic.java

@@ -0,0 +1,9 @@
+package xbc.formula.calcmgr.logic;
+
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+
+public interface CalculateManagerLogic {
+	
+	CalculateResponseVo calculate(CalculateRequestVo requestVo);
+}

+ 8 - 0
src/main/java/xbc/formula/calcmgr/logic/QueryCalcResultLogic.java

@@ -0,0 +1,8 @@
+package xbc.formula.calcmgr.logic;
+
+import xbc.formula.calcmgr.vo.CalcResultDescVo;
+
+public interface QueryCalcResultLogic {
+
+	CalcResultDescVo getCalcResultDesc(long calcResultId);
+}

+ 10 - 0
src/main/java/xbc/formula/calcmgr/logic/SaveResultLogic.java

@@ -0,0 +1,10 @@
+package xbc.formula.calcmgr.logic;
+
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+public interface SaveResultLogic {
+
+	void saveResult(CalculateRequestVo requestVo, CalculateResponseVo responseVo, XbcFmlFormulaVo fv);
+}

+ 49 - 0
src/main/java/xbc/formula/calcmgr/logic/impl/CalculateManagerLogicImpl.java

@@ -0,0 +1,49 @@
+package xbc.formula.calcmgr.logic.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import xbc.formula.calcmgr.dao.CalcFormulaDao;
+import xbc.formula.calcmgr.logic.CalculateManagerLogic;
+import xbc.formula.calcmgr.logic.SaveResultLogic;
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+import xbc.formula.engine.fml.XbcFmlCalculator;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+import xbc.formula.fmlmgr.logic.FormulaLogic;
+import xbc.formula.framework.exception.FormulaException;
+
+@Component
+public class CalculateManagerLogicImpl implements CalculateManagerLogic {
+	
+	@Autowired
+	private CalcFormulaDao calcFormulaDao;
+	
+	@Autowired
+	private FormulaLogic formulaLogic;
+	
+	@Autowired
+	private SaveResultLogic saveResultLogic;
+
+	@Override
+	public CalculateResponseVo calculate(CalculateRequestVo requestVo) {
+		if(requestVo == null) {
+			throw new FormulaException("请输入计算参数。");
+		}
+		String formulaCode = calcFormulaDao.getFormulaCode(requestVo.getProductId(), requestVo.getBizType());
+		if(formulaCode == null) {
+			throw new FormulaException("没有找到对应的公式编码,product_id: " + requestVo.getProductId() + ", biz_type: " + requestVo.getBizType());
+		}
+		XbcFmlFormulaVo fv = formulaLogic.getFormulaByCode(formulaCode);
+		if(fv == null) {
+			throw new FormulaException("没有找到公式,公式编码: " + formulaCode);
+		}
+		Object ov = XbcFmlCalculator.calculte(fv, requestVo.getParamMap());
+		CalculateResponseVo responseVo = new CalculateResponseVo();
+		responseVo.setResultType(ov.getClass().getName());
+		responseVo.setResultValue(ov.toString());
+		saveResultLogic.saveResult(requestVo, responseVo, fv);
+		return responseVo;
+	}
+
+}

+ 68 - 0
src/main/java/xbc/formula/calcmgr/logic/impl/QueryCalcResultLogicImpl.java

@@ -0,0 +1,68 @@
+package xbc.formula.calcmgr.logic.impl;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import xbc.formula.calcmgr.dao.CalcIndicatorResultDao;
+import xbc.formula.calcmgr.dao.CalcResultDao;
+import xbc.formula.calcmgr.dao.entity.CalcIndicatorResult;
+import xbc.formula.calcmgr.dao.entity.CalcResult;
+import xbc.formula.calcmgr.logic.QueryCalcResultLogic;
+import xbc.formula.calcmgr.vo.CalcResultDescVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo;
+import xbc.formula.framework.exception.FormulaException;
+
+@Component
+public class QueryCalcResultLogicImpl implements QueryCalcResultLogic {
+	
+	@Autowired
+	private CalcResultDao calcResultDao;
+	
+	@Autowired
+	private CalcIndicatorResultDao calcIndicatorResultDao;
+
+	@Override
+	public CalcResultDescVo getCalcResultDesc(long calcResultId) {
+		CalcResult cr = calcResultDao.getByKeyId(calcResultId);
+		if(cr == null) {
+			throw new FormulaException("没有找到计算结果, keyId: " + calcResultId);
+		}
+		CalcResultDescVo crdv = new CalcResultDescVo();
+		crdv.setProductId(cr.getProductId());
+		crdv.setBizType(cr.getBizType());
+		crdv.setBizCode(cr.getBizCode());
+		crdv.setFormulaName(cr.getFormulaName());
+		crdv.setFormulaCode(cr.getFormulaCode());
+		crdv.setFormulaValue(cr.getFormulaValue());
+		List<CalcIndicatorResult> cirs = calcIndicatorResultDao.getByCalcResultId(calcResultId);
+        StringBuilder sb = new StringBuilder();
+		sb.append(cr.getFormulaName() + ": " + cr.getCalcResult() + ": " + cr.getFormulaValue() + "\r\n");
+		for(CalcIndicatorResult cir : cirs) {
+			String ssv = null;
+        	if(cir.getIndicatorType() == XbcFmlFormulaIndicatorVo.PARAM_TYPE) {
+        		ssv = "(" + cir.getSpecifiedVal() + ")";
+        	} else {
+        		ssv = "";
+        	}
+            String line = getPrefix(cir.getNodeCode()) + cir.getIndicatorName() + ssv + ": " + cir.getCalcResult();
+            sb.append(line);
+            if(cir.getIndicatorType() == XbcFmlFormulaIndicatorVo.FORMULA_TYPE) {
+                sb.append(": " + cir.getFormulaValue());
+            }
+            sb.append("\r\n");
+		}
+        String desc = sb.toString();
+        crdv.setDesc(desc);
+		return crdv;
+	}
+
+    private String getPrefix(String nodeCode) {
+        StringBuilder sb = new StringBuilder();
+        for(int k = 0;k < nodeCode.length();k ++) {
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+}

+ 181 - 0
src/main/java/xbc/formula/calcmgr/logic/impl/SaveResultLogicImpl.java

@@ -0,0 +1,181 @@
+package xbc.formula.calcmgr.logic.impl;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import xbc.formula.calcmgr.dao.CalcIndicatorResultDao;
+import xbc.formula.calcmgr.dao.CalcNoManagerDao;
+import xbc.formula.calcmgr.dao.CalcResultDao;
+import xbc.formula.calcmgr.dao.entity.CalcIndicatorResult;
+import xbc.formula.calcmgr.dao.entity.CalcResult;
+import xbc.formula.calcmgr.logic.SaveResultLogic;
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+@Component
+public class SaveResultLogicImpl implements SaveResultLogic {
+	
+	private static final int NO_TYPE_FORMULA = 1;
+	
+	private static final int NO_TYPE_INDICATOR = 2;
+
+	@Autowired
+	private CalcNoManagerDao calcNoManagerDao;
+	
+	@Autowired
+	private CalcResultDao calcResultDao;
+	
+	@Autowired
+	private CalcIndicatorResultDao calcIndicatorResultDao;
+	
+	@Override
+	public void saveResult(CalculateRequestVo requestVo, CalculateResponseVo responseVo, XbcFmlFormulaVo fv) {
+		if(!requestVo.isSaveResult()) {
+			responseVo.setCalcResultId(0);
+			return;
+		}
+		
+		Timestamp thistime = new Timestamp(System.currentTimeMillis());
+		CalcResult cr = createFormulaResult(requestVo, fv, thistime);
+		RetrieveIndicatorVo riv = new RetrieveIndicatorVo();
+		riv.thistime = thistime;
+		riv.cirs = new ArrayList<>();
+		riv.savedFvSet = new HashSet<>();
+		riv.pos = 1;
+		retrieveIndicators(fv, 0, "", riv);
+		
+		long formulaResultId = calcNoManagerDao.getCurrentNoLock(NO_TYPE_FORMULA);
+		formulaResultId ++;
+		calcNoManagerDao.updateCurrentNo(NO_TYPE_FORMULA, formulaResultId, thistime);
+		cr.setKeyId(formulaResultId);
+		calcResultDao.save(cr);
+		long baseIndicatorId = calcNoManagerDao.getCurrentNoLock(NO_TYPE_INDICATOR);
+		if(riv.cirs.size() > 0) {
+			for(CalcIndicatorResult cir : riv.cirs) {
+				cir.setCalcResultId(formulaResultId);
+				cir.setKeyId(cir.getKeyId() + baseIndicatorId);
+				long pid = cir.getParentId();
+				if(pid > 0) {
+					cir.setParentId(pid + baseIndicatorId);
+				}
+			}
+			saveIndicators(riv.cirs);
+			calcNoManagerDao.updateCurrentNo(NO_TYPE_INDICATOR, baseIndicatorId + riv.cirs.size(), thistime);
+		}
+		responseVo.setCalcResultId(formulaResultId);
+	}
+	
+	private class RetrieveIndicatorVo {
+		protected Timestamp thistime;
+		protected List<CalcIndicatorResult> cirs;
+		protected long pos;
+		protected Set<String> savedFvSet;
+	}
+	
+	private static final int SAVE_SIZE_ONCE = 100;
+	
+	private void saveIndicators(List<CalcIndicatorResult> cirs) {
+		if(cirs.size() <= SAVE_SIZE_ONCE) {
+			calcIndicatorResultDao.saveBatch(cirs);
+			return;
+		}
+		List<CalcIndicatorResult> list = new ArrayList<>(SAVE_SIZE_ONCE);
+		for(int k = 0;k < cirs.size();k ++) {
+			if(k > 0 && (k % SAVE_SIZE_ONCE == 0)) {
+				System.out.println("save, list.size: " + list.size());
+				calcIndicatorResultDao.saveBatch(list);
+				list.clear();
+			}
+			list.add(cirs.get(k));
+		}
+		if(list.size() > 0) {
+			System.out.println("save, list.size: " + list.size());
+			calcIndicatorResultDao.saveBatch(list);
+		}
+	}
+	
+	private void retrieveIndicators(XbcFmlFormulaVo fv, long parentId, String baseNodeCode, RetrieveIndicatorVo riv) {
+		if(riv.savedFvSet.contains(fv.getFormulaCode())) {
+			return;
+		}
+		riv.savedFvSet.add(fv.getFormulaCode());
+		List<XbcFmlFormulaIndicatorVo> ivs = fv.getIndicatorVos();
+		if(ivs == null || ivs.size() == 0) {
+			return;
+		}
+		ivs = ivs.stream().sorted(Comparator.comparing(XbcFmlFormulaIndicatorVo::getIndicatorName)).collect(Collectors.toList());
+		int pos = 0;
+		for(XbcFmlFormulaIndicatorVo iv : ivs) {
+			String nodeCode = baseNodeCode + ((10000 + pos) + "").substring(1);
+			pos ++;
+			CalcIndicatorResult cir = new CalcIndicatorResult();
+			cir.setKeyId(riv.pos);
+			riv.pos = riv.pos + 1;
+			cir.setCalcResultId(0);
+			cir.setIndicatorId(iv.getKeyId());
+			cir.setIndicatorType(iv.getIndicatorType());
+			cir.setIndicatorName(iv.getIndicatorName());
+			cir.setSpecifiedVal(iv.getSpecifiedVal());
+			cir.setCalcResultType(iv.getResult() == null ? "" : iv.getResult().getClass().getName());
+			cir.setCalcResult(objToString(iv.getResult()));
+			cir.setParentId(parentId);
+			cir.setNodeCode(nodeCode);
+			cir.setCreateTime(riv.thistime);
+			if(iv.getIndicatorType() == XbcFmlFormulaIndicatorVo.FORMULA_TYPE) {
+				XbcFmlFormulaVo tmpFv = iv.getFormulaVo();
+				cir.setFormulaValue(tmpFv.getFormulaValue());
+				retrieveIndicators(tmpFv, cir.getKeyId(), nodeCode, riv);
+			} else {
+				cir.setFormulaValue("");
+			}
+			riv.cirs.add(cir);
+		}
+	}
+
+	private CalcResult createFormulaResult(CalculateRequestVo requestVo, XbcFmlFormulaVo fv, Timestamp thistime) {
+		CalcResult cr = new CalcResult();
+		cr.setKeyId(0);
+		cr.setProductId(requestVo.getProductId());
+		cr.setBizType(requestVo.getBizType());
+		if(requestVo.getBizCode() != null) {
+			cr.setBizCode(requestVo.getBizCode());
+		} else {
+			cr.setBizCode("");
+		}
+		cr.setFormulaCode(fv.getFormulaCode());
+		cr.setCalcResultType(fv.getResult() == null ? "" : fv.getResult().getClass().getName());
+		cr.setCalcResult(objToString(fv.getResult()));
+		cr.setFormulaName(fv.getFormulaName());
+		cr.setFormulaValue(fv.getFormulaValue());
+		cr.setCreateTime(thistime);
+		return cr;
+	}
+	
+	private String objToString(Object ov) {
+		if(ov == null) {
+			return "null";
+		}
+		Class<?> clazz = ov.getClass();
+		if(clazz == BigDecimal.class) {
+			BigDecimal bv = (BigDecimal)ov;
+			return bv.toPlainString();
+		}
+		if(clazz == Double.class || clazz == double.class || clazz == Float.class || clazz == float.class) {
+			Double dv = (Double)ov;
+			BigDecimal bv = new BigDecimal(dv);
+			return bv.toPlainString();
+		}
+		return ov.toString();
+	}
+}

+ 12 - 0
src/main/java/xbc/formula/calcmgr/service/CalculateManagerService.java

@@ -0,0 +1,12 @@
+package xbc.formula.calcmgr.service;
+
+import xbc.formula.calcmgr.vo.CalcResultDescVo;
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+
+public interface CalculateManagerService {
+	
+	CalculateResponseVo calculate(CalculateRequestVo requestVo);
+	
+	CalcResultDescVo getCalcResultDesc(long calcResultId);
+}

+ 34 - 0
src/main/java/xbc/formula/calcmgr/service/impl/CalculateManagerServiceImpl.java

@@ -0,0 +1,34 @@
+package xbc.formula.calcmgr.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import xbc.formula.calcmgr.logic.CalculateManagerLogic;
+import xbc.formula.calcmgr.logic.QueryCalcResultLogic;
+import xbc.formula.calcmgr.service.CalculateManagerService;
+import xbc.formula.calcmgr.vo.CalcResultDescVo;
+import xbc.formula.calcmgr.vo.CalculateRequestVo;
+import xbc.formula.calcmgr.vo.CalculateResponseVo;
+
+@Service
+public class CalculateManagerServiceImpl implements CalculateManagerService {
+	
+	@Autowired
+	private CalculateManagerLogic calculateManagerLogic;
+	
+	@Autowired
+	private QueryCalcResultLogic queryCalcResultLogic;
+
+	@Override
+	public CalculateResponseVo calculate(CalculateRequestVo requestVo) {
+    	CalculateResponseVo responseVo = calculateManagerLogic.calculate(requestVo);
+    	return responseVo;
+	}
+
+	@Override
+	public CalcResultDescVo getCalcResultDesc(long calcResultId) {
+		CalcResultDescVo crdv = queryCalcResultLogic.getCalcResultDesc(calcResultId);
+    	return crdv;
+	}
+
+}

+ 53 - 0
src/main/java/xbc/formula/calcmgr/vo/CalcResultDescVo.java

@@ -0,0 +1,53 @@
+package xbc.formula.calcmgr.vo;
+
+public class CalcResultDescVo {
+	private long productId;
+	private String bizType;
+	private String bizCode;
+	private String formulaName;
+	private String formulaCode;
+	private String formulaValue;
+	private String desc;
+	public long getProductId() {
+		return productId;
+	}
+	public void setProductId(long productId) {
+		this.productId = productId;
+	}
+	public String getBizType() {
+		return bizType;
+	}
+	public void setBizType(String bizType) {
+		this.bizType = bizType;
+	}
+	public String getBizCode() {
+		return bizCode;
+	}
+	public void setBizCode(String bizCode) {
+		this.bizCode = bizCode;
+	}
+	public String getFormulaName() {
+		return formulaName;
+	}
+	public void setFormulaName(String formulaName) {
+		this.formulaName = formulaName;
+	}
+	public String getFormulaCode() {
+		return formulaCode;
+	}
+	public void setFormulaCode(String formulaCode) {
+		this.formulaCode = formulaCode;
+	}
+	public String getFormulaValue() {
+		return formulaValue;
+	}
+	public void setFormulaValue(String formulaValue) {
+		this.formulaValue = formulaValue;
+	}
+	public String getDesc() {
+		return desc;
+	}
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+}

+ 41 - 0
src/main/java/xbc/formula/calcmgr/vo/CalculateRequestVo.java

@@ -0,0 +1,41 @@
+package xbc.formula.calcmgr.vo;
+
+import java.util.Map;
+
+public class CalculateRequestVo {
+	private long productId;
+	private String bizType;
+	private String bizCode;
+	private Map<String, Object> paramMap;
+	private boolean saveResult;
+	public long getProductId() {
+		return productId;
+	}
+	public void setProductId(long productId) {
+		this.productId = productId;
+	}
+	public String getBizType() {
+		return bizType;
+	}
+	public void setBizType(String bizType) {
+		this.bizType = bizType;
+	}
+	public String getBizCode() {
+		return bizCode;
+	}
+	public void setBizCode(String bizCode) {
+		this.bizCode = bizCode;
+	}
+	public Map<String, Object> getParamMap() {
+		return paramMap;
+	}
+	public void setParamMap(Map<String, Object> paramMap) {
+		this.paramMap = paramMap;
+	}
+	public boolean isSaveResult() {
+		return saveResult;
+	}
+	public void setSaveResult(boolean saveResult) {
+		this.saveResult = saveResult;
+	}
+}

+ 25 - 0
src/main/java/xbc/formula/calcmgr/vo/CalculateResponseVo.java

@@ -0,0 +1,25 @@
+package xbc.formula.calcmgr.vo;
+
+public class CalculateResponseVo {
+	private long calcResultId;
+	private String resultType;
+	private String resultValue;
+	public long getCalcResultId() {
+		return calcResultId;
+	}
+	public void setCalcResultId(long calcResultId) {
+		this.calcResultId = calcResultId;
+	}
+	public String getResultType() {
+		return resultType;
+	}
+	public void setResultType(String resultType) {
+		this.resultType = resultType;
+	}
+	public String getResultValue() {
+		return resultValue;
+	}
+	public void setResultValue(String resultValue) {
+		this.resultValue = resultValue;
+	}
+}

+ 24 - 0
src/main/java/xbc/formula/fmlmgr/controller/FormulaController.java

@@ -0,0 +1,24 @@
+package xbc.formula.fmlmgr.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+import xbc.formula.fmlmgr.service.FormulaService;
+
+@RestController
+@RequestMapping("/formulamanager")
+public class FormulaController {
+	
+	@Autowired
+	private FormulaService formulaService;
+
+	@GetMapping("/get_formula_by_code")
+	XbcFmlFormulaVo getFormulaByCode(@RequestParam("formulaCode") String formulaCode) {
+		XbcFmlFormulaVo fv = formulaService.getFormulaByCode(formulaCode);
+		return fv;
+	}
+}

+ 14 - 0
src/main/java/xbc/formula/fmlmgr/dao/FormulaDao.java

@@ -0,0 +1,14 @@
+package xbc.formula.fmlmgr.dao;
+
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+@Repository
+public interface FormulaDao {
+
+	XbcFmlFormulaVo getFormulaByCode(@Param("formulaCode") String formulaCode);
+
+	XbcFmlFormulaVo getFormulaByName(@Param("formulaName") String formulaName);
+}

+ 10 - 0
src/main/java/xbc/formula/fmlmgr/dao/IndicatorDao.java

@@ -0,0 +1,10 @@
+package xbc.formula.fmlmgr.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo;
+
+public interface IndicatorDao {
+
+	XbcFmlFormulaIndicatorVo getIndicatorByName(@Param("indicatorName") String indicatorName);
+}

+ 8 - 0
src/main/java/xbc/formula/fmlmgr/logic/FormulaLogic.java

@@ -0,0 +1,8 @@
+package xbc.formula.fmlmgr.logic;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+public interface FormulaLogic {
+
+	XbcFmlFormulaVo getFormulaByCode(String formulaCode);
+}

+ 81 - 0
src/main/java/xbc/formula/fmlmgr/logic/impl/FormulaLogicImpl.java

@@ -0,0 +1,81 @@
+package xbc.formula.fmlmgr.logic.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import xbc.formula.engine.exp.XbcExpCalculator;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo;
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+import xbc.formula.fmlmgr.dao.FormulaDao;
+import xbc.formula.fmlmgr.dao.IndicatorDao;
+import xbc.formula.fmlmgr.logic.FormulaLogic;
+import xbc.formula.framework.exception.FormulaException;
+
+@Component
+public class FormulaLogicImpl implements FormulaLogic {
+
+	@Autowired
+	private FormulaDao formulaDao;
+	
+	@Autowired
+	private IndicatorDao indicatorDao;
+
+	@Override
+	public XbcFmlFormulaVo getFormulaByCode(String formulaCode) {
+		XbcFmlFormulaVo fv = formulaDao.getFormulaByCode(formulaCode);
+		if(fv == null) {
+			throw new FormulaException("没有找到公式,公式编码: " + formulaCode);
+		}
+        if(fv.getFormulaValue() == null) {
+            throw new FormulaException("公式【" + formulaCode + "】没有配置具体的公式表达式。");
+        }
+        Set<Long> keyIdSet = new HashSet<>(); // 检查公式循环调用的Set
+        retrieveFormula(fv, keyIdSet);
+		return fv;
+	}
+
+	private void retrieveFormula(XbcFmlFormulaVo fv, Set<Long> keyIdSet) {
+        if(keyIdSet.contains(fv.getKeyId())) {
+            throw new FormulaException("公式不能循环调用,keyId: " + fv.getKeyId());
+        }
+        keyIdSet.add(fv.getKeyId());
+		List<XbcFmlFormulaIndicatorVo> ivs = new ArrayList<>();
+		fv.setIndicatorVos(ivs);
+		List<String> indicatorNames = XbcExpCalculator.getParamters(fv.getFormulaValue());
+		for(String in : indicatorNames) {
+			XbcFmlFormulaIndicatorVo iv = indicatorDao.getIndicatorByName(in);
+			if(iv == null) {
+				XbcFmlFormulaVo tmpFv = formulaDao.getFormulaByName(in);
+				if(tmpFv == null) {
+					throw new FormulaException("【" + in + "】不是指标,也不是公式。公式: " + fv.getFormulaValue());
+				}
+				// 构建一个公式类指标
+				iv = new XbcFmlFormulaIndicatorVo();
+				iv.setKeyId(tmpFv.getKeyId());
+				iv.setIndicatorType(XbcFmlFormulaIndicatorVo.FORMULA_TYPE);
+				iv.setIndicatorCode(tmpFv.getFormulaCode());
+				iv.setIndicatorName(tmpFv.getFormulaName());
+				iv.setSpecifiedVal(tmpFv.getFormulaCode());
+				iv.setFormulaVo(tmpFv);
+				ivs.add(iv);
+				retrieveFormula(tmpFv, keyIdSet);
+			} else {
+	            ivs.add(iv);
+	            if(iv.getIndicatorType() == XbcFmlFormulaIndicatorVo.FORMULA_TYPE) {
+	            	XbcFmlFormulaVo tmpFv = formulaDao.getFormulaByCode(iv.getSpecifiedVal());
+	            	if(tmpFv == null) {
+	            		throw new FormulaException("指标【" + in + "】的[specified_val]配置【" + iv.getSpecifiedVal() + "】不是一个公式。公式: " + fv.getFormulaValue());
+	            	}
+					iv.setFormulaVo(tmpFv);
+					retrieveFormula(tmpFv, keyIdSet);
+	            }
+			}
+		}
+        keyIdSet.remove(fv.getKeyId());
+	}
+}

+ 8 - 0
src/main/java/xbc/formula/fmlmgr/service/FormulaService.java

@@ -0,0 +1,8 @@
+package xbc.formula.fmlmgr.service;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+
+public interface FormulaService {
+
+	XbcFmlFormulaVo getFormulaByCode(String formulaCode);
+}

+ 22 - 0
src/main/java/xbc/formula/fmlmgr/service/impl/FormulaServiceImpl.java

@@ -0,0 +1,22 @@
+package xbc.formula.fmlmgr.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import xbc.formula.engine.fml.vo.XbcFmlFormulaVo;
+import xbc.formula.fmlmgr.logic.FormulaLogic;
+import xbc.formula.fmlmgr.service.FormulaService;
+
+@Service
+public class FormulaServiceImpl implements FormulaService {
+	
+	@Autowired
+	private FormulaLogic formulaLogic;
+
+	@Override
+	public XbcFmlFormulaVo getFormulaByCode(String formulaCode) {
+		XbcFmlFormulaVo fv = formulaLogic.getFormulaByCode(formulaCode);
+		return fv;
+	}
+
+}

+ 15 - 0
src/main/java/xbc/formula/framework/exception/FormulaException.java

@@ -0,0 +1,15 @@
+package xbc.formula.framework.exception;
+
+public class FormulaException extends RuntimeException {
+
+	private static final long serialVersionUID = 198043505304047174L;
+
+    public FormulaException(String message) {
+        super(message);
+    }
+    
+    public FormulaException(Throwable cause, String message) {
+        super(message);
+        initCause(cause);
+    }
+}

+ 22 - 0
src/main/java/xbc/formula/framework/interceptor/ExceptionResponseHandler.java

@@ -0,0 +1,22 @@
+package xbc.formula.framework.interceptor;
+
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import xbc.formula.framework.exception.FormulaException;
+import xbc.formula.framework.vo.ResponseVo;
+
+@RestControllerAdvice
+public class ExceptionResponseHandler {
+
+    @ExceptionHandler(FormulaException.class)
+    public ResponseVo checkExceptionHandler(FormulaException ax) {
+        return ResponseVo.fail(ax.getMessage());
+    }
+
+    @ExceptionHandler(Exception.class)
+    public ResponseVo exceptionHandler(Exception x) {
+    	x.printStackTrace();
+        return ResponseVo.fail("系统发生预想外异常,请联系管理员处理。");
+    }
+}

+ 46 - 0
src/main/java/xbc/formula/framework/interceptor/NormalResponseHandler.java

@@ -0,0 +1,46 @@
+package xbc.formula.framework.interceptor;
+
+import java.lang.reflect.Method;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import xbc.formula.framework.vo.ResponseVo;
+
+@RestControllerAdvice
+public class NormalResponseHandler implements ResponseBodyAdvice<Object> {
+
+    @Override
+    public Object beforeBodyWrite(
+            Object obj, MethodParameter mp, MediaType mt, Class<? extends HttpMessageConverter<?>> clazz,
+            ServerHttpRequest request, ServerHttpResponse response) {
+    	ResponseVo rv = new ResponseVo();
+        rv.setCode(0);
+        rv.setData(obj);
+        return rv;
+    }
+
+    @Override
+    public boolean supports(MethodParameter mp, Class<? extends HttpMessageConverter<?>> clazz) {
+        Method method = mp.getMethod();
+        Class<?> dc = method.getDeclaringClass();
+        TransferToResponseVoAnnotation ttrva = dc.getAnnotation(TransferToResponseVoAnnotation.class);
+        if (ttrva != null && !ttrva.value()) {
+            return false;
+        }
+        Class<?> rtnClass = method.getReturnType();
+        if (!ResponseVo.class.isAssignableFrom(rtnClass)) {
+            // 如果返回值不是  CdyfszResponseVo
+            ttrva = method.getAnnotation(TransferToResponseVoAnnotation.class);
+            if (ttrva == null || ttrva.value()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 40 - 0
src/main/java/xbc/formula/framework/interceptor/TransactionAdviceConfig.java

@@ -0,0 +1,40 @@
+package xbc.formula.framework.interceptor;
+
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.aop.Advisor;
+import org.springframework.aop.aspectj.AspectJExpressionPointcut;
+import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionManager;
+import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
+import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+@Aspect
+@Configuration
+public class TransactionAdviceConfig {
+
+    private static final String AOP_POINTCUT_EXPRESSION = "execution (* xbc.formula..service..*.*(..))";
+
+    @Autowired
+    private TransactionManager transactionManager;
+
+    @Bean
+    public TransactionInterceptor txAdvice() {
+        DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute();
+        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
+        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
+        source.addTransactionalMethod("*", txAttrRequired);
+        return new TransactionInterceptor(transactionManager, source);
+    }
+
+    @Bean
+    public Advisor txAdviceAdvisor() {
+        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
+        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
+        return new DefaultPointcutAdvisor(pointcut, txAdvice());
+    }
+}

+ 12 - 0
src/main/java/xbc/formula/framework/interceptor/TransferToResponseVoAnnotation.java

@@ -0,0 +1,12 @@
+package xbc.formula.framework.interceptor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TransferToResponseVoAnnotation {
+    boolean value() default true;
+}

+ 38 - 0
src/main/java/xbc/formula/framework/vo/ResponseVo.java

@@ -0,0 +1,38 @@
+package xbc.formula.framework.vo;
+
+public class ResponseVo {
+	private int code;
+	private String message;
+	private Object data;
+	
+	public static ResponseVo fail(String msg) {
+		ResponseVo rv = new ResponseVo();
+		rv.setCode(100);
+		rv.setMessage(msg);
+		return rv;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public void setMessage(String message) {
+		this.message = message;
+	}
+
+	public Object getData() {
+		return data;
+	}
+
+	public void setData(Object data) {
+		this.data = data;
+	}
+}

+ 8 - 0
src/main/resources/application.properties

@@ -0,0 +1,8 @@
+server.port=12389
+
+spring.datasource.url=jdbc:mysql://localhost:3306/calcmgr?serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&useUnicode=true&characterEncoding=utf-8&useSSL=false
+spring.datasource.username=root
+spring.datasource.password=chengdu
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+mybatis.mapper-locations=classpath:mapper/*.xml

+ 14 - 0
src/main/resources/mapper/CalcFormulaMapper.xml

@@ -0,0 +1,14 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcFormulaDao">
+
+    <select id="getFormulaCode" resultType="java.lang.String">
+        select
+            formula_code
+        from
+            calc_formula
+        where
+            product_id = #{productId}
+            and biz_type = #{bizType}
+            and del_status = 0
+    </select>
+</mapper>

+ 45 - 0
src/main/resources/mapper/CalcIndicatorResultMapper.xml

@@ -0,0 +1,45 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcIndicatorResultDao">
+
+    <insert id="saveBatch" parameterType="java.util.List">
+        insert into calc_indicator_result (key_id, calc_result_id, indicator_id, indicator_type, indicator_name, specified_val, calc_result_type, calc_result, parent_id, node_code, formula_value, create_time) values 
+        <foreach collection="pos" item="po" index="index" separator=",">
+        (
+            #{po.keyId},
+            #{po.calcResultId},
+            #{po.indicatorId},
+            #{po.indicatorType},
+            #{po.indicatorName},
+            #{po.specifiedVal},
+            #{po.calcResultType},
+            #{po.calcResult},
+            #{po.parentId},
+            #{po.nodeCode},
+            #{po.formulaValue},
+            #{po.createTime}
+        )
+        </foreach>
+    </insert>
+    
+    <select id="getByCalcResultId" resultType="xbc.formula.calcmgr.dao.entity.CalcIndicatorResult">
+        select
+            key_id as keyId,
+            calc_result_id as calcResultId,
+            indicator_id as indicatorId,
+            indicator_type as indicatorType,
+            indicator_name as indicatorName,
+            specified_val as specifiedVal,
+            calc_result_type as calcResultType,
+            calc_result as calcResult,
+            parent_id as parentId,
+            node_code as nodeCode,
+            formula_value as formulaValue,
+            create_time as createTime
+        from
+            calc_indicator_result
+        where
+            calc_result_id = #{calcResultId}
+        order by
+            node_code, indicator_name
+    </select>
+</mapper>

+ 23 - 0
src/main/resources/mapper/CalcNoManagerMapper.xml

@@ -0,0 +1,23 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcNoManagerDao">
+
+    <select id="getCurrentNoLock" resultType="long">
+        select
+            no_value
+        from
+            calc_no_manager
+        where
+            result_type = #{resultType}
+        for update
+    </select>
+    
+    <update id="updateCurrentNo">
+        update 
+            calc_no_manager
+        set
+            no_value = #{noValue},
+            modify_time = #{thistime}
+        where
+            result_type = #{resultType}
+    </update>
+</mapper>

+ 37 - 0
src/main/resources/mapper/CalcResultMapper.xml

@@ -0,0 +1,37 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcResultDao">
+
+    <insert id="save" parameterType="xbc.formula.calcmgr.dao.entity.CalcResult">
+        insert into calc_result (key_id, product_id, biz_type, biz_code, formula_code, calc_result_type, calc_result, formula_name, formula_value, create_time) values
+        (
+            #{keyId},
+            #{productId},
+            #{bizType},
+            #{bizCode},
+            #{formulaCode},
+            #{calcResultType},
+            #{calcResult},
+            #{formulaName},
+            #{formulaValue},
+            #{createTime}
+        )
+    </insert>
+    
+    <select id="getByKeyId" resultType="xbc.formula.calcmgr.dao.entity.CalcResult">
+        select
+            key_id as keyId,
+            product_id as productId,
+            biz_type as bizType,
+            biz_code as bizCode,
+            formula_code as formulaCode,
+            calc_result_type as calcResultType,
+            calc_result as calcResult,
+            formula_name as formulaName,
+            formula_value as formulaValue,
+            create_time as createTime
+        from
+            calc_result
+        where
+            key_id = #{keyId}
+    </select>
+</mapper>

+ 29 - 0
src/main/resources/mapper/FormulaMapper.xml

@@ -0,0 +1,29 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.fmlmgr.dao.FormulaDao">
+
+    <select id="getFormulaByCode" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaVo">
+        select
+            key_id as keyId,
+            formula_name as formulaName,
+            formula_code as formulaCode,
+            formula_value as formulaValue
+        from
+            fml_formula
+        where
+            formula_code = #{formulaCode}
+            and del_status = 0
+    </select>
+    
+    <select id="getFormulaByName" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaVo">
+        select
+            key_id as keyId,
+            formula_name as formulaName,
+            formula_code as formulaCode,
+            formula_value as formulaValue
+        from
+            fml_formula
+        where
+            formula_name = #{formulaName}
+            and del_status = 0
+    </select>
+</mapper>

+ 17 - 0
src/main/resources/mapper/IndicatorMapper.xml

@@ -0,0 +1,17 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.fmlmgr.dao.IndicatorDao">
+
+    <select id="getIndicatorByName" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo">
+        select
+            key_id as keyId,
+            indicator_type as indicatorType,
+            indicator_name as indicatorName,
+            indicator_code as indicatorCode,
+            specified_val as specifiedVal
+        from
+            fml_formula_indicator
+        where
+            indicator_name = #{indicatorName}
+            and del_status = 0
+    </select>
+</mapper>

+ 29 - 0
table/reset_ddl.txt

@@ -0,0 +1,29 @@
+-- 重新设置公式表数据
+TRUNCATE TABLE `fml_formula`;
+INSERT INTO `fml_formula` VALUES (1000, '个人所得税', 'gerensuodeshui', '(工资 {\r\n  <= 5000 : 0,\r\n  (5000, 8000] : (工资 - 5000) * 0.03,\r\n  (8000, 17000] : 90 + (工资 - 8000) * 0.1,\r\n  (17000, 30000] : 990 + (工资 - 17000) * 0.2,\r\n  (30000, 40000] : 3590 + (工资 - 30000) * 0.25,\r\n  (40000, 60000] : 6090 + (工资 - 40000) * 0.3,\r\n  (60000, 85000] : 12090 + (工资 - 60000) * 0.35,\r\n  > 850000 : 20840 + (工资 - 85000) * 0.45\r\n}) + 杂税', '', 0, current_timestamp());
+INSERT INTO `fml_formula` VALUES (1001, '工资', 'gongzi', '基本工资 + 绩效工资 + 其它收入', '', 0, current_timestamp());
+INSERT INTO `fml_formula` VALUES (1002, '其它收入', 'qitashouru', '兼职收入 * 0.8 + 彩票收入 * 0.5', '', 0, current_timestamp());
+INSERT INTO `fml_formula` VALUES (1003, '绩效工资', 'jixiaogongzi', '基本绩效 + 其它收入', '', 0, current_timestamp());
+
+-- 重新设置指标表数据
+TRUNCATE TABLE `fml_formula_indicator`;
+INSERT INTO `fml_formula_indicator` VALUES (3001, 4, '基本工资', 'jibengongzi', 'jibengongzi', '', 0, current_timestamp());
+INSERT INTO `fml_formula_indicator` VALUES (3002, 4, '基本绩效', 'jibenjixiao', 'jibenjixiao', '', 0, current_timestamp());
+INSERT INTO `fml_formula_indicator` VALUES (3003, 4, '兼职收入', 'jianzhishouru', 'jianzhishouru', '', 0, current_timestamp());
+INSERT INTO `fml_formula_indicator` VALUES (3004, 4, '彩票收入', 'caipiaoshouru', 'caipiaoshouru', '', 0, current_timestamp());
+INSERT INTO `fml_formula_indicator` VALUES (3005, 4, '杂税', 'zashui', 'zashui', '', 0, current_timestamp());
+
+-- 重新设置业务使用公式配置表数据
+TRUNCATE TABLE `calc_formula`;
+INSERT INTO `calc_formula` VALUES (1, 1, 'geshui', 'gerensuodeshui', 1, 0, current_timestamp());
+
+-- 重新设置运行结果id管理表数据
+TRUNCATE TABLE `calc_no_manager`;
+INSERT INTO `calc_no_manager` VALUES (1, 0, current_timestamp(), current_timestamp());
+INSERT INTO `calc_no_manager` VALUES (2, 0, current_timestamp(), current_timestamp());
+
+-- 清除公式运算结果表数据
+TRUNCATE TABLE `calc_result`;
+
+-- 清除公式运算结果指标表数据
+TRUNCATE TABLE `calc_indicator_result`;

+ 91 - 0
table/table_dml.txt

@@ -0,0 +1,91 @@
+-- 基础公式配置表(开始)
+
+CREATE TABLE `fml_formula`(
+	`key_id`                           bigint(19)           NOT NULL DEFAULT 0                     COMMENT '主键',
+	`formula_name`                     varchar(40)          NOT NULL DEFAULT ''                    COMMENT '公式名称',
+	`formula_code`                     varchar(40)          NOT NULL DEFAULT ''                    COMMENT '公式自定编码',
+	`formula_value`                    varchar(2000)        NOT NULL DEFAULT ''                    COMMENT '公式',
+	`formula_desc`                     varchar(255)         NOT NULL DEFAULT ''                    COMMENT '说明',
+	`del_status`                       tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '删除标识(0:未删除 / 1:已删除)',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `key_id` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='公式配置表';
+
+
+CREATE TABLE `fml_formula_indicator`(
+	`key_id`                           bigint(19)           NOT NULL DEFAULT 0                     COMMENT '主键',
+	`indicator_type`                   tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '指标类型:1: 录入 / 2: 脚本 / 3: 公式 / 4: 从参数的Map里面取 / 5: 函数',
+	`indicator_name`                   varchar(50)          NOT NULL DEFAULT ''                    COMMENT '指标名称',
+	`indicator_code`                   varchar(40)          NOT NULL DEFAULT ''                    COMMENT '指标code',
+	`specified_val`                    varchar(50)          NOT NULL DEFAULT ''                    COMMENT '预设值:固定值',
+	`indicator_comment`                varchar(200)         NOT NULL DEFAULT ''                    COMMENT '备注:备注',
+	`del_status`                       tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '删除标识(0:未删除 / 1:已删除)',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `key_id` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='指标配置表';
+
+-- 基础公式配置表(结束)
+
+
+
+-- 业务使用公式配置表(开始)
+
+CREATE TABLE `calc_formula`(
+	`key_id`                           bigint(19)           NOT NULL DEFAULT 0                     COMMENT '主键',
+	`product_id`                       bigint(19)           NOT NULL DEFAULT 0                     COMMENT '产品ID',
+	`biz_type`                         varchar(40)          NOT NULL DEFAULT ''                    COMMENT '业务类型',
+	`formula_code`                     varchar(40)          NOT NULL DEFAULT ''                    COMMENT '公式自定编码',
+	`show_index`                       int(4)               NOT NULL DEFAULT 0                     COMMENT '显示顺序',
+	`del_status`                       tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '删除标识(0:未删除 / 1:已删除)',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `key_id` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='业务公式配置表';
+
+-- 业务使用公式配置表(结束)
+
+
+
+-- 公式计算结果管理表(开始)
+
+CREATE TABLE `calc_no_manager`(
+    `result_type`                      tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '结果类型:1: 公式 / 2: 指标',
+    `no_value`                         bigint(19)           NOT NULL DEFAULT 0                     COMMENT '番号',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	`modify_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `result_type` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='运行结果id管理表';
+
+insert into calc_no_manager (result_type, no_value, create_time, modify_time) values (1, 0, current_timestamp(), current_timestamp());
+insert into calc_no_manager (result_type, no_value, create_time, modify_time) values (2, 0, current_timestamp(), current_timestamp());
+
+CREATE TABLE `calc_result`(
+	`key_id`                           bigint(19)           NOT NULL DEFAULT 0                     COMMENT '主键',
+	`product_id`                       bigint(19)           NOT NULL DEFAULT 0                     COMMENT '产品ID',
+	`biz_type`                         varchar(40)          NOT NULL DEFAULT ''                    COMMENT '业务类型',
+	`biz_code`                         varchar(40)          NOT NULL DEFAULT ''                    COMMENT '业务编码',
+	`formula_code`                     varchar(40)          NOT NULL DEFAULT ''                    COMMENT '公式自定编码',
+	`calc_result_type`                 varchar(50)          NOT NULL DEFAULT ''                    COMMENT '公式运算结果的类型',
+	`calc_result`                      varchar(255)         NOT NULL DEFAULT ''                    COMMENT '公式运算结果',
+	`formula_name`                     varchar(40)          NOT NULL DEFAULT ''                    COMMENT '公式名称',
+	`formula_value`                    varchar(1000)        NOT NULL DEFAULT ''                    COMMENT '公式',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `key_id` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='业务公式运行结果表';
+
+CREATE TABLE `calc_indicator_result`(
+	`key_id`                           bigint(19)           NOT NULL DEFAULT 0                     COMMENT '主键',
+	`calc_result_id`                   bigint(19)           NOT NULL DEFAULT 0                     COMMENT '业务公式运行结果表ID',
+	`indicator_id`                     bigint(19)           NOT NULL DEFAULT 0                     COMMENT '指标配置表ID',
+	`indicator_type`                   tinyint(3) unsigned  NOT NULL DEFAULT 0                     COMMENT '指标类型:1: 录入 / 2: 脚本 / 3: 公式 / 4: 从参数的Map里面取 / 5: 函数',
+	`indicator_name`                   varchar(50)          NOT NULL DEFAULT ''                    COMMENT '指标名称',
+	`specified_val`                    varchar(50)          NOT NULL DEFAULT ''                    COMMENT '预设值:固定值',
+	`calc_result_type`                 varchar(50)          NOT NULL DEFAULT ''                    COMMENT '指标值的类型',
+	`calc_result`                      varchar(255)         NOT NULL DEFAULT ''                    COMMENT '指标值',
+	`parent_id`                        bigint(19)           NOT NULL DEFAULT 0                     COMMENT '父节点ID:当指标为公式时',
+	`node_code`                        varchar(100)         NOT NULL DEFAULT ''                    COMMENT '节点编码:当指标为公式时',
+	`formula_value`                    varchar(1000)        NOT NULL DEFAULT ''                    COMMENT '公式',
+	`create_time`                      timestamp            NOT NULL DEFAULT CURRENT_TIMESTAMP     COMMENT '创建时间',
+	PRIMARY KEY ( `key_id` )
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='参与业务公式运行的指标表';
+
+-- 公式计算结果管理表(结束)

+ 8 - 0
target/classes/application.properties

@@ -0,0 +1,8 @@
+server.port=12389
+
+spring.datasource.url=jdbc:mysql://localhost:3306/calcmgr?serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&useUnicode=true&characterEncoding=utf-8&useSSL=false
+spring.datasource.username=root
+spring.datasource.password=chengdu
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+mybatis.mapper-locations=classpath:mapper/*.xml

+ 14 - 0
target/classes/mapper/CalcFormulaMapper.xml

@@ -0,0 +1,14 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcFormulaDao">
+
+    <select id="getFormulaCode" resultType="java.lang.String">
+        select
+            formula_code
+        from
+            calc_formula
+        where
+            product_id = #{productId}
+            and biz_type = #{bizType}
+            and del_status = 0
+    </select>
+</mapper>

+ 45 - 0
target/classes/mapper/CalcIndicatorResultMapper.xml

@@ -0,0 +1,45 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcIndicatorResultDao">
+
+    <insert id="saveBatch" parameterType="java.util.List">
+        insert into calc_indicator_result (key_id, calc_result_id, indicator_id, indicator_type, indicator_name, specified_val, calc_result_type, calc_result, parent_id, node_code, formula_value, create_time) values 
+        <foreach collection="pos" item="po" index="index" separator=",">
+        (
+            #{po.keyId},
+            #{po.calcResultId},
+            #{po.indicatorId},
+            #{po.indicatorType},
+            #{po.indicatorName},
+            #{po.specifiedVal},
+            #{po.calcResultType},
+            #{po.calcResult},
+            #{po.parentId},
+            #{po.nodeCode},
+            #{po.formulaValue},
+            #{po.createTime}
+        )
+        </foreach>
+    </insert>
+    
+    <select id="getByCalcResultId" resultType="xbc.formula.calcmgr.dao.entity.CalcIndicatorResult">
+        select
+            key_id as keyId,
+            calc_result_id as calcResultId,
+            indicator_id as indicatorId,
+            indicator_type as indicatorType,
+            indicator_name as indicatorName,
+            specified_val as specifiedVal,
+            calc_result_type as calcResultType,
+            calc_result as calcResult,
+            parent_id as parentId,
+            node_code as nodeCode,
+            formula_value as formulaValue,
+            create_time as createTime
+        from
+            calc_indicator_result
+        where
+            calc_result_id = #{calcResultId}
+        order by
+            node_code, indicator_name
+    </select>
+</mapper>

+ 23 - 0
target/classes/mapper/CalcNoManagerMapper.xml

@@ -0,0 +1,23 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcNoManagerDao">
+
+    <select id="getCurrentNoLock" resultType="long">
+        select
+            no_value
+        from
+            calc_no_manager
+        where
+            result_type = #{resultType}
+        for update
+    </select>
+    
+    <update id="updateCurrentNo">
+        update 
+            calc_no_manager
+        set
+            no_value = #{noValue},
+            modify_time = #{thistime}
+        where
+            result_type = #{resultType}
+    </update>
+</mapper>

+ 37 - 0
target/classes/mapper/CalcResultMapper.xml

@@ -0,0 +1,37 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.calcmgr.dao.CalcResultDao">
+
+    <insert id="save" parameterType="xbc.formula.calcmgr.dao.entity.CalcResult">
+        insert into calc_result (key_id, product_id, biz_type, biz_code, formula_code, calc_result_type, calc_result, formula_name, formula_value, create_time) values
+        (
+            #{keyId},
+            #{productId},
+            #{bizType},
+            #{bizCode},
+            #{formulaCode},
+            #{calcResultType},
+            #{calcResult},
+            #{formulaName},
+            #{formulaValue},
+            #{createTime}
+        )
+    </insert>
+    
+    <select id="getByKeyId" resultType="xbc.formula.calcmgr.dao.entity.CalcResult">
+        select
+            key_id as keyId,
+            product_id as productId,
+            biz_type as bizType,
+            biz_code as bizCode,
+            formula_code as formulaCode,
+            calc_result_type as calcResultType,
+            calc_result as calcResult,
+            formula_name as formulaName,
+            formula_value as formulaValue,
+            create_time as createTime
+        from
+            calc_result
+        where
+            key_id = #{keyId}
+    </select>
+</mapper>

+ 29 - 0
target/classes/mapper/FormulaMapper.xml

@@ -0,0 +1,29 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.fmlmgr.dao.FormulaDao">
+
+    <select id="getFormulaByCode" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaVo">
+        select
+            key_id as keyId,
+            formula_name as formulaName,
+            formula_code as formulaCode,
+            formula_value as formulaValue
+        from
+            fml_formula
+        where
+            formula_code = #{formulaCode}
+            and del_status = 0
+    </select>
+    
+    <select id="getFormulaByName" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaVo">
+        select
+            key_id as keyId,
+            formula_name as formulaName,
+            formula_code as formulaCode,
+            formula_value as formulaValue
+        from
+            fml_formula
+        where
+            formula_name = #{formulaName}
+            and del_status = 0
+    </select>
+</mapper>

+ 17 - 0
target/classes/mapper/IndicatorMapper.xml

@@ -0,0 +1,17 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="xbc.formula.fmlmgr.dao.IndicatorDao">
+
+    <select id="getIndicatorByName" resultType="xbc.formula.engine.fml.vo.XbcFmlFormulaIndicatorVo">
+        select
+            key_id as keyId,
+            indicator_type as indicatorType,
+            indicator_name as indicatorName,
+            indicator_code as indicatorCode,
+            specified_val as specifiedVal
+        from
+            fml_formula_indicator
+        where
+            indicator_name = #{indicatorName}
+            and del_status = 0
+    </select>
+</mapper>

BIN
target/classes/xbc/formula/CalculateManagerApplication.class


BIN
target/classes/xbc/formula/calcmgr/controller/CalculateManagerController.class


BIN
target/classes/xbc/formula/calcmgr/dao/CalcFormulaDao.class


BIN
target/classes/xbc/formula/calcmgr/dao/CalcIndicatorResultDao.class


BIN
target/classes/xbc/formula/calcmgr/dao/CalcNoManagerDao.class


BIN
target/classes/xbc/formula/calcmgr/dao/CalcResultDao.class


BIN
target/classes/xbc/formula/calcmgr/dao/entity/CalcIndicatorResult.class


BIN
target/classes/xbc/formula/calcmgr/dao/entity/CalcResult.class


BIN
target/classes/xbc/formula/calcmgr/logic/CalculateManagerLogic.class


BIN
target/classes/xbc/formula/calcmgr/logic/QueryCalcResultLogic.class


BIN
target/classes/xbc/formula/calcmgr/logic/SaveResultLogic.class


BIN
target/classes/xbc/formula/calcmgr/logic/impl/CalculateManagerLogicImpl.class


BIN
target/classes/xbc/formula/calcmgr/logic/impl/QueryCalcResultLogicImpl.class


BIN
target/classes/xbc/formula/calcmgr/logic/impl/SaveResultLogicImpl$RetrieveIndicatorVo.class


Some files were not shown because too many files changed in this diff