mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Merge v3.5 (#682)
* feat: Batch approve 1 (#654) * InternalPermission * feat: batch-approve * Share assign n 90 (#657) * fix css * be: styles * feat: 导入仅更新不要新建 * feat: meetByFormula * feat: img for ContentWithFieldVars * feat: 表单引用 mob * feat: UTYPE_ACCOUNT20 * Feat datalist btn 80 (#658) * feat: regRowAction * be: protable width * feat: pdfjs * be: pdf preview * feat: proxy-download * be: message with files * Quick query and more (#661) * enh: show isDisabled in trigger * Gitee#I818F8 * enh: 记录转换3级字段 * enh: advfilter deep3 in `admin` * enh: buildFieldsWithRefs * be: $pages * be: countdown for 批量修改 * fix: trigger timer * Gitee#I7VHJX * be: FIELDAGGREGATION=GROUPAGGREGATION; RBJOIN FILE * Members in dept view (#663) * feat: RBJOIN2 * enh: LiteFormModal * feat: Member show on view * Diagramming for trigger RB-93 (#664) * be:evalTriggerTimes * be: RbFormRefform * be: ObservableService order * entities er * be: user delete checks * be: row selected * be: .dataTables_oper.invisible * be: BatchOperator default selected * feat: mermaid * be: comp {@NOW} * be: passwd 10 --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Api logs view (#667) * be: show nums&dims * feat: api-logs * Update QueryFactory.java * Bump js libs * be:RRL --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * tmpl better (#668) * be: isSame * socketTimeout=0 * feat: front * WordReportGenerator * style: print * be: common-save * be: 本地加载PDF * fops * inTriggerTime2 * report 100w * word tmpl * word table * style * Update @rbv * AppHome._InTab * IsNullFunction * modal onDoubleClick * Datasync 94 (#671) * feat: buildFormData:retAll, onProTableLineUpdated * feat:getLineFrom * api:KnownExceptionConverter * be:复制打印设置 * be:doc2html * be:form hook 传参 * ToHtml * be: html all sheets, colspan * trigger KnownExceptionConverter * getCurrentForm * fix: clearFields * AUTOTRANSFORM following * fix: trigger on share use ID and getFixedRecordId * 1.RevisionHistory AutoId; * uc bind * Update OpenApiSDK.java * styles --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Mode2 fields 19 (#673) * readExcelRows * feat: match-fields * fix: ntext riching fields * devMode return null * fix: keep request-data raw * mvnw * guide new * nav-style35 * feat: sort n * be: backup * ignoreTables * fix: tag default value * DatabaseFixer * fixV346 * feat: _PageMourningMode * be: zoom * DATEPICKAT * $select2MatcherAll, --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * File n select (#676) * feat: details ahowAt2 * enh: file preview on form * be: readonlyw 自动值 * Be files (#677) * fix: upload error 413 * be: files styles * _Notification * be: 驳回默认上一级 * error-container * enh: 审批成功后通知提交人 * word img * be: fields * be: file preview * feat: view-add n2nref * Update nginx-rebuild.conf * api error format * be: styles * file-share forever * checkNullable35 * Lazy wait details finished (#678) * lazy HookUrl * fix: $same * enh: 记录转换 AnyRef * Be v3.5 (#680) * frontjs * be: 公式日期值 * IMP: 明细使用自有 Service * 公开注册 * be: this._defaultBackPath * style: dataTables_oper.invisible2 * varRecord * fix: 明细未配置或出错 * feat: autoLocation * Fix 3.5.0 (#681) * __LAB_CHARTANIMATION * feat: RBJOIN3 * fix: miss logs * 3.5.0-beta1 * better * fix: TextFunction * fix: api --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
dba501d616
commit
67e12fb774
|
@ -1,23 +1,33 @@
|
|||
# include nginx-rebuild.conf;
|
||||
server {
|
||||
server_name YOUR_DOMAIN;
|
||||
listen 80;
|
||||
server_name YOUR_IP_OR_DOMAIN;
|
||||
listen 80;
|
||||
# HTTPS
|
||||
#listen 443 ssl http2;
|
||||
#ssl_certificate /path/to/ssl.crt;
|
||||
#ssl_certificate_key /path/to/ssl.key;
|
||||
#listen 443 ssl http2;
|
||||
#ssl_certificate /path/to/ssl.crt;
|
||||
#ssl_certificate_key /path/to/ssl.key;
|
||||
# PROXY
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_set_header Host $host;
|
||||
#proxy_set_header Host $host:$server_port;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_set_header Host $host:$server_port;
|
||||
proxy_set_header Remote-Host $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 300;
|
||||
proxy_buffer_size 8k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
client_max_body_size 200m; # Max of file upload
|
||||
client_body_buffer_size 128k;
|
||||
# PASS
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18080;
|
||||
etag on;
|
||||
proxy_pass http://127.0.0.1:18080;
|
||||
etag on;
|
||||
}
|
||||
location /assets {
|
||||
proxy_pass http://127.0.0.1:18080/assets;
|
||||
expires 90d;
|
||||
proxy_pass http://127.0.0.1:18080/assets;
|
||||
expires 90d;
|
||||
}
|
||||
}
|
||||
|
|
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
18
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
18
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# 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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 0f5ad73c12a1f5a617314efe665074687f17e490
|
||||
Subproject commit 15d7f439a84cef4be39dadada2d51e2e6ab4f375
|
22
README.md
22
README.md
|
@ -13,23 +13,23 @@ REBUILD 侧重于业务需求实现,而非基础技术框架或项目启动模
|
|||
|
||||
更多详情介绍 [https://getrebuild.com/](https://getrebuild.com/)
|
||||
|
||||
> **福利:加入 REBUILD QQ 交流群 819865721(满) 1013051587 GET 使用技能**
|
||||
> **福利:加入 REBUILD VIP 用户 QQ 交流群 819865721 1013051587 GET 使用技能**
|
||||
|
||||
## V3.4 新特性
|
||||
## V3.5 新特性
|
||||
|
||||
本次更新为你带来众多功能增强与优化。
|
||||
|
||||
1. [新增] 发送通知触发器支持发送钉钉/企业微信(机器人)群消息
|
||||
2. [新增] 表单回填支持启用后端回填
|
||||
3. [新增] 角色扩展权限“允许撤销审批”
|
||||
4. [新增] 字段聚合、分组聚合触发器支持多引用字段“连接”模式
|
||||
5. [新增] 标签类字段支持自定义颜色
|
||||
6. [新增] 手机版对 HTML5+ 环境的支持
|
||||
8. [新增] 字段“允许重复”选项支持配置检查数据范围、逻辑条件
|
||||
7. [新增] 日期条件支持去年/明年、上季度/下季度、上月/下月、上周/下周
|
||||
1. [新增] 批量审批
|
||||
2. [新增] 表单引用组件
|
||||
3. [新增] 触发器执行流程图
|
||||
4. [新增] 触发器高级表达式函数 `ISNULL` `DATEPICKAT`
|
||||
5. [新增] WORD 模板
|
||||
6. [新增] 手机版支持导出报表
|
||||
7. [优化] 手机版导航样式优化
|
||||
8. [优化] 明细实体支持显示在视图页下方
|
||||
9. ...
|
||||
|
||||
更多新特性请参见 [更新日志](https://getrebuild.com/docs/dev/changelog)
|
||||
更多更新详情请参见 [更新日志](https://getrebuild.com/docs/dev/changelog)
|
||||
|
||||
## 在线体验
|
||||
|
||||
|
|
308
mvnw
vendored
Executable file
308
mvnw
vendored
Executable file
|
@ -0,0 +1,308 @@
|
|||
#!/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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# 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
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); 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 2>/dev/null; \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
|
||||
|
||||
# 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/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
##########################################################################################
|
||||
# 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.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
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 "$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
|
||||
|
||||
# shellcheck disable=SC2086 # safe args
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
205
mvnw.cmd
vendored
Normal file
205
mvnw.cmd
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
@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 http://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 Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_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 WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_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('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@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%
|
49
pom.xml
49
pom.xml
|
@ -5,12 +5,12 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.12</version>
|
||||
<version>2.7.17</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
<artifactId>rebuild</artifactId>
|
||||
<version>3.4.6</version>
|
||||
<version>3.5.0-beta1</version>
|
||||
<name>rebuild</name>
|
||||
<description>Building your business-systems freely!</description>
|
||||
<!-- UNCOMMENT USE TOMCAT -->
|
||||
|
@ -257,7 +257,6 @@
|
|||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -271,7 +270,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>commons</artifactId>
|
||||
<version>1.3.10</version>
|
||||
<version>1.3.13</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>httpclient</artifactId>
|
||||
|
@ -291,7 +290,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>persist4j</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<version>1.7.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
@ -316,17 +315,17 @@
|
|||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.19</version>
|
||||
<version>1.9.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.18</version>
|
||||
<version>1.2.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.0.33</version>
|
||||
<version>8.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
|
@ -336,12 +335,12 @@
|
|||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>4.4.1</version>
|
||||
<version>4.4.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.13.1</version>
|
||||
<version>7.14.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
|
@ -351,7 +350,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.4.2</version>
|
||||
<version>6.4.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
|
@ -399,15 +398,20 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.deepoove</groupId>
|
||||
<artifactId>poi-tl</artifactId>
|
||||
<version>1.12.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<version>5.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<version>5.2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
|
@ -427,7 +431,7 @@
|
|||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>0.4.19</version>
|
||||
<version>0.4.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>es.moki.ratelimitj</groupId>
|
||||
|
@ -437,12 +441,12 @@
|
|||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>3.5.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.21.3</version>
|
||||
<version>3.23.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
@ -468,12 +472,12 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>5.8.19</version>
|
||||
<version>5.8.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
<version>3.13.0</version>
|
||||
</dependency>
|
||||
<!-- Need JDK11+ -->
|
||||
<!--
|
||||
|
@ -488,17 +492,22 @@
|
|||
<artifactId>jansi</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zwobble.mammoth</groupId>
|
||||
<artifactId>mammoth</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- fix: CVEs -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.12.0</version>
|
||||
<version>2.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.23.0</version>
|
||||
<version>1.24.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
|
|
|
@ -11,10 +11,12 @@ import cn.devezhao.persist4j.engine.ID;
|
|||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +54,7 @@ public class ApiContext {
|
|||
* @param bindUser
|
||||
*/
|
||||
public ApiContext(Map<String, String> reqParams, JSON postData, String appId, ID bindUser) {
|
||||
this.reqParams = reqParams;
|
||||
this.reqParams = Collections.unmodifiableMap(reqParams);
|
||||
this.postData = postData;
|
||||
this.appId = appId;
|
||||
this.bindUser = bindUser;
|
||||
|
@ -88,7 +90,7 @@ public class ApiContext {
|
|||
* @return
|
||||
*/
|
||||
public JSON getPostData() {
|
||||
return postData == null ? new JSONObject() : postData;
|
||||
return postData == null ? new JSONObject() : JSONUtils.clone(postData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,7 +99,7 @@ public class ApiContext {
|
|||
* @throws ApiInvokeException
|
||||
*/
|
||||
public String getParameterNotBlank(String name) throws ApiInvokeException {
|
||||
String value = getParameterMap().get(name);
|
||||
String value = reqParams.get(name);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADPARAMS, "Parameter [" + name + "] cannot be null");
|
||||
}
|
||||
|
@ -109,7 +111,7 @@ public class ApiContext {
|
|||
* @return
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
return getParameterMap().get(name);
|
||||
return reqParams.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,29 +130,18 @@ public class ApiContext {
|
|||
* @return
|
||||
*/
|
||||
public int getParameterAsInt(String name, int defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
String value = reqParams.get(name);
|
||||
if (NumberUtils.isNumber(value)) return NumberUtils.toInt(value);
|
||||
else return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
public long getParameterAsLong(String name, long defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
if (NumberUtils.isNumber(value)) return NumberUtils.toLong(value);
|
||||
else return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
public boolean getParameterAsBool(String name, boolean defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
String value = reqParams.get(name);
|
||||
if (StringUtils.isBlank(value)) return defaultValue;
|
||||
else return BooleanUtils.toBoolean(value);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ package com.rebuild.api;
|
|||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.EncryptUtils;
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.ThrowableUtils;
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
@ -60,7 +61,7 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
// 基于 IP 限流
|
||||
private static final RequestRateLimiter RRL = RateLimiters.createRateLimiter(
|
||||
new int[] { 10, 60 },
|
||||
new int[] { 600, 6000 });
|
||||
new int[] { 600, 3000 });
|
||||
|
||||
private static final Map<String, Class<? extends BaseApi>> API_CLASSES = new HashMap<>();
|
||||
|
||||
|
@ -96,7 +97,7 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
final String requestId = CommonsUtils.randomHex();
|
||||
|
||||
response.addHeader("X-RB-Server", ServerStatus.STARTUP_ONCE + "/" + Application.BUILD);
|
||||
response.setHeader("X-Request-Id", requestId);
|
||||
response.setHeader("X-RB-RequestId", requestId);
|
||||
|
||||
if (RRL.overLimitWhenIncremented("ip:" + remoteIp)) {
|
||||
JSON error = formatFailure("Request frequency exceeded", ApiInvokeException.ERR_FREQUENCY);
|
||||
|
@ -111,7 +112,9 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
ApiContext context = null;
|
||||
try {
|
||||
final BaseApi api = createApi(apiName);
|
||||
context = verfiy(request, api);
|
||||
|
||||
context = buildBaseApiContext(request);
|
||||
context = verfiy(request, context, api);
|
||||
|
||||
UserContextHolder.setReqip(remoteIp);
|
||||
UserContextHolder.setUser(context.getBindUser());
|
||||
|
@ -130,7 +133,7 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
errorMsg = ex.getLocalizedMessage();
|
||||
} catch (Throwable ex) {
|
||||
errorCode = Controller.CODE_SERV_ERROR;
|
||||
errorMsg = ex.getLocalizedMessage();
|
||||
errorMsg = ThrowableUtils.getRootCause(ex).getLocalizedMessage();
|
||||
log.error("Server Internal Error ({})", requestId, ex);
|
||||
|
||||
String knownError = KnownExceptionConverter.convert2ErrorMsg(ex);
|
||||
|
@ -154,16 +157,12 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
* 验证请求并构建请求上下文
|
||||
*
|
||||
* @param request
|
||||
* @param base
|
||||
* @param useApi
|
||||
* @return
|
||||
*/
|
||||
protected ApiContext verfiy(HttpServletRequest request, @SuppressWarnings("unused") BaseApi useApi) {
|
||||
final Map<String, String> sortedMap = new TreeMap<>();
|
||||
for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
|
||||
String[] vv = e.getValue();
|
||||
sortedMap.put(e.getKey(), vv == null || vv.length == 0 ? null : vv[0]);
|
||||
}
|
||||
|
||||
protected ApiContext verfiy(HttpServletRequest request, ApiContext base, @SuppressWarnings("unused") BaseApi useApi) {
|
||||
final Map<String, String> sortedMap = new TreeMap<>(base.getParameterMap());
|
||||
final String appid = getParameterNotNull(sortedMap, "appid");
|
||||
final String sign = getParameterNotNull(sortedMap, "sign");
|
||||
|
||||
|
@ -228,15 +227,40 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
}
|
||||
}
|
||||
|
||||
// 组合请求数据
|
||||
|
||||
String postData = ServletUtils.getRequestString(request);
|
||||
JSON postJson = postData != null ? (JSON) JSON.parse(postData) : null;
|
||||
ID bindUser = apiConfig.getID("bindUser");
|
||||
// 默认绑定系统用户
|
||||
if (bindUser == null) bindUser = UserService.SYSTEM_USER;
|
||||
|
||||
return new ApiContext(sortedMap, postJson, appid, bindUser);
|
||||
return new ApiContext(sortedMap, base.getPostData(), appid, bindUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName
|
||||
* @return
|
||||
*/
|
||||
private BaseApi createApi(String apiName) {
|
||||
if (!API_CLASSES.containsKey(apiName)) {
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADAPI, "Unknown API : " + apiName);
|
||||
}
|
||||
return (BaseApi) ReflectUtils.newInstance(API_CLASSES.get(apiName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private ApiContext buildBaseApiContext(HttpServletRequest request) {
|
||||
Map<String, String> sortedMap = new TreeMap<>();
|
||||
for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
|
||||
String[] item = e.getValue();
|
||||
sortedMap.put(e.getKey(), item == null || item.length == 0 ? null : item[0]);
|
||||
}
|
||||
|
||||
String appid = getParameterNotNull(sortedMap, "appid");
|
||||
String postData = ServletUtils.getRequestString(request);
|
||||
JSON postJson = postData != null ? (JSON) JSON.parse(postData) : null;
|
||||
|
||||
return new ApiContext(sortedMap, postJson, appid, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,17 +276,6 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param apiName
|
||||
* @return
|
||||
*/
|
||||
private BaseApi createApi(String apiName) {
|
||||
if (!API_CLASSES.containsKey(apiName)) {
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADAPI, "Unknown API : " + apiName);
|
||||
}
|
||||
return (BaseApi) ReflectUtils.newInstance(API_CLASSES.get(apiName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录请求日志
|
||||
*
|
||||
|
@ -277,15 +290,16 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
Record record = EntityHelper.forNew(EntityHelper.RebuildApiRequest, UserService.SYSTEM_USER);
|
||||
record.setString("requestUrl", apiName);
|
||||
record.setString("remoteIp", remoteIp);
|
||||
record.setString("responseBody", requestId + ":" + (result == null ? "{}" : CommonsUtils.maxstr(result.toJSONString(), 10000)));
|
||||
record.setString("responseBody",
|
||||
requestId + ":" + (result == null ? "{}" : CommonsUtils.maxstr(result.toJSONString(), 32767)));
|
||||
record.setDate("requestTime", requestTime);
|
||||
record.setDate("responseTime", CalendarUtils.now());
|
||||
|
||||
if (context != null) {
|
||||
record.setString("appId", context.getAppId());
|
||||
if (context.getPostData() != null) {
|
||||
record.setString("requestBody",
|
||||
CommonsUtils.maxstr(context.getPostData().toJSONString(), 10000));
|
||||
JSON post;
|
||||
if ((post = context.getPostData()) != null) {
|
||||
record.setString("requestBody", CommonsUtils.maxstr(post.toJSONString(), 32767));
|
||||
}
|
||||
if (!context.getParameterMap().isEmpty()) {
|
||||
record.setString("requestUrl",
|
||||
|
|
|
@ -49,11 +49,11 @@ public class AuthTokenManager {
|
|||
* 生成 Token
|
||||
*
|
||||
* @param user
|
||||
* @param expires
|
||||
* @param seconds
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected static String generateToken(ID user, int expires, String type) {
|
||||
protected static String generateToken(ID user, int seconds, String type) {
|
||||
// Type,User,Time,Version
|
||||
String desc = String.format("%s,%s,%d,v2",
|
||||
ObjectUtils.defaultIfNull(type, TYPE_ACCESS_TOKEN),
|
||||
|
@ -61,7 +61,7 @@ public class AuthTokenManager {
|
|||
System.nanoTime());
|
||||
String token = EncryptUtils.toSHA1Hex(desc);
|
||||
|
||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, expires);
|
||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, seconds);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
@ -84,12 +84,12 @@ public class AuthTokenManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param expires
|
||||
* @param seconds
|
||||
* @return
|
||||
* @see #TYPE_CSRF_TOKEN
|
||||
*/
|
||||
public static String generateCsrfToken(int expires) {
|
||||
return generateToken(null, expires, TYPE_CSRF_TOKEN);
|
||||
public static String generateCsrfToken(int seconds) {
|
||||
return generateToken(null, seconds, TYPE_CSRF_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,16 +30,16 @@ import es.moki.ratelimitj.core.limiter.request.RequestRateLimiter;
|
|||
public class LoginToken extends BaseApi {
|
||||
|
||||
// 基于用户限流
|
||||
private static final RequestRateLimiter RRL = RateLimiters.createRateLimiter(
|
||||
new int[] { 30, 60, 3600 },
|
||||
new int[] { 5, 10, 100 });
|
||||
private static final RequestRateLimiter RRL_4USER = RateLimiters.createRateLimiter(
|
||||
new int[] { 60, 600, 3600 },
|
||||
new int[] { 5, 15, 30 });
|
||||
|
||||
@Override
|
||||
public JSON execute(ApiContext context) throws ApiInvokeException {
|
||||
final String user = context.getParameterNotBlank("user");
|
||||
final String password = context.getParameterNotBlank("password");
|
||||
|
||||
if (RRL.overLimitWhenIncremented("user:" + user)) {
|
||||
if (RRL_4USER.overLimitWhenIncremented("user:" + user)) {
|
||||
return formatFailure(Language.L("请求过于频繁,请稍后重试"), ApiInvokeException.ERR_FREQUENCY);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.rebuild.web.OnlineSessionStore;
|
|||
import com.rebuild.web.RebuildWebConfigurer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
@ -73,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Version
|
||||
*/
|
||||
public static final String VER = "3.4.6";
|
||||
public static final String VER = "3.5.0-beta1";
|
||||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
*/
|
||||
public static final int BUILD = 3040611;
|
||||
public static final int BUILD = 3050000;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
@ -219,9 +220,14 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
RebuildConfiguration.set(ConfigurationItem.AppBuild, BUILD);
|
||||
}
|
||||
|
||||
StringBuilder logConf = new StringBuilder();
|
||||
// 刷新配置缓存
|
||||
for (ConfigurationItem item : ConfigurationItem.values()) {
|
||||
RebuildConfiguration.get(item, true);
|
||||
String v = RebuildConfiguration.get(item, true);
|
||||
logConf.append(StringUtils.rightPad(item.name(), 31)).append(" : ").append(v == null ? "" : v).append("\n");
|
||||
}
|
||||
if (log.isDebugEnabled() || Application.devMode()) {
|
||||
log.info("Use RebuildConfiguration :\n----------\n{}----------", logConf);
|
||||
}
|
||||
|
||||
// 加载自定义实体
|
||||
|
|
|
@ -147,6 +147,14 @@ public class UserContextHolder {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see #replaceUser(ID)
|
||||
*/
|
||||
public static ID getRestoreUser() {
|
||||
return CALLER_PREV.get();
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.rebuild.core.privileges.UserService;
|
|||
import com.rebuild.core.service.DataSpecificationException;
|
||||
import com.rebuild.core.service.InternalPersistService;
|
||||
import com.rebuild.core.service.query.QueryHelper;
|
||||
import com.rebuild.core.support.CommonsLock;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
|
@ -43,11 +42,6 @@ public abstract class BaseConfigurationService extends InternalPersistService {
|
|||
|
||||
@Override
|
||||
public Record update(Record record) {
|
||||
ID locked = hasLock() ? CommonsLock.getLockedUser(record.getPrimary()) : null;
|
||||
if (locked != null && !locked.equals(UserContextHolder.getUser())) {
|
||||
throw new DataSpecificationException(Language.L("操作失败 (已被锁定)"));
|
||||
}
|
||||
|
||||
throwIfNotSelf(record.getPrimary());
|
||||
cleanCache(record.getPrimary());
|
||||
return super.update(putCreateBy4ShareTo(record));
|
||||
|
@ -55,11 +49,6 @@ public abstract class BaseConfigurationService extends InternalPersistService {
|
|||
|
||||
@Override
|
||||
public int delete(ID recordId) {
|
||||
ID locked = hasLock() ? CommonsLock.getLockedUser(recordId) : null;
|
||||
if (locked != null && !locked.equals(UserContextHolder.getUser())) {
|
||||
throw new DataSpecificationException(Language.L("操作失败 (已被锁定)"));
|
||||
}
|
||||
|
||||
throwIfNotSelf(recordId);
|
||||
cleanCache(recordId);
|
||||
return super.delete(recordId);
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.rebuild.utils.JSONable;
|
|||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -120,7 +121,7 @@ public class ConfigBean implements Serializable, Cloneable, JSONable {
|
|||
/**
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Object> toMap() {
|
||||
return data;
|
||||
public Map<String, Object> getRawData() {
|
||||
return Collections.unmodifiableMap(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,16 @@ public class AutoFillinManager implements ConfigManager {
|
|||
return fillin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单后端回填
|
||||
*
|
||||
* @param record
|
||||
* @return
|
||||
*/
|
||||
public int fillinRecord(Record record) {
|
||||
return fillinRecord(record, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单后端回填
|
||||
*
|
||||
|
|
|
@ -71,6 +71,8 @@ public class FormsBuilder extends FormsManager {
|
|||
|
||||
// 分割线
|
||||
public static final String DIVIDER_LINE = "$DIVIDER$";
|
||||
// 引用
|
||||
public static final String REFFORM_LINE = "$REFFORM$";
|
||||
|
||||
// 引用主记录
|
||||
public static final String DV_MAINID = "$MAINID$";
|
||||
|
@ -121,7 +123,7 @@ public class FormsBuilder extends FormsManager {
|
|||
|
||||
final Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||
if (record != null) {
|
||||
Assert.isTrue(entityMeta.getEntityCode().equals(record.getEntityCode()), "[entity] and [record] do not match");
|
||||
Assert.isTrue(entityMeta.getEntityCode().equals(record.getEntityCode()), "[entity] and [record] do not matchs");
|
||||
|
||||
if (MetadataHelper.isBizzEntity(entityMeta) && !UserFilters.allowAccessBizz(user, record)) {
|
||||
return formatModelError(Language.L("无权读取此记录或记录已被删除"));
|
||||
|
@ -290,13 +292,12 @@ public class FormsBuilder extends FormsManager {
|
|||
return RobotApprovalManager.instance.hadApproval(entity, null);
|
||||
}
|
||||
|
||||
// 普通实体
|
||||
// 普通实体(非明细)
|
||||
if (entity.getMainEntity() == null) {
|
||||
return RobotApprovalManager.instance.hadApproval(entity, recordId);
|
||||
}
|
||||
|
||||
// 明细实体
|
||||
|
||||
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
||||
if (mainid == null) {
|
||||
Field dtmField = MetadataHelper.getDetailToMainField(entity);
|
||||
|
@ -335,6 +336,7 @@ public class FormsBuilder extends FormsManager {
|
|||
JSONObject el = (JSONObject) iter.next();
|
||||
String fieldName = el.getString("field");
|
||||
if (DIVIDER_LINE.equalsIgnoreCase(fieldName)) continue;
|
||||
if (REFFORM_LINE.equalsIgnoreCase(fieldName)) continue;
|
||||
|
||||
// 已删除字段
|
||||
if (!MetadataHelper.checkAndWarnField(entity, fieldName)) {
|
||||
|
@ -343,12 +345,13 @@ public class FormsBuilder extends FormsManager {
|
|||
}
|
||||
|
||||
// v2.2 高级控制
|
||||
Object displayOnCreate = el.remove("displayOnCreate");
|
||||
Object displayOnUpdate = el.remove("displayOnUpdate");
|
||||
Object requiredOnCreate = el.remove("requiredOnCreate");
|
||||
Object requiredOnUpdate = el.remove("requiredOnUpdate");
|
||||
if (viewModel) useAdvControl = false;
|
||||
if (useAdvControl) {
|
||||
Object displayOnCreate = el.remove("displayOnCreate");
|
||||
Object displayOnUpdate = el.remove("displayOnUpdate");
|
||||
Object requiredOnCreate = el.remove("requiredOnCreate");
|
||||
Object requiredOnUpdate = el.remove("requiredOnUpdate");
|
||||
|
||||
// fix v3.3.4 跟随主记录新建/更新
|
||||
boolean isNew2 = isNew;
|
||||
if (entity.getMainEntity() != null) {
|
||||
|
@ -650,14 +653,10 @@ public class FormsBuilder extends FormsManager {
|
|||
* @param initialVal 此值优先级大于字段默认值
|
||||
*/
|
||||
public void setFormInitialValue(Entity entity, JSON formModel, JSONObject initialVal) {
|
||||
if (initialVal == null || initialVal.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (initialVal == null || initialVal.isEmpty()) return;
|
||||
|
||||
JSONArray elements = ((JSONObject) formModel).getJSONArray("elements");
|
||||
if (elements == null || elements.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (elements == null || elements.isEmpty()) return;
|
||||
|
||||
// 已布局字段。字段是否布局会影响返回值
|
||||
Set<String> inFormFields = new HashSet<>();
|
||||
|
@ -702,7 +701,7 @@ public class FormsBuilder extends FormsManager {
|
|||
// 其他
|
||||
else if (entity.containsField(field)) {
|
||||
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(field));
|
||||
if (easyField.getDisplayType() == DisplayType.REFERENCE) {
|
||||
if (easyField.getDisplayType() == DisplayType.REFERENCE || easyField.getDisplayType() == DisplayType.N2NREFERENCE) {
|
||||
|
||||
// v3.4 如果字段设置了附加过滤条件,从相关项新建时要检查是否符合
|
||||
String dataFilter = easyField.getExtraAttr(EasyFieldConfigProps.REFERENCE_DATAFILTER);
|
||||
|
@ -720,9 +719,16 @@ public class FormsBuilder extends FormsManager {
|
|||
|
||||
Object mixValue = inFormFields.contains(field) ? getReferenceMixValue(value) : value;
|
||||
if (mixValue != null) {
|
||||
initialValReady.put(field, mixValue);
|
||||
if (easyField.getDisplayType() == DisplayType.REFERENCE) {
|
||||
initialValReady.put(field, mixValue);
|
||||
} else {
|
||||
// N2N 是数组
|
||||
initialValReady.put(field,
|
||||
inFormFields.contains(field) ? new Object[] { mixValue } : value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.warn("Unknown value pair : " + field + " = " + value);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ import com.rebuild.utils.JSONUtils;
|
|||
|
||||
/**
|
||||
* 轻量级表单
|
||||
* Issue1. 针对自动只读字段无效
|
||||
* Issue2. 表单高级控制无效
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2022/12/28
|
||||
|
|
|
@ -20,11 +20,11 @@ import com.rebuild.core.UserContextHolder;
|
|||
import com.rebuild.core.configuration.BaseConfigurationService;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.AdminGuard;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -114,7 +114,7 @@ public class PickListService extends BaseConfigurationService implements AdminGu
|
|||
r.setString("text", item.getString("text"));
|
||||
r.setBoolean("isHide", false);
|
||||
r.setBoolean("isDefault", item.getBoolean("default"));
|
||||
r.setString("color", StringUtils.defaultString(item.getString("color"), ""));
|
||||
r.setString("color", Objects.toString(item.getString("color"), ""));
|
||||
if (id2id == null) {
|
||||
r.setString("belongEntity", field.getOwnEntity().getName());
|
||||
r.setString("belongField", field.getName());
|
||||
|
|
|
@ -21,11 +21,14 @@ import com.rebuild.core.configuration.ConfigBean;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.MetadataSorter;
|
||||
import com.rebuild.core.metadata.easymeta.EasyEntity;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -79,9 +82,11 @@ public class ViewAddonsManager extends BaseLayoutManager {
|
|||
if (ifMain.getDetailEntity() != null) {
|
||||
JSONArray tabsFluent = new JSONArray();
|
||||
for (Entity de : MetadataSorter.sortDetailEntities(ifMain)) {
|
||||
JSONObject deJson = EasyMetaFactory.toJSON(de);
|
||||
EasyEntity deEasy = EasyMetaFactory.valueOf(de);
|
||||
JSONObject deJson = (JSONObject) deEasy.toJSON();
|
||||
deJson.put("entity", de.getName() + "." + MetadataHelper.getDetailToMainField(de).getName());
|
||||
deJson.put("_showAtBottom", false);
|
||||
// 显示位置
|
||||
deJson.put("showAt2", BooleanUtils.toBoolean(deEasy.getExtraAttr(EasyEntityConfigProps.DETAILS_SHOWAT2)) ? 2 : 1);
|
||||
tabsFluent.add(deJson);
|
||||
}
|
||||
|
||||
|
@ -158,9 +163,9 @@ public class ViewAddonsManager extends BaseLayoutManager {
|
|||
if (!MetadataHelper.isBusinessEntity(e)) continue;
|
||||
if (ArrayUtils.contains(entityMeta.getDetialEntities(), e)) continue;
|
||||
|
||||
// 新建项无明细、多引用
|
||||
// 新建项排除明细
|
||||
if (TYPE_ADD.equals(applyType)) {
|
||||
if (e.getMainEntity() != null || field.getType() != FieldType.REFERENCE) continue;
|
||||
if (e.getMainEntity() != null) continue;
|
||||
}
|
||||
|
||||
Entity eCheck = ObjectUtils.defaultIfNull(e.getMainEntity(), e);
|
||||
|
|
|
@ -16,6 +16,8 @@ import cn.devezhao.persist4j.record.FieldValueException;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.general.AutoFillinManager;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -26,6 +28,7 @@ import java.util.Date;
|
|||
* @see MetadataHelper
|
||||
* @since 1.0, 2018-6-26
|
||||
*/
|
||||
@Slf4j
|
||||
public class EntityHelper {
|
||||
|
||||
// 虚拟 ID 后缀
|
||||
|
@ -34,19 +37,31 @@ public class EntityHelper {
|
|||
public static final ID UNSAVED_ID = ID.valueOf("000" + UNSAVED_ID_SUFFIX);
|
||||
|
||||
/**
|
||||
* 解析 JSON 到 Record
|
||||
* 解析 JSON 为 Record
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
* @see #parse(JSONObject, ID, boolean, boolean)
|
||||
*/
|
||||
public static Record parse(JSONObject data) {
|
||||
log.info("Use SYSTEM_USER do parse");
|
||||
return parse(data, UserService.SYSTEM_USER, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 为 Record
|
||||
*
|
||||
* @param data
|
||||
* @param user
|
||||
* @return
|
||||
* @see EntityRecordCreator
|
||||
* @see #parse(JSONObject, ID, boolean, boolean)
|
||||
*/
|
||||
public static Record parse(JSONObject data, ID user) {
|
||||
return parse(data, user, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 到 Record
|
||||
* 解析 JSON 为 Record
|
||||
*
|
||||
* @param data
|
||||
* @param user
|
||||
|
@ -88,12 +103,25 @@ public class EntityHelper {
|
|||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建更新 Record
|
||||
*
|
||||
* @param recordId
|
||||
* @return
|
||||
* @see #forUpdate(ID, ID, boolean)
|
||||
*/
|
||||
public static Record forUpdate(ID recordId) {
|
||||
log.info("Use SYSTEM_USER do forUpdate");
|
||||
return forUpdate(recordId, UserService.SYSTEM_USER, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建更新 Record
|
||||
*
|
||||
* @param recordId
|
||||
* @param user
|
||||
* @return
|
||||
* @see #forUpdate(ID, ID, boolean)
|
||||
*/
|
||||
public static Record forUpdate(ID recordId, ID user) {
|
||||
return forUpdate(recordId, user, true);
|
||||
|
@ -120,12 +148,25 @@ public class EntityHelper {
|
|||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建新建 Record
|
||||
*
|
||||
* @param entity
|
||||
* @return
|
||||
* @see #forNew(int, ID, boolean)
|
||||
*/
|
||||
public static Record forNew(int entity) {
|
||||
log.info("Use SYSTEM_USER do forNew");
|
||||
return forNew(entity, UserService.SYSTEM_USER, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建新建 Record
|
||||
*
|
||||
* @param entity
|
||||
* @param user
|
||||
* @return
|
||||
* @see #forNew(int, ID, boolean)
|
||||
*/
|
||||
public static Record forNew(int entity, ID user) {
|
||||
return forNew(entity, user, true);
|
||||
|
@ -136,24 +177,14 @@ public class EntityHelper {
|
|||
*
|
||||
* @param entity
|
||||
* @param user
|
||||
* @param bindCommons
|
||||
* @return
|
||||
*/
|
||||
public static Record forNew(int entity, ID user, boolean bindCommons) {
|
||||
return forNew(MetadataHelper.getEntity(entity), user, bindCommons);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建新建 Record
|
||||
*
|
||||
* @param entity
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
private static Record forNew(Entity entity, ID user, boolean bindCommons) {
|
||||
Assert.notNull(entity, "[entity] cannot be null");
|
||||
Assert.isTrue(MetadataHelper.containsEntity(entity), "[entity] does not exists : " + entity);
|
||||
Assert.notNull(user, "[user] cannot be null");
|
||||
|
||||
Record record = new StandardRecord(entity, user);
|
||||
Record record = new StandardRecord(MetadataHelper.getEntity(entity), user);
|
||||
if (bindCommons) {
|
||||
bindCommonsFieldsValue(record, true);
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
|||
@Override
|
||||
public boolean onSetFieldValueWarn(Field field, String value, Record record) {
|
||||
// 非业务实体
|
||||
if (MetadataHelper.isBusinessEntity(field.getOwnEntity())) return true;
|
||||
if (!MetadataHelper.isBusinessEntity(field.getOwnEntity())) return true;
|
||||
|
||||
final boolean isNew = record.getPrimary() == null;
|
||||
|
||||
|
@ -76,15 +76,15 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
|||
if (isNew && isDtmField(field)) return true;
|
||||
|
||||
// 公共字段前台可能会布局出来
|
||||
// 此处忽略检查没问题,因为最后还会复写,即 #bindCommonsFieldsValue
|
||||
// 此处忽略检查没问题,因为最后还会复写,即 EntityHelper#bindCommonsFieldsValue
|
||||
boolean isCommonField = MetadataHelper.isCommonsField(field);
|
||||
if (!isCommonField) return false;
|
||||
|
||||
String fieldName = field.getName();
|
||||
return isNew || (!EntityHelper.OwningUser.equalsIgnoreCase(fieldName)
|
||||
&& !EntityHelper.OwningDept.equalsIgnoreCase(fieldName)
|
||||
&& !EntityHelper.CreatedBy.equalsIgnoreCase(fieldName)
|
||||
&& !EntityHelper.CreatedOn.equalsIgnoreCase(fieldName));
|
||||
if (isNew) return true;
|
||||
|
||||
String n = field.getName();
|
||||
return !(EntityHelper.OwningUser.equalsIgnoreCase(n) || EntityHelper.OwningDept.equalsIgnoreCase(n)
|
||||
|| EntityHelper.CreatedBy.equalsIgnoreCase(n) || EntityHelper.CreatedOn.equalsIgnoreCase(n));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,7 +172,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
|||
|
||||
if (!notNulls.isEmpty()) {
|
||||
throw new DataSpecificationException(
|
||||
Language.L("%s 不允许为空", StringUtils.join(notNulls, " / ")));
|
||||
Language.L("%s 不能为空", StringUtils.join(notNulls, " / ")));
|
||||
}
|
||||
if (!notWells.isEmpty()) {
|
||||
throw new DataSpecificationException(
|
||||
|
@ -212,6 +212,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
|||
return patt == null || patt.matcher((CharSequence) val).matches();
|
||||
}
|
||||
|
||||
// 是否需要去除外链
|
||||
private void keepFieldValueSafe(Record record) {
|
||||
for (String fieldName : record.getAvailableFields()) {
|
||||
final Object value = record.getObjectValue(fieldName);
|
||||
|
|
|
@ -111,7 +111,8 @@ public class MetadataSorter {
|
|||
|
||||
List<BaseMeta> entities = new ArrayList<>();
|
||||
CollectionUtils.addAll(entities, mainEntity.getDetialEntities());
|
||||
sortByLabel(entities);
|
||||
// SORT: 名称。默认是返回按CODE大小
|
||||
if (entities.size() > 1) sortByLabel(entities);
|
||||
return entities.toArray(new Entity[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,4 +40,9 @@ public class EasyBarCode extends EasyField {
|
|||
if (value != null) log.warn("Cannot wrap value of EasyBarCode : " + value);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exprDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,6 @@ public class EasyDecimal extends EasyField {
|
|||
*/
|
||||
public static String clearFlaged(Object flagedValue) {
|
||||
if (flagedValue == null) return null;
|
||||
return flagedValue.toString().replaceAll("[^\\d^.^-]", "");
|
||||
return flagedValue.toString().replaceAll("[^\\d.-]", "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,9 @@ public class EasyFile extends EasyField {
|
|||
if (value instanceof JSONArray) return value;
|
||||
return JSON.parseArray(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exprDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,4 +30,9 @@ public class EasySeries extends EasyField {
|
|||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exprDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,4 +28,5 @@ public class EasySign extends EasyField {
|
|||
Assert.isTrue(targetField.getDisplayType() == getDisplayType(), "type-by-type is must");
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ import com.rebuild.core.rbstore.MetaSchemaGenerator;
|
|||
import com.rebuild.core.rbstore.MetaschemaImporter;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.task.TaskExecutors;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
|
||||
/**
|
||||
* 复制实体
|
||||
|
@ -83,7 +83,7 @@ public class CopyEntity extends Entity2Schema {
|
|||
String uniqueEntityName = toPinyinName(entityName);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (MetadataHelper.containsEntity(uniqueEntityName)) {
|
||||
uniqueEntityName += RandomUtils.nextInt(0, 9);
|
||||
uniqueEntityName += CommonsUtils.randomInt(0, 9);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ public class EasyEntityConfigProps {
|
|||
* 明细重复判断模式为全部数据(否则为主记录下的)
|
||||
*/
|
||||
public static final String DETAILS_GLOBALREPEAT = "detailsGlobalRepeat";
|
||||
/**
|
||||
* 明细显示位置
|
||||
*/
|
||||
public static final String DETAILS_SHOWAT2 = "detailsShowAt2";
|
||||
|
||||
/**
|
||||
* 隐藏常用查询面板
|
||||
|
|
|
@ -25,9 +25,9 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.support.License;
|
||||
import com.rebuild.core.support.NeedRbvException;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
|
||||
/**
|
||||
* 创建实体
|
||||
|
@ -74,7 +74,7 @@ public class Entity2Schema extends Field2Schema {
|
|||
entityName = toPinyinName(entityLabel);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (MetadataHelper.containsEntity(entityName)) {
|
||||
entityName += RandomUtils.nextInt(0, 9);
|
||||
entityName += CommonsUtils.randomInt(0, 9);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import com.rebuild.utils.RbAssert;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.CharSet;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
|
@ -109,7 +108,7 @@ public class Field2Schema extends SetUser {
|
|||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (entity.containsField(fieldName) || MetadataHelper.isCommonsField(fieldName)) {
|
||||
fieldName += RandomUtils.nextInt(1, 99);
|
||||
fieldName += CommonsUtils.randomInt(1, 99);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
@ -396,7 +395,7 @@ public class Field2Schema extends SetUser {
|
|||
identifier = HanLP.convertToPinyinString(identifier, "", false);
|
||||
identifier = identifier.replaceAll("[^a-zA-Z0-9]", "");
|
||||
if (StringUtils.isBlank(identifier)) {
|
||||
identifier = "rb" + RandomUtils.nextInt(1000, 9999);
|
||||
identifier = "rb" + CommonsUtils.randomInt(1000, 9999);
|
||||
}
|
||||
|
||||
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0))) {
|
||||
|
@ -407,7 +406,7 @@ public class Field2Schema extends SetUser {
|
|||
if (identifier.length() > 40) {
|
||||
identifier = identifier.substring(0, 40);
|
||||
} else if (identifier.length() < 4) {
|
||||
identifier += RandomUtils.nextInt(1000, 9999);
|
||||
identifier += CommonsUtils.randomInt(1000, 9999);
|
||||
}
|
||||
|
||||
return identifier;
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.UserContextHolder;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||
import com.rebuild.core.service.CommonsService;
|
||||
import com.rebuild.core.service.general.BulkContext;
|
||||
import com.rebuild.core.service.general.EntityService;
|
||||
|
@ -190,7 +191,7 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
|||
} else if (action.startsWith("share")) {
|
||||
return BizzPermission.SHARE;
|
||||
} else if (action.startsWith("unshare")) {
|
||||
return EntityService.UNSHARE;
|
||||
return InternalPermission.UNSHARE;
|
||||
}
|
||||
throw new PrivilegesException("No such Permission found : " + action);
|
||||
}
|
||||
|
@ -213,8 +214,10 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
|||
actionHuman = Language.L("分配");
|
||||
} else if (action == BizzPermission.SHARE) {
|
||||
actionHuman = Language.L("共享");
|
||||
} else if (action == EntityService.UNSHARE) {
|
||||
} else if (action == InternalPermission.UNSHARE) {
|
||||
actionHuman = Language.L("取消共享");
|
||||
} else if (action == InternalPermission.APPROVAL) {
|
||||
actionHuman = Language.L("审批");
|
||||
}
|
||||
|
||||
if (target == null) {
|
||||
|
|
|
@ -30,7 +30,6 @@ import com.rebuild.core.privileges.bizz.User;
|
|||
import com.rebuild.core.privileges.bizz.ZeroEntry;
|
||||
import com.rebuild.core.privileges.bizz.ZeroPrivileges;
|
||||
import com.rebuild.core.service.NoRecordFoundException;
|
||||
import com.rebuild.core.service.general.EntityService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
@ -220,10 +219,11 @@ public class PrivilegesManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
// FIXME v35 批量审批无权限
|
||||
if (action == InternalPermission.APPROVAL) return true;
|
||||
|
||||
Boolean a = userAllow(user);
|
||||
if (a != null) {
|
||||
return a;
|
||||
}
|
||||
if (a != null) return a;
|
||||
|
||||
Role role = theUserStore.getUser(user).getOwningRole();
|
||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
||||
|
@ -233,7 +233,7 @@ public class PrivilegesManager {
|
|||
}
|
||||
|
||||
// 取消共享与共享共用权限
|
||||
if (action == EntityService.UNSHARE) {
|
||||
if (action == InternalPermission.UNSHARE) {
|
||||
action = BizzPermission.SHARE;
|
||||
}
|
||||
|
||||
|
@ -285,9 +285,7 @@ public class PrivilegesManager {
|
|||
}
|
||||
|
||||
Boolean a = userAllow(user);
|
||||
if (a != null) {
|
||||
return a;
|
||||
}
|
||||
if (a != null) return a;
|
||||
|
||||
Role role = theUserStore.getUser(user).getOwningRole();
|
||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
||||
|
@ -305,7 +303,7 @@ public class PrivilegesManager {
|
|||
}
|
||||
|
||||
// 取消共享与共享共用权限
|
||||
if (action == EntityService.UNSHARE) {
|
||||
if (action == InternalPermission.UNSHARE) {
|
||||
action = BizzPermission.SHARE;
|
||||
}
|
||||
|
||||
|
@ -492,9 +490,7 @@ public class PrivilegesManager {
|
|||
*/
|
||||
public boolean allow(ID user, ZeroEntry entry) {
|
||||
Boolean a = userAllow(user);
|
||||
if (a != null) {
|
||||
return a;
|
||||
}
|
||||
if (a != null) return a;
|
||||
|
||||
Role role = theUserStore.getUser(user).getOwningRole();
|
||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
||||
|
@ -543,7 +539,7 @@ public class PrivilegesManager {
|
|||
BizzPermission.READ,
|
||||
BizzPermission.ASSIGN,
|
||||
BizzPermission.SHARE,
|
||||
EntityService.UNSHARE
|
||||
InternalPermission.UNSHARE
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -132,9 +132,14 @@ public class UserService extends BaseService {
|
|||
String dsql = String.format("delete from `layout_config` where `CREATED_BY` = '%s'", recordId);
|
||||
Application.getSqlExecutor().execute(dsql);
|
||||
|
||||
// 2.删除并刷新缓存
|
||||
// 2.删除三方
|
||||
String dsql2 = String.format("delete from `external_user` where `BIND_USER` = '%s'", recordId);
|
||||
Application.getSqlExecutor().execute(dsql2);
|
||||
|
||||
// 3.删除并刷新缓存
|
||||
super.delete(recordId);
|
||||
Application.getUserStore().removeUser(recordId);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -214,9 +219,7 @@ public class UserService extends BaseService {
|
|||
}
|
||||
|
||||
int policy = RebuildConfiguration.getInt(ConfigurationItem.PasswordPolicy);
|
||||
if (policy <= 1) {
|
||||
return;
|
||||
}
|
||||
if (policy <= 1) return;
|
||||
|
||||
int countUpper = 0;
|
||||
int countLower = 0;
|
||||
|
@ -237,8 +240,8 @@ public class UserService extends BaseService {
|
|||
if (countUpper == 0 || countLower == 0 || countDigit == 0) {
|
||||
throw new DataSpecificationException(Language.L("密码不能小于 6 位,且必须包含数字和大小写字母"));
|
||||
}
|
||||
if (policy >= 3 && (countSpecial == 0 || password.length() < 8)) {
|
||||
throw new DataSpecificationException(Language.L("密码不能小于 8 位,且必须包含数字和大小写字母及特殊字符"));
|
||||
if (policy >= 3 && (countSpecial == 0 || password.length() < 10)) {
|
||||
throw new DataSpecificationException(Language.L("密码不能小于 10 位,且必须包含数字和大小写字母及特殊字符"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -489,13 +492,6 @@ public class UserService extends BaseService {
|
|||
"select user from LoginLog where user = ?")
|
||||
.setParameter(1, user)
|
||||
.unique();
|
||||
if (hasLogin != null) return true;
|
||||
|
||||
// 绑定
|
||||
Object[] hasBind = Application.createQueryNoFilter(
|
||||
"select bindUser from ExternalUser where bindUser = ?")
|
||||
.setParameter(1, user)
|
||||
.unique();
|
||||
return hasBind != null;
|
||||
return hasLogin != null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,4 +32,11 @@ public class InternalPermission {
|
|||
*/
|
||||
public static final Permission APPROVAL = new BizzPermission("APPROVAL", 0, false);
|
||||
|
||||
/**
|
||||
* 取消共享,跟随共享权限
|
||||
*
|
||||
* @see BizzPermission
|
||||
*/
|
||||
public static final Permission UNSHARE = new BizzPermission("UNSHARE", 1 << 6, true);
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.metadata.easymeta.EasyTag;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.general.QuickCodeReindexTask;
|
||||
import com.rebuild.utils.Callable2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
|
@ -147,33 +146,38 @@ public class BaseService extends InternalPersistService {
|
|||
final Map<String, ID[]> holdN2NValues = new HashMap<>();
|
||||
|
||||
for (Field n2nField : n2nFields) {
|
||||
ID[] newRefs;
|
||||
ID[] newValue;
|
||||
if (isNew) {
|
||||
Object maybeNull = record.getObjectValue(n2nField.getName());
|
||||
newRefs = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
||||
if (newRefs == null || newRefs.length == 0) continue;
|
||||
if (maybeNull == null) continue;
|
||||
|
||||
newValue = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
||||
if (newValue.length == 0) {
|
||||
record.setNull(n2nField.getName());
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (record.hasValue(n2nField.getName())) {
|
||||
Object maybeNull = record.getObjectValue(n2nField.getName());
|
||||
newRefs = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
||||
newValue = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 保持值
|
||||
holdN2NValues.put(n2nField.getName(), newRefs);
|
||||
holdN2NValues.put(n2nField.getName(), newValue);
|
||||
|
||||
// 仅保留第一个用于标识是否为空
|
||||
if (newRefs.length == 0) record.setNull(n2nField.getName());
|
||||
else record.setIDArray(n2nField.getName(), new ID[] { newRefs[0] });
|
||||
if (newValue.length == 0) record.setNull(n2nField.getName());
|
||||
else record.setIDArray(n2nField.getName(), new ID[] { newValue[0] });
|
||||
|
||||
// 哪个字段
|
||||
n2nRecord.setString("belongField", n2nField.getName());
|
||||
|
||||
// 新建
|
||||
if (isNew) {
|
||||
for (ID refId : newRefs) {
|
||||
for (ID refId : newValue) {
|
||||
Record clone = n2nRecord.clone();
|
||||
clone.setID("referenceId", refId);
|
||||
addItems.add(clone);
|
||||
|
@ -193,7 +197,7 @@ public class BaseService extends InternalPersistService {
|
|||
}
|
||||
|
||||
Set<ID> afterRefs = new LinkedHashSet<>();
|
||||
CollectionUtils.addAll(afterRefs, newRefs);
|
||||
CollectionUtils.addAll(afterRefs, newValue);
|
||||
|
||||
for (Iterator<ID> iter = afterRefs.iterator(); iter.hasNext(); ) {
|
||||
ID a = iter.next();
|
||||
|
@ -278,31 +282,37 @@ public class BaseService extends InternalPersistService {
|
|||
final Map<String, String[]> holdTagValues = new HashMap<>();
|
||||
|
||||
for (Field tagField : tagFields) {
|
||||
String[] newTags;
|
||||
String[] newValue;
|
||||
if (isNew) {
|
||||
newTags = cleanNameArray(record.getObjectValue(tagField.getName()));
|
||||
if (newTags.length == 0) continue;
|
||||
Object maybeNull = record.getObjectValue(tagField.getName());
|
||||
if (maybeNull == null) continue;
|
||||
|
||||
newValue = cleanNameArray(maybeNull);
|
||||
if (newValue.length == 0) {
|
||||
record.setNull(tagField.getName());
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (record.hasValue(tagField.getName())) {
|
||||
newTags = cleanNameArray(record.getObjectValue(tagField.getName()));
|
||||
newValue = cleanNameArray(record.getObjectValue(tagField.getName()));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 保持值
|
||||
holdTagValues.put(tagField.getName(), newTags);
|
||||
holdTagValues.put(tagField.getName(), newValue);
|
||||
|
||||
// 仅保留第一个用于标识是否为空
|
||||
if (newTags.length == 0) record.setNull(tagField.getName());
|
||||
else record.setString(tagField.getName(), newTags[0]);
|
||||
if (newValue.length == 0) record.setNull(tagField.getName());
|
||||
else record.setString(tagField.getName(), newValue[0]);
|
||||
|
||||
// 哪个字段
|
||||
tagRecord.setString("belongField", tagField.getName());
|
||||
|
||||
// 新建
|
||||
if (isNew) {
|
||||
for (String tagName : newTags) {
|
||||
for (String tagName : newValue) {
|
||||
Record clone = tagRecord.clone();
|
||||
clone.setString("tagName", tagName);
|
||||
addItems.add(clone);
|
||||
|
@ -322,7 +332,7 @@ public class BaseService extends InternalPersistService {
|
|||
}
|
||||
|
||||
Set<String> afterTags = new LinkedHashSet<>();
|
||||
CollectionUtils.addAll(afterTags, newTags);
|
||||
CollectionUtils.addAll(afterTags, newValue);
|
||||
|
||||
for (Iterator<String> iter = afterTags.iterator(); iter.hasNext(); ) {
|
||||
String a = iter.next();
|
||||
|
|
|
@ -5,7 +5,7 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
|||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.utils;
|
||||
package com.rebuild.core.service;
|
||||
|
||||
/**
|
||||
* @author devezhao
|
|
@ -25,7 +25,7 @@ import org.springframework.util.Assert;
|
|||
* <br>- 有权限的实体使用此类需要指定 <tt>strictMode=false</tt>
|
||||
*
|
||||
* @author Zixin (RB)
|
||||
* @since 11/06/2017
|
||||
* @since 11/06/2019
|
||||
*/
|
||||
@Service
|
||||
public class CommonsService extends InternalPersistService {
|
||||
|
|
|
@ -16,7 +16,7 @@ import com.rebuild.core.Application;
|
|||
* 持久化服务
|
||||
*
|
||||
* @author Zixin (RB)
|
||||
* @since 05/21/2017
|
||||
* @since 05/21/2019
|
||||
*/
|
||||
public abstract class InternalPersistService implements ServiceSpec {
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
|||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* 手动事物管理。默认事务管理见 `application-bean.xml`
|
||||
|
@ -27,6 +28,8 @@ public class TransactionManual {
|
|||
* 开启一个事物
|
||||
*
|
||||
* @return
|
||||
* @see #commit(TransactionStatus)
|
||||
* @see #rollback(TransactionStatus)
|
||||
*/
|
||||
public static TransactionStatus newTransaction() {
|
||||
DefaultTransactionAttribute attr = new DefaultTransactionAttribute();
|
||||
|
@ -34,15 +37,6 @@ public class TransactionManual {
|
|||
return getTxManager().getTransaction(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for TransactionAspectSupport#currentTransactionStatus
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TransactionStatus currentTransaction() {
|
||||
return TransactionAspectSupport.currentTransactionStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*
|
||||
|
@ -72,4 +66,21 @@ public class TransactionManual {
|
|||
return Application.getBean(DataSourceTransactionManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for <tt>TransactionAspectSupport#currentTransactionStatus</tt>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TransactionStatus currentTransactionStatus() {
|
||||
return TransactionAspectSupport.currentTransactionStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for <tt>TransactionSynchronizationManager.getCurrentTransactionName</tt>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String currentTransactionName() {
|
||||
return TransactionSynchronizationManager.getCurrentTransactionName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class ApprovalHelper {
|
|||
Object[] o = Application.getQueryFactory().uniqueNoFilter(recordId,
|
||||
EntityHelper.ApprovalId, EntityHelper.ApprovalId + ".name", EntityHelper.ApprovalState, EntityHelper.ApprovalStepNode);
|
||||
if (o == null) {
|
||||
throw new NoRecordFoundException(recordId, Boolean.TRUE);
|
||||
throw new NoRecordFoundException(recordId, true);
|
||||
}
|
||||
return new ApprovalStatus((ID) o[0], (String) o[1], (Integer) o[2], (String) o[3], recordId);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import cn.devezhao.commons.CalendarUtils;
|
|||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
|
@ -122,7 +123,7 @@ public class ApprovalProcessor extends SetUser {
|
|||
* @throws ApprovalException
|
||||
*/
|
||||
public void approve(ID approver, ApprovalState state, String remark, JSONObject selectNextUsers) throws ApprovalException {
|
||||
approve(approver, state, remark, selectNextUsers, null, null, null);
|
||||
approve(approver, state, remark, selectNextUsers, null, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,13 +136,14 @@ public class ApprovalProcessor extends SetUser {
|
|||
* @param addedData
|
||||
* @param checkUseGroup
|
||||
* @param rejectNode
|
||||
* @param batchMode
|
||||
* @throws ApprovalException
|
||||
*/
|
||||
public void approve(ID approver, ApprovalState state, String remark, JSONObject selectNextUsers, Record addedData, String checkUseGroup, String rejectNode) throws ApprovalException {
|
||||
public void approve(ID approver, ApprovalState state, String remark, JSONObject selectNextUsers, Record addedData, String checkUseGroup, String rejectNode, boolean batchMode) throws ApprovalException {
|
||||
final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
||||
|
||||
final Object[] stepApprover = Application.createQueryNoFilter(
|
||||
"select stepId,state,node,approvalId from RobotApprovalStep where recordId = ? and approver = ? and node = ? and isCanceled = 'F' order by createdOn desc")
|
||||
"select stepId,state,node,approvalId,attrMore from RobotApprovalStep where recordId = ? and approver = ? and node = ? and isCanceled = 'F' order by createdOn desc")
|
||||
.setParameter(1, this.record)
|
||||
.setParameter(2, approver)
|
||||
.setParameter(3, getCurrentNodeId(status))
|
||||
|
@ -159,6 +161,13 @@ public class ApprovalProcessor extends SetUser {
|
|||
approvedStep.setString("remark", remark);
|
||||
}
|
||||
|
||||
if (batchMode) {
|
||||
JSONObject attrMore = JSONUtils.wellFormat((String) stepApprover[4])
|
||||
? JSON.parseObject((String) stepApprover[4]) : new JSONObject();
|
||||
attrMore.put("batchMode", true);
|
||||
approvedStep.setString("attrMore", attrMore.toJSONString());
|
||||
}
|
||||
|
||||
this.approval = (ID) stepApprover[3];
|
||||
FlowNodeGroup nextNodes = getNextNodes((String) stepApprover[2]);
|
||||
|
||||
|
@ -575,6 +584,9 @@ public class ApprovalProcessor extends SetUser {
|
|||
// 加签
|
||||
String countersignFrom = attrMored.getString("countersignFrom");
|
||||
s.put("countersignFrom", ID.isId(countersignFrom) ? UserHelper.getName(ID.valueOf(countersignFrom)) : null);
|
||||
// 批量
|
||||
String batchMode = attrMored.getString("batchMode");
|
||||
s.put("batchMode", batchMode != null);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
|
|
@ -265,6 +265,10 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
if (goNextNode && (nextApprovers == null || nextNode == null)) {
|
||||
super.update(recordOfMain);
|
||||
|
||||
String approvedMsg = Language.L("你提交的 %s 已审批通过", entityLabel);
|
||||
if (StringUtils.isNotBlank(remark)) approvedMsg += "\n > " + remark;
|
||||
sendNotification(submitter, approvedMsg, recordId);
|
||||
|
||||
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, approver);
|
||||
return;
|
||||
}
|
||||
|
@ -460,6 +464,8 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
setApprovalLastX(recordOfMain, useApprover, Language.L("自动审批"));
|
||||
super.update(recordOfMain);
|
||||
|
||||
// NOTE 自动审批不会给提交人发通知
|
||||
|
||||
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, useApprover);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,11 @@ import com.rebuild.core.privileges.bizz.Department;
|
|||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 流程节点(包括审批、抄送)
|
||||
|
@ -148,6 +152,14 @@ public class FlowNode {
|
|||
return b != null && b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public boolean allowBatch() {
|
||||
Boolean b = getDataMap().getBoolean("allowBatch");
|
||||
return b != null && b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相关人员(提交人/审批人/抄送人)
|
||||
*
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.service.query.QueryHelper;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -29,6 +31,7 @@ import java.util.List;
|
|||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/06/24
|
||||
*/
|
||||
@Slf4j
|
||||
public class RobotApprovalManager implements ConfigManager {
|
||||
|
||||
public static final RobotApprovalManager instance = new RobotApprovalManager();
|
||||
|
@ -42,16 +45,22 @@ public class RobotApprovalManager implements ConfigManager {
|
|||
* 获取实体/记录流程状态
|
||||
*
|
||||
* @param entity
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @return <tt>null</tt> 表示没有流程
|
||||
*/
|
||||
public ApprovalState hadApproval(Entity entity, ID record) {
|
||||
public ApprovalState hadApproval(Entity entity, ID recordId) {
|
||||
if (entity.getMainEntity() != null || !MetadataHelper.hasApprovalField(entity)) return null;
|
||||
|
||||
// 记录的
|
||||
if (record != null) {
|
||||
Object[] o = Application.getQueryFactory().unique(
|
||||
record, EntityHelper.ApprovalId, EntityHelper.ApprovalState);
|
||||
if (recordId != null) {
|
||||
if (!recordId.getEntityCode().equals(entity.getEntityCode())) {
|
||||
log.warn("Entity and Record/ID mismatch : {}, {}", entity, recordId);
|
||||
CommonsUtils.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(
|
||||
recordId, EntityHelper.ApprovalId, EntityHelper.ApprovalState);
|
||||
if (o != null && o[0] != null) {
|
||||
return (ApprovalState) ApprovalState.valueOf((Integer) o[1]);
|
||||
}
|
||||
|
@ -103,17 +112,17 @@ public class RobotApprovalManager implements ConfigManager {
|
|||
/**
|
||||
* 获取用户可用流程
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
public FlowDefinition[] getFlowDefinitions(ID record, ID user) {
|
||||
FlowDefinition[] defs = getFlowDefinitions(MetadataHelper.getEntity(record.getEntityCode()));
|
||||
public FlowDefinition[] getFlowDefinitions(ID recordId, ID user) {
|
||||
FlowDefinition[] defs = getFlowDefinitions(MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||
if (defs.length == 0) {
|
||||
return new FlowDefinition[0];
|
||||
}
|
||||
|
||||
ID owning = Application.getRecordOwningCache().getOwningUser(record);
|
||||
ID owning = Application.getRecordOwningCache().getOwningUser(recordId);
|
||||
// 过滤可用的
|
||||
List<FlowDefinition> workable = new ArrayList<>();
|
||||
for (FlowDefinition def : defs) {
|
||||
|
@ -130,11 +139,11 @@ public class RobotApprovalManager implements ConfigManager {
|
|||
|
||||
if (FlowNode.USER_ALL.equals(users.getString(0))
|
||||
|| (FlowNode.USER_OWNS.equals(users.getString(0)) && owning.equals(user))
|
||||
|| UserHelper.parseUsers(users, record).contains(user)) {
|
||||
|| UserHelper.parseUsers(users, recordId).contains(user)) {
|
||||
|
||||
// 过滤条件
|
||||
JSONObject filter = root.getDataMap().getJSONObject("filter");
|
||||
if (QueryHelper.isMatchAdvFilter(record, filter)) {
|
||||
if (QueryHelper.isMatchAdvFilter(recordId, filter)) {
|
||||
workable.add(def);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,9 @@ import java.util.List;
|
|||
public class DataExporter extends SetUser {
|
||||
|
||||
/**
|
||||
* 最大行数
|
||||
* 最大导出行数
|
||||
*/
|
||||
public static final int MAX_ROWS = 65535 - 1;
|
||||
public static final int MAX_ROWS = 1000000;
|
||||
|
||||
final private JSONObject queryData;
|
||||
// 字段
|
||||
|
|
|
@ -96,7 +96,13 @@ public class DataImporter extends HeavyTask<Integer> {
|
|||
traceLogs.add(new Object[] { firstCell.getRowNo(), "SKIP" });
|
||||
} else {
|
||||
|
||||
boolean isNew = record.getPrimary() == null;
|
||||
final boolean isNew = record.getPrimary() == null;
|
||||
|
||||
if (isNew && rule.getRepeatOpt() == ImportRule.REPEAT_OPT_UPDATE && rule.isOnlyUpdate()) {
|
||||
traceLogs.add(new Object[] { firstCell.getRowNo(), "SKIP" });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isViaAdmin) {
|
||||
String error = null;
|
||||
if (isNew) {
|
||||
|
|
|
@ -13,8 +13,7 @@ import cn.devezhao.persist4j.engine.ID;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -31,10 +30,9 @@ import java.util.Set;
|
|||
* @author devezhao
|
||||
* @since 01/10/2019
|
||||
*/
|
||||
@Slf4j
|
||||
public class ImportRule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ImportRule.class);
|
||||
|
||||
public static final int REPEAT_OPT_UPDATE = 1;
|
||||
public static final int REPEAT_OPT_SKIP = 2;
|
||||
public static final int REPEAT_OPT_IGNORE = 3;
|
||||
|
@ -44,6 +42,7 @@ public class ImportRule {
|
|||
|
||||
private int repeatOpt;
|
||||
private Field[] repeatFields;
|
||||
private boolean onlyUpdate;
|
||||
|
||||
private ID defaultOwningUser;
|
||||
|
||||
|
@ -54,15 +53,17 @@ public class ImportRule {
|
|||
* @param toEntity
|
||||
* @param repeatOpt
|
||||
* @param repeatFields
|
||||
* @param onlyUpdate
|
||||
* @param defaultOwningUser
|
||||
* @param filedsMapping
|
||||
*/
|
||||
protected ImportRule(File sourceFile, Entity toEntity, int repeatOpt, Field[] repeatFields, ID defaultOwningUser,
|
||||
ImportRule(File sourceFile, Entity toEntity, int repeatOpt, Field[] repeatFields, boolean onlyUpdate, ID defaultOwningUser,
|
||||
Map<Field, Integer> filedsMapping) {
|
||||
this.sourceFile = sourceFile;
|
||||
this.toEntity = toEntity;
|
||||
this.repeatOpt = repeatOpt;
|
||||
this.repeatFields = repeatFields;
|
||||
this.onlyUpdate = onlyUpdate;
|
||||
this.defaultOwningUser = defaultOwningUser;
|
||||
this.filedsMapping = filedsMapping;
|
||||
}
|
||||
|
@ -91,6 +92,10 @@ public class ImportRule {
|
|||
return filedsMapping;
|
||||
}
|
||||
|
||||
public boolean isOnlyUpdate() {
|
||||
return onlyUpdate;
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
|
@ -119,7 +124,7 @@ public class ImportRule {
|
|||
throw new IllegalArgumentException("File not found : " + file, e);
|
||||
}
|
||||
}
|
||||
LOG.warn("Use file from TestCase : " + file);
|
||||
log.warn("Use file from TestCase : " + file);
|
||||
}
|
||||
if (!file.exists()) {
|
||||
throw new IllegalArgumentException("File not found : " + file);
|
||||
|
@ -146,6 +151,7 @@ public class ImportRule {
|
|||
filedsMapping.put(entity.getField(e.getKey()), (Integer) e.getValue());
|
||||
}
|
||||
|
||||
return new ImportRule(file, entity, repeatOpt, repeatFields, ownUser, filedsMapping);
|
||||
return new ImportRule(
|
||||
file, entity, repeatOpt, repeatFields, rule.getBooleanValue("only_update"), ownUser, filedsMapping);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -335,8 +335,7 @@ public class RecordCheckout {
|
|||
while (num >= 0) {
|
||||
int remainder = num % 26;
|
||||
name.insert(0, (char) (remainder + 65));
|
||||
//noinspection IntegerDivisionInFloatingPointContext
|
||||
num = (int) Math.floor(num / 26) - 1;
|
||||
num = (int) (double) (num / 26) - 1;
|
||||
}
|
||||
name.append(cell.getRowNo() + 1);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.configuration.ConfigBean;
|
|||
import com.rebuild.core.configuration.ConfigManager;
|
||||
import com.rebuild.core.configuration.ConfigurationException;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.general.ContentWithFieldVars;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
|
@ -26,7 +27,9 @@ import org.apache.commons.lang.StringUtils;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据报表
|
||||
|
@ -42,22 +45,42 @@ public class DataReportManager implements ConfigManager {
|
|||
|
||||
public static final int TYPE_RECORD = 1;
|
||||
public static final int TYPE_LIST = 2;
|
||||
public static final int TYPE_HTML5 = 3;
|
||||
public static final int TYPE_WORD = 4;
|
||||
|
||||
/**
|
||||
* 获取报表列表
|
||||
* 获取可用报表
|
||||
*
|
||||
* @param entity
|
||||
* @param type
|
||||
* @param type 指定类型
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
public JSONArray getReports(Entity entity, int type) {
|
||||
JSONArray list = new JSONArray();
|
||||
public JSONArray getReports(Entity entity, int type, ID user) {
|
||||
JSONArray alist = new JSONArray();
|
||||
for (ConfigBean e : getReportsRaw(entity)) {
|
||||
if (!e.getBoolean("disabled") && e.getInteger("type") == type) {
|
||||
list.add(e.toJSON("id", "name", "outputType"));
|
||||
if (e.getBoolean("disabled")) continue;
|
||||
|
||||
boolean can;
|
||||
int aType = e.getInteger("type");
|
||||
if (type == DataReportManager.TYPE_LIST) {
|
||||
can = aType == type;
|
||||
} else {
|
||||
can = aType == DataReportManager.TYPE_RECORD || aType == DataReportManager.TYPE_WORD;
|
||||
}
|
||||
|
||||
if (can) {
|
||||
// v3.5
|
||||
String vuDef = e.getString("visibleUsers");
|
||||
if (StringUtils.isNotBlank(vuDef)) {
|
||||
Set<ID> users = UserHelper.parseUsers(Arrays.asList(vuDef.split(",")), null);
|
||||
if (!users.contains(user)) continue;
|
||||
}
|
||||
|
||||
alist.add(e.toJSON("id", "name", "outputType"));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
return alist;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,14 +90,12 @@ public class DataReportManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public ConfigBean[] getReportsRaw(Entity entity) {
|
||||
final String cKey = "DataReportManager33-" + entity.getName();
|
||||
final String cKey = "DataReportManager35-" + entity.getName();
|
||||
ConfigBean[] cached = (ConfigBean[]) Application.getCommonsCache().getx(cKey);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
if (cached != null) return cached;
|
||||
|
||||
Object[][] array = Application.createQueryNoFilter(
|
||||
"select configId,name,isDisabled,templateFile,templateType,extraDefinition from DataReportConfig where belongEntity = ?")
|
||||
"select configId,name,isDisabled,templateFile,templateType,extraDefinition,templateContent from DataReportConfig where belongEntity = ?")
|
||||
.setParameter(1, entity.getName())
|
||||
.array();
|
||||
|
||||
|
@ -83,15 +104,21 @@ public class DataReportManager implements ConfigManager {
|
|||
JSONObject extra = o[5] == null ? JSONUtils.EMPTY_OBJECT : JSON.parseObject((String) o[5]);
|
||||
String outputType = StringUtils.defaultIfBlank(extra.getString("outputType"), "excel");
|
||||
int templateVersion = extra.containsKey("templateVersion") ? extra.getInteger("templateVersion") : 2;
|
||||
String visibleUsersDef = extra.getString("visibleUsers");
|
||||
|
||||
int type = ObjectUtils.toInt(o[4], TYPE_RECORD);
|
||||
if (type == TYPE_WORD && outputType.contains("excel")) outputType += ",word";
|
||||
|
||||
ConfigBean cb = new ConfigBean()
|
||||
.set("id", o[0])
|
||||
.set("name", o[1])
|
||||
.set("disabled", o[2])
|
||||
.set("template", o[3])
|
||||
.set("type", ObjectUtils.toInt(o[4], TYPE_RECORD))
|
||||
.set("type", type)
|
||||
.set("outputType", outputType)
|
||||
.set("templateVersion", templateVersion);
|
||||
.set("templateVersion", templateVersion)
|
||||
.set("visibleUsers", visibleUsersDef)
|
||||
.set("templateContent", o[6]);
|
||||
alist.add(cb);
|
||||
}
|
||||
|
||||
|
@ -106,29 +133,36 @@ public class DataReportManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public TemplateFile getTemplateFile(Entity entity, ID reportId) {
|
||||
String template = null;
|
||||
boolean isList = false;
|
||||
String templateFile = null;
|
||||
String templateContent = null;
|
||||
int type = DataReportManager.TYPE_RECORD;
|
||||
boolean isV33 = false;
|
||||
|
||||
for (ConfigBean e : getReportsRaw(entity)) {
|
||||
if (e.getID("id").equals(reportId)) {
|
||||
template = e.getString("template");
|
||||
isList = e.getInteger("type") == TYPE_LIST;
|
||||
templateFile = e.getString("template");
|
||||
templateContent = e.getString("templateContent");
|
||||
type = e.getInteger("type");
|
||||
isV33 = e.getInteger("templateVersion") == 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (template == null) {
|
||||
// v35 HTML5
|
||||
if (templateContent != null) {
|
||||
return new TemplateFile(templateContent, entity);
|
||||
}
|
||||
|
||||
if (templateFile == null) {
|
||||
throw new ConfigurationException("No template of report found : " + reportId);
|
||||
}
|
||||
|
||||
File file = RebuildConfiguration.getFileOfData(template);
|
||||
File file = RebuildConfiguration.getFileOfData(templateFile);
|
||||
if (!file.exists()) {
|
||||
throw new ConfigurationException("File of template not extsts : " + file);
|
||||
}
|
||||
|
||||
return new TemplateFile(file, entity, isList, isV33);
|
||||
return new TemplateFile(file, entity, type, isV33);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,23 +171,22 @@ public class DataReportManager implements ConfigManager {
|
|||
* @see #getTemplateFile(Entity, ID) 性能好
|
||||
*/
|
||||
public TemplateFile getTemplateFile(ID reportId) {
|
||||
Object[] report = Application.createQueryNoFilter(
|
||||
"select belongEntity from DataReportConfig where configId = ?")
|
||||
.setParameter(1, reportId)
|
||||
.unique();
|
||||
if (report == null || !MetadataHelper.containsEntity((String) report[0])) {
|
||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(reportId, "belongEntity");
|
||||
if (o == null || !MetadataHelper.containsEntity((String) o[0])) {
|
||||
throw new ConfigurationException("No config of report found : " + reportId);
|
||||
}
|
||||
|
||||
return getTemplateFile(MetadataHelper.getEntity((String) report[0]), reportId);
|
||||
return getTemplateFile(MetadataHelper.getEntity((String) o[0]), reportId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean(Object entity) {
|
||||
final String cKey = "DataReportManager33-" + ((Entity) entity).getName();
|
||||
final String cKey = "DataReportManager35-" + ((Entity) entity).getName();
|
||||
Application.getCommonsCache().evict(cKey);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
* 获取报表名称
|
||||
*
|
||||
|
@ -163,7 +196,8 @@ public class DataReportManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public static String getReportName(ID reportId, Object idOrEntity, String fileName) {
|
||||
Entity be = idOrEntity instanceof ID ? MetadataHelper.getEntity(((ID) idOrEntity).getEntityCode())
|
||||
final Entity be = idOrEntity instanceof ID
|
||||
? MetadataHelper.getEntity(((ID) idOrEntity).getEntityCode())
|
||||
: MetadataHelper.getEntity((String) idOrEntity);
|
||||
|
||||
String name = null;
|
||||
|
@ -178,6 +212,7 @@ public class DataReportManager implements ConfigManager {
|
|||
|
||||
// suffix
|
||||
if (fileName.endsWith(".pdf")) name += ".pdf";
|
||||
else if (fileName.endsWith(".docx")) name += ".docx";
|
||||
else name += fileName.endsWith(".xlsx") ? ".xlsx" : ".xls";
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ import static com.rebuild.core.service.datareport.TemplateExtractor.PLACEHOLDER;
|
|||
@Slf4j
|
||||
public class EasyExcelGenerator extends SetUser {
|
||||
|
||||
protected File template;
|
||||
protected File templateFile;
|
||||
protected Integer writeSheetAt = null;
|
||||
protected ID recordId;
|
||||
|
||||
|
@ -92,7 +92,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
* @param recordId
|
||||
*/
|
||||
protected EasyExcelGenerator(File template, ID recordId) {
|
||||
this.template = getFixTemplate(template);
|
||||
this.templateFile = getFixTemplate(template);
|
||||
this.recordId = recordId;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
|
||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||
|
||||
try (ExcelWriter excelWriter = EasyExcel.write(target).withTemplate(template).build()) {
|
||||
try (ExcelWriter excelWriter = EasyExcel.write(target).withTemplate(templateFile).build()) {
|
||||
WriteSheet writeSheet = EasyExcel.writerSheet(writeSheetAt)
|
||||
.registerWriteHandler(new FixsMergeStrategy())
|
||||
.registerWriteHandler(new FormulaCellWriteHandler())
|
||||
|
@ -150,8 +150,13 @@ public class EasyExcelGenerator extends SetUser {
|
|||
* @return
|
||||
*/
|
||||
protected File getTargetFile() {
|
||||
return RebuildConfiguration.getFileOfTemp(String.format("RBREPORT-%d.%s",
|
||||
System.currentTimeMillis(), template.getName().endsWith(".xlsx") ? "xlsx" : "xls"));
|
||||
String suffix = "xls";
|
||||
if (templateFile.getName().endsWith(".xlsx")) suffix = "xlsx";
|
||||
if (templateFile.getName().endsWith(".html")) suffix = "html";
|
||||
if (templateFile.getName().endsWith(".docx")) suffix = "docx";
|
||||
if (templateFile.getName().endsWith(".doc")) suffix = "doc";
|
||||
|
||||
return RebuildConfiguration.getFileOfTemp(String.format("RBREPORT-%d.%s", System.currentTimeMillis(), suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,9 +165,9 @@ public class EasyExcelGenerator extends SetUser {
|
|||
* @return 第一个为主记录(若有)
|
||||
*/
|
||||
protected List<Map<String, Object>> buildData() {
|
||||
Entity entity = MetadataHelper.getEntity(this.recordId.getEntityCode());
|
||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
|
||||
TemplateExtractor templateExtractor = new TemplateExtractor(this.template);
|
||||
TemplateExtractor templateExtractor = new TemplateExtractor(templateFile);
|
||||
Map<String, String> varsMap = templateExtractor.transformVars(entity);
|
||||
|
||||
Map<String, String> varsMapOfMain = new HashMap<>();
|
||||
|
@ -193,7 +198,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
}
|
||||
// 无效字段
|
||||
else if (validField == null) {
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), this.template);
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -219,9 +224,9 @@ public class EasyExcelGenerator extends SetUser {
|
|||
entity.getPrimaryField().getName(), entity.getName(), entity.getPrimaryField().getName());
|
||||
|
||||
Record record = Application.createQuery(sql, this.getUser())
|
||||
.setParameter(1, this.recordId)
|
||||
.setParameter(1, recordId)
|
||||
.record();
|
||||
Assert.notNull(record, "No record found : " + this.recordId);
|
||||
Assert.notNull(record, "No record found : " + recordId);
|
||||
|
||||
datas.add(buildData(record, varsMapOfMain));
|
||||
this.hasMain = true;
|
||||
|
@ -236,7 +241,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
MetadataHelper.getDetailToMainField(entity.getDetailEntity()).getName());
|
||||
|
||||
List<Record> list = Application.createQuery(sql, this.getUser())
|
||||
.setParameter(1, this.recordId)
|
||||
.setParameter(1, recordId)
|
||||
.list();
|
||||
|
||||
phNumber = 1;
|
||||
|
@ -253,7 +258,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
StringUtils.join(fieldsOfApproval, ","));
|
||||
|
||||
List<Record> list = Application.createQueryNoFilter(sql)
|
||||
.setParameter(1, this.recordId)
|
||||
.setParameter(1, recordId)
|
||||
.list();
|
||||
|
||||
phNumber = 1;
|
||||
|
@ -273,6 +278,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
*/
|
||||
protected Map<String, Object> buildData(Record record, Map<String, String> varsMap) {
|
||||
final Entity entity = record.getEntity();
|
||||
final boolean isApproval = entity.getEntityCode() == EntityHelper.RobotApprovalStep;
|
||||
|
||||
final String invalidFieldTip = Language.L("[无效字段]");
|
||||
final String unsupportFieldTip = Language.L("[暂不支持]");
|
||||
|
@ -347,8 +353,12 @@ public class EasyExcelGenerator extends SetUser {
|
|||
fieldValue = FieldValueHelper.wrapFieldValue(fieldValue, easyField, Boolean.TRUE);
|
||||
}
|
||||
|
||||
if (record.getEntity().getEntityCode() == EntityHelper.RobotApprovalStep && "state".equalsIgnoreCase(fieldName)) {
|
||||
fieldValue = Language.L(ApprovalState.valueOf(ObjectUtils.toInt(fieldValue)));
|
||||
if (isApproval && "state".equalsIgnoreCase(fieldName)) {
|
||||
int state = ObjectUtils.toInt(fieldValue);
|
||||
if (state < 1) fieldValue = Language.L("提交");
|
||||
else if (state == ApprovalState.DRAFT.getState()) fieldValue = Language.L("待审批");
|
||||
else fieldValue = Language.L(ApprovalState.valueOf(state));
|
||||
|
||||
} else if (FieldValueHelper.isUseDesensitized(easyField, this.getUser())) {
|
||||
fieldValue = FieldValueHelper.desensitized(easyField, fieldValue);
|
||||
}
|
||||
|
@ -371,6 +381,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.put(varName, fieldValue);
|
||||
}
|
||||
}
|
||||
|
@ -519,7 +530,8 @@ public class EasyExcelGenerator extends SetUser {
|
|||
if (recordIds.size() == 1) {
|
||||
return create(reportId, recordId);
|
||||
} else {
|
||||
TemplateFile tt = DataReportManager.instance.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
||||
TemplateFile tt = DataReportManager.instance
|
||||
.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
||||
return new EasyExcelGenerator33(tt.templateFile, recordIds);
|
||||
}
|
||||
}
|
||||
|
@ -530,7 +542,8 @@ public class EasyExcelGenerator extends SetUser {
|
|||
* @return
|
||||
*/
|
||||
public static EasyExcelGenerator create(ID reportId, ID recordId) {
|
||||
TemplateFile tt = DataReportManager.instance.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
||||
TemplateFile tt = DataReportManager.instance
|
||||
.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
||||
return create(tt.templateFile, recordId, tt.isV33);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,21 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.service.datareport;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.approval.ApprovalHelper;
|
||||
import com.rebuild.core.support.general.RecordBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.poi.ss.usermodel.PrintSetup;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
|
@ -28,6 +33,7 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -45,23 +51,23 @@ import static com.rebuild.core.service.datareport.TemplateExtractor33.DETAIL_PRE
|
|||
@Slf4j
|
||||
public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||
|
||||
final private List<ID> recordIds;
|
||||
final private List<ID> recordIdMultiple;
|
||||
|
||||
protected EasyExcelGenerator33(File template, ID recordId) {
|
||||
super(template, recordId);
|
||||
this.recordIds = null;
|
||||
protected EasyExcelGenerator33(File templateFile, ID recordId) {
|
||||
super(templateFile, recordId);
|
||||
this.recordIdMultiple = null;
|
||||
}
|
||||
|
||||
protected EasyExcelGenerator33(File template, List<ID> recordIds) {
|
||||
super(template, recordIds.get(0));
|
||||
this.recordIds = recordIds;
|
||||
this.recordIdMultiple = recordIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Map<String, Object>> buildData() {
|
||||
final Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
|
||||
final TemplateExtractor33 templateExtractor33 = new TemplateExtractor33(template);
|
||||
final TemplateExtractor33 templateExtractor33 = this.buildTemplateExtractor33();
|
||||
final Map<String, String> varsMap = templateExtractor33.transformVars(entity);
|
||||
|
||||
// 变量
|
||||
|
@ -102,7 +108,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
if (TemplateExtractor33.isPlaceholder(varName)) continue;
|
||||
// 无效字段
|
||||
if (fieldName == null) {
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), template);
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -144,7 +150,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
if (isApproval) {
|
||||
querySql += " and isWaiting = 'F' and isCanceled = 'F' order by createdOn";
|
||||
querySql = String.format(querySql, StringUtils.join(e.getValue(), ","),
|
||||
"stepId", "RobotApprovalStep", "recordId");
|
||||
"createdOn,recordId,state,stepId", "RobotApprovalStep", "recordId");
|
||||
|
||||
} else if (refName.startsWith(DETAIL_PREFIX)) {
|
||||
Entity de = entity.getDetailEntity();
|
||||
|
@ -172,8 +178,30 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
.setParameter(1, recordId)
|
||||
.list();
|
||||
|
||||
// 补充提交节点
|
||||
if (isApproval && !list.isEmpty()) {
|
||||
Record firstNode = list.get(0);
|
||||
Record submit = RecordBuilder.builder(EntityHelper.RobotApprovalStep)
|
||||
.add("approver", ApprovalHelper.getSubmitter(firstNode.getID("recordId")))
|
||||
.add("approvedTime", CalendarUtils.getUTCDateTimeFormat().format(firstNode.getDate("createdOn")))
|
||||
.add("state", 0)
|
||||
.build(UserService.SYSTEM_USER);
|
||||
|
||||
List<Record> list2 = new ArrayList<>();
|
||||
list2.add(submit);
|
||||
list2.addAll(list);
|
||||
list = list2;
|
||||
}
|
||||
|
||||
phNumber = 1;
|
||||
for (Record c : list) {
|
||||
// 特殊处理
|
||||
if (isApproval) {
|
||||
int state = c.getInt("state");
|
||||
Date approvedTime = c.getDate("approvedTime");
|
||||
if (approvedTime == null && state > 1) c.setDate("approvedTime", c.getDate("createdOn"));
|
||||
}
|
||||
|
||||
datas.add(buildData(c, varsMapOfRefs.get(refName)));
|
||||
phNumber++;
|
||||
}
|
||||
|
@ -182,21 +210,29 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
return datas;
|
||||
}
|
||||
|
||||
// -- V34 多个
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
protected TemplateExtractor33 buildTemplateExtractor33() {
|
||||
return new TemplateExtractor33(templateFile);
|
||||
}
|
||||
|
||||
// -- V34 支持多记录导出
|
||||
|
||||
@Override
|
||||
public File generate() {
|
||||
if (recordIds == null) return super.generate();
|
||||
if (recordIdMultiple == null) return super.generate();
|
||||
|
||||
// init
|
||||
File targetFile = super.getTargetFile();
|
||||
try {
|
||||
FileUtils.copyFile(this.template, targetFile);
|
||||
FileUtils.copyFile(templateFile, targetFile);
|
||||
} catch (IOException e) {
|
||||
throw new ReportsException(e);
|
||||
}
|
||||
|
||||
for (ID recordId : this.recordIds) {
|
||||
PrintSetup copyPrintSetup = null;
|
||||
for (ID recordId : recordIdMultiple) {
|
||||
int newSheetAt;
|
||||
try (Workbook wb = WorkbookFactory.create(Files.newInputStream(targetFile.toPath()))) {
|
||||
// 1.复制模板
|
||||
|
@ -210,7 +246,15 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
wb.setSheetName(newSheetAt, newSheetName);
|
||||
}
|
||||
|
||||
// 2.保存模板
|
||||
// 2.复制打印设置(POI 不会自己复制???)
|
||||
// https://www.bilibili.com/read/cv15053559/
|
||||
if (copyPrintSetup == null) {
|
||||
copyPrintSetup = wb.getSheetAt(0).getPrintSetup();
|
||||
}
|
||||
newSheet.getPrintSetup().setLandscape(copyPrintSetup.getLandscape());
|
||||
newSheet.getPrintSetup().setPaperSize(copyPrintSetup.getPaperSize());
|
||||
|
||||
// 3.保存模板
|
||||
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
|
||||
wb.write(fos);
|
||||
}
|
||||
|
@ -220,7 +264,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
}
|
||||
|
||||
// 生成报表
|
||||
this.template = targetFile;
|
||||
this.templateFile = targetFile;
|
||||
this.writeSheetAt = newSheetAt;
|
||||
this.recordId = recordId;
|
||||
this.hasMain = false;
|
||||
|
|
|
@ -53,7 +53,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
|||
@Override
|
||||
protected List<Map<String, Object>> buildData() {
|
||||
Entity entity = MetadataHelper.getEntity(queryData.getString("entity"));
|
||||
TemplateExtractor varsExtractor = new TemplateExtractor(this.template, Boolean.TRUE);
|
||||
TemplateExtractor varsExtractor = new TemplateExtractor(templateFile, Boolean.TRUE);
|
||||
Map<String, String> varsMap = varsExtractor.transformVars(entity);
|
||||
|
||||
List<String> validFields = new ArrayList<>();
|
||||
|
@ -68,7 +68,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
|||
if (validField != null && e.getKey().startsWith(NROW_PREFIX)) {
|
||||
validFields.add(validField);
|
||||
} else {
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), this.template);
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,11 +55,11 @@ public class TemplateExtractor {
|
|||
// 当前日期时间
|
||||
protected static final String PH__CURRENTDATETIME = PLACEHOLDER + "CURRENTDATETIME";
|
||||
|
||||
// v2:{xxx} v1:${xxx}
|
||||
// 变量匹配 v2:{xxx} v1:${xxx}
|
||||
protected static final Pattern PATT_V2 = Pattern.compile("\\{(.*?)}");
|
||||
|
||||
final protected File template;
|
||||
final private boolean isList;
|
||||
final protected File templateFile;
|
||||
final private boolean isListType;
|
||||
|
||||
/**
|
||||
* @param template
|
||||
|
@ -69,12 +69,12 @@ public class TemplateExtractor {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param template
|
||||
* @param templateFile
|
||||
* @param isList 列表模板
|
||||
*/
|
||||
public TemplateExtractor(File template, boolean isList) {
|
||||
this.template = template;
|
||||
this.isList = isList;
|
||||
public TemplateExtractor(File templateFile, boolean isList) {
|
||||
this.templateFile = templateFile;
|
||||
this.isListType = isList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ public class TemplateExtractor {
|
|||
public Map<String, String> transformVars(Entity entity) {
|
||||
final Set<String> vars = extractVars();
|
||||
|
||||
Entity detailEntity = this.isList ? null : entity.getDetailEntity();
|
||||
Entity detailEntity = this.isListType ? null : entity.getDetailEntity();
|
||||
Entity approvalEntity = MetadataHelper.hasApprovalField(entity)
|
||||
? MetadataHelper.getEntity(EntityHelper.RobotApprovalStep) : null;
|
||||
|
||||
|
@ -135,13 +135,16 @@ public class TemplateExtractor {
|
|||
* @return
|
||||
*/
|
||||
protected Set<String> extractVars() {
|
||||
List<Cell[]> rows = ExcelUtils.readExcel(this.template);
|
||||
List<Cell[]> rows = ExcelUtils.readExcel(templateFile);
|
||||
|
||||
Set<String> vars = new HashSet<>();
|
||||
for (Cell[] row : rows) {
|
||||
for (Cell cell : row) {
|
||||
if (cell.isEmpty()) continue;
|
||||
|
||||
// 变量套变量无法支持
|
||||
// {.__KEEP:(=IF(ISBLANK({.LimitedCredit}), "", "{.LimitedCredit}天付款"))}
|
||||
|
||||
String cellText = cell.asString();
|
||||
Matcher matcher = PATT_V2.matcher(cellText);
|
||||
while (matcher.find()) {
|
||||
|
|
|
@ -34,10 +34,10 @@ public class TemplateExtractor33 extends TemplateExtractor {
|
|||
private Map<String, String> sortFields = new HashMap<>();
|
||||
|
||||
/**
|
||||
* @param template
|
||||
* @param templateFile
|
||||
*/
|
||||
public TemplateExtractor33(File template) {
|
||||
super(template, Boolean.FALSE);
|
||||
public TemplateExtractor33(File templateFile) {
|
||||
super(templateFile, Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +57,7 @@ public class TemplateExtractor33 extends TemplateExtractor {
|
|||
for (final String varName : vars) {
|
||||
// 列表型字段
|
||||
if (varName.startsWith(NROW_PREFIX)) {
|
||||
final String listField = varName.substring(1);
|
||||
final String listField = varName.substring(1).replace("$", ".");
|
||||
|
||||
if (isPlaceholder(listField)) {
|
||||
map.put(varName, null);
|
||||
|
|
|
@ -18,14 +18,25 @@ import java.io.File;
|
|||
public class TemplateFile {
|
||||
|
||||
final public File templateFile;
|
||||
final public String templateContent;
|
||||
final public Entity entity;
|
||||
final public boolean isList;
|
||||
final public int type;
|
||||
final public boolean isV33;
|
||||
|
||||
public TemplateFile(File templateFile, Entity entity, boolean isList, boolean isV33) {
|
||||
public TemplateFile(File templateFile, Entity entity, int type, boolean isV33) {
|
||||
this.templateFile = templateFile;
|
||||
this.entity = entity;
|
||||
this.isList = isList;
|
||||
this.type = type;
|
||||
this.isV33 = isV33;
|
||||
this.templateContent = null;
|
||||
}
|
||||
|
||||
// HTML5
|
||||
public TemplateFile(String templateContent, Entity entity) {
|
||||
this.templateContent = templateContent;
|
||||
this.entity = entity;
|
||||
this.type = DataReportManager.TYPE_HTML5;
|
||||
this.isV33 = true;
|
||||
this.templateFile = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import com.alibaba.fastjson.JSONArray;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.service.DataSpecificationException;
|
||||
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
||||
import com.rebuild.core.support.general.BatchOperatorQuery;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -27,18 +26,18 @@ import org.apache.commons.lang.StringUtils;
|
|||
* @since 2019/12/2
|
||||
*/
|
||||
@Slf4j
|
||||
public class BulkBacthUpdate extends BulkOperator {
|
||||
public class BulkBatchUpdate extends BulkOperator {
|
||||
|
||||
/**
|
||||
* 修改为
|
||||
*/
|
||||
public static final String OP_SET = "SET";
|
||||
protected static final String OP_SET = "SET";
|
||||
/**
|
||||
* 置空
|
||||
*/
|
||||
public static final String OP_NULL = "NULL";
|
||||
protected static final String OP_NULL = "NULL";
|
||||
|
||||
public BulkBacthUpdate(BulkContext context, GeneralEntityService ges) {
|
||||
public BulkBatchUpdate(BulkContext context, GeneralEntityService ges) {
|
||||
super(context, ges);
|
||||
}
|
||||
|
||||
|
@ -84,7 +83,7 @@ public class BulkBacthUpdate extends BulkOperator {
|
|||
ges.createOrUpdate(record);
|
||||
this.addSucceeded();
|
||||
|
||||
} catch (DataSpecificationException ex) {
|
||||
} catch (Exception ex) {
|
||||
log.warn("Cannot update `{}` because : {}", id, ex.getLocalizedMessage());
|
||||
|
||||
} finally {
|
||||
|
@ -109,6 +108,6 @@ public class BulkBacthUpdate extends BulkOperator {
|
|||
JSONObject customData = (JSONObject) context.getExtraParams().get("customData");
|
||||
int dataRange = customData.getIntValue("_dataRange");
|
||||
BatchOperatorQuery query = new BatchOperatorQuery(dataRange, customData.getJSONObject("queryData"));
|
||||
return query.getQueryedRecords();
|
||||
return query.getQueryedRecordIds();
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.service.general;
|
||||
|
||||
import cn.devezhao.bizz.privileges.Permission;
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
@ -25,63 +24,56 @@ import java.util.List;
|
|||
*/
|
||||
public interface EntityService extends ServiceSpec {
|
||||
|
||||
/**
|
||||
* 取消共享,跟随共享权限
|
||||
*
|
||||
* @see BizzPermission
|
||||
*/
|
||||
Permission UNSHARE = new BizzPermission("UNSHARE", 1 << 6, true);
|
||||
|
||||
/**
|
||||
* 删除(带级联)
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param cascades 需要级联删除的实体
|
||||
* @return
|
||||
*/
|
||||
int delete(ID record, String[] cascades);
|
||||
int delete(ID recordId, String[] cascades);
|
||||
|
||||
/**
|
||||
* 分配
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param to
|
||||
* @param cascades 需要级联分配的实体
|
||||
* @return
|
||||
*/
|
||||
int assign(ID record, ID to, String[] cascades);
|
||||
int assign(ID recordId, ID to, String[] cascades);
|
||||
|
||||
/**
|
||||
* 共享
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param to
|
||||
* @param cascades
|
||||
* @return
|
||||
*/
|
||||
default int share(ID record, ID to, String[] cascades) {
|
||||
return share(record, to, cascades, BizzPermission.READ.getMask());
|
||||
default int share(ID recordId, ID to, String[] cascades) {
|
||||
return share(recordId, to, cascades, BizzPermission.READ.getMask());
|
||||
}
|
||||
|
||||
/**
|
||||
* 共享
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param to
|
||||
* @param cascades 需要级联分配的实体
|
||||
* @param rights 共享权限
|
||||
* @return
|
||||
*/
|
||||
int share(ID record, ID to, String[] cascades, int rights);
|
||||
int share(ID recordId, ID to, String[] cascades, int rights);
|
||||
|
||||
/**
|
||||
* 取消共享
|
||||
*
|
||||
* @param record 主记录
|
||||
* @param recordId 主记录
|
||||
* @param accessId 共享的 AccessID
|
||||
* @return
|
||||
*/
|
||||
int unshare(ID record, ID accessId);
|
||||
int unshare(ID recordId, ID accessId);
|
||||
|
||||
/**
|
||||
* 批量操作
|
||||
|
@ -104,7 +96,7 @@ public interface EntityService extends ServiceSpec {
|
|||
* 检查并获取(如有)重复记录
|
||||
*
|
||||
* @param checkRecord
|
||||
* @param limit
|
||||
* @param limit 最大查重返回数量
|
||||
* @return
|
||||
*/
|
||||
List<Record> getAndCheckRepeated(Record checkRecord, int limit);
|
||||
|
@ -112,11 +104,11 @@ public interface EntityService extends ServiceSpec {
|
|||
/**
|
||||
* 审批
|
||||
*
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param state 只接受通过或撤销
|
||||
* @param approvalUser 审批人
|
||||
* @see com.rebuild.core.service.approval.ApprovalStepService
|
||||
* @see com.rebuild.core.service.approval.ApprovalProcessor
|
||||
*/
|
||||
void approve(ID record, ApprovalState state, ID approvalUser);
|
||||
void approve(ID recordId, ApprovalState state, ID approvalUser);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.core.service.general;
|
|||
|
||||
import cn.devezhao.bizz.privileges.Permission;
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
import cn.devezhao.commons.ReflectUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.Filter;
|
||||
|
@ -24,7 +25,6 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.MetadataSorter;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
|
@ -52,6 +52,7 @@ import com.rebuild.utils.CommonsUtils;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -62,6 +63,7 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Observer;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
@ -73,10 +75,8 @@ import java.util.TreeMap;
|
|||
*
|
||||
* <p>如有需要,其他实体可根据自身业务继承并复写</p>
|
||||
*
|
||||
* FIXME 删除主记录时会关联删除明细记录(持久层实现),但明细记录不会触发业务规则
|
||||
*
|
||||
* @author Zixin (RB)
|
||||
* @since 11/06/2017
|
||||
* @since 11/06/2019
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
|
@ -87,11 +87,18 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
protected GeneralEntityService(PersistManagerFactory aPMFactory) {
|
||||
super(aPMFactory);
|
||||
}
|
||||
|
||||
// 通知
|
||||
addObserver(new NotificationObserver());
|
||||
// 触发器
|
||||
addObserver(new RobotTriggerObserver());
|
||||
@Override
|
||||
protected Observer[] getOrderObservers() {
|
||||
Observer[] obs = new Observer[] {
|
||||
// 触发器
|
||||
new RobotTriggerObserver(),
|
||||
// 通知
|
||||
new NotificationObserver(),
|
||||
};
|
||||
obs = ArrayUtils.addAll(obs, super.getOrderObservers());
|
||||
return obs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -119,49 +126,68 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
// 含明细
|
||||
final boolean hasDetails = details != null && !details.isEmpty();
|
||||
|
||||
boolean lazyAutoApprovalForDetails = false;
|
||||
boolean lazyAutoTransformForDetails = false;
|
||||
// 延迟执行触发器,因为明细尚未保存好
|
||||
boolean lazyAutoApproval4Details = false;
|
||||
boolean lazyAutoTransform4Details = false;
|
||||
boolean lazyHookUrl4Details = false;
|
||||
if (hasDetails) {
|
||||
// fix: v3.2.2
|
||||
|
||||
// 自动审批 fix: v3.2.2
|
||||
|
||||
TriggerAction[] hasAutoApprovalTriggers = getSpecTriggers(
|
||||
record.getEntity(), ActionType.AUTOAPPROVAL, TriggerWhen.CREATE, TriggerWhen.UPDATE);
|
||||
lazyAutoApprovalForDetails = hasAutoApprovalTriggers.length > 0;
|
||||
lazyAutoApproval4Details = hasAutoApprovalTriggers.length > 0;
|
||||
// FIXME 此判断可能无意义,待进一步测试后确定是否保留
|
||||
if (!lazyAutoApprovalForDetails) {
|
||||
if (!lazyAutoApproval4Details) {
|
||||
TriggerAction[] hasOnApprovedTriggers = getSpecTriggers(
|
||||
record.getEntity().getDetailEntity(), null, TriggerWhen.APPROVED);
|
||||
lazyAutoApprovalForDetails = hasOnApprovedTriggers.length > 0;
|
||||
lazyAutoApproval4Details = hasOnApprovedTriggers.length > 0;
|
||||
}
|
||||
// 自动审批延迟执行,因为明细尚未保存好
|
||||
if (lazyAutoApprovalForDetails) AutoApproval.setLazyAutoApproval();
|
||||
if (lazyAutoApproval4Details) AutoApproval.setLazy();
|
||||
|
||||
// 自动转换
|
||||
|
||||
TriggerAction[] hasAutoTransformTriggers = getSpecTriggers(
|
||||
record.getEntity(), ActionType.AUTOTRANSFORM, TriggerWhen.CREATE, TriggerWhen.UPDATE);
|
||||
lazyAutoTransformForDetails = hasAutoTransformTriggers.length > 0;
|
||||
lazyAutoTransform4Details = hasAutoTransformTriggers.length > 0;
|
||||
// 记录转换延迟执行,因为明细尚未保存好
|
||||
if (lazyAutoTransformForDetails) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#setLazyAutoTransform");
|
||||
if (lazyAutoTransform4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#setLazy");
|
||||
|
||||
// URL 回调 v3.5
|
||||
|
||||
TriggerAction[] hasHookUrlTriggers = getSpecTriggers(
|
||||
record.getEntity(), ActionType.HOOKURL, TriggerWhen.CREATE, TriggerWhen.UPDATE, TriggerWhen.DELETE);
|
||||
lazyHookUrl4Details = hasHookUrlTriggers.length > 0;
|
||||
// 对于全量推送,明细尚未保存好
|
||||
if (lazyHookUrl4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.HookUrl#setLazy");
|
||||
}
|
||||
|
||||
// 保证顺序
|
||||
// 保证执行顺序
|
||||
Map<Integer, ID> detaileds = new TreeMap<>();
|
||||
|
||||
try {
|
||||
record = record.getPrimary() == null ? create(record) : update(record);
|
||||
if (!hasDetails) return record;
|
||||
|
||||
// 主记录+明细记录处理
|
||||
// 明细记录处理
|
||||
|
||||
final String dtfField = MetadataHelper.getDetailToMainField(record.getEntity().getDetailEntity()).getName();
|
||||
final Entity detailEntity = record.getEntity().getDetailEntity();
|
||||
final String dtfField = MetadataHelper.getDetailToMainField(detailEntity).getName();
|
||||
final ID mainid = record.getPrimary();
|
||||
|
||||
final boolean checkDetailsRepeated = rcm == GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS
|
||||
|| rcm == GeneralEntityServiceContextHolder.RCM_CHECK_ALL;
|
||||
|
||||
// 明细可能有自己的 Service
|
||||
EntityService des = Application.getEntityService(detailEntity.getEntityCode());
|
||||
if (des.getEntityCode() == 0) des = this;
|
||||
|
||||
// 先删除
|
||||
for (int i = 0; i < details.size(); i++) {
|
||||
Record d = details.get(i);
|
||||
if (d instanceof DeleteRecord) {
|
||||
delete(d.getPrimary());
|
||||
des.delete(d.getPrimary());
|
||||
detaileds.put(i, d.getPrimary());
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +200,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
if (checkDetailsRepeated) {
|
||||
d.setID(dtfField, mainid); // for check
|
||||
|
||||
List<Record> repeated = getAndCheckRepeated(d, 20);
|
||||
List<Record> repeated = des.getAndCheckRepeated(d, 20);
|
||||
if (!repeated.isEmpty()) {
|
||||
throw new RepeatedRecordsException(repeated);
|
||||
}
|
||||
|
@ -182,9 +208,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
if (d.getPrimary() == null) {
|
||||
d.setID(dtfField, mainid);
|
||||
create(d);
|
||||
des.create(d);
|
||||
} else {
|
||||
update(d);
|
||||
des.update(d);
|
||||
}
|
||||
detaileds.put(i, d.getPrimary());
|
||||
}
|
||||
|
@ -193,12 +219,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
return record;
|
||||
|
||||
} finally {
|
||||
if (lazyAutoApprovalForDetails) {
|
||||
AutoApproval.executeLazyAutoApproval();
|
||||
}
|
||||
if (lazyAutoTransformForDetails) {
|
||||
CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#executeLazyAutoTransform");
|
||||
}
|
||||
if (lazyAutoApproval4Details) AutoApproval.executeLazy();
|
||||
if (lazyAutoTransform4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#executeLazy");
|
||||
if (lazyHookUrl4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.HookUrl#executeLazy");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,7 +477,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
if (countObservers() > 0) {
|
||||
setChanged();
|
||||
notifyObservers(OperatingContext.create(currentUser, UNSHARE, unsharedBefore, null));
|
||||
notifyObservers(OperatingContext.create(currentUser, InternalPermission.UNSHARE, unsharedBefore, null));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -550,11 +573,15 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
return new BulkAssign(context, this);
|
||||
} else if (context.getAction() == BizzPermission.SHARE) {
|
||||
return new BulkShare(context, this);
|
||||
} else if (context.getAction() == UNSHARE) {
|
||||
} else if (context.getAction() == InternalPermission.UNSHARE) {
|
||||
return new BulkUnshare(context, this);
|
||||
} else if (context.getAction() == BizzPermission.UPDATE) {
|
||||
return new BulkBacthUpdate(context, this);
|
||||
return new BulkBatchUpdate(context, this);
|
||||
} else if (context.getAction() == InternalPermission.APPROVAL) {
|
||||
return (BulkOperator) ReflectUtils.newObject(
|
||||
"com.rebuild.rbv.approval.BulkBatchApprove", context, this);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unsupported bulk action : " + context.getAction());
|
||||
}
|
||||
|
||||
|
@ -673,10 +700,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
continue;
|
||||
}
|
||||
|
||||
EasyField easyField = EasyMetaFactory.valueOf(field);
|
||||
if (easyField.getDisplayType() == DisplayType.SERIES) continue;
|
||||
|
||||
Object defaultValue = easyField.exprDefaultValue();
|
||||
Object defaultValue = EasyMetaFactory.valueOf(field).exprDefaultValue();
|
||||
if (defaultValue != null) {
|
||||
recordOfNew.setObjectValue(field.getName(), defaultValue);
|
||||
}
|
||||
|
@ -831,4 +855,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
}
|
||||
return specTriggers.toArray(new TriggerAction[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getEntityCode() + "#" + super.toString();
|
||||
}
|
||||
}
|
|
@ -45,15 +45,21 @@ public abstract class ObservableService extends Observable implements ServiceSpe
|
|||
protected ObservableService(PersistManagerFactory aPMFactory) {
|
||||
this.delegateService = new BaseService(aPMFactory);
|
||||
|
||||
// 默认监听者
|
||||
addObserver(new RevisionHistoryObserver());
|
||||
addObserver(new AttachmentAwareObserver());
|
||||
for (Observer o : getOrderObservers()) {
|
||||
log.info("Add observer : {} for [ {} ] ", o, getEntityCode());
|
||||
addObserver(o);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addObserver(Observer o) {
|
||||
super.addObserver(o);
|
||||
log.info("Add observer : {} for [ {} ] ", o, getEntityCode());
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
protected Observer[] getOrderObservers() {
|
||||
// 默认监听者
|
||||
return new Observer[] {
|
||||
new RevisionHistoryObserver(), // 2
|
||||
new AttachmentAwareObserver(), // 1
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,7 +93,8 @@ public abstract class ObservableService extends Observable implements ServiceSpe
|
|||
|
||||
@Override
|
||||
public int delete(ID recordId) {
|
||||
final ID currentUser = UserContextHolder.getUser();
|
||||
ID currentUser = UserContextHolder.getRestoreUser();
|
||||
if (currentUser == null) currentUser = UserContextHolder.getUser();
|
||||
|
||||
Record deleted = null;
|
||||
if (countObservers() > 0) {
|
||||
|
|
|
@ -11,6 +11,8 @@ import cn.devezhao.bizz.privileges.Permission;
|
|||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.UserContextHolder;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -47,7 +49,7 @@ public class OperatingContext {
|
|||
this.action = action;
|
||||
this.beforeRecord = beforeRecord;
|
||||
this.afterRecord = afterRecord;
|
||||
this.affected = affected == null ? new ID[]{getAnyRecord().getPrimary()} : affected;
|
||||
this.affected = affected == null ? new ID[]{ getFixedRecordId() } : affected;
|
||||
this.operationIp = operationIp;
|
||||
}
|
||||
|
||||
|
@ -80,12 +82,31 @@ public class OperatingContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* NOTE!!! 请注意当共享时得到的是共享实体 Record
|
||||
* 如果是为了获取源纪录 ID 推荐使用 {@link #getFixedRecordId()}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Record getAnyRecord() {
|
||||
return getAfterRecord() != null ? getAfterRecord() : getBeforeRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* v35 获取实际影响记录ID
|
||||
* 例如在共享时传入的 Record 是 ShareAccess,而实际影响的是其中的 recordId 记录
|
||||
*
|
||||
* @return
|
||||
* @see ActionContext#getSourceRecord()
|
||||
*/
|
||||
public ID getFixedRecordId() {
|
||||
ID recordId = getAnyRecord().getPrimary();
|
||||
if (recordId.getEntityCode() == EntityHelper.ShareAccess) {
|
||||
recordId = getAnyRecord().getID("recordId");
|
||||
Assert.notNull(recordId, "[recordId] in ShareAccess cannot be null");
|
||||
}
|
||||
return recordId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次操作可能涉及多条记录
|
||||
*
|
||||
|
|
|
@ -61,7 +61,7 @@ public abstract class OperatingObserver implements Observer {
|
|||
onAssign(ctx);
|
||||
} else if (ctx.getAction() == BizzPermission.SHARE) {
|
||||
onShare(ctx);
|
||||
} else if (ctx.getAction() == EntityService.UNSHARE) {
|
||||
} else if (ctx.getAction() == InternalPermission.UNSHARE) {
|
||||
onUnshare(ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.service.general;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
|
@ -19,11 +18,11 @@ import com.alibaba.fastjson.JSONObject;
|
|||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 两个 Record 的不同
|
||||
|
@ -52,6 +51,13 @@ public class RecordDifference {
|
|||
return diffMerge(after, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不同
|
||||
*
|
||||
* @param after
|
||||
* @param diffCommons
|
||||
* @return
|
||||
*/
|
||||
protected JSON diffMerge(Record after, boolean diffCommons) {
|
||||
if (record == null && after == null) {
|
||||
throw new RebuildException("Both records cannot be null");
|
||||
|
@ -67,26 +73,28 @@ public class RecordDifference {
|
|||
if (record != null) {
|
||||
JSONObject recordSerialize = (JSONObject) record.serialize();
|
||||
for (Map.Entry<String, Object> e : recordSerialize.entrySet()) {
|
||||
String field = e.getKey();
|
||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
||||
String fieldName = e.getKey();
|
||||
if (!entity.containsField(fieldName)) continue;
|
||||
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||
|
||||
Object beforeVal = e.getValue();
|
||||
if (NullValue.is(beforeVal)) beforeVal = null;
|
||||
|
||||
merged.put(field, new Object[]{beforeVal, null});
|
||||
merged.put(fieldName, new Object[]{beforeVal, null});
|
||||
}
|
||||
}
|
||||
|
||||
if (after != null) {
|
||||
JSONObject afterSerialize = (JSONObject) after.serialize();
|
||||
for (Map.Entry<String, Object> e : afterSerialize.entrySet()) {
|
||||
String field = e.getKey();
|
||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
||||
String fieldName = e.getKey();
|
||||
if (!entity.containsField(fieldName)) continue;
|
||||
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||
|
||||
Object afterVal = e.getValue();
|
||||
if (NullValue.is(afterVal)) continue;
|
||||
|
||||
Object[] mergedValue = merged.computeIfAbsent(field, k -> new Object[]{null, null});
|
||||
Object[] mergedValue = merged.computeIfAbsent(fieldName, k -> new Object[]{null, null});
|
||||
mergedValue[1] = afterVal;
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +104,7 @@ public class RecordDifference {
|
|||
for (Map.Entry<String, Object[]> e : merged.entrySet()) {
|
||||
Object[] vals = e.getValue();
|
||||
if (vals[0] == null && vals[1] == null) continue;
|
||||
if (isEquals(vals[0], vals[1])) continue;
|
||||
if (CommonsUtils.isSame(vals[0], vals[1])) continue;
|
||||
|
||||
JSON item = JSONUtils.toJSONObject(
|
||||
new String[]{"field", "before", "after"},
|
||||
|
@ -107,10 +115,10 @@ public class RecordDifference {
|
|||
}
|
||||
|
||||
/**
|
||||
* 是否不同
|
||||
* 是否相同
|
||||
*
|
||||
* @param diff
|
||||
* @param diffCommons
|
||||
* @param diffCommons 是否比较系统共用字段
|
||||
* @return
|
||||
* @see #diffMerge(Record)
|
||||
*/
|
||||
|
@ -135,21 +143,4 @@ public class RecordDifference {
|
|||
|| field.getType() == FieldType.PRIMARY
|
||||
|| MetadataHelper.isApprovalField(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 相等
|
||||
*
|
||||
* @param v1
|
||||
* @param v2
|
||||
* @return
|
||||
*/
|
||||
private boolean isEquals(Object v1, Object v2) {
|
||||
boolean e = Objects.equals(v1, v2);
|
||||
if (!e) {
|
||||
if (v1 instanceof Number && v2 instanceof Number) {
|
||||
e = ObjectUtils.toDouble(v1) == ObjectUtils.toDouble(v2);
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class RevisionHistoryObserver extends OperatingObserver {
|
|||
}
|
||||
|
||||
private boolean isIgnore(OperatingContext ctx) {
|
||||
int entity = ctx.getAnyRecord().getEntity().getEntityCode();
|
||||
int entity = ctx.getFixedRecordId().getEntityCode();
|
||||
return entity == EntityHelper.FeedsComment || entity == EntityHelper.ProjectTaskComment;
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,8 @@ public class RevisionHistoryObserver extends OperatingObserver {
|
|||
TriggerSource triggerSource = RobotTriggerObserver.getTriggerSource();
|
||||
if (triggerSource != null) {
|
||||
record.setID("channelWith", triggerSource.getOriginRecord());
|
||||
// v35 系统用户
|
||||
record.setID("revisionBy", UserService.SYSTEM_USER);
|
||||
}
|
||||
|
||||
if (context.getOperationIp() != null) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.rebuild.core.support.ConfigurationItem;
|
|||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.distributed.DistributedJobLock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -109,19 +110,52 @@ public class RecycleBinCleanerJob extends DistributedJobLock {
|
|||
Application.getSqlExecutor().execute(delSql, 60 * 3);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
private static final ThreadLocal<Boolean> SKIP_RECYCLEBIN = new NamedThreadLocal<>("Skip recycle-bin");
|
||||
private static final ThreadLocal<Boolean> SKIP_REVISIONHISTORY = new NamedThreadLocal<>("Skip revision history");
|
||||
|
||||
/**
|
||||
* 回收站激活
|
||||
* 回收站是否激活
|
||||
* @return
|
||||
* @see #setSkipRecyclebinOnce()
|
||||
*/
|
||||
public static boolean isEnableRecycleBin() {
|
||||
return RebuildConfiguration.getInt(ConfigurationItem.RecycleBinKeepingDays) > 0;
|
||||
boolean enable = RebuildConfiguration.getInt(ConfigurationItem.RecycleBinKeepingDays) > 0;
|
||||
if (enable) {
|
||||
Boolean skip = SKIP_RECYCLEBIN.get();
|
||||
SKIP_RECYCLEBIN.remove();
|
||||
if (skip != null && skip) return false;
|
||||
}
|
||||
return enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改历史激活
|
||||
* 变更历史是否激活
|
||||
* @return
|
||||
* @see #setSkipRevisionHistoryOnce()
|
||||
*/
|
||||
public static boolean isEnableRevisionHistory() {
|
||||
return RebuildConfiguration.getInt(ConfigurationItem.RevisionHistoryKeepingDays) > 0;
|
||||
boolean enable = RebuildConfiguration.getInt(ConfigurationItem.RevisionHistoryKeepingDays) > 0;
|
||||
if (enable) {
|
||||
Boolean skip = SKIP_REVISIONHISTORY.get();
|
||||
SKIP_REVISIONHISTORY.remove();
|
||||
if (skip != null && skip) return false;
|
||||
}
|
||||
return enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过一次回收站
|
||||
*/
|
||||
public static void setSkipRecyclebinOnce() {
|
||||
SKIP_RECYCLEBIN.set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过一次变更历史
|
||||
*/
|
||||
public static void setSkipRevisionHistoryOnce() {
|
||||
SKIP_REVISIONHISTORY.set(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.configuration.ConfigBean;
|
|||
import com.rebuild.core.configuration.ConfigurationException;
|
||||
import com.rebuild.core.configuration.general.TransformManager;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.EntityRecordCreator;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
|
@ -28,7 +29,7 @@ import com.rebuild.core.service.general.GeneralEntityService;
|
|||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||
import com.rebuild.core.service.query.FilterRecordChecker;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -95,11 +96,11 @@ public class RecordTransfomer extends SetUser {
|
|||
|
||||
/**
|
||||
* @param sourceRecordId
|
||||
* @param mainId
|
||||
* @param specMainId 转换明细时需指定主记录 ID
|
||||
* @return
|
||||
* @see #checkFilter(ID)
|
||||
*/
|
||||
public ID transform(ID sourceRecordId, ID mainId) {
|
||||
public ID transform(ID sourceRecordId, ID specMainId) {
|
||||
// 检查配置
|
||||
Entity sourceEntity = MetadataHelper.getEntity(sourceRecordId.getEntityCode());
|
||||
Entity sourceDetailEntity = null;
|
||||
|
@ -132,13 +133,22 @@ public class RecordTransfomer extends SetUser {
|
|||
}
|
||||
|
||||
Map<String, Object> dvMap = null;
|
||||
if (mainId != null) {
|
||||
if (specMainId != null) {
|
||||
Field targetDtf = MetadataHelper.getDetailToMainField(targetEntity);
|
||||
dvMap = Collections.singletonMap(targetDtf.getName(), mainId);
|
||||
dvMap = Collections.singletonMap(targetDtf.getName(), specMainId);
|
||||
}
|
||||
|
||||
Record main = transformRecord(sourceEntity, targetEntity, fieldsMapping, sourceRecordId, dvMap, false);
|
||||
ID newId;
|
||||
// v3.5 此配置未开放
|
||||
// 在之前的版本中,虽然文档写明非空字段无值会转换失败,但是从来没有做过非空检查
|
||||
// 为保持兼容性,此选项不启用,即入参保持为 false,如有需要可指定为 true
|
||||
final boolean checkNullable = transConfig.getBooleanValue("checkNullable35");
|
||||
|
||||
Record main = transformRecord(sourceEntity, targetEntity, fieldsMapping, sourceRecordId, dvMap, false, false, checkNullable);
|
||||
ID theNewId;
|
||||
|
||||
// v3.5 需要先回填
|
||||
// 因为可能以回填字段作为条件进行转换一次判断
|
||||
final boolean fillbackFix = fillback(sourceRecordId, EntityHelper.newUnsavedId(main.getEntity().getEntityCode()));
|
||||
|
||||
// 有多条(主+明细)
|
||||
if (sourceDetails != null && sourceDetails.length > 0) {
|
||||
|
@ -146,18 +156,18 @@ public class RecordTransfomer extends SetUser {
|
|||
List<Record> detailsList = new ArrayList<>();
|
||||
for (Object[] d : sourceDetails) {
|
||||
detailsList.add(
|
||||
transformRecord(sourceDetailEntity, targetDetailEntity, fieldsMappingDetail, (ID) d[0], null, false));
|
||||
transformRecord(sourceDetailEntity, targetDetailEntity, fieldsMappingDetail, (ID) d[0], null, false, false, checkNullable));
|
||||
}
|
||||
|
||||
newId = saveRecord(main, detailsList);
|
||||
theNewId = saveRecord(main, detailsList);
|
||||
} else {
|
||||
newId = saveRecord(main, null);
|
||||
theNewId = saveRecord(main, null);
|
||||
}
|
||||
|
||||
// 回填
|
||||
fillback(sourceRecordId, newId);
|
||||
// 回填修正
|
||||
if (fillbackFix) fillback(sourceRecordId, theNewId);
|
||||
|
||||
return newId;
|
||||
return theNewId;
|
||||
}
|
||||
|
||||
private ID saveRecord(Record record, List<Record> detailsList) {
|
||||
|
@ -198,7 +208,7 @@ public class RecordTransfomer extends SetUser {
|
|||
|
||||
// 此配置未开放
|
||||
int fillbackMode = transConfig.getIntValue("fillbackMode");
|
||||
if (fillbackMode == 2) {
|
||||
if (fillbackMode == 2 && !EntityHelper.isUnsavedId(newId)) {
|
||||
GeneralEntityServiceContextHolder.setAllowForceUpdate(updateSource.getPrimary());
|
||||
try {
|
||||
Application.getEntityService(sourceEntity.getEntityCode()).update(updateSource);
|
||||
|
@ -206,7 +216,7 @@ public class RecordTransfomer extends SetUser {
|
|||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||
}
|
||||
} else {
|
||||
// FIXME 回填仅更新,无业务规则
|
||||
// 无传播更新
|
||||
Application.getCommonsService().update(updateSource, false);
|
||||
}
|
||||
|
||||
|
@ -214,19 +224,21 @@ public class RecordTransfomer extends SetUser {
|
|||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* 转换 Record
|
||||
*
|
||||
* @param sourceEntity
|
||||
* @param targetEntity
|
||||
* @param fieldsMapping
|
||||
* @param sourceRecordId
|
||||
* @param defaultValue
|
||||
* @param ignoreUncreateable
|
||||
* @param ignoreUncreateable 忽略不可新建字段
|
||||
* @param forceNullValue v3.5 强制设定空字段值(更新记录时)
|
||||
* @param checkNullable v3.5 检查不允许为空的字段是否都有值
|
||||
* @return
|
||||
*/
|
||||
protected Record transformRecord(
|
||||
Entity sourceEntity, Entity targetEntity, JSONObject fieldsMapping,
|
||||
ID sourceRecordId, Map<String, Object> defaultValue, boolean ignoreUncreateable) {
|
||||
ID sourceRecordId, Map<String, Object> defaultValue, boolean ignoreUncreateable, boolean forceNullValue, boolean checkNullable) {
|
||||
|
||||
Record target = EntityHelper.forNew(targetEntity.getEntityCode(), getUser());
|
||||
|
||||
|
@ -249,9 +261,13 @@ public class RecordTransfomer extends SetUser {
|
|||
ID specOwningUser = null;
|
||||
|
||||
for (Map.Entry<String, Object> e : fieldsMapping.entrySet()) {
|
||||
if (e.getValue() == null) continue;
|
||||
final String targetField = e.getKey();
|
||||
|
||||
if (e.getValue() == null) {
|
||||
if (forceNullValue) target.setNull(targetField);
|
||||
continue;
|
||||
}
|
||||
|
||||
String targetField = e.getKey();
|
||||
EasyField targetFieldEasy = EasyMetaFactory.valueOf(targetEntity.getField(targetField));
|
||||
if (ignoreUncreateable && !targetFieldEasy.isCreatable()) continue;
|
||||
|
||||
|
@ -271,6 +287,9 @@ public class RecordTransfomer extends SetUser {
|
|||
|
||||
Object targetValue = sourceFieldEasy.convertCompatibleValue(sourceValue, targetFieldEasy);
|
||||
target.setObjectValue(targetField, targetValue);
|
||||
|
||||
} else if (forceNullValue) {
|
||||
target.setNull(targetField);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +303,10 @@ public class RecordTransfomer extends SetUser {
|
|||
(ID) Application.getUserStore().getUser(specOwningUser).getOwningDept().getIdentity());
|
||||
}
|
||||
|
||||
if (checkNullable) {
|
||||
new EntityRecordCreator(targetEntity, JSONUtils.EMPTY_OBJECT, getUser()).verify(target);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ public class TransformerPreview {
|
|||
try {
|
||||
for (ID did : ids) {
|
||||
Record targetRecord = transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, did, null, true);
|
||||
sourceEntity, targetEntity, fieldsMapping, did, null, true, false, false);
|
||||
|
||||
fillLabelOfReference(targetRecord);
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class TransformerPreview {
|
|||
}
|
||||
|
||||
Record targetRecord = transfomer.transformRecord(
|
||||
sourceEntity, targetEntity, fieldsMapping, sourceId, null, true);
|
||||
sourceEntity, targetEntity, fieldsMapping, sourceId, null, true, false, false);
|
||||
fillLabelOfReference(targetRecord);
|
||||
|
||||
// 转为明细
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.regex.Pattern;
|
|||
*
|
||||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/07/12
|
||||
* @see com.rebuild.core.support.general.ContentWithFieldVars
|
||||
*/
|
||||
public class MessageBuilder {
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.bizz.Department;
|
||||
import com.rebuild.core.support.License;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
|
@ -108,12 +109,12 @@ public class AdvFilterParser extends SetUser {
|
|||
|
||||
/**
|
||||
* @param filterExpr
|
||||
* @param varRecord 条件中包含字段变量,将从此记录中提取实际值。注意如果包括字段变量但是字段无值,则过滤项会被忽略,应在配置条件时考虑此问题(设置不允许为空)
|
||||
* @param varRecord 条件中包含字段变量,将从该记录中提取实际值替换
|
||||
*/
|
||||
public AdvFilterParser(JSONObject filterExpr, ID varRecord) {
|
||||
this.filterExpr = filterExpr;
|
||||
this.rootEntity = MetadataHelper.getEntity(varRecord.getEntityCode());
|
||||
this.varRecord = varRecord;
|
||||
this.varRecord = License.isRbvAttached() ? varRecord : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -702,6 +703,7 @@ public class AdvFilterParser extends SetUser {
|
|||
private static final String PATT_FIELDVAR = "\\{@([\\w.]+)}";
|
||||
// `当前`变量(当前日期、时间、用户)
|
||||
private static final String CURRENT_ANY = "CURRENT";
|
||||
private static final String CURRENT_DATE = "NOW";
|
||||
|
||||
private String useValueOfVarRecord(String value, Field queryField) {
|
||||
if (StringUtils.isBlank(value) || !value.matches(PATT_FIELDVAR)) return value;
|
||||
|
@ -712,7 +714,7 @@ public class AdvFilterParser extends SetUser {
|
|||
Object useValue = null;
|
||||
|
||||
// {@CURRENT} DATE
|
||||
if (CURRENT_ANY.equals(fieldName)) {
|
||||
if (CURRENT_ANY.equals(fieldName) || CURRENT_DATE.equals(fieldName)) {
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(queryField);
|
||||
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME || dt == DisplayType.TIME) {
|
||||
useValue = CalendarUtils.now();
|
||||
|
|
|
@ -229,6 +229,9 @@ public class ParseHelper {
|
|||
|| dt == DisplayType.LOCATION) {
|
||||
return StringUtils.defaultIfEmpty(fieldPath, field.getName());
|
||||
|
||||
} else if (dt == DisplayType.ANYREFERENCE) {
|
||||
return fieldPath;
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -30,15 +30,15 @@ import java.util.List;
|
|||
* 查询服务
|
||||
*
|
||||
* @author Zixin (RB)
|
||||
* @since 05/21/2017
|
||||
* @since 05/21/2019
|
||||
* @see RoleBaseQueryFilter
|
||||
* @see QueryHelper
|
||||
*/
|
||||
@Service
|
||||
public class QueryFactory {
|
||||
|
||||
private static final int QUERY_TIMEOUT = 10 * 1000;
|
||||
private static final int SLOW_LOGGER_TIME = 1000;
|
||||
private static final int QUERY_TIMEOUT = 15; // s
|
||||
private static final int SLOW_LOGGER_TIME = 3 * 1000; // ms
|
||||
|
||||
private final PersistManagerFactory aPMFactory;
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.springframework.util.Assert;
|
|||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -108,7 +107,7 @@ public class QueryHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取明细(完整)记录
|
||||
* 获取明细列表记录
|
||||
*
|
||||
* @param mainId
|
||||
* @return
|
||||
|
@ -129,7 +128,7 @@ public class QueryHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取明细 ID
|
||||
* 获取明细列表 ID
|
||||
*
|
||||
* @param mainId
|
||||
* @return
|
||||
|
@ -217,12 +216,9 @@ public class QueryHelper {
|
|||
final ID primaryId = base.getPrimary();
|
||||
Assert.notNull(primaryId, "Record primary cannot be null");
|
||||
|
||||
Set<String> fields = new HashSet<>();
|
||||
for (Iterator<String> iter = base.getAvailableFieldIterator(); iter.hasNext(); ) {
|
||||
fields.add(iter.next());
|
||||
}
|
||||
|
||||
Set<String> fields = new HashSet<>(base.getAvailableFields());
|
||||
fields.add(base.getEntity().getPrimaryField().getName());
|
||||
|
||||
Record snap = Application.getQueryFactory().recordNoFilter(primaryId, fields.toArray(new String[0]));
|
||||
|
||||
if (snap == null) throw new NoRecordFoundException(primaryId);
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.service.trigger;
|
||||
|
||||
/**
|
||||
* 部分触发器执行需要等到明细记录处理完成才能执行
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2023/11/11
|
||||
*/
|
||||
public interface LazyWaitDetailsFinished {
|
||||
|
||||
String FLAG_LAZY = "lazy";
|
||||
|
||||
// setLazy
|
||||
// isLazy
|
||||
// executeLazy
|
||||
}
|
|
@ -42,12 +42,12 @@ public class RobotTriggerManager implements ConfigManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param when
|
||||
* @return
|
||||
*/
|
||||
public TriggerAction[] getActions(ID record, TriggerWhen... when) {
|
||||
return filterActions(MetadataHelper.getEntity(record.getEntityCode()), record, when);
|
||||
public TriggerAction[] getActions(ID recordId, TriggerWhen... when) {
|
||||
return filterActions(MetadataHelper.getEntity(recordId.getEntityCode()), recordId, when);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,21 +61,21 @@ public class RobotTriggerManager implements ConfigManager {
|
|||
|
||||
private static final ThreadLocal<List<String>> TRIGGERS_CHAIN_4DEBUG = ThreadLocal.withInitial(ArrayList::new);
|
||||
/**
|
||||
* @param record
|
||||
* @param recordId
|
||||
* @param entity
|
||||
* @param when
|
||||
* @return
|
||||
*/
|
||||
private TriggerAction[] filterActions(Entity entity, ID record, TriggerWhen... when) {
|
||||
private TriggerAction[] filterActions(Entity entity, ID recordId, TriggerWhen... when) {
|
||||
List<TriggerAction> actions = new ArrayList<>();
|
||||
for (ConfigBean cb : getConfig(entity)) {
|
||||
// 发生动作
|
||||
if (allowedWhen(cb, when)) {
|
||||
// 附加过滤条件
|
||||
if (record == null
|
||||
|| QueryHelper.isMatchAdvFilter(record, (JSONObject) cb.getJSON("whenFilter"), Boolean.TRUE)) {
|
||||
if (recordId == null
|
||||
|| QueryHelper.isMatchAdvFilter(recordId, (JSONObject) cb.getJSON("whenFilter"), true)) {
|
||||
|
||||
ActionContext ctx = new ActionContext(record, entity, cb.getJSON("actionContent"), cb.getID("id"));
|
||||
ActionContext ctx = new ActionContext(recordId, entity, cb.getJSON("actionContent"), cb.getID("id"));
|
||||
TriggerAction o = ActionFactory.createAction(cb.getString("actionType"), ctx);
|
||||
if (o.getClass().getName().contains("NoRbv")) {
|
||||
log.warn("Trigger action {} is RBV", cb.getString("actionType"));
|
||||
|
@ -86,7 +86,7 @@ public class RobotTriggerManager implements ConfigManager {
|
|||
|
||||
if (log.isDebugEnabled()) {
|
||||
TRIGGERS_CHAIN_4DEBUG.get().add(
|
||||
String.format("%s (%s) on %s %s (%s)", o.getType(), ctx.getConfigId(), when[0], entity.getName(), record));
|
||||
String.format("%s (%s) on %s %s (%s)", o.getType(), ctx.getConfigId(), when[0], entity.getName(), recordId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ public class RobotTriggerManager implements ConfigManager {
|
|||
|
||||
if (!TRIGGERS_CHAIN_4DEBUG.get().isEmpty()) {
|
||||
log.info("Record ({}) triggers chain : \n > {}",
|
||||
record, StringUtils.join(TRIGGERS_CHAIN_4DEBUG.get(), "\n > "));
|
||||
recordId, StringUtils.join(TRIGGERS_CHAIN_4DEBUG.get(), "\n > "));
|
||||
}
|
||||
|
||||
return actions.toArray(new TriggerAction[0]);
|
||||
|
|
|
@ -8,7 +8,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.trigger;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.general.OperatingObserver;
|
||||
import com.rebuild.core.service.general.RepeatedRecordsException;
|
||||
|
@ -16,6 +15,7 @@ import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
|||
import com.rebuild.core.support.CommonsLog;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.web.KnownExceptionConverter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
|
@ -76,7 +76,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
|
||||
@Override
|
||||
protected void onDeleteBefore(OperatingContext context) {
|
||||
final ID primary = context.getAnyRecord().getPrimary();
|
||||
final ID primary = context.getFixedRecordId();
|
||||
|
||||
TriggerAction[] deleteActions = RobotTriggerManager.instance.getActions(primary, TriggerWhen.DELETE);
|
||||
for (TriggerAction action : deleteActions) {
|
||||
|
@ -94,7 +94,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
|
||||
@Override
|
||||
protected void onDelete(OperatingContext context) {
|
||||
final ID primary = context.getAnyRecord().getPrimary();
|
||||
final ID primary = context.getFixedRecordId();
|
||||
try {
|
||||
execAction(context, TriggerWhen.DELETE);
|
||||
} finally {
|
||||
|
@ -109,11 +109,11 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
* @param when
|
||||
*/
|
||||
protected void execAction(OperatingContext context, TriggerWhen when) {
|
||||
final ID primaryId = context.getAnyRecord().getPrimary();
|
||||
final ID primaryId = context.getFixedRecordId();
|
||||
|
||||
TriggerAction[] beExecuted = when == TriggerWhen.DELETE
|
||||
? DELETE_BEFORE_HOLD.get(primaryId)
|
||||
: RobotTriggerManager.instance.getActions(getRealRecordId(context), when);
|
||||
: RobotTriggerManager.instance.getActions(context.getFixedRecordId(), when);
|
||||
if (beExecuted == null || beExecuted.length == 0) return;
|
||||
|
||||
TriggerSource triggerSource = getTriggerSource();
|
||||
|
@ -186,7 +186,8 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
if (ex instanceof TriggerException) {
|
||||
throw (TriggerException) ex;
|
||||
} else {
|
||||
String errMsg = ex.getLocalizedMessage();
|
||||
String errMsg = KnownExceptionConverter.convert2ErrorMsg(ex);
|
||||
if (errMsg == null) errMsg = ex.getLocalizedMessage();
|
||||
if (ex instanceof RepeatedRecordsException) errMsg = Language.L("存在重复记录");
|
||||
if (StringUtils.isBlank(errMsg)) errMsg = ex.getClass().getSimpleName().toUpperCase();
|
||||
|
||||
|
@ -217,20 +218,6 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际影响的记录。
|
||||
* 例如在共享时传入的 Record 是 ShareAccess,而实际影响的是其中的 recordId 记录
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ID getRealRecordId(OperatingContext context) {
|
||||
ID recordId = context.getAnyRecord().getPrimary();
|
||||
if (recordId.getEntityCode() == EntityHelper.ShareAccess) {
|
||||
recordId = context.getAnyRecord().getID("recordId");
|
||||
}
|
||||
return recordId;
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,10 @@ public abstract class TriggerAction {
|
|||
* 自己
|
||||
*/
|
||||
public static final String SOURCE_SELF = "$PRIMARY$";
|
||||
/**
|
||||
* 任意实体(字段匹配)
|
||||
*/
|
||||
public static final String TARGET_ANY = "$";
|
||||
|
||||
final protected ActionContext actionContext;
|
||||
|
||||
|
|
|
@ -75,6 +75,15 @@ public class TriggerResult implements JSONAware {
|
|||
return new TriggerResult(1, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param affected
|
||||
* @return
|
||||
*/
|
||||
public static TriggerResult success(String message, Collection<ID> affected) {
|
||||
return new TriggerResult(1, message, affected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @return
|
||||
|
@ -144,4 +153,13 @@ public class TriggerResult implements JSONAware {
|
|||
public static TriggerResult noUpdateFields() {
|
||||
return wran("No update fields");
|
||||
}
|
||||
|
||||
/**
|
||||
* 无效配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TriggerResult badConfig() {
|
||||
return wran("Bad config");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class TriggerSource {
|
|||
}
|
||||
|
||||
public ID getOriginRecord() {
|
||||
return getOrigin().getAnyRecord().getPrimary();
|
||||
return getOrigin().getFixedRecordId();
|
||||
}
|
||||
|
||||
public OperatingContext getLast() {
|
||||
|
@ -66,7 +66,7 @@ public class TriggerSource {
|
|||
|
||||
public String getLastSourceKey() {
|
||||
Object[] last = sources.get(sources.size() - 1);
|
||||
return ((OperatingContext) last[0]).getAnyRecord().getPrimary() + ":" + ((TriggerWhen) last[1]).name().charAt(0);
|
||||
return ((OperatingContext) last[0]).getFixedRecordId() + ":" + ((TriggerWhen) last[1]).name().charAt(0);
|
||||
}
|
||||
|
||||
public void setSkipOnce() {
|
||||
|
|
|
@ -8,7 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.trigger;
|
||||
|
||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||
import com.rebuild.core.service.general.EntityService;
|
||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||
|
||||
/**
|
||||
* 触发动作定义
|
||||
|
@ -46,7 +46,7 @@ public enum TriggerWhen {
|
|||
/**
|
||||
* 取消共享时
|
||||
*/
|
||||
UNSHARE(EntityService.UNSHARE.getMask()),
|
||||
UNSHARE(InternalPermission.UNSHARE.getMask()),
|
||||
/**
|
||||
* 审批通过
|
||||
*/
|
||||
|
|
|
@ -22,12 +22,12 @@ import java.util.Map;
|
|||
public class AviatorDate extends AviatorObject {
|
||||
private static final long serialVersionUID = 2930549924386648595L;
|
||||
|
||||
protected static final String DU_YEAR = "Y";
|
||||
protected static final String DU_MONTH = "M";
|
||||
protected static final String DU_DAY = "D";
|
||||
protected static final String DU_HOUR = "H";
|
||||
protected static final String DU_MINUTE = "I";
|
||||
protected static final String DU_SECOND = "S";
|
||||
public static final String DU_YEAR = "Y";
|
||||
public static final String DU_MONTH = "M";
|
||||
public static final String DU_DAY = "D";
|
||||
public static final String DU_HOUR = "H";
|
||||
public static final String DU_MINUTE = "I";
|
||||
public static final String DU_SECOND = "S";
|
||||
|
||||
final private Date dateValue;
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ public class AviatorUtils {
|
|||
addCustomFunction(new CurrentDateFunction());
|
||||
addCustomFunction(new ChineseYuanFunction());
|
||||
addCustomFunction(new TextFunction());
|
||||
addCustomFunction(new IsNullFunction());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.service.trigger.aviator;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.persist4j.engine.NullValue;
|
||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorBoolean;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Usage: ISNULL($any)
|
||||
* Return: Boolean
|
||||
*
|
||||
* @author RB
|
||||
* @since 2023/10/18
|
||||
*/
|
||||
@Slf4j
|
||||
public class IsNullFunction extends AbstractFunction {
|
||||
private static final long serialVersionUID = -7849948179882904490L;
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
|
||||
final Object $any = arg1.getValue(env);
|
||||
|
||||
if (NullValue.isNull($any)) return AviatorBoolean.TRUE;
|
||||
|
||||
if ($any instanceof Number) {
|
||||
if (ObjectUtils.toDouble($any) == 0d) return AviatorBoolean.TRUE;
|
||||
if (ObjectUtils.toLong($any) == 0L) return AviatorBoolean.TRUE;
|
||||
}
|
||||
|
||||
if ($any instanceof Object[]) {
|
||||
return ((Object[]) $any).length == 0 ? AviatorBoolean.TRUE : AviatorBoolean.FALSE;
|
||||
}
|
||||
|
||||
if ($any instanceof Collection) {
|
||||
return ((Collection<?>) $any).isEmpty() ? AviatorBoolean.TRUE : AviatorBoolean.FALSE;
|
||||
}
|
||||
|
||||
return StringUtils.isEmpty($any.toString()) ? AviatorBoolean.TRUE : AviatorBoolean.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ISNULL";
|
||||
}
|
||||
}
|
|
@ -7,22 +7,30 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.service.trigger.aviator;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorJavaType;
|
||||
import com.googlecode.aviator.runtime.type.AviatorNil;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorString;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.web.commons.MetadataGetting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Usage: TEXT($id|$id[], [$defaultValue], [$separator])
|
||||
* Usage: TEXT($id|$id[], [$defaultValue], [$separator], [$fieldName])
|
||||
* Return: String
|
||||
*
|
||||
* @author RB
|
||||
|
@ -37,37 +45,62 @@ public class TextFunction extends AbstractFunction {
|
|||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
|
||||
return call(env, arg1, BLANK, SEPARATOR);
|
||||
return call(env, arg1, BLANK, SEPARATOR, AviatorNil.NIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
return call(env, arg1, BLANK, SEPARATOR);
|
||||
return call(env, arg1, arg2, SEPARATOR, AviatorNil.NIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
|
||||
return call(env, arg1, arg2, arg3, AviatorNil.NIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3, AviatorObject arg4) {
|
||||
final Object $id = arg1.getValue(env);
|
||||
final Object $defaultValue = arg2.getValue(env);
|
||||
final String sep = ObjectUtils.defaultIfNull(arg3.getValue(env), ", ").toString();
|
||||
final String fieldName = arg4.getValue(env) == null ? null : arg4.getValue(env).toString();
|
||||
|
||||
// 引用 ID
|
||||
if (ID.isId($id)) {
|
||||
ID anyid = $id instanceof ID ? (ID) $id : ID.valueOf($id.toString());
|
||||
String text = FieldValueHelper.getLabel(anyid, null);
|
||||
return text == null ? arg2 : new AviatorString(text);
|
||||
String text = getLabel(anyid, fieldName);
|
||||
|
||||
if (text == null && $defaultValue != null) text = $defaultValue.toString();
|
||||
return new AviatorString(text);
|
||||
}
|
||||
|
||||
// 多引用 ID[]
|
||||
if ($id instanceof ID[]) {
|
||||
List<String> texts = new ArrayList<>();
|
||||
for (ID anyid : (ID[]) $id) {
|
||||
String t = FieldValueHelper.getLabel(anyid, null);
|
||||
if (t != null) texts.add(t);
|
||||
|
||||
Object idArray = $id;
|
||||
if ($id instanceof Collection) {
|
||||
List<ID> list = null;
|
||||
for (Object o : (Collection<?>) $id) {
|
||||
if (o instanceof ID) {
|
||||
if (list == null) list = new ArrayList<>();
|
||||
list.add((ID) o);
|
||||
}
|
||||
}
|
||||
|
||||
if (texts.isEmpty()) return arg2;
|
||||
if (list != null) idArray = list.toArray(new ID[0]);
|
||||
}
|
||||
|
||||
String sep = ObjectUtils.defaultIfNull(arg3.getValue(env), ", ").toString();
|
||||
return new AviatorString(StringUtils.join(texts, sep));
|
||||
if (idArray instanceof ID[]) {
|
||||
List<String> text = new ArrayList<>();
|
||||
for (ID anyid : (ID[]) idArray) {
|
||||
String item = getLabel(anyid, fieldName);
|
||||
|
||||
if (item == null && $defaultValue != null) item = $defaultValue.toString();
|
||||
if (item != null) text.add(item);
|
||||
}
|
||||
|
||||
if (text.isEmpty()) return arg2;
|
||||
|
||||
return new AviatorString(StringUtils.join(text, sep));
|
||||
}
|
||||
|
||||
if (arg1 instanceof AviatorJavaType) {
|
||||
|
@ -81,6 +114,20 @@ public class TextFunction extends AbstractFunction {
|
|||
return arg2;
|
||||
}
|
||||
|
||||
// 获取字段内容
|
||||
private String getLabel(ID id, String fieldName) {
|
||||
if (fieldName == null) return FieldValueHelper.getLabelNotry(id);
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(id.getEntityCode());
|
||||
Field field = MetadataHelper.getLastJoinField(entity, fieldName);
|
||||
|
||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(id, fieldName);
|
||||
if (o == null || o[0] == null) return null;
|
||||
|
||||
Object label = FieldValueHelper.wrapFieldValue(o[0], field, true);
|
||||
return label == null ? null : label.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TEXT";
|
||||
|
|
|
@ -28,9 +28,11 @@ import org.apache.commons.collections4.CollectionUtils;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -69,9 +71,11 @@ public class AggregationEvaluator {
|
|||
if ("FORMULA".equalsIgnoreCase(calcMode)) {
|
||||
return evalFormula();
|
||||
}
|
||||
// ONLY FieldAggregation
|
||||
else if ("RBJOIN".equalsIgnoreCase(calcMode)) {
|
||||
return evalRbJoin();
|
||||
// for FieldAggregation/GroupAggregation
|
||||
else if ("RBJOIN".equalsIgnoreCase(calcMode)
|
||||
|| "RBJOIN2".equalsIgnoreCase(calcMode) || "RBJOIN3".equalsIgnoreCase(calcMode)) {
|
||||
int mode = "RBJOIN2".equalsIgnoreCase(calcMode) ? 2 : ("RBJOIN3".equalsIgnoreCase(calcMode) ? 3 : 1);
|
||||
return evalRbJoin(mode);
|
||||
}
|
||||
|
||||
String sourceField = item.getString("sourceField");
|
||||
|
@ -193,9 +197,10 @@ public class AggregationEvaluator {
|
|||
/**
|
||||
* 智能连接
|
||||
*
|
||||
* @param mode 去重模式
|
||||
* @return
|
||||
*/
|
||||
private Object evalRbJoin() {
|
||||
private Object evalRbJoin(int mode) {
|
||||
String sourceField = item.getString("sourceField");
|
||||
Field field;
|
||||
if ((field = MetadataHelper.getLastJoinField(sourceEntity, sourceField)) == null) {
|
||||
|
@ -205,9 +210,19 @@ public class AggregationEvaluator {
|
|||
String ql = String.format("select %s,%s from %s where %s",
|
||||
sourceField, sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), filterSql);
|
||||
Object[][] array = Application.createQueryNoFilter(ql).array();
|
||||
if (array.length == 0) return new Object[0];
|
||||
|
||||
EasyField easyField = EasyMetaFactory.valueOf(field);
|
||||
List<Object> nvList = new ArrayList<>();
|
||||
|
||||
Collection<Object> nvList;
|
||||
Map<Object, Integer> countList = null;
|
||||
if (mode == 2 || mode == 3) {
|
||||
nvList = new LinkedHashSet<>();
|
||||
if (mode == 3) countList = new HashMap<>(); // 针对文本
|
||||
} else {
|
||||
nvList = new ArrayList<>();
|
||||
}
|
||||
|
||||
for (Object[] o : array) {
|
||||
Object n = o[0];
|
||||
if (n == null) continue;
|
||||
|
@ -216,21 +231,54 @@ public class AggregationEvaluator {
|
|||
if (n instanceof ID[]) {
|
||||
CollectionUtils.addAll(nvList, (ID[]) n);
|
||||
} else if (n instanceof ID) {
|
||||
nvList.add(n);
|
||||
if (field.getType() == FieldType.PRIMARY) {
|
||||
nvList.add(n.toString()); // 保持主键
|
||||
} else {
|
||||
nvList.add(n);
|
||||
}
|
||||
} else {
|
||||
Object v = easyField.wrapValue(n);
|
||||
if (v == null) continue;
|
||||
|
||||
if (easyField.getDisplayType() == DisplayType.MULTISELECT) {
|
||||
DisplayType dt = easyField.getDisplayType();
|
||||
if (dt == DisplayType.MULTISELECT) {
|
||||
JSONArray a = ((JSONObject) v).getJSONArray("text");
|
||||
CollectionUtils.addAll(nvList, a);
|
||||
|
||||
// TEXT*N
|
||||
if (countList != null) {
|
||||
for (Object item : a) {
|
||||
Integer c = countList.get(item);
|
||||
if (c == null) c = 0;
|
||||
countList.put(item, ++c);
|
||||
}
|
||||
}
|
||||
} else if (dt == DisplayType.FILE || dt == DisplayType.IMAGE) {
|
||||
nvList.addAll((JSONArray) v);
|
||||
} else {
|
||||
// TEXT
|
||||
nvList.add(v);
|
||||
|
||||
// TEXT*N
|
||||
if (countList != null) {
|
||||
Integer c = countList.get(v);
|
||||
if (c == null) c = 0;
|
||||
countList.put(v, ++c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (countList == null || countList.isEmpty()) {
|
||||
// Use array
|
||||
return nvList.toArray(new Object[0]);
|
||||
}
|
||||
|
||||
Collection<Object> nvList2 = new LinkedHashSet<>();
|
||||
for (Object v : nvList) {
|
||||
nvList2.add(v + "*" + countList.getOrDefault(v, 1));
|
||||
}
|
||||
// Use array
|
||||
return nvList.toArray(new Object[0]);
|
||||
return nvList2.toArray(new Object[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.rebuild.core.service.approval.ApprovalStepService;
|
|||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.core.service.trigger.ActionType;
|
||||
import com.rebuild.core.service.trigger.LazyWaitDetailsFinished;
|
||||
import com.rebuild.core.service.trigger.TriggerAction;
|
||||
import com.rebuild.core.service.trigger.TriggerException;
|
||||
import com.rebuild.core.service.trigger.TriggerResult;
|
||||
|
@ -37,10 +38,11 @@ import static com.rebuild.core.support.CommonsLog.TYPE_TRIGGER;
|
|||
* @since 2020/7/31
|
||||
*/
|
||||
@Slf4j
|
||||
public class AutoApproval extends TriggerAction {
|
||||
public class AutoApproval extends TriggerAction implements LazyWaitDetailsFinished {
|
||||
|
||||
// 延迟执行
|
||||
private static final ThreadLocal<List<AutoApproval>> LAZY_AUTOAPPROVAL = new NamedThreadLocal<>("Lazy AutoApproval");
|
||||
private OperatingContext operatingContext;
|
||||
private OperatingContext keepOperatingContext;
|
||||
|
||||
public AutoApproval(ActionContext context) {
|
||||
super(context);
|
||||
|
@ -58,19 +60,19 @@ public class AutoApproval extends TriggerAction {
|
|||
|
||||
@Override
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
this.operatingContext = operatingContext;
|
||||
this.keepOperatingContext = operatingContext;
|
||||
List<AutoApproval> lazyed;
|
||||
if ((lazyed = isLazyAutoApproval(Boolean.FALSE)) != null) {
|
||||
if ((lazyed = isLazy(false)) != null) {
|
||||
lazyed.add(this);
|
||||
log.info("Lazy AutoApproval : {}", lazyed);
|
||||
return "lazy";
|
||||
return FLAG_LAZY;
|
||||
}
|
||||
|
||||
ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
ID recordId = operatingContext.getFixedRecordId();
|
||||
String useApproval = ((JSONObject) actionContext.getActionContent()).getString("useApproval");
|
||||
|
||||
// 优先使用当前用户
|
||||
ID approver = ObjectUtils.defaultIfNull(UserContextHolder.getUser(Boolean.TRUE), UserService.SYSTEM_USER);
|
||||
ID approver = ObjectUtils.defaultIfNull(UserContextHolder.getUser(true), UserService.SYSTEM_USER);
|
||||
ID approvalId = ID.isId(useApproval) ? ID.valueOf(useApproval) : null;
|
||||
|
||||
// v2.10
|
||||
|
@ -89,24 +91,23 @@ public class AutoApproval extends TriggerAction {
|
|||
@Override
|
||||
public String toString() {
|
||||
String s = super.toString();
|
||||
if (operatingContext != null) s += "#OperatingContext:" + operatingContext;
|
||||
if (keepOperatingContext != null) s += "#OperatingContext:" + keepOperatingContext;
|
||||
return s;
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
* 跳过自动审批
|
||||
* @see #isLazyAutoApproval(boolean)
|
||||
*/
|
||||
public static void setLazyAutoApproval() {
|
||||
public static void setLazy() {
|
||||
LAZY_AUTOAPPROVAL.set(new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param once
|
||||
* @return
|
||||
*/
|
||||
public static List<AutoApproval> isLazyAutoApproval(boolean once) {
|
||||
public static List<AutoApproval> isLazy(boolean once) {
|
||||
List<AutoApproval> lazyed = LAZY_AUTOAPPROVAL.get();
|
||||
if (lazyed != null && once) LAZY_AUTOAPPROVAL.remove();
|
||||
return lazyed;
|
||||
|
@ -115,12 +116,12 @@ public class AutoApproval extends TriggerAction {
|
|||
/**
|
||||
* @return
|
||||
*/
|
||||
public static int executeLazyAutoApproval() {
|
||||
List<AutoApproval> lazyed = isLazyAutoApproval(Boolean.TRUE);
|
||||
public static int executeLazy() {
|
||||
List<AutoApproval> lazyed = isLazy(true);
|
||||
if (lazyed != null) {
|
||||
for (AutoApproval a : lazyed) {
|
||||
log.info("Lazy AutoApproval execute : {}", a);
|
||||
Object res = a.execute(a.operatingContext);
|
||||
Object res = a.execute(a.keepOperatingContext);
|
||||
|
||||
CommonsLog.createLog(TYPE_TRIGGER,
|
||||
UserService.SYSTEM_USER, a.getActionContext().getConfigId(), res.toString());
|
||||
|
|
|
@ -56,7 +56,7 @@ public class AutoAssign extends TriggerAction {
|
|||
@Override
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
final ID recordId = operatingContext.getFixedRecordId();
|
||||
|
||||
JSONArray assignTo = content.getJSONArray("assignTo");
|
||||
Set<ID> toUsers = UserHelper.parseUsers(assignTo, recordId, true);
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||
import com.rebuild.core.service.NoRecordFoundException;
|
||||
import com.rebuild.core.service.approval.ApprovalHelper;
|
||||
import com.rebuild.core.service.approval.ApprovalState;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
|
@ -46,7 +47,7 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
|||
protected void prepare(OperatingContext operatingContext) throws TriggerException {
|
||||
if (operatingContext.getAction() == InternalPermission.DELETE_BEFORE) {
|
||||
willRecords = getRelatedRecords(
|
||||
actionContext, operatingContext.getAnyRecord().getPrimary());
|
||||
actionContext, operatingContext.getFixedRecordId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +60,7 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
|||
protected Set<ID> getWillRecords(OperatingContext operatingContext) {
|
||||
if (willRecords == null) {
|
||||
willRecords = getRelatedRecords(
|
||||
actionContext, operatingContext.getAnyRecord().getPrimary());
|
||||
actionContext, operatingContext.getFixedRecordId());
|
||||
}
|
||||
return willRecords;
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
|||
}
|
||||
|
||||
for (Field refto : fieldsRefto) {
|
||||
Entity ownEntity = refto.getOwnEntity();
|
||||
final Entity ownEntity = refto.getOwnEntity();
|
||||
String sql = String.format("select %s from %s where ",
|
||||
ownEntity.getPrimaryField().getName(), ownEntity.getName());
|
||||
|
||||
|
@ -153,8 +154,13 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
|||
* @see ApprovalHelper#getApprovalState(ID)
|
||||
*/
|
||||
protected static ApprovalState getApprovalState(ID recordId) {
|
||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
if (MetadataHelper.hasApprovalField(entity)) return ApprovalHelper.getApprovalState(recordId);
|
||||
else return null;
|
||||
if (MetadataHelper.hasApprovalField(MetadataHelper.getEntity(recordId.getEntityCode()))) {
|
||||
try {
|
||||
return ApprovalHelper.getApprovalState(recordId);
|
||||
} catch (NoRecordFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class AutoShare extends TriggerAction {
|
|||
@Override
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
||||
final ID recordId = operatingContext.getFixedRecordId();
|
||||
|
||||
JSONArray shareTo = content.getJSONArray("shareTo");
|
||||
Set<ID> toUsers = UserHelper.parseUsers(shareTo, recordId, true);
|
||||
|
|
|
@ -14,6 +14,7 @@ import cn.devezhao.persist4j.Entity;
|
|||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
|
@ -43,7 +44,9 @@ import org.apache.commons.lang.StringUtils;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 字段聚合,场景 N>1
|
||||
|
@ -139,7 +142,7 @@ public class FieldAggregation extends TriggerAction {
|
|||
@Override
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final String chainName = String.format("%s:%s:%s", actionContext.getConfigId(),
|
||||
operatingContext.getAnyRecord().getPrimary(), operatingContext.getAction().getName());
|
||||
operatingContext.getFixedRecordId(), operatingContext.getAction().getName());
|
||||
final List<String> tschain = checkTriggerChain(chainName);
|
||||
if (tschain == null) return TriggerResult.triggerOnce();
|
||||
|
||||
|
@ -159,7 +162,7 @@ public class FieldAggregation extends TriggerAction {
|
|||
JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter");
|
||||
String dataFilterSql = null;
|
||||
if (dataFilter != null && !dataFilter.isEmpty()) {
|
||||
dataFilterSql = new AdvFilterParser(dataFilter).toSqlWhere();
|
||||
dataFilterSql = new AdvFilterParser(dataFilter, operatingContext.getFixedRecordId()).toSqlWhere();
|
||||
}
|
||||
|
||||
String filterSql = followSourceWhere;
|
||||
|
@ -190,13 +193,13 @@ public class FieldAggregation extends TriggerAction {
|
|||
if (evalValue instanceof Date) targetRecord.setDate(targetField, (Date) evalValue);
|
||||
else targetRecord.setNull(targetField);
|
||||
|
||||
} else if (dt == DisplayType.NTEXT || dt == DisplayType.N2NREFERENCE) {
|
||||
} else if (dt == DisplayType.NTEXT || dt == DisplayType.N2NREFERENCE || dt == DisplayType.FILE) {
|
||||
Object[] oArray = (Object[]) evalValue;
|
||||
|
||||
if (oArray.length == 0) {
|
||||
targetRecord.setNull(targetField);
|
||||
} else if (dt == DisplayType.NTEXT) {
|
||||
// 使用文本
|
||||
// ID 则使用文本
|
||||
if (oArray[0] instanceof ID) {
|
||||
List<String> labelList = new ArrayList<>();
|
||||
for (Object id : oArray) {
|
||||
|
@ -207,10 +210,16 @@ public class FieldAggregation extends TriggerAction {
|
|||
|
||||
String join = StringUtils.join(oArray, ", ");
|
||||
targetRecord.setString(targetField, join);
|
||||
|
||||
} else if (dt == DisplayType.N2NREFERENCE) {
|
||||
// 强制去重
|
||||
Set<ID> idSet = new LinkedHashSet<>();
|
||||
for (Object id : oArray) idSet.add((ID) id);
|
||||
targetRecord.setIDArray(targetField, idSet.toArray(new ID[0]));
|
||||
|
||||
} else {
|
||||
List<ID> idList = new ArrayList<>();
|
||||
for (Object id : oArray) idList.add((ID) id);
|
||||
targetRecord.setIDArray(targetField, idList.toArray(new ID[0]));
|
||||
String join = JSON.toJSONString(oArray);
|
||||
targetRecord.setString(targetField, join);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -293,6 +302,13 @@ public class FieldAggregation extends TriggerAction {
|
|||
targetEntity = MetadataHelper.getEntity(targetFieldEntity[1]);
|
||||
|
||||
String followSourceField = targetFieldEntity[0];
|
||||
if (TARGET_ANY.equals(followSourceField)) {
|
||||
TargetWithMatchFields targetWithMatchFields = new TargetWithMatchFields();
|
||||
targetRecordId = targetWithMatchFields.match(actionContext);
|
||||
followSourceWhere = StringUtils.join(targetWithMatchFields.getQFieldsFollow().iterator(), " and ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sourceEntity.containsField(followSourceField)) {
|
||||
throw new MissingMetaExcetion(followSourceField, sourceEntity.getName());
|
||||
}
|
||||
|
@ -328,8 +344,7 @@ public class FieldAggregation extends TriggerAction {
|
|||
protected boolean isCurrentSame(Record record) {
|
||||
if (!ignoreSame) return false;
|
||||
|
||||
Record c = Application.getQueryFactory().recordNoFilter(
|
||||
record.getPrimary(), record.getAvailableFields().toArray(new String[0]));
|
||||
Record c = QueryHelper.querySnap(record);
|
||||
return new RecordDifference(record).isSame(c, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.general.OperatingContext;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.core.service.trigger.TriggerAction;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
|
@ -36,9 +37,7 @@ public class FieldAggregationRefresh {
|
|||
/**
|
||||
*/
|
||||
public void refresh() {
|
||||
if (operatingContext.getBeforeRecord() == null || operatingContext.getAfterRecord() == null) {
|
||||
return;
|
||||
}
|
||||
if (operatingContext.getBeforeRecord() == null || operatingContext.getAfterRecord() == null) return;
|
||||
|
||||
ID triggerUser = UserService.SYSTEM_USER;
|
||||
ActionContext parentAc = parent.getActionContext();
|
||||
|
@ -47,6 +46,11 @@ public class FieldAggregationRefresh {
|
|||
String[] targetFieldEntity = ((JSONObject) parentAc.getActionContent()).getString("targetEntity").split("\\.");
|
||||
String followSourceField = targetFieldEntity[0];
|
||||
|
||||
if (TriggerAction.TARGET_ANY.equals(followSourceField)) {
|
||||
log.debug("Use match-fields does not support refresh");
|
||||
return;
|
||||
}
|
||||
|
||||
final ID beforeValue = operatingContext.getBeforeRecord().getID(followSourceField);
|
||||
final ID afterValue = operatingContext.getAfterRecord().getID(followSourceField);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import com.rebuild.core.support.general.ContentWithFieldVars;
|
|||
import com.rebuild.core.support.general.N2NReferenceSupport;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
@ -105,7 +106,7 @@ public class FieldWriteback extends FieldAggregation {
|
|||
@Override
|
||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||
final String chainName = String.format("%s:%s:%s", actionContext.getConfigId(),
|
||||
operatingContext.getAnyRecord().getPrimary(), operatingContext.getAction().getName());
|
||||
operatingContext.getFixedRecordId(), operatingContext.getAction().getName());
|
||||
final List<String> tschain = checkTriggerChain(chainName);
|
||||
if (tschain == null) return TriggerResult.triggerOnce();
|
||||
|
||||
|
@ -130,7 +131,7 @@ public class FieldWriteback extends FieldAggregation {
|
|||
for (ID targetRecordId : targetRecordIds) {
|
||||
// 删除时无需更新自己
|
||||
if (operatingContext.getAction() == BizzPermission.DELETE
|
||||
&& targetRecordId.equals(operatingContext.getAnyRecord().getPrimary())) {
|
||||
&& targetRecordId.equals(operatingContext.getFixedRecordId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -200,8 +201,14 @@ public class FieldWriteback extends FieldAggregation {
|
|||
|
||||
targetRecordIds = new HashSet<>();
|
||||
|
||||
if (SOURCE_SELF.equalsIgnoreCase(targetFieldEntity[0])) {
|
||||
// 自己更新自己
|
||||
// v35
|
||||
if (TARGET_ANY.equals(targetFieldEntity[0])) {
|
||||
TargetWithMatchFields targetWithMatchFields = new TargetWithMatchFields();
|
||||
ID[] ids = targetWithMatchFields.matchMulti(actionContext);
|
||||
CollectionUtils.addAll(targetRecordIds, ids);
|
||||
}
|
||||
// 自己更新自己
|
||||
else if (SOURCE_SELF.equalsIgnoreCase(targetFieldEntity[0])) {
|
||||
targetRecordIds.add(actionContext.getSourceRecord());
|
||||
}
|
||||
// 1:1
|
||||
|
@ -247,13 +254,13 @@ public class FieldWriteback extends FieldAggregation {
|
|||
// N:N v3.1
|
||||
Field targetField = targetEntity.getField(targetFieldEntity[0]);
|
||||
if (targetField.getType() == FieldType.REFERENCE_LIST) {
|
||||
Set<ID> set = N2NReferenceSupport.findReferences(targetField, operatingContext.getAnyRecord().getPrimary());
|
||||
Set<ID> set = N2NReferenceSupport.findReferences(targetField, operatingContext.getFixedRecordId());
|
||||
targetRecordIds.addAll(set);
|
||||
} else {
|
||||
String sql = String.format("select %s from %s where %s = ?",
|
||||
targetEntity.getPrimaryField().getName(), targetFieldEntity[1], targetFieldEntity[0]);
|
||||
Object[][] array = Application.createQueryNoFilter(sql)
|
||||
.setParameter(1, operatingContext.getAnyRecord().getPrimary())
|
||||
.setParameter(1, operatingContext.getFixedRecordId())
|
||||
.array();
|
||||
|
||||
for (Object[] o : array) {
|
||||
|
@ -461,7 +468,13 @@ public class FieldWriteback extends FieldAggregation {
|
|||
} else if (dt == DisplayType.DECIMAL) {
|
||||
targetRecord.setDouble(targetField, ObjectUtils.toDouble(newValue));
|
||||
} else if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||
targetRecord.setDate(targetField, (Date) newValue);
|
||||
if (newValue instanceof Date) {
|
||||
targetRecord.setDate(targetField, (Date) newValue);
|
||||
} else {
|
||||
Date newValueCast = CalendarUtils.parse(newValue.toString());
|
||||
if (newValueCast == null) log.warn("Cannot cast string to date : {}", newValue);
|
||||
else targetRecord.setDate(targetField, newValueCast);
|
||||
}
|
||||
} else {
|
||||
newValue = checkoutFieldValue(newValue, targetFieldEasy);
|
||||
if (newValue != null) {
|
||||
|
|
|
@ -33,7 +33,11 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 分组聚合,场景 N[+N]>1
|
||||
|
@ -137,6 +141,7 @@ public class GroupAggregation extends FieldAggregation {
|
|||
? null : operatingContext.getBeforeRecord().getObjectValue(sourceField);
|
||||
Object afterValue = operatingContext.getAfterRecord().getObjectValue(sourceField);
|
||||
if (NullValue.isNull(beforeValue) && NullValue.isNull(afterValue)) {
|
||||
// All null
|
||||
} else {
|
||||
valueChanged.put(sourceField, new Object[] { beforeValue, afterValue });
|
||||
}
|
||||
|
@ -205,7 +210,7 @@ public class GroupAggregation extends FieldAggregation {
|
|||
|
||||
// 需要匹配等级的值
|
||||
if (sourceFieldLevel != targetFieldLevel) {
|
||||
ID parent = getItemWithLevel((ID) val, targetFieldLevel);
|
||||
ID parent = TargetWithMatchFields.getItemWithLevel((ID) val, targetFieldLevel);
|
||||
Assert.isTrue(parent != null, Language.L("分类字段等级不兼容"));
|
||||
|
||||
val = parent;
|
||||
|
@ -231,7 +236,7 @@ public class GroupAggregation extends FieldAggregation {
|
|||
if (!valueChanged.isEmpty()) {
|
||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
|
||||
} else {
|
||||
log.warn("All group-fields are null, ignored");
|
||||
log.warn("All values of group-fields are null, ignored");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -283,21 +288,4 @@ public class GroupAggregation extends FieldAggregation {
|
|||
|
||||
targetRecordId = newTargetRecord.getPrimary();
|
||||
}
|
||||
|
||||
private ID getItemWithLevel(ID itemId, int specLevel) {
|
||||
ID current = itemId;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select level,parent from ClassificationData where itemId = ?")
|
||||
.setParameter(1, current)
|
||||
.unique();
|
||||
|
||||
if (o == null) break;
|
||||
if ((int) o[0] < specLevel) break;
|
||||
|
||||
if ((int) o[0] == specLevel) return current;
|
||||
else current = (ID) o[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public class SendNotification extends TriggerAction {
|
|||
private static final int MTYPE_DINGTALK = 5; // 钉钉群
|
||||
private static final int UTYPE_USER = 1; // 内部用户
|
||||
private static final int UTYPE_ACCOUNT = 2; // 外部人员
|
||||
private static final int UTYPE_ACCOUNT20 = 20; // 外部人员-输入
|
||||
private static final int UTYPE_WXWORK = 4; // 企微群
|
||||
private static final int UTYPE_DINGTALK = 5; // 钉钉群
|
||||
|
||||
|
@ -87,8 +88,8 @@ public class SendNotification extends TriggerAction {
|
|||
}
|
||||
|
||||
Set<Object> s;
|
||||
if (userType == UTYPE_ACCOUNT) {
|
||||
s = sendToAccounts(operatingContext);
|
||||
if (userType == UTYPE_ACCOUNT || userType == UTYPE_ACCOUNT20) {
|
||||
s = sendToAccounts(operatingContext, userType);
|
||||
} else if (userType == UTYPE_WXWORK) {
|
||||
s = sendToWxwork(operatingContext);
|
||||
} else if (userType == UTYPE_DINGTALK) {
|
||||
|
@ -115,62 +116,68 @@ public class SendNotification extends TriggerAction {
|
|||
Set<Object> send = new HashSet<>();
|
||||
|
||||
for (ID user : toUsers) {
|
||||
if (send.contains(user)) continue;
|
||||
|
||||
if (msgType == MTYPE_MAIL) {
|
||||
String emailAddr = Application.getUserStore().getUser(user).getEmail();
|
||||
if (RegexUtils.isEMail(emailAddr) && !send.contains(emailAddr)) {
|
||||
SMSender.sendMailAsync(emailAddr, message[1], message[0]);
|
||||
if (RegexUtils.isEMail(emailAddr)) {
|
||||
String mdHtml = MarkdownUtils.render(message[0]);
|
||||
SMSender.sendMailAsync(emailAddr, message[1], mdHtml);
|
||||
send.add(emailAddr);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (msgType == MTYPE_SMS) {
|
||||
if (msgType == MTYPE_SMS) {
|
||||
String mobileAddr = Application.getUserStore().getUser(user).getWorkphone();
|
||||
if (RegexUtils.isCNMobile(mobileAddr) && !send.contains(mobileAddr)) {
|
||||
if (RegexUtils.isCNMobile(mobileAddr)) {
|
||||
SMSender.sendSMSAsync(mobileAddr, message[0]);
|
||||
send.add(mobileAddr);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// TYPE_NOTIFICATION
|
||||
if (!send.contains(user)) {
|
||||
Message m = MessageBuilder.createMessage(user, message[0], Message.TYPE_DEFAULT, actionContext.getSourceRecord());
|
||||
Application.getNotifications().send(m);
|
||||
send.add(user);
|
||||
}
|
||||
if (msgType == MTYPE_NOTIFICATION) {
|
||||
Message m = MessageBuilder.createMessage(user, message[0], Message.TYPE_DEFAULT, actionContext.getSourceRecord());
|
||||
Application.getNotifications().send(m);
|
||||
send.add(user);
|
||||
}
|
||||
}
|
||||
return send;
|
||||
}
|
||||
|
||||
private Set<Object> sendToAccounts(OperatingContext operatingContext) {
|
||||
private Set<Object> sendToAccounts(OperatingContext operatingContext, int userType) {
|
||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||
final int msgType = content.getIntValue("type");
|
||||
|
||||
JSONArray fieldsDef = content.getJSONArray("sendTo");
|
||||
if (fieldsDef == null || fieldsDef.isEmpty()) return null;
|
||||
|
||||
List<String> validFields = new ArrayList<>();
|
||||
for (Object field : fieldsDef) {
|
||||
if (MetadataHelper.getLastJoinField(actionContext.getSourceEntity(), field.toString()) != null) {
|
||||
validFields.add(field.toString());
|
||||
}
|
||||
}
|
||||
if (validFields.isEmpty()) return null;
|
||||
|
||||
Object[] to = null;
|
||||
// v3.4 删除就尝试从快照中取
|
||||
if (operatingContext.getAction() == BizzPermission.DELETE) {
|
||||
Record beforeRecord = operatingContext.getBeforeRecord();
|
||||
if (beforeRecord != null) {
|
||||
List<String> toList = new ArrayList<>();
|
||||
for (String s : validFields) {
|
||||
Object v;
|
||||
if ((v = beforeRecord.getObjectValue(s)) != null) toList.add(v.toString());
|
||||
}
|
||||
to = toList.toArray(new String[0]);
|
||||
}
|
||||
if (userType == UTYPE_ACCOUNT20) {
|
||||
to = content.getString("sendTo").split("[,,;;]");
|
||||
} else {
|
||||
to = Application.getQueryFactory().uniqueNoFilter(
|
||||
actionContext.getSourceRecord(), validFields.toArray(new String[0]));
|
||||
JSONArray fieldsDef = content.getJSONArray("sendTo");
|
||||
if (fieldsDef == null || fieldsDef.isEmpty()) return null;
|
||||
|
||||
List<String> validFields = new ArrayList<>();
|
||||
for (Object field : fieldsDef) {
|
||||
if (MetadataHelper.getLastJoinField(actionContext.getSourceEntity(), field.toString()) != null) {
|
||||
validFields.add(field.toString());
|
||||
}
|
||||
}
|
||||
if (validFields.isEmpty()) return null;
|
||||
|
||||
// v3.4 删除就尝试从快照中取
|
||||
if (operatingContext.getAction() == BizzPermission.DELETE) {
|
||||
Record beforeRecord = operatingContext.getBeforeRecord();
|
||||
if (beforeRecord != null) {
|
||||
List<String> toList = new ArrayList<>();
|
||||
for (String s : validFields) {
|
||||
Object v;
|
||||
if ((v = beforeRecord.getObjectValue(s)) != null) toList.add(v.toString());
|
||||
}
|
||||
to = toList.toArray(new String[0]);
|
||||
}
|
||||
} else {
|
||||
to = Application.getQueryFactory().uniqueNoFilter(
|
||||
actionContext.getSourceRecord(), validFields.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
if (to == null) return null;
|
||||
|
||||
|
@ -180,7 +187,7 @@ public class SendNotification extends TriggerAction {
|
|||
for (Object item : to) {
|
||||
if (item == null) continue;
|
||||
|
||||
String mobileOrEmail = item.toString();
|
||||
String mobileOrEmail = item.toString().trim();
|
||||
if (send.contains(mobileOrEmail)) continue;
|
||||
|
||||
if (msgType == MTYPE_SMS && RegexUtils.isCNMobile(mobileOrEmail)) {
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.service.trigger.impl;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.general.ClassificationManager;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.core.service.trigger.ActionContext;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 寻找目标实体记录
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2023/10/25
|
||||
*/
|
||||
@Slf4j
|
||||
public class TargetWithMatchFields {
|
||||
|
||||
private Entity sourceEntity;
|
||||
|
||||
@Getter
|
||||
private List<String> qFieldsFollow;
|
||||
@Getter
|
||||
private Object targetRecordId;
|
||||
|
||||
protected TargetWithMatchFields() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionContext
|
||||
* @return
|
||||
*/
|
||||
public ID match(ActionContext actionContext) {
|
||||
return (ID) match(actionContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param actionContext
|
||||
* @return
|
||||
*/
|
||||
public ID[] matchMulti(ActionContext actionContext) {
|
||||
Object o = match(actionContext, true);
|
||||
return o == null ? new ID[0] : (ID[]) o;
|
||||
}
|
||||
|
||||
private Object match(ActionContext actionContext, boolean m) {
|
||||
if (sourceEntity != null) return targetRecordId; // 已做匹配
|
||||
|
||||
final JSONObject actionContent = (JSONObject) actionContext.getActionContent();
|
||||
sourceEntity = actionContext.getSourceEntity();
|
||||
Entity targetEntity = MetadataHelper.getEntity(actionContent.getString("targetEntity").split("\\.")[1]);
|
||||
|
||||
// 0.字段关联 <Source, Target>
|
||||
|
||||
Map<String, String> matchFieldsMapping = new HashMap<>();
|
||||
for (Object o : actionContent.getJSONArray("targetEntityMatchFields")) {
|
||||
JSONObject item = (JSONObject) o;
|
||||
String sourceField = item.getString("sourceField");
|
||||
String targetField = item.getString("targetField");
|
||||
|
||||
if (MetadataHelper.getLastJoinField(sourceEntity, sourceField) == null) {
|
||||
throw new MissingMetaExcetion(sourceField, sourceEntity.getName());
|
||||
}
|
||||
if (!targetEntity.containsField(targetField)) {
|
||||
throw new MissingMetaExcetion(targetField, targetEntity.getName());
|
||||
}
|
||||
matchFieldsMapping.put(sourceField, targetField);
|
||||
}
|
||||
|
||||
if (matchFieldsMapping.isEmpty()) {
|
||||
log.warn("No match-fields specified");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1.源记录数据
|
||||
|
||||
String aSql = String.format("select %s from %s where %s = ?",
|
||||
StringUtils.join(matchFieldsMapping.keySet().iterator(), ","),
|
||||
sourceEntity.getName(), sourceEntity.getPrimaryField().getName());
|
||||
|
||||
final Record sourceRecord = Application.createQueryNoFilter(aSql)
|
||||
.setParameter(1, actionContext.getSourceRecord())
|
||||
.record();
|
||||
|
||||
// 2.找到目标记录
|
||||
|
||||
boolean allNull = true;
|
||||
List<String> qFields = new ArrayList<>();
|
||||
qFieldsFollow = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, String> e : matchFieldsMapping.entrySet()) {
|
||||
String sourceField = e.getKey();
|
||||
String targetField = e.getValue();
|
||||
|
||||
Object val = sourceRecord.getObjectValue(sourceField);
|
||||
if (val == null) {
|
||||
qFields.add(String.format("%s is null", targetField));
|
||||
qFieldsFollow.add(String.format("%s is null", sourceField));
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
EasyField sourceFieldEasy = EasyMetaFactory.valueOf(MetadataHelper.getLastJoinField(sourceEntity, sourceField));
|
||||
//noinspection ConstantConditions
|
||||
EasyField targetFieldEasy = EasyMetaFactory.valueOf(MetadataHelper.getLastJoinField(targetEntity, targetField));
|
||||
|
||||
// @see Dimension#getSqlName
|
||||
|
||||
// 日期分组
|
||||
if (sourceFieldEasy.getDisplayType() == DisplayType.DATE
|
||||
|| sourceFieldEasy.getDisplayType() == DisplayType.DATETIME) {
|
||||
|
||||
String formatKey = sourceFieldEasy.getDisplayType() == DisplayType.DATE
|
||||
? EasyFieldConfigProps.DATE_FORMAT
|
||||
: EasyFieldConfigProps.DATETIME_FORMAT;
|
||||
int sourceFieldLength = StringUtils.defaultIfBlank(
|
||||
sourceFieldEasy.getExtraAttr(formatKey), sourceFieldEasy.getDisplayType().getDefaultFormat()).length();
|
||||
|
||||
// 目标字段仅使用日期
|
||||
int targetFieldLength = StringUtils.defaultIfBlank(
|
||||
targetFieldEasy.getExtraAttr(EasyFieldConfigProps.DATE_FORMAT), targetFieldEasy.getDisplayType().getDefaultFormat()).length();
|
||||
|
||||
// 目标格式(长度)必须小于等于源格式
|
||||
Assert.isTrue(targetFieldLength <= sourceFieldLength,
|
||||
Language.L("日期字段格式不兼容") + String.format(" (%d,%d)", targetFieldLength, sourceFieldLength));
|
||||
|
||||
if (targetFieldLength == 4) { // 'Y'
|
||||
targetField = String.format("DATE_FORMAT(%s,'%s')", targetField, "%Y");
|
||||
val = CalendarUtils.format("yyyy", (Date) val);
|
||||
} else if (targetFieldLength == 7) { // 'M'
|
||||
targetField = String.format("DATE_FORMAT(%s,'%s')", targetField, "%Y-%m");
|
||||
val = CalendarUtils.format("yyyy-MM", (Date) val);
|
||||
} else { // 'D' is default
|
||||
targetField = String.format("DATE_FORMAT(%s,'%s')", targetField, "%Y-%m-%d");
|
||||
val = CalendarUtils.format("yyyy-MM-dd", (Date) val);
|
||||
}
|
||||
}
|
||||
// 分类分组
|
||||
else if (sourceFieldEasy.getDisplayType() == DisplayType.CLASSIFICATION) {
|
||||
int sourceFieldLevel = ClassificationManager.instance.getOpenLevel(
|
||||
MetadataHelper.getLastJoinField(sourceEntity, sourceField));
|
||||
int targetFieldLevel = ClassificationManager.instance.getOpenLevel(
|
||||
MetadataHelper.getLastJoinField(targetEntity, targetField));
|
||||
|
||||
// 目标等级必须小于等于源等级
|
||||
Assert.isTrue(targetFieldLevel <= sourceFieldLevel,
|
||||
Language.L("分类字段等级不兼容") + String.format(" (%d,%d)", targetFieldLevel, sourceFieldLevel));
|
||||
|
||||
// 需要匹配等级的值
|
||||
if (sourceFieldLevel != targetFieldLevel) {
|
||||
ID parent = getItemWithLevel((ID) val, targetFieldLevel);
|
||||
Assert.isTrue(parent != null, Language.L("分类字段等级不兼容"));
|
||||
|
||||
val = parent;
|
||||
sourceRecord.setID(sourceField, (ID) val);
|
||||
|
||||
for (int i = 0; i < sourceFieldLevel - targetFieldLevel; i++) {
|
||||
//noinspection StringConcatenationInLoop
|
||||
sourceField += ".parent";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qFields.add(String.format("%s = '%s'", targetField, val));
|
||||
qFieldsFollow.add(String.format("%s = '%s'", sourceField, val));
|
||||
allNull = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allNull) {
|
||||
log.warn("All values of match-fields are null, ignored");
|
||||
return null;
|
||||
}
|
||||
|
||||
aSql = String.format("select %s from %s where ( %s )",
|
||||
targetEntity.getPrimaryField().getName(), targetEntity.getName(),
|
||||
StringUtils.join(qFields.iterator(), " and "));
|
||||
|
||||
// 多个 1:N
|
||||
if (m) {
|
||||
Object[][] array = Application.createQueryNoFilter(aSql).array();
|
||||
List<ID> targetRecordIds = new ArrayList<>();
|
||||
for (Object[] o : array) targetRecordIds.add((ID) o[0]);
|
||||
|
||||
targetRecordId = targetRecordIds.toArray(new ID[0]);
|
||||
return targetRecordId;
|
||||
}
|
||||
|
||||
// 单个
|
||||
Object[] targetRecord = Application.createQueryNoFilter(aSql).unique();
|
||||
targetRecordId = targetRecord == null ? null : (ID) targetRecord[0];
|
||||
return targetRecordId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分类字段级别
|
||||
*
|
||||
* @param itemId
|
||||
* @param specLevel
|
||||
* @return
|
||||
*/
|
||||
static ID getItemWithLevel(ID itemId, int specLevel) {
|
||||
ID current = itemId;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select level,parent from ClassificationData where itemId = ?")
|
||||
.setParameter(1, current)
|
||||
.unique();
|
||||
|
||||
if (o == null) break;
|
||||
if ((int) o[0] < specLevel) break;
|
||||
|
||||
if ((int) o[0] == specLevel) return current;
|
||||
else current = (ID) o[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -108,6 +108,8 @@ public enum ConfigurationItem {
|
|||
PortalBaiduMapAk,
|
||||
PortalOfficePreviewUrl,
|
||||
PortalUploadMaxSize(200),
|
||||
MobileNavStyle(34),
|
||||
PageMourningMode(false),
|
||||
|
||||
// !!! 命令行适用
|
||||
DataDirectory, // 数据目录
|
||||
|
@ -118,6 +120,7 @@ public enum ConfigurationItem {
|
|||
SecurityEnhanced(false), // 安全增强
|
||||
TrustedAllUrl(false), // 可信外部地址
|
||||
LibreofficeBin, // Libreoffice 命令
|
||||
UnsafeImgAccess(false), // 不安全图片访问
|
||||
|
||||
;
|
||||
|
||||
|
@ -136,6 +139,7 @@ public enum ConfigurationItem {
|
|||
|| SecurityEnhanced.name().equalsIgnoreCase(name)
|
||||
|| TrustedAllUrl.name().equalsIgnoreCase(name)
|
||||
|| LibreofficeBin.name().equalsIgnoreCase(name)
|
||||
|| UnsafeImgAccess.name().equals(name)
|
||||
|| SN.name().equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,8 @@ import java.util.HashMap;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* !!!! 请勿修改或删除本文件
|
||||
* !!!! 请严格遵守《REBUILD 用户服务协议》https://getrebuild.com/legal/service-terms
|
||||
*/
|
||||
// !!!! 请勿对本文件做任何改动,否则导致的一切损失由您自行承担!
|
||||
// !!!! 请严格遵守《REBUILD 用户服务协议》https://getrebuild.com/legal/service-terms
|
||||
@Slf4j
|
||||
public final class License {
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ package com.rebuild.core.support;
|
|||
import cn.devezhao.commons.CodecUtils;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.cache.CommonsCache;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
|
||||
/**
|
||||
* 验证码助手
|
||||
|
@ -44,7 +44,7 @@ public class VerfiyCode {
|
|||
} else if (level == 2) {
|
||||
vcode = CodecUtils.randomCode(8);
|
||||
} else {
|
||||
vcode = RandomUtils.nextInt(100000, 999999) + "";
|
||||
vcode = CommonsUtils.randomInt(100000, 999999) + "";
|
||||
}
|
||||
|
||||
// 缓存 10 分钟
|
||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.support.general;
|
||||
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
|
@ -15,7 +16,9 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -66,12 +69,14 @@ public class BatchOperatorQuery extends SetUser {
|
|||
public JSONObject wrapQueryData(int maxRows, boolean clearFields) {
|
||||
if (this.dataRange != DR_PAGED) {
|
||||
queryData.put("pageNo", 1);
|
||||
queryData.put("pageSize", maxRows); // Max
|
||||
queryData.put("pageSize", maxRows);
|
||||
}
|
||||
|
||||
if (this.dataRange == DR_SELECTED || this.dataRange == DR_ALL) {
|
||||
queryData.remove("filter");
|
||||
queryData.remove("advFilter");
|
||||
}
|
||||
|
||||
if (this.dataRange == DR_SELECTED) {
|
||||
JSONObject idsItem = new JSONObject();
|
||||
idsItem.put("op", ParseHelper.IN);
|
||||
|
@ -88,6 +93,7 @@ public class BatchOperatorQuery extends SetUser {
|
|||
if (clearFields) {
|
||||
queryData.put("fields", new String[]{getEntity().getPrimaryField().getName()});
|
||||
}
|
||||
|
||||
queryData.put("reload", Boolean.FALSE);
|
||||
return queryData;
|
||||
}
|
||||
|
@ -109,7 +115,7 @@ public class BatchOperatorQuery extends SetUser {
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
public ID[] getQueryedRecords() {
|
||||
public ID[] getQueryedRecordIds() {
|
||||
if (this.dataRange == DR_SELECTED) {
|
||||
String selected = queryData.getString("_selected");
|
||||
|
||||
|
@ -122,14 +128,13 @@ public class BatchOperatorQuery extends SetUser {
|
|||
return ids.toArray(new ID[0]);
|
||||
}
|
||||
|
||||
String sql = String.format("select %s %s",
|
||||
getEntity().getPrimaryField().getName(), getFromClauseSql());
|
||||
String sql = String.format("select %s %s", getEntity().getPrimaryField().getName(), getFromClauseSql());
|
||||
int pageNo = queryData.getIntValue("pageNo");
|
||||
int pageSize = queryData.getIntValue("pageSize");
|
||||
|
||||
Object[][] array = Application.createQuery(sql, getUser())
|
||||
.setLimit(pageSize, pageNo * pageSize - pageSize)
|
||||
.setTimeout(60)
|
||||
.setTimeout(180)
|
||||
.array();
|
||||
|
||||
Set<ID> ids = new HashSet<>();
|
||||
|
@ -140,7 +145,20 @@ public class BatchOperatorQuery extends SetUser {
|
|||
}
|
||||
|
||||
private Entity getEntity() {
|
||||
String entityName = queryData.getString("entity");
|
||||
return MetadataHelper.getEntity(entityName);
|
||||
return MetadataHelper.getEntity(queryData.getString("entity"));
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static BatchOperatorQuery create(HttpServletRequest request, String entity) {
|
||||
JSONObject requestData = (JSONObject) ServletUtils.getRequestJson(request);
|
||||
requestData.put("entity", entity);
|
||||
|
||||
int dr = NumberUtils.toInt(request.getParameter("dr"), DR_PAGED);
|
||||
return new BatchOperatorQuery(dr, requestData);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue