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;
|
# include nginx-rebuild.conf;
|
||||||
server {
|
server {
|
||||||
server_name YOUR_DOMAIN;
|
server_name YOUR_IP_OR_DOMAIN;
|
||||||
listen 80;
|
listen 80;
|
||||||
# HTTPS
|
# HTTPS
|
||||||
#listen 443 ssl http2;
|
#listen 443 ssl http2;
|
||||||
#ssl_certificate /path/to/ssl.crt;
|
#ssl_certificate /path/to/ssl.crt;
|
||||||
#ssl_certificate_key /path/to/ssl.key;
|
#ssl_certificate_key /path/to/ssl.key;
|
||||||
# PROXY
|
# PROXY
|
||||||
proxy_redirect http:// $scheme://;
|
proxy_redirect http:// $scheme://;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host:$server_port;
|
||||||
#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-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
#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 / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:18080;
|
proxy_pass http://127.0.0.1:18080;
|
||||||
etag on;
|
etag on;
|
||||||
}
|
}
|
||||||
location /assets {
|
location /assets {
|
||||||
proxy_pass http://127.0.0.1:18080/assets;
|
proxy_pass http://127.0.0.1:18080/assets;
|
||||||
expires 90d;
|
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/)
|
更多详情介绍 [https://getrebuild.com/](https://getrebuild.com/)
|
||||||
|
|
||||||
> **福利:加入 REBUILD QQ 交流群 819865721(满) 1013051587 GET 使用技能**
|
> **福利:加入 REBUILD VIP 用户 QQ 交流群 819865721 1013051587 GET 使用技能**
|
||||||
|
|
||||||
## V3.4 新特性
|
## V3.5 新特性
|
||||||
|
|
||||||
本次更新为你带来众多功能增强与优化。
|
本次更新为你带来众多功能增强与优化。
|
||||||
|
|
||||||
1. [新增] 发送通知触发器支持发送钉钉/企业微信(机器人)群消息
|
1. [新增] 批量审批
|
||||||
2. [新增] 表单回填支持启用后端回填
|
2. [新增] 表单引用组件
|
||||||
3. [新增] 角色扩展权限“允许撤销审批”
|
3. [新增] 触发器执行流程图
|
||||||
4. [新增] 字段聚合、分组聚合触发器支持多引用字段“连接”模式
|
4. [新增] 触发器高级表达式函数 `ISNULL` `DATEPICKAT`
|
||||||
5. [新增] 标签类字段支持自定义颜色
|
5. [新增] WORD 模板
|
||||||
6. [新增] 手机版对 HTML5+ 环境的支持
|
6. [新增] 手机版支持导出报表
|
||||||
8. [新增] 字段“允许重复”选项支持配置检查数据范围、逻辑条件
|
7. [优化] 手机版导航样式优化
|
||||||
7. [新增] 日期条件支持去年/明年、上季度/下季度、上月/下月、上周/下周
|
8. [优化] 明细实体支持显示在视图页下方
|
||||||
9. ...
|
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>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>2.7.12</version>
|
<version>2.7.17</version>
|
||||||
<relativePath/>
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.rebuild</groupId>
|
<groupId>com.rebuild</groupId>
|
||||||
<artifactId>rebuild</artifactId>
|
<artifactId>rebuild</artifactId>
|
||||||
<version>3.4.6</version>
|
<version>3.5.0-beta1</version>
|
||||||
<name>rebuild</name>
|
<name>rebuild</name>
|
||||||
<description>Building your business-systems freely!</description>
|
<description>Building your business-systems freely!</description>
|
||||||
<!-- UNCOMMENT USE TOMCAT -->
|
<!-- UNCOMMENT USE TOMCAT -->
|
||||||
|
@ -257,7 +257,6 @@
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-context-support</artifactId>
|
<artifactId>spring-context-support</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
@ -271,7 +270,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.devezhao</groupId>
|
<groupId>com.github.devezhao</groupId>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
<version>1.3.10</version>
|
<version>1.3.13</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
|
@ -291,7 +290,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.devezhao</groupId>
|
<groupId>com.github.devezhao</groupId>
|
||||||
<artifactId>persist4j</artifactId>
|
<artifactId>persist4j</artifactId>
|
||||||
<version>1.7.2</version>
|
<version>1.7.5</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
|
@ -316,17 +315,17 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.aspectj</groupId>
|
<groupId>org.aspectj</groupId>
|
||||||
<artifactId>aspectjweaver</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
<version>1.9.19</version>
|
<version>1.9.20</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>druid</artifactId>
|
<artifactId>druid</artifactId>
|
||||||
<version>1.2.18</version>
|
<version>1.2.20</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.mysql</groupId>
|
<groupId>com.mysql</groupId>
|
||||||
<artifactId>mysql-connector-j</artifactId>
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
<version>8.0.33</version>
|
<version>8.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.ehcache</groupId>
|
<groupId>net.sf.ehcache</groupId>
|
||||||
|
@ -336,12 +335,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
<version>4.4.1</version>
|
<version>4.4.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.qiniu</groupId>
|
<groupId>com.qiniu</groupId>
|
||||||
<artifactId>qiniu-java-sdk</artifactId>
|
<artifactId>qiniu-java-sdk</artifactId>
|
||||||
<version>7.13.1</version>
|
<version>7.14.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.whvcse</groupId>
|
<groupId>com.github.whvcse</groupId>
|
||||||
|
@ -351,7 +350,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.oshi</groupId>
|
<groupId>com.github.oshi</groupId>
|
||||||
<artifactId>oshi-core</artifactId>
|
<artifactId>oshi-core</artifactId>
|
||||||
<version>6.4.2</version>
|
<version>6.4.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jsoup</groupId>
|
<groupId>org.jsoup</groupId>
|
||||||
|
@ -399,15 +398,20 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.deepoove</groupId>
|
||||||
|
<artifactId>poi-tl</artifactId>
|
||||||
|
<version>1.12.1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.poi</groupId>
|
<groupId>org.apache.poi</groupId>
|
||||||
<artifactId>poi</artifactId>
|
<artifactId>poi</artifactId>
|
||||||
<version>4.1.2</version>
|
<version>5.2.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.poi</groupId>
|
<groupId>org.apache.poi</groupId>
|
||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>4.1.2</version>
|
<version>5.2.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.poi</groupId>
|
<groupId>org.apache.poi</groupId>
|
||||||
|
@ -427,7 +431,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.coobird</groupId>
|
<groupId>net.coobird</groupId>
|
||||||
<artifactId>thumbnailator</artifactId>
|
<artifactId>thumbnailator</artifactId>
|
||||||
<version>0.4.19</version>
|
<version>0.4.20</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>es.moki.ratelimitj</groupId>
|
<groupId>es.moki.ratelimitj</groupId>
|
||||||
|
@ -437,12 +441,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.zxing</groupId>
|
<groupId>com.google.zxing</groupId>
|
||||||
<artifactId>javase</artifactId>
|
<artifactId>javase</artifactId>
|
||||||
<version>3.5.1</version>
|
<version>3.5.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.redisson</groupId>
|
<groupId>org.redisson</groupId>
|
||||||
<artifactId>redisson</artifactId>
|
<artifactId>redisson</artifactId>
|
||||||
<version>3.21.3</version>
|
<version>3.23.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
@ -468,12 +472,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>5.8.19</version>
|
<version>5.8.21</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-lang3</artifactId>
|
<artifactId>commons-lang3</artifactId>
|
||||||
<version>3.12.0</version>
|
<version>3.13.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Need JDK11+ -->
|
<!-- Need JDK11+ -->
|
||||||
<!--
|
<!--
|
||||||
|
@ -488,17 +492,22 @@
|
||||||
<artifactId>jansi</artifactId>
|
<artifactId>jansi</artifactId>
|
||||||
<version>2.4.0</version>
|
<version>2.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.zwobble.mammoth</groupId>
|
||||||
|
<artifactId>mammoth</artifactId>
|
||||||
|
<version>1.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- fix: CVEs -->
|
<!-- fix: CVEs -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>2.12.0</version>
|
<version>2.13.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.23.0</version>
|
<version>1.24.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
|
|
|
@ -11,10 +11,12 @@ import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
|
import com.rebuild.utils.JSONUtils;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.lang.math.NumberUtils;
|
import org.apache.commons.lang.math.NumberUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +54,7 @@ public class ApiContext {
|
||||||
* @param bindUser
|
* @param bindUser
|
||||||
*/
|
*/
|
||||||
public ApiContext(Map<String, String> reqParams, JSON postData, String appId, ID 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.postData = postData;
|
||||||
this.appId = appId;
|
this.appId = appId;
|
||||||
this.bindUser = bindUser;
|
this.bindUser = bindUser;
|
||||||
|
@ -88,7 +90,7 @@ public class ApiContext {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public JSON getPostData() {
|
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
|
* @throws ApiInvokeException
|
||||||
*/
|
*/
|
||||||
public String getParameterNotBlank(String name) throws ApiInvokeException {
|
public String getParameterNotBlank(String name) throws ApiInvokeException {
|
||||||
String value = getParameterMap().get(name);
|
String value = reqParams.get(name);
|
||||||
if (StringUtils.isBlank(value)) {
|
if (StringUtils.isBlank(value)) {
|
||||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADPARAMS, "Parameter [" + name + "] cannot be null");
|
throw new ApiInvokeException(ApiInvokeException.ERR_BADPARAMS, "Parameter [" + name + "] cannot be null");
|
||||||
}
|
}
|
||||||
|
@ -109,7 +111,7 @@ public class ApiContext {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getParameter(String name) {
|
public String getParameter(String name) {
|
||||||
return getParameterMap().get(name);
|
return reqParams.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,29 +130,18 @@ public class ApiContext {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int getParameterAsInt(String name, int defaultValue) {
|
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);
|
if (NumberUtils.isNumber(value)) return NumberUtils.toInt(value);
|
||||||
else return defaultValue;
|
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 name
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean getParameterAsBool(String name, boolean defaultValue) {
|
public boolean getParameterAsBool(String name, boolean defaultValue) {
|
||||||
String value = getParameterMap().get(name);
|
String value = reqParams.get(name);
|
||||||
if (StringUtils.isBlank(value)) return defaultValue;
|
if (StringUtils.isBlank(value)) return defaultValue;
|
||||||
else return BooleanUtils.toBoolean(value);
|
else return BooleanUtils.toBoolean(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ package com.rebuild.api;
|
||||||
import cn.devezhao.commons.CalendarUtils;
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import cn.devezhao.commons.EncryptUtils;
|
import cn.devezhao.commons.EncryptUtils;
|
||||||
import cn.devezhao.commons.ObjectUtils;
|
import cn.devezhao.commons.ObjectUtils;
|
||||||
|
import cn.devezhao.commons.ThrowableUtils;
|
||||||
import cn.devezhao.commons.web.ServletUtils;
|
import cn.devezhao.commons.web.ServletUtils;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
@ -60,7 +61,7 @@ public class ApiGateway extends Controller implements Initialization {
|
||||||
// 基于 IP 限流
|
// 基于 IP 限流
|
||||||
private static final RequestRateLimiter RRL = RateLimiters.createRateLimiter(
|
private static final RequestRateLimiter RRL = RateLimiters.createRateLimiter(
|
||||||
new int[] { 10, 60 },
|
new int[] { 10, 60 },
|
||||||
new int[] { 600, 6000 });
|
new int[] { 600, 3000 });
|
||||||
|
|
||||||
private static final Map<String, Class<? extends BaseApi>> API_CLASSES = new HashMap<>();
|
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();
|
final String requestId = CommonsUtils.randomHex();
|
||||||
|
|
||||||
response.addHeader("X-RB-Server", ServerStatus.STARTUP_ONCE + "/" + Application.BUILD);
|
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)) {
|
if (RRL.overLimitWhenIncremented("ip:" + remoteIp)) {
|
||||||
JSON error = formatFailure("Request frequency exceeded", ApiInvokeException.ERR_FREQUENCY);
|
JSON error = formatFailure("Request frequency exceeded", ApiInvokeException.ERR_FREQUENCY);
|
||||||
|
@ -111,7 +112,9 @@ public class ApiGateway extends Controller implements Initialization {
|
||||||
ApiContext context = null;
|
ApiContext context = null;
|
||||||
try {
|
try {
|
||||||
final BaseApi api = createApi(apiName);
|
final BaseApi api = createApi(apiName);
|
||||||
context = verfiy(request, api);
|
|
||||||
|
context = buildBaseApiContext(request);
|
||||||
|
context = verfiy(request, context, api);
|
||||||
|
|
||||||
UserContextHolder.setReqip(remoteIp);
|
UserContextHolder.setReqip(remoteIp);
|
||||||
UserContextHolder.setUser(context.getBindUser());
|
UserContextHolder.setUser(context.getBindUser());
|
||||||
|
@ -130,7 +133,7 @@ public class ApiGateway extends Controller implements Initialization {
|
||||||
errorMsg = ex.getLocalizedMessage();
|
errorMsg = ex.getLocalizedMessage();
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
errorCode = Controller.CODE_SERV_ERROR;
|
errorCode = Controller.CODE_SERV_ERROR;
|
||||||
errorMsg = ex.getLocalizedMessage();
|
errorMsg = ThrowableUtils.getRootCause(ex).getLocalizedMessage();
|
||||||
log.error("Server Internal Error ({})", requestId, ex);
|
log.error("Server Internal Error ({})", requestId, ex);
|
||||||
|
|
||||||
String knownError = KnownExceptionConverter.convert2ErrorMsg(ex);
|
String knownError = KnownExceptionConverter.convert2ErrorMsg(ex);
|
||||||
|
@ -154,16 +157,12 @@ public class ApiGateway extends Controller implements Initialization {
|
||||||
* 验证请求并构建请求上下文
|
* 验证请求并构建请求上下文
|
||||||
*
|
*
|
||||||
* @param request
|
* @param request
|
||||||
|
* @param base
|
||||||
* @param useApi
|
* @param useApi
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected ApiContext verfiy(HttpServletRequest request, @SuppressWarnings("unused") BaseApi useApi) {
|
protected ApiContext verfiy(HttpServletRequest request, ApiContext base, @SuppressWarnings("unused") BaseApi useApi) {
|
||||||
final Map<String, String> sortedMap = new TreeMap<>();
|
final Map<String, String> sortedMap = new TreeMap<>(base.getParameterMap());
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String appid = getParameterNotNull(sortedMap, "appid");
|
final String appid = getParameterNotNull(sortedMap, "appid");
|
||||||
final String sign = getParameterNotNull(sortedMap, "sign");
|
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");
|
ID bindUser = apiConfig.getID("bindUser");
|
||||||
// 默认绑定系统用户
|
// 默认绑定系统用户
|
||||||
if (bindUser == null) bindUser = UserService.SYSTEM_USER;
|
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;
|
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 record = EntityHelper.forNew(EntityHelper.RebuildApiRequest, UserService.SYSTEM_USER);
|
||||||
record.setString("requestUrl", apiName);
|
record.setString("requestUrl", apiName);
|
||||||
record.setString("remoteIp", remoteIp);
|
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("requestTime", requestTime);
|
||||||
record.setDate("responseTime", CalendarUtils.now());
|
record.setDate("responseTime", CalendarUtils.now());
|
||||||
|
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
record.setString("appId", context.getAppId());
|
record.setString("appId", context.getAppId());
|
||||||
if (context.getPostData() != null) {
|
JSON post;
|
||||||
record.setString("requestBody",
|
if ((post = context.getPostData()) != null) {
|
||||||
CommonsUtils.maxstr(context.getPostData().toJSONString(), 10000));
|
record.setString("requestBody", CommonsUtils.maxstr(post.toJSONString(), 32767));
|
||||||
}
|
}
|
||||||
if (!context.getParameterMap().isEmpty()) {
|
if (!context.getParameterMap().isEmpty()) {
|
||||||
record.setString("requestUrl",
|
record.setString("requestUrl",
|
||||||
|
|
|
@ -49,11 +49,11 @@ public class AuthTokenManager {
|
||||||
* 生成 Token
|
* 生成 Token
|
||||||
*
|
*
|
||||||
* @param user
|
* @param user
|
||||||
* @param expires
|
* @param seconds
|
||||||
* @param type
|
* @param type
|
||||||
* @return
|
* @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
|
// Type,User,Time,Version
|
||||||
String desc = String.format("%s,%s,%d,v2",
|
String desc = String.format("%s,%s,%d,v2",
|
||||||
ObjectUtils.defaultIfNull(type, TYPE_ACCESS_TOKEN),
|
ObjectUtils.defaultIfNull(type, TYPE_ACCESS_TOKEN),
|
||||||
|
@ -61,7 +61,7 @@ public class AuthTokenManager {
|
||||||
System.nanoTime());
|
System.nanoTime());
|
||||||
String token = EncryptUtils.toSHA1Hex(desc);
|
String token = EncryptUtils.toSHA1Hex(desc);
|
||||||
|
|
||||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, expires);
|
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, seconds);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,12 +84,12 @@ public class AuthTokenManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param expires
|
* @param seconds
|
||||||
* @return
|
* @return
|
||||||
* @see #TYPE_CSRF_TOKEN
|
* @see #TYPE_CSRF_TOKEN
|
||||||
*/
|
*/
|
||||||
public static String generateCsrfToken(int expires) {
|
public static String generateCsrfToken(int seconds) {
|
||||||
return generateToken(null, expires, TYPE_CSRF_TOKEN);
|
return generateToken(null, seconds, TYPE_CSRF_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,16 +30,16 @@ import es.moki.ratelimitj.core.limiter.request.RequestRateLimiter;
|
||||||
public class LoginToken extends BaseApi {
|
public class LoginToken extends BaseApi {
|
||||||
|
|
||||||
// 基于用户限流
|
// 基于用户限流
|
||||||
private static final RequestRateLimiter RRL = RateLimiters.createRateLimiter(
|
private static final RequestRateLimiter RRL_4USER = RateLimiters.createRateLimiter(
|
||||||
new int[] { 30, 60, 3600 },
|
new int[] { 60, 600, 3600 },
|
||||||
new int[] { 5, 10, 100 });
|
new int[] { 5, 15, 30 });
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JSON execute(ApiContext context) throws ApiInvokeException {
|
public JSON execute(ApiContext context) throws ApiInvokeException {
|
||||||
final String user = context.getParameterNotBlank("user");
|
final String user = context.getParameterNotBlank("user");
|
||||||
final String password = context.getParameterNotBlank("password");
|
final String password = context.getParameterNotBlank("password");
|
||||||
|
|
||||||
if (RRL.overLimitWhenIncremented("user:" + user)) {
|
if (RRL_4USER.overLimitWhenIncremented("user:" + user)) {
|
||||||
return formatFailure(Language.L("请求过于频繁,请稍后重试"), ApiInvokeException.ERR_FREQUENCY);
|
return formatFailure(Language.L("请求过于频繁,请稍后重试"), ApiInvokeException.ERR_FREQUENCY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ import com.rebuild.web.OnlineSessionStore;
|
||||||
import com.rebuild.web.RebuildWebConfigurer;
|
import com.rebuild.web.RebuildWebConfigurer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.ehcache.CacheManager;
|
import net.sf.ehcache.CacheManager;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
|
@ -73,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
||||||
/**
|
/**
|
||||||
* Rebuild Version
|
* 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}
|
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||||
*/
|
*/
|
||||||
public static final int BUILD = 3040611;
|
public static final int BUILD = 3050000;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Driver for DB
|
// Driver for DB
|
||||||
|
@ -219,9 +220,14 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
||||||
RebuildConfiguration.set(ConfigurationItem.AppBuild, BUILD);
|
RebuildConfiguration.set(ConfigurationItem.AppBuild, BUILD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringBuilder logConf = new StringBuilder();
|
||||||
// 刷新配置缓存
|
// 刷新配置缓存
|
||||||
for (ConfigurationItem item : ConfigurationItem.values()) {
|
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 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.DataSpecificationException;
|
||||||
import com.rebuild.core.service.InternalPersistService;
|
import com.rebuild.core.service.InternalPersistService;
|
||||||
import com.rebuild.core.service.query.QueryHelper;
|
import com.rebuild.core.service.query.QueryHelper;
|
||||||
import com.rebuild.core.support.CommonsLock;
|
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
|
||||||
|
@ -43,11 +42,6 @@ public abstract class BaseConfigurationService extends InternalPersistService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Record update(Record record) {
|
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());
|
throwIfNotSelf(record.getPrimary());
|
||||||
cleanCache(record.getPrimary());
|
cleanCache(record.getPrimary());
|
||||||
return super.update(putCreateBy4ShareTo(record));
|
return super.update(putCreateBy4ShareTo(record));
|
||||||
|
@ -55,11 +49,6 @@ public abstract class BaseConfigurationService extends InternalPersistService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(ID recordId) {
|
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);
|
throwIfNotSelf(recordId);
|
||||||
cleanCache(recordId);
|
cleanCache(recordId);
|
||||||
return super.delete(recordId);
|
return super.delete(recordId);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.rebuild.utils.JSONable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ public class ConfigBean implements Serializable, Cloneable, JSONable {
|
||||||
/**
|
/**
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> toMap() {
|
public Map<String, Object> getRawData() {
|
||||||
return data;
|
return Collections.unmodifiableMap(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,16 @@ public class AutoFillinManager implements ConfigManager {
|
||||||
return fillin;
|
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 DIVIDER_LINE = "$DIVIDER$";
|
||||||
|
// 引用
|
||||||
|
public static final String REFFORM_LINE = "$REFFORM$";
|
||||||
|
|
||||||
// 引用主记录
|
// 引用主记录
|
||||||
public static final String DV_MAINID = "$MAINID$";
|
public static final String DV_MAINID = "$MAINID$";
|
||||||
|
@ -121,7 +123,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
|
|
||||||
final Entity entityMeta = MetadataHelper.getEntity(entity);
|
final Entity entityMeta = MetadataHelper.getEntity(entity);
|
||||||
if (record != null) {
|
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)) {
|
if (MetadataHelper.isBizzEntity(entityMeta) && !UserFilters.allowAccessBizz(user, record)) {
|
||||||
return formatModelError(Language.L("无权读取此记录或记录已被删除"));
|
return formatModelError(Language.L("无权读取此记录或记录已被删除"));
|
||||||
|
@ -290,13 +292,12 @@ public class FormsBuilder extends FormsManager {
|
||||||
return RobotApprovalManager.instance.hadApproval(entity, null);
|
return RobotApprovalManager.instance.hadApproval(entity, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通实体
|
// 普通实体(非明细)
|
||||||
if (entity.getMainEntity() == null) {
|
if (entity.getMainEntity() == null) {
|
||||||
return RobotApprovalManager.instance.hadApproval(entity, recordId);
|
return RobotApprovalManager.instance.hadApproval(entity, recordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 明细实体
|
// 明细实体
|
||||||
|
|
||||||
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
||||||
if (mainid == null) {
|
if (mainid == null) {
|
||||||
Field dtmField = MetadataHelper.getDetailToMainField(entity);
|
Field dtmField = MetadataHelper.getDetailToMainField(entity);
|
||||||
|
@ -335,6 +336,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
JSONObject el = (JSONObject) iter.next();
|
JSONObject el = (JSONObject) iter.next();
|
||||||
String fieldName = el.getString("field");
|
String fieldName = el.getString("field");
|
||||||
if (DIVIDER_LINE.equalsIgnoreCase(fieldName)) continue;
|
if (DIVIDER_LINE.equalsIgnoreCase(fieldName)) continue;
|
||||||
|
if (REFFORM_LINE.equalsIgnoreCase(fieldName)) continue;
|
||||||
|
|
||||||
// 已删除字段
|
// 已删除字段
|
||||||
if (!MetadataHelper.checkAndWarnField(entity, fieldName)) {
|
if (!MetadataHelper.checkAndWarnField(entity, fieldName)) {
|
||||||
|
@ -343,12 +345,13 @@ public class FormsBuilder extends FormsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// v2.2 高级控制
|
// 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 (viewModel) useAdvControl = false;
|
||||||
if (useAdvControl) {
|
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 跟随主记录新建/更新
|
// fix v3.3.4 跟随主记录新建/更新
|
||||||
boolean isNew2 = isNew;
|
boolean isNew2 = isNew;
|
||||||
if (entity.getMainEntity() != null) {
|
if (entity.getMainEntity() != null) {
|
||||||
|
@ -650,14 +653,10 @@ public class FormsBuilder extends FormsManager {
|
||||||
* @param initialVal 此值优先级大于字段默认值
|
* @param initialVal 此值优先级大于字段默认值
|
||||||
*/
|
*/
|
||||||
public void setFormInitialValue(Entity entity, JSON formModel, JSONObject initialVal) {
|
public void setFormInitialValue(Entity entity, JSON formModel, JSONObject initialVal) {
|
||||||
if (initialVal == null || initialVal.isEmpty()) {
|
if (initialVal == null || initialVal.isEmpty()) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONArray elements = ((JSONObject) formModel).getJSONArray("elements");
|
JSONArray elements = ((JSONObject) formModel).getJSONArray("elements");
|
||||||
if (elements == null || elements.isEmpty()) {
|
if (elements == null || elements.isEmpty()) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 已布局字段。字段是否布局会影响返回值
|
// 已布局字段。字段是否布局会影响返回值
|
||||||
Set<String> inFormFields = new HashSet<>();
|
Set<String> inFormFields = new HashSet<>();
|
||||||
|
@ -702,7 +701,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
// 其他
|
// 其他
|
||||||
else if (entity.containsField(field)) {
|
else if (entity.containsField(field)) {
|
||||||
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(field));
|
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(field));
|
||||||
if (easyField.getDisplayType() == DisplayType.REFERENCE) {
|
if (easyField.getDisplayType() == DisplayType.REFERENCE || easyField.getDisplayType() == DisplayType.N2NREFERENCE) {
|
||||||
|
|
||||||
// v3.4 如果字段设置了附加过滤条件,从相关项新建时要检查是否符合
|
// v3.4 如果字段设置了附加过滤条件,从相关项新建时要检查是否符合
|
||||||
String dataFilter = easyField.getExtraAttr(EasyFieldConfigProps.REFERENCE_DATAFILTER);
|
String dataFilter = easyField.getExtraAttr(EasyFieldConfigProps.REFERENCE_DATAFILTER);
|
||||||
|
@ -720,9 +719,16 @@ public class FormsBuilder extends FormsManager {
|
||||||
|
|
||||||
Object mixValue = inFormFields.contains(field) ? getReferenceMixValue(value) : value;
|
Object mixValue = inFormFields.contains(field) ? getReferenceMixValue(value) : value;
|
||||||
if (mixValue != null) {
|
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 {
|
} else {
|
||||||
log.warn("Unknown value pair : " + field + " = " + value);
|
log.warn("Unknown value pair : " + field + " = " + value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ import com.rebuild.utils.JSONUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 轻量级表单
|
* 轻量级表单
|
||||||
|
* Issue1. 针对自动只读字段无效
|
||||||
|
* Issue2. 表单高级控制无效
|
||||||
*
|
*
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
* @since 2022/12/28
|
* @since 2022/12/28
|
||||||
|
|
|
@ -20,11 +20,11 @@ import com.rebuild.core.UserContextHolder;
|
||||||
import com.rebuild.core.configuration.BaseConfigurationService;
|
import com.rebuild.core.configuration.BaseConfigurationService;
|
||||||
import com.rebuild.core.metadata.EntityHelper;
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.privileges.AdminGuard;
|
import com.rebuild.core.privileges.AdminGuard;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -114,7 +114,7 @@ public class PickListService extends BaseConfigurationService implements AdminGu
|
||||||
r.setString("text", item.getString("text"));
|
r.setString("text", item.getString("text"));
|
||||||
r.setBoolean("isHide", false);
|
r.setBoolean("isHide", false);
|
||||||
r.setBoolean("isDefault", item.getBoolean("default"));
|
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) {
|
if (id2id == null) {
|
||||||
r.setString("belongEntity", field.getOwnEntity().getName());
|
r.setString("belongEntity", field.getOwnEntity().getName());
|
||||||
r.setString("belongField", field.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.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.MetadataSorter;
|
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.easymeta.EasyMetaFactory;
|
||||||
|
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@ -79,9 +82,11 @@ public class ViewAddonsManager extends BaseLayoutManager {
|
||||||
if (ifMain.getDetailEntity() != null) {
|
if (ifMain.getDetailEntity() != null) {
|
||||||
JSONArray tabsFluent = new JSONArray();
|
JSONArray tabsFluent = new JSONArray();
|
||||||
for (Entity de : MetadataSorter.sortDetailEntities(ifMain)) {
|
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("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);
|
tabsFluent.add(deJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,9 +163,9 @@ public class ViewAddonsManager extends BaseLayoutManager {
|
||||||
if (!MetadataHelper.isBusinessEntity(e)) continue;
|
if (!MetadataHelper.isBusinessEntity(e)) continue;
|
||||||
if (ArrayUtils.contains(entityMeta.getDetialEntities(), e)) continue;
|
if (ArrayUtils.contains(entityMeta.getDetialEntities(), e)) continue;
|
||||||
|
|
||||||
// 新建项无明细、多引用
|
// 新建项排除明细
|
||||||
if (TYPE_ADD.equals(applyType)) {
|
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);
|
Entity eCheck = ObjectUtils.defaultIfNull(e.getMainEntity(), e);
|
||||||
|
|
|
@ -16,6 +16,8 @@ import cn.devezhao.persist4j.record.FieldValueException;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.configuration.general.AutoFillinManager;
|
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.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@ -26,6 +28,7 @@ import java.util.Date;
|
||||||
* @see MetadataHelper
|
* @see MetadataHelper
|
||||||
* @since 1.0, 2018-6-26
|
* @since 1.0, 2018-6-26
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class EntityHelper {
|
public class EntityHelper {
|
||||||
|
|
||||||
// 虚拟 ID 后缀
|
// 虚拟 ID 后缀
|
||||||
|
@ -34,19 +37,31 @@ public class EntityHelper {
|
||||||
public static final ID UNSAVED_ID = ID.valueOf("000" + UNSAVED_ID_SUFFIX);
|
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 data
|
||||||
* @param user
|
* @param user
|
||||||
* @return
|
* @return
|
||||||
* @see EntityRecordCreator
|
* @see #parse(JSONObject, ID, boolean, boolean)
|
||||||
*/
|
*/
|
||||||
public static Record parse(JSONObject data, ID user) {
|
public static Record parse(JSONObject data, ID user) {
|
||||||
return parse(data, user, true, false);
|
return parse(data, user, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析 JSON 到 Record
|
* 解析 JSON 为 Record
|
||||||
*
|
*
|
||||||
* @param data
|
* @param data
|
||||||
* @param user
|
* @param user
|
||||||
|
@ -88,12 +103,25 @@ public class EntityHelper {
|
||||||
return record;
|
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
|
* 构建更新 Record
|
||||||
*
|
*
|
||||||
* @param recordId
|
* @param recordId
|
||||||
* @param user
|
* @param user
|
||||||
* @return
|
* @return
|
||||||
|
* @see #forUpdate(ID, ID, boolean)
|
||||||
*/
|
*/
|
||||||
public static Record forUpdate(ID recordId, ID user) {
|
public static Record forUpdate(ID recordId, ID user) {
|
||||||
return forUpdate(recordId, user, true);
|
return forUpdate(recordId, user, true);
|
||||||
|
@ -120,12 +148,25 @@ public class EntityHelper {
|
||||||
return record;
|
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
|
* 构建新建 Record
|
||||||
*
|
*
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param user
|
* @param user
|
||||||
* @return
|
* @return
|
||||||
|
* @see #forNew(int, ID, boolean)
|
||||||
*/
|
*/
|
||||||
public static Record forNew(int entity, ID user) {
|
public static Record forNew(int entity, ID user) {
|
||||||
return forNew(entity, user, true);
|
return forNew(entity, user, true);
|
||||||
|
@ -136,24 +177,14 @@ public class EntityHelper {
|
||||||
*
|
*
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param user
|
* @param user
|
||||||
|
* @param bindCommons
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Record forNew(int entity, ID user, boolean bindCommons) {
|
public static Record forNew(int entity, ID user, boolean bindCommons) {
|
||||||
return forNew(MetadataHelper.getEntity(entity), user, bindCommons);
|
Assert.isTrue(MetadataHelper.containsEntity(entity), "[entity] does not exists : " + entity);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建新建 Record
|
|
||||||
*
|
|
||||||
* @param entity
|
|
||||||
* @param user
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static Record forNew(Entity entity, ID user, boolean bindCommons) {
|
|
||||||
Assert.notNull(entity, "[entity] cannot be null");
|
|
||||||
Assert.notNull(user, "[user] cannot be null");
|
Assert.notNull(user, "[user] cannot be null");
|
||||||
|
|
||||||
Record record = new StandardRecord(entity, user);
|
Record record = new StandardRecord(MetadataHelper.getEntity(entity), user);
|
||||||
if (bindCommons) {
|
if (bindCommons) {
|
||||||
bindCommonsFieldsValue(record, true);
|
bindCommonsFieldsValue(record, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
||||||
@Override
|
@Override
|
||||||
public boolean onSetFieldValueWarn(Field field, String value, Record record) {
|
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;
|
final boolean isNew = record.getPrimary() == null;
|
||||||
|
|
||||||
|
@ -76,15 +76,15 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
||||||
if (isNew && isDtmField(field)) return true;
|
if (isNew && isDtmField(field)) return true;
|
||||||
|
|
||||||
// 公共字段前台可能会布局出来
|
// 公共字段前台可能会布局出来
|
||||||
// 此处忽略检查没问题,因为最后还会复写,即 #bindCommonsFieldsValue
|
// 此处忽略检查没问题,因为最后还会复写,即 EntityHelper#bindCommonsFieldsValue
|
||||||
boolean isCommonField = MetadataHelper.isCommonsField(field);
|
boolean isCommonField = MetadataHelper.isCommonsField(field);
|
||||||
if (!isCommonField) return false;
|
if (!isCommonField) return false;
|
||||||
|
|
||||||
String fieldName = field.getName();
|
if (isNew) return true;
|
||||||
return isNew || (!EntityHelper.OwningUser.equalsIgnoreCase(fieldName)
|
|
||||||
&& !EntityHelper.OwningDept.equalsIgnoreCase(fieldName)
|
String n = field.getName();
|
||||||
&& !EntityHelper.CreatedBy.equalsIgnoreCase(fieldName)
|
return !(EntityHelper.OwningUser.equalsIgnoreCase(n) || EntityHelper.OwningDept.equalsIgnoreCase(n)
|
||||||
&& !EntityHelper.CreatedOn.equalsIgnoreCase(fieldName));
|
|| EntityHelper.CreatedBy.equalsIgnoreCase(n) || EntityHelper.CreatedOn.equalsIgnoreCase(n));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -172,7 +172,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
||||||
|
|
||||||
if (!notNulls.isEmpty()) {
|
if (!notNulls.isEmpty()) {
|
||||||
throw new DataSpecificationException(
|
throw new DataSpecificationException(
|
||||||
Language.L("%s 不允许为空", StringUtils.join(notNulls, " / ")));
|
Language.L("%s 不能为空", StringUtils.join(notNulls, " / ")));
|
||||||
}
|
}
|
||||||
if (!notWells.isEmpty()) {
|
if (!notWells.isEmpty()) {
|
||||||
throw new DataSpecificationException(
|
throw new DataSpecificationException(
|
||||||
|
@ -212,6 +212,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
|
||||||
return patt == null || patt.matcher((CharSequence) val).matches();
|
return patt == null || patt.matcher((CharSequence) val).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否需要去除外链
|
||||||
private void keepFieldValueSafe(Record record) {
|
private void keepFieldValueSafe(Record record) {
|
||||||
for (String fieldName : record.getAvailableFields()) {
|
for (String fieldName : record.getAvailableFields()) {
|
||||||
final Object value = record.getObjectValue(fieldName);
|
final Object value = record.getObjectValue(fieldName);
|
||||||
|
|
|
@ -111,7 +111,8 @@ public class MetadataSorter {
|
||||||
|
|
||||||
List<BaseMeta> entities = new ArrayList<>();
|
List<BaseMeta> entities = new ArrayList<>();
|
||||||
CollectionUtils.addAll(entities, mainEntity.getDetialEntities());
|
CollectionUtils.addAll(entities, mainEntity.getDetialEntities());
|
||||||
sortByLabel(entities);
|
// SORT: 名称。默认是返回按CODE大小
|
||||||
|
if (entities.size() > 1) sortByLabel(entities);
|
||||||
return entities.toArray(new Entity[0]);
|
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);
|
if (value != null) log.warn("Cannot wrap value of EasyBarCode : " + value);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object exprDefaultValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,6 @@ public class EasyDecimal extends EasyField {
|
||||||
*/
|
*/
|
||||||
public static String clearFlaged(Object flagedValue) {
|
public static String clearFlaged(Object flagedValue) {
|
||||||
if (flagedValue == null) return null;
|
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;
|
if (value instanceof JSONArray) return value;
|
||||||
return JSON.parseArray(value.toString());
|
return JSON.parseArray(value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object exprDefaultValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,9 @@ public class EasySeries extends EasyField {
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
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");
|
Assert.isTrue(targetField.getDisplayType() == getDisplayType(), "type-by-type is must");
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ import com.rebuild.core.rbstore.MetaSchemaGenerator;
|
||||||
import com.rebuild.core.rbstore.MetaschemaImporter;
|
import com.rebuild.core.rbstore.MetaschemaImporter;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import com.rebuild.core.support.task.TaskExecutors;
|
import com.rebuild.core.support.task.TaskExecutors;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
import com.rebuild.utils.RbAssert;
|
import com.rebuild.utils.RbAssert;
|
||||||
import org.apache.commons.lang.StringUtils;
|
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);
|
String uniqueEntityName = toPinyinName(entityName);
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (MetadataHelper.containsEntity(uniqueEntityName)) {
|
if (MetadataHelper.containsEntity(uniqueEntityName)) {
|
||||||
uniqueEntityName += RandomUtils.nextInt(0, 9);
|
uniqueEntityName += CommonsUtils.randomInt(0, 9);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,10 @@ public class EasyEntityConfigProps {
|
||||||
* 明细重复判断模式为全部数据(否则为主记录下的)
|
* 明细重复判断模式为全部数据(否则为主记录下的)
|
||||||
*/
|
*/
|
||||||
public static final String DETAILS_GLOBALREPEAT = "detailsGlobalRepeat";
|
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.License;
|
||||||
import com.rebuild.core.support.NeedRbvException;
|
import com.rebuild.core.support.NeedRbvException;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建实体
|
* 创建实体
|
||||||
|
@ -74,7 +74,7 @@ public class Entity2Schema extends Field2Schema {
|
||||||
entityName = toPinyinName(entityLabel);
|
entityName = toPinyinName(entityLabel);
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (MetadataHelper.containsEntity(entityName)) {
|
if (MetadataHelper.containsEntity(entityName)) {
|
||||||
entityName += RandomUtils.nextInt(0, 9);
|
entityName += CommonsUtils.randomInt(0, 9);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import com.rebuild.utils.RbAssert;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.CharSet;
|
import org.apache.commons.lang.CharSet;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -109,7 +108,7 @@ public class Field2Schema extends SetUser {
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (entity.containsField(fieldName) || MetadataHelper.isCommonsField(fieldName)) {
|
if (entity.containsField(fieldName) || MetadataHelper.isCommonsField(fieldName)) {
|
||||||
fieldName += RandomUtils.nextInt(1, 99);
|
fieldName += CommonsUtils.randomInt(1, 99);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -396,7 +395,7 @@ public class Field2Schema extends SetUser {
|
||||||
identifier = HanLP.convertToPinyinString(identifier, "", false);
|
identifier = HanLP.convertToPinyinString(identifier, "", false);
|
||||||
identifier = identifier.replaceAll("[^a-zA-Z0-9]", "");
|
identifier = identifier.replaceAll("[^a-zA-Z0-9]", "");
|
||||||
if (StringUtils.isBlank(identifier)) {
|
if (StringUtils.isBlank(identifier)) {
|
||||||
identifier = "rb" + RandomUtils.nextInt(1000, 9999);
|
identifier = "rb" + CommonsUtils.randomInt(1000, 9999);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0))) {
|
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0))) {
|
||||||
|
@ -407,7 +406,7 @@ public class Field2Schema extends SetUser {
|
||||||
if (identifier.length() > 40) {
|
if (identifier.length() > 40) {
|
||||||
identifier = identifier.substring(0, 40);
|
identifier = identifier.substring(0, 40);
|
||||||
} else if (identifier.length() < 4) {
|
} else if (identifier.length() < 4) {
|
||||||
identifier += RandomUtils.nextInt(1000, 9999);
|
identifier += CommonsUtils.randomInt(1000, 9999);
|
||||||
}
|
}
|
||||||
|
|
||||||
return identifier;
|
return identifier;
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.UserContextHolder;
|
import com.rebuild.core.UserContextHolder;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
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.CommonsService;
|
||||||
import com.rebuild.core.service.general.BulkContext;
|
import com.rebuild.core.service.general.BulkContext;
|
||||||
import com.rebuild.core.service.general.EntityService;
|
import com.rebuild.core.service.general.EntityService;
|
||||||
|
@ -190,7 +191,7 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
||||||
} else if (action.startsWith("share")) {
|
} else if (action.startsWith("share")) {
|
||||||
return BizzPermission.SHARE;
|
return BizzPermission.SHARE;
|
||||||
} else if (action.startsWith("unshare")) {
|
} else if (action.startsWith("unshare")) {
|
||||||
return EntityService.UNSHARE;
|
return InternalPermission.UNSHARE;
|
||||||
}
|
}
|
||||||
throw new PrivilegesException("No such Permission found : " + action);
|
throw new PrivilegesException("No such Permission found : " + action);
|
||||||
}
|
}
|
||||||
|
@ -213,8 +214,10 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
||||||
actionHuman = Language.L("分配");
|
actionHuman = Language.L("分配");
|
||||||
} else if (action == BizzPermission.SHARE) {
|
} else if (action == BizzPermission.SHARE) {
|
||||||
actionHuman = Language.L("共享");
|
actionHuman = Language.L("共享");
|
||||||
} else if (action == EntityService.UNSHARE) {
|
} else if (action == InternalPermission.UNSHARE) {
|
||||||
actionHuman = Language.L("取消共享");
|
actionHuman = Language.L("取消共享");
|
||||||
|
} else if (action == InternalPermission.APPROVAL) {
|
||||||
|
actionHuman = Language.L("审批");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target == null) {
|
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.ZeroEntry;
|
||||||
import com.rebuild.core.privileges.bizz.ZeroPrivileges;
|
import com.rebuild.core.privileges.bizz.ZeroPrivileges;
|
||||||
import com.rebuild.core.service.NoRecordFoundException;
|
import com.rebuild.core.service.NoRecordFoundException;
|
||||||
import com.rebuild.core.service.general.EntityService;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
@ -220,10 +219,11 @@ public class PrivilegesManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME v35 批量审批无权限
|
||||||
|
if (action == InternalPermission.APPROVAL) return true;
|
||||||
|
|
||||||
Boolean a = userAllow(user);
|
Boolean a = userAllow(user);
|
||||||
if (a != null) {
|
if (a != null) return a;
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
Role role = theUserStore.getUser(user).getOwningRole();
|
Role role = theUserStore.getUser(user).getOwningRole();
|
||||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
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;
|
action = BizzPermission.SHARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,9 +285,7 @@ public class PrivilegesManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean a = userAllow(user);
|
Boolean a = userAllow(user);
|
||||||
if (a != null) {
|
if (a != null) return a;
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
Role role = theUserStore.getUser(user).getOwningRole();
|
Role role = theUserStore.getUser(user).getOwningRole();
|
||||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
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;
|
action = BizzPermission.SHARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,9 +490,7 @@ public class PrivilegesManager {
|
||||||
*/
|
*/
|
||||||
public boolean allow(ID user, ZeroEntry entry) {
|
public boolean allow(ID user, ZeroEntry entry) {
|
||||||
Boolean a = userAllow(user);
|
Boolean a = userAllow(user);
|
||||||
if (a != null) {
|
if (a != null) return a;
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
Role role = theUserStore.getUser(user).getOwningRole();
|
Role role = theUserStore.getUser(user).getOwningRole();
|
||||||
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
if (RoleService.ADMIN_ROLE.equals(role.getIdentity())) {
|
||||||
|
@ -543,7 +539,7 @@ public class PrivilegesManager {
|
||||||
BizzPermission.READ,
|
BizzPermission.READ,
|
||||||
BizzPermission.ASSIGN,
|
BizzPermission.ASSIGN,
|
||||||
BizzPermission.SHARE,
|
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);
|
String dsql = String.format("delete from `layout_config` where `CREATED_BY` = '%s'", recordId);
|
||||||
Application.getSqlExecutor().execute(dsql);
|
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);
|
super.delete(recordId);
|
||||||
Application.getUserStore().removeUser(recordId);
|
Application.getUserStore().removeUser(recordId);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,9 +219,7 @@ public class UserService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
int policy = RebuildConfiguration.getInt(ConfigurationItem.PasswordPolicy);
|
int policy = RebuildConfiguration.getInt(ConfigurationItem.PasswordPolicy);
|
||||||
if (policy <= 1) {
|
if (policy <= 1) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int countUpper = 0;
|
int countUpper = 0;
|
||||||
int countLower = 0;
|
int countLower = 0;
|
||||||
|
@ -237,8 +240,8 @@ public class UserService extends BaseService {
|
||||||
if (countUpper == 0 || countLower == 0 || countDigit == 0) {
|
if (countUpper == 0 || countLower == 0 || countDigit == 0) {
|
||||||
throw new DataSpecificationException(Language.L("密码不能小于 6 位,且必须包含数字和大小写字母"));
|
throw new DataSpecificationException(Language.L("密码不能小于 6 位,且必须包含数字和大小写字母"));
|
||||||
}
|
}
|
||||||
if (policy >= 3 && (countSpecial == 0 || password.length() < 8)) {
|
if (policy >= 3 && (countSpecial == 0 || password.length() < 10)) {
|
||||||
throw new DataSpecificationException(Language.L("密码不能小于 8 位,且必须包含数字和大小写字母及特殊字符"));
|
throw new DataSpecificationException(Language.L("密码不能小于 10 位,且必须包含数字和大小写字母及特殊字符"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,13 +492,6 @@ public class UserService extends BaseService {
|
||||||
"select user from LoginLog where user = ?")
|
"select user from LoginLog where user = ?")
|
||||||
.setParameter(1, user)
|
.setParameter(1, user)
|
||||||
.unique();
|
.unique();
|
||||||
if (hasLogin != null) return true;
|
return hasLogin != null;
|
||||||
|
|
||||||
// 绑定
|
|
||||||
Object[] hasBind = Application.createQueryNoFilter(
|
|
||||||
"select bindUser from ExternalUser where bindUser = ?")
|
|
||||||
.setParameter(1, user)
|
|
||||||
.unique();
|
|
||||||
return hasBind != null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,11 @@ public class InternalPermission {
|
||||||
*/
|
*/
|
||||||
public static final Permission APPROVAL = new BizzPermission("APPROVAL", 0, false);
|
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.metadata.easymeta.EasyTag;
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.service.general.QuickCodeReindexTask;
|
import com.rebuild.core.service.general.QuickCodeReindexTask;
|
||||||
import com.rebuild.utils.Callable2;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang.ArrayUtils;
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
|
@ -147,33 +146,38 @@ public class BaseService extends InternalPersistService {
|
||||||
final Map<String, ID[]> holdN2NValues = new HashMap<>();
|
final Map<String, ID[]> holdN2NValues = new HashMap<>();
|
||||||
|
|
||||||
for (Field n2nField : n2nFields) {
|
for (Field n2nField : n2nFields) {
|
||||||
ID[] newRefs;
|
ID[] newValue;
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
Object maybeNull = record.getObjectValue(n2nField.getName());
|
Object maybeNull = record.getObjectValue(n2nField.getName());
|
||||||
newRefs = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
if (maybeNull == null) continue;
|
||||||
if (newRefs == null || newRefs.length == 0) continue;
|
|
||||||
|
newValue = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
|
||||||
|
if (newValue.length == 0) {
|
||||||
|
record.setNull(n2nField.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (record.hasValue(n2nField.getName())) {
|
if (record.hasValue(n2nField.getName())) {
|
||||||
Object maybeNull = record.getObjectValue(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 {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保持值
|
// 保持值
|
||||||
holdN2NValues.put(n2nField.getName(), newRefs);
|
holdN2NValues.put(n2nField.getName(), newValue);
|
||||||
|
|
||||||
// 仅保留第一个用于标识是否为空
|
// 仅保留第一个用于标识是否为空
|
||||||
if (newRefs.length == 0) record.setNull(n2nField.getName());
|
if (newValue.length == 0) record.setNull(n2nField.getName());
|
||||||
else record.setIDArray(n2nField.getName(), new ID[] { newRefs[0] });
|
else record.setIDArray(n2nField.getName(), new ID[] { newValue[0] });
|
||||||
|
|
||||||
// 哪个字段
|
// 哪个字段
|
||||||
n2nRecord.setString("belongField", n2nField.getName());
|
n2nRecord.setString("belongField", n2nField.getName());
|
||||||
|
|
||||||
// 新建
|
// 新建
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
for (ID refId : newRefs) {
|
for (ID refId : newValue) {
|
||||||
Record clone = n2nRecord.clone();
|
Record clone = n2nRecord.clone();
|
||||||
clone.setID("referenceId", refId);
|
clone.setID("referenceId", refId);
|
||||||
addItems.add(clone);
|
addItems.add(clone);
|
||||||
|
@ -193,7 +197,7 @@ public class BaseService extends InternalPersistService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ID> afterRefs = new LinkedHashSet<>();
|
Set<ID> afterRefs = new LinkedHashSet<>();
|
||||||
CollectionUtils.addAll(afterRefs, newRefs);
|
CollectionUtils.addAll(afterRefs, newValue);
|
||||||
|
|
||||||
for (Iterator<ID> iter = afterRefs.iterator(); iter.hasNext(); ) {
|
for (Iterator<ID> iter = afterRefs.iterator(); iter.hasNext(); ) {
|
||||||
ID a = iter.next();
|
ID a = iter.next();
|
||||||
|
@ -278,31 +282,37 @@ public class BaseService extends InternalPersistService {
|
||||||
final Map<String, String[]> holdTagValues = new HashMap<>();
|
final Map<String, String[]> holdTagValues = new HashMap<>();
|
||||||
|
|
||||||
for (Field tagField : tagFields) {
|
for (Field tagField : tagFields) {
|
||||||
String[] newTags;
|
String[] newValue;
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
newTags = cleanNameArray(record.getObjectValue(tagField.getName()));
|
Object maybeNull = record.getObjectValue(tagField.getName());
|
||||||
if (newTags.length == 0) continue;
|
if (maybeNull == null) continue;
|
||||||
|
|
||||||
|
newValue = cleanNameArray(maybeNull);
|
||||||
|
if (newValue.length == 0) {
|
||||||
|
record.setNull(tagField.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (record.hasValue(tagField.getName())) {
|
if (record.hasValue(tagField.getName())) {
|
||||||
newTags = cleanNameArray(record.getObjectValue(tagField.getName()));
|
newValue = cleanNameArray(record.getObjectValue(tagField.getName()));
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保持值
|
// 保持值
|
||||||
holdTagValues.put(tagField.getName(), newTags);
|
holdTagValues.put(tagField.getName(), newValue);
|
||||||
|
|
||||||
// 仅保留第一个用于标识是否为空
|
// 仅保留第一个用于标识是否为空
|
||||||
if (newTags.length == 0) record.setNull(tagField.getName());
|
if (newValue.length == 0) record.setNull(tagField.getName());
|
||||||
else record.setString(tagField.getName(), newTags[0]);
|
else record.setString(tagField.getName(), newValue[0]);
|
||||||
|
|
||||||
// 哪个字段
|
// 哪个字段
|
||||||
tagRecord.setString("belongField", tagField.getName());
|
tagRecord.setString("belongField", tagField.getName());
|
||||||
|
|
||||||
// 新建
|
// 新建
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
for (String tagName : newTags) {
|
for (String tagName : newValue) {
|
||||||
Record clone = tagRecord.clone();
|
Record clone = tagRecord.clone();
|
||||||
clone.setString("tagName", tagName);
|
clone.setString("tagName", tagName);
|
||||||
addItems.add(clone);
|
addItems.add(clone);
|
||||||
|
@ -322,7 +332,7 @@ public class BaseService extends InternalPersistService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> afterTags = new LinkedHashSet<>();
|
Set<String> afterTags = new LinkedHashSet<>();
|
||||||
CollectionUtils.addAll(afterTags, newTags);
|
CollectionUtils.addAll(afterTags, newValue);
|
||||||
|
|
||||||
for (Iterator<String> iter = afterTags.iterator(); iter.hasNext(); ) {
|
for (Iterator<String> iter = afterTags.iterator(); iter.hasNext(); ) {
|
||||||
String a = iter.next();
|
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.
|
See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.rebuild.utils;
|
package com.rebuild.core.service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author devezhao
|
* @author devezhao
|
|
@ -25,7 +25,7 @@ import org.springframework.util.Assert;
|
||||||
* <br>- 有权限的实体使用此类需要指定 <tt>strictMode=false</tt>
|
* <br>- 有权限的实体使用此类需要指定 <tt>strictMode=false</tt>
|
||||||
*
|
*
|
||||||
* @author Zixin (RB)
|
* @author Zixin (RB)
|
||||||
* @since 11/06/2017
|
* @since 11/06/2019
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class CommonsService extends InternalPersistService {
|
public class CommonsService extends InternalPersistService {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import com.rebuild.core.Application;
|
||||||
* 持久化服务
|
* 持久化服务
|
||||||
*
|
*
|
||||||
* @author Zixin (RB)
|
* @author Zixin (RB)
|
||||||
* @since 05/21/2017
|
* @since 05/21/2019
|
||||||
*/
|
*/
|
||||||
public abstract class InternalPersistService implements ServiceSpec {
|
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.TransactionStatus;
|
||||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手动事物管理。默认事务管理见 `application-bean.xml`
|
* 手动事物管理。默认事务管理见 `application-bean.xml`
|
||||||
|
@ -27,6 +28,8 @@ public class TransactionManual {
|
||||||
* 开启一个事物
|
* 开启一个事物
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
|
* @see #commit(TransactionStatus)
|
||||||
|
* @see #rollback(TransactionStatus)
|
||||||
*/
|
*/
|
||||||
public static TransactionStatus newTransaction() {
|
public static TransactionStatus newTransaction() {
|
||||||
DefaultTransactionAttribute attr = new DefaultTransactionAttribute();
|
DefaultTransactionAttribute attr = new DefaultTransactionAttribute();
|
||||||
|
@ -34,15 +37,6 @@ public class TransactionManual {
|
||||||
return getTxManager().getTransaction(attr);
|
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);
|
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,
|
Object[] o = Application.getQueryFactory().uniqueNoFilter(recordId,
|
||||||
EntityHelper.ApprovalId, EntityHelper.ApprovalId + ".name", EntityHelper.ApprovalState, EntityHelper.ApprovalStepNode);
|
EntityHelper.ApprovalId, EntityHelper.ApprovalId + ".name", EntityHelper.ApprovalState, EntityHelper.ApprovalStepNode);
|
||||||
if (o == null) {
|
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);
|
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.commons.ObjectUtils;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
|
@ -122,7 +123,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
* @throws ApprovalException
|
* @throws ApprovalException
|
||||||
*/
|
*/
|
||||||
public void approve(ID approver, ApprovalState state, String remark, JSONObject selectNextUsers) 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 addedData
|
||||||
* @param checkUseGroup
|
* @param checkUseGroup
|
||||||
* @param rejectNode
|
* @param rejectNode
|
||||||
|
* @param batchMode
|
||||||
* @throws ApprovalException
|
* @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 ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
||||||
|
|
||||||
final Object[] stepApprover = Application.createQueryNoFilter(
|
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(1, this.record)
|
||||||
.setParameter(2, approver)
|
.setParameter(2, approver)
|
||||||
.setParameter(3, getCurrentNodeId(status))
|
.setParameter(3, getCurrentNodeId(status))
|
||||||
|
@ -159,6 +161,13 @@ public class ApprovalProcessor extends SetUser {
|
||||||
approvedStep.setString("remark", remark);
|
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];
|
this.approval = (ID) stepApprover[3];
|
||||||
FlowNodeGroup nextNodes = getNextNodes((String) stepApprover[2]);
|
FlowNodeGroup nextNodes = getNextNodes((String) stepApprover[2]);
|
||||||
|
|
||||||
|
@ -575,6 +584,9 @@ public class ApprovalProcessor extends SetUser {
|
||||||
// 加签
|
// 加签
|
||||||
String countersignFrom = attrMored.getString("countersignFrom");
|
String countersignFrom = attrMored.getString("countersignFrom");
|
||||||
s.put("countersignFrom", ID.isId(countersignFrom) ? UserHelper.getName(ID.valueOf(countersignFrom)) : null);
|
s.put("countersignFrom", ID.isId(countersignFrom) ? UserHelper.getName(ID.valueOf(countersignFrom)) : null);
|
||||||
|
// 批量
|
||||||
|
String batchMode = attrMored.getString("batchMode");
|
||||||
|
s.put("batchMode", batchMode != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
|
|
@ -265,6 +265,10 @@ public class ApprovalStepService extends InternalPersistService {
|
||||||
if (goNextNode && (nextApprovers == null || nextNode == null)) {
|
if (goNextNode && (nextApprovers == null || nextNode == null)) {
|
||||||
super.update(recordOfMain);
|
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);
|
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, approver);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -460,6 +464,8 @@ public class ApprovalStepService extends InternalPersistService {
|
||||||
setApprovalLastX(recordOfMain, useApprover, Language.L("自动审批"));
|
setApprovalLastX(recordOfMain, useApprover, Language.L("自动审批"));
|
||||||
super.update(recordOfMain);
|
super.update(recordOfMain);
|
||||||
|
|
||||||
|
// NOTE 自动审批不会给提交人发通知
|
||||||
|
|
||||||
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, useApprover);
|
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, useApprover);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,11 @@ import com.rebuild.core.privileges.bizz.Department;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
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 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.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.service.query.QueryHelper;
|
import com.rebuild.core.service.query.QueryHelper;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -29,6 +31,7 @@ import java.util.List;
|
||||||
* @author devezhao zhaofang123@gmail.com
|
* @author devezhao zhaofang123@gmail.com
|
||||||
* @since 2019/06/24
|
* @since 2019/06/24
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class RobotApprovalManager implements ConfigManager {
|
public class RobotApprovalManager implements ConfigManager {
|
||||||
|
|
||||||
public static final RobotApprovalManager instance = new RobotApprovalManager();
|
public static final RobotApprovalManager instance = new RobotApprovalManager();
|
||||||
|
@ -42,16 +45,22 @@ public class RobotApprovalManager implements ConfigManager {
|
||||||
* 获取实体/记录流程状态
|
* 获取实体/记录流程状态
|
||||||
*
|
*
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param record
|
* @param recordId
|
||||||
* @return <tt>null</tt> 表示没有流程
|
* @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 (entity.getMainEntity() != null || !MetadataHelper.hasApprovalField(entity)) return null;
|
||||||
|
|
||||||
// 记录的
|
// 记录的
|
||||||
if (record != null) {
|
if (recordId != null) {
|
||||||
Object[] o = Application.getQueryFactory().unique(
|
if (!recordId.getEntityCode().equals(entity.getEntityCode())) {
|
||||||
record, EntityHelper.ApprovalId, EntityHelper.ApprovalState);
|
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) {
|
if (o != null && o[0] != null) {
|
||||||
return (ApprovalState) ApprovalState.valueOf((Integer) o[1]);
|
return (ApprovalState) ApprovalState.valueOf((Integer) o[1]);
|
||||||
}
|
}
|
||||||
|
@ -103,17 +112,17 @@ public class RobotApprovalManager implements ConfigManager {
|
||||||
/**
|
/**
|
||||||
* 获取用户可用流程
|
* 获取用户可用流程
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param user
|
* @param user
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public FlowDefinition[] getFlowDefinitions(ID record, ID user) {
|
public FlowDefinition[] getFlowDefinitions(ID recordId, ID user) {
|
||||||
FlowDefinition[] defs = getFlowDefinitions(MetadataHelper.getEntity(record.getEntityCode()));
|
FlowDefinition[] defs = getFlowDefinitions(MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||||
if (defs.length == 0) {
|
if (defs.length == 0) {
|
||||||
return new FlowDefinition[0];
|
return new FlowDefinition[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
ID owning = Application.getRecordOwningCache().getOwningUser(record);
|
ID owning = Application.getRecordOwningCache().getOwningUser(recordId);
|
||||||
// 过滤可用的
|
// 过滤可用的
|
||||||
List<FlowDefinition> workable = new ArrayList<>();
|
List<FlowDefinition> workable = new ArrayList<>();
|
||||||
for (FlowDefinition def : defs) {
|
for (FlowDefinition def : defs) {
|
||||||
|
@ -130,11 +139,11 @@ public class RobotApprovalManager implements ConfigManager {
|
||||||
|
|
||||||
if (FlowNode.USER_ALL.equals(users.getString(0))
|
if (FlowNode.USER_ALL.equals(users.getString(0))
|
||||||
|| (FlowNode.USER_OWNS.equals(users.getString(0)) && owning.equals(user))
|
|| (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");
|
JSONObject filter = root.getDataMap().getJSONObject("filter");
|
||||||
if (QueryHelper.isMatchAdvFilter(record, filter)) {
|
if (QueryHelper.isMatchAdvFilter(recordId, filter)) {
|
||||||
workable.add(def);
|
workable.add(def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,10 +62,10 @@ import java.util.List;
|
||||||
public class DataExporter extends SetUser {
|
public class DataExporter extends SetUser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 最大行数
|
* 最大导出行数
|
||||||
*/
|
*/
|
||||||
public static final int MAX_ROWS = 65535 - 1;
|
public static final int MAX_ROWS = 1000000;
|
||||||
|
|
||||||
final private JSONObject queryData;
|
final private JSONObject queryData;
|
||||||
// 字段
|
// 字段
|
||||||
private List<Field> headFields = new ArrayList<>();
|
private List<Field> headFields = new ArrayList<>();
|
||||||
|
|
|
@ -96,7 +96,13 @@ public class DataImporter extends HeavyTask<Integer> {
|
||||||
traceLogs.add(new Object[] { firstCell.getRowNo(), "SKIP" });
|
traceLogs.add(new Object[] { firstCell.getRowNo(), "SKIP" });
|
||||||
} else {
|
} 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) {
|
if (!isViaAdmin) {
|
||||||
String error = null;
|
String error = null;
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
|
|
|
@ -13,8 +13,7 @@ import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.support.RebuildConfiguration;
|
import com.rebuild.core.support.RebuildConfiguration;
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -31,10 +30,9 @@ import java.util.Set;
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
* @since 01/10/2019
|
* @since 01/10/2019
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class ImportRule {
|
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_UPDATE = 1;
|
||||||
public static final int REPEAT_OPT_SKIP = 2;
|
public static final int REPEAT_OPT_SKIP = 2;
|
||||||
public static final int REPEAT_OPT_IGNORE = 3;
|
public static final int REPEAT_OPT_IGNORE = 3;
|
||||||
|
@ -44,6 +42,7 @@ public class ImportRule {
|
||||||
|
|
||||||
private int repeatOpt;
|
private int repeatOpt;
|
||||||
private Field[] repeatFields;
|
private Field[] repeatFields;
|
||||||
|
private boolean onlyUpdate;
|
||||||
|
|
||||||
private ID defaultOwningUser;
|
private ID defaultOwningUser;
|
||||||
|
|
||||||
|
@ -54,15 +53,17 @@ public class ImportRule {
|
||||||
* @param toEntity
|
* @param toEntity
|
||||||
* @param repeatOpt
|
* @param repeatOpt
|
||||||
* @param repeatFields
|
* @param repeatFields
|
||||||
|
* @param onlyUpdate
|
||||||
* @param defaultOwningUser
|
* @param defaultOwningUser
|
||||||
* @param filedsMapping
|
* @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) {
|
Map<Field, Integer> filedsMapping) {
|
||||||
this.sourceFile = sourceFile;
|
this.sourceFile = sourceFile;
|
||||||
this.toEntity = toEntity;
|
this.toEntity = toEntity;
|
||||||
this.repeatOpt = repeatOpt;
|
this.repeatOpt = repeatOpt;
|
||||||
this.repeatFields = repeatFields;
|
this.repeatFields = repeatFields;
|
||||||
|
this.onlyUpdate = onlyUpdate;
|
||||||
this.defaultOwningUser = defaultOwningUser;
|
this.defaultOwningUser = defaultOwningUser;
|
||||||
this.filedsMapping = filedsMapping;
|
this.filedsMapping = filedsMapping;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +92,10 @@ public class ImportRule {
|
||||||
return filedsMapping;
|
return filedsMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOnlyUpdate() {
|
||||||
|
return onlyUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
// --
|
// --
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +124,7 @@ public class ImportRule {
|
||||||
throw new IllegalArgumentException("File not found : " + file, e);
|
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()) {
|
if (!file.exists()) {
|
||||||
throw new IllegalArgumentException("File not found : " + file);
|
throw new IllegalArgumentException("File not found : " + file);
|
||||||
|
@ -146,6 +151,7 @@ public class ImportRule {
|
||||||
filedsMapping.put(entity.getField(e.getKey()), (Integer) e.getValue());
|
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) {
|
while (num >= 0) {
|
||||||
int remainder = num % 26;
|
int remainder = num % 26;
|
||||||
name.insert(0, (char) (remainder + 65));
|
name.insert(0, (char) (remainder + 65));
|
||||||
//noinspection IntegerDivisionInFloatingPointContext
|
num = (int) (double) (num / 26) - 1;
|
||||||
num = (int) Math.floor(num / 26) - 1;
|
|
||||||
}
|
}
|
||||||
name.append(cell.getRowNo() + 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.ConfigManager;
|
||||||
import com.rebuild.core.configuration.ConfigurationException;
|
import com.rebuild.core.configuration.ConfigurationException;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.support.RebuildConfiguration;
|
import com.rebuild.core.support.RebuildConfiguration;
|
||||||
import com.rebuild.core.support.general.ContentWithFieldVars;
|
import com.rebuild.core.support.general.ContentWithFieldVars;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
@ -26,7 +27,9 @@ import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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_RECORD = 1;
|
||||||
public static final int TYPE_LIST = 2;
|
public static final int TYPE_LIST = 2;
|
||||||
|
public static final int TYPE_HTML5 = 3;
|
||||||
|
public static final int TYPE_WORD = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取报表列表
|
* 获取可用报表
|
||||||
*
|
*
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param type
|
* @param type 指定类型
|
||||||
|
* @param user
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public JSONArray getReports(Entity entity, int type) {
|
public JSONArray getReports(Entity entity, int type, ID user) {
|
||||||
JSONArray list = new JSONArray();
|
JSONArray alist = new JSONArray();
|
||||||
for (ConfigBean e : getReportsRaw(entity)) {
|
for (ConfigBean e : getReportsRaw(entity)) {
|
||||||
if (!e.getBoolean("disabled") && e.getInteger("type") == type) {
|
if (e.getBoolean("disabled")) continue;
|
||||||
list.add(e.toJSON("id", "name", "outputType"));
|
|
||||||
|
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
|
* @return
|
||||||
*/
|
*/
|
||||||
public ConfigBean[] getReportsRaw(Entity entity) {
|
public ConfigBean[] getReportsRaw(Entity entity) {
|
||||||
final String cKey = "DataReportManager33-" + entity.getName();
|
final String cKey = "DataReportManager35-" + entity.getName();
|
||||||
ConfigBean[] cached = (ConfigBean[]) Application.getCommonsCache().getx(cKey);
|
ConfigBean[] cached = (ConfigBean[]) Application.getCommonsCache().getx(cKey);
|
||||||
if (cached != null) {
|
if (cached != null) return cached;
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object[][] array = Application.createQueryNoFilter(
|
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())
|
.setParameter(1, entity.getName())
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
|
@ -83,15 +104,21 @@ public class DataReportManager implements ConfigManager {
|
||||||
JSONObject extra = o[5] == null ? JSONUtils.EMPTY_OBJECT : JSON.parseObject((String) o[5]);
|
JSONObject extra = o[5] == null ? JSONUtils.EMPTY_OBJECT : JSON.parseObject((String) o[5]);
|
||||||
String outputType = StringUtils.defaultIfBlank(extra.getString("outputType"), "excel");
|
String outputType = StringUtils.defaultIfBlank(extra.getString("outputType"), "excel");
|
||||||
int templateVersion = extra.containsKey("templateVersion") ? extra.getInteger("templateVersion") : 2;
|
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()
|
ConfigBean cb = new ConfigBean()
|
||||||
.set("id", o[0])
|
.set("id", o[0])
|
||||||
.set("name", o[1])
|
.set("name", o[1])
|
||||||
.set("disabled", o[2])
|
.set("disabled", o[2])
|
||||||
.set("template", o[3])
|
.set("template", o[3])
|
||||||
.set("type", ObjectUtils.toInt(o[4], TYPE_RECORD))
|
.set("type", type)
|
||||||
.set("outputType", outputType)
|
.set("outputType", outputType)
|
||||||
.set("templateVersion", templateVersion);
|
.set("templateVersion", templateVersion)
|
||||||
|
.set("visibleUsers", visibleUsersDef)
|
||||||
|
.set("templateContent", o[6]);
|
||||||
alist.add(cb);
|
alist.add(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,29 +133,36 @@ public class DataReportManager implements ConfigManager {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public TemplateFile getTemplateFile(Entity entity, ID reportId) {
|
public TemplateFile getTemplateFile(Entity entity, ID reportId) {
|
||||||
String template = null;
|
String templateFile = null;
|
||||||
boolean isList = false;
|
String templateContent = null;
|
||||||
|
int type = DataReportManager.TYPE_RECORD;
|
||||||
boolean isV33 = false;
|
boolean isV33 = false;
|
||||||
|
|
||||||
for (ConfigBean e : getReportsRaw(entity)) {
|
for (ConfigBean e : getReportsRaw(entity)) {
|
||||||
if (e.getID("id").equals(reportId)) {
|
if (e.getID("id").equals(reportId)) {
|
||||||
template = e.getString("template");
|
templateFile = e.getString("template");
|
||||||
isList = e.getInteger("type") == TYPE_LIST;
|
templateContent = e.getString("templateContent");
|
||||||
|
type = e.getInteger("type");
|
||||||
isV33 = e.getInteger("templateVersion") == 3;
|
isV33 = e.getInteger("templateVersion") == 3;
|
||||||
break;
|
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);
|
throw new ConfigurationException("No template of report found : " + reportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = RebuildConfiguration.getFileOfData(template);
|
File file = RebuildConfiguration.getFileOfData(templateFile);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
throw new ConfigurationException("File of template not extsts : " + file);
|
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) 性能好
|
* @see #getTemplateFile(Entity, ID) 性能好
|
||||||
*/
|
*/
|
||||||
public TemplateFile getTemplateFile(ID reportId) {
|
public TemplateFile getTemplateFile(ID reportId) {
|
||||||
Object[] report = Application.createQueryNoFilter(
|
Object[] o = Application.getQueryFactory().uniqueNoFilter(reportId, "belongEntity");
|
||||||
"select belongEntity from DataReportConfig where configId = ?")
|
if (o == null || !MetadataHelper.containsEntity((String) o[0])) {
|
||||||
.setParameter(1, reportId)
|
|
||||||
.unique();
|
|
||||||
if (report == null || !MetadataHelper.containsEntity((String) report[0])) {
|
|
||||||
throw new ConfigurationException("No config of report found : " + reportId);
|
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
|
@Override
|
||||||
public void clean(Object entity) {
|
public void clean(Object entity) {
|
||||||
final String cKey = "DataReportManager33-" + ((Entity) entity).getName();
|
final String cKey = "DataReportManager35-" + ((Entity) entity).getName();
|
||||||
Application.getCommonsCache().evict(cKey);
|
Application.getCommonsCache().evict(cKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取报表名称
|
* 获取报表名称
|
||||||
*
|
*
|
||||||
|
@ -163,7 +196,8 @@ public class DataReportManager implements ConfigManager {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getReportName(ID reportId, Object idOrEntity, String fileName) {
|
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);
|
: MetadataHelper.getEntity((String) idOrEntity);
|
||||||
|
|
||||||
String name = null;
|
String name = null;
|
||||||
|
@ -178,6 +212,7 @@ public class DataReportManager implements ConfigManager {
|
||||||
|
|
||||||
// suffix
|
// suffix
|
||||||
if (fileName.endsWith(".pdf")) name += ".pdf";
|
if (fileName.endsWith(".pdf")) name += ".pdf";
|
||||||
|
else if (fileName.endsWith(".docx")) name += ".docx";
|
||||||
else name += fileName.endsWith(".xlsx") ? ".xlsx" : ".xls";
|
else name += fileName.endsWith(".xlsx") ? ".xlsx" : ".xls";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ import static com.rebuild.core.service.datareport.TemplateExtractor.PLACEHOLDER;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EasyExcelGenerator extends SetUser {
|
public class EasyExcelGenerator extends SetUser {
|
||||||
|
|
||||||
protected File template;
|
protected File templateFile;
|
||||||
protected Integer writeSheetAt = null;
|
protected Integer writeSheetAt = null;
|
||||||
protected ID recordId;
|
protected ID recordId;
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
* @param recordId
|
* @param recordId
|
||||||
*/
|
*/
|
||||||
protected EasyExcelGenerator(File template, ID recordId) {
|
protected EasyExcelGenerator(File template, ID recordId) {
|
||||||
this.template = getFixTemplate(template);
|
this.templateFile = getFixTemplate(template);
|
||||||
this.recordId = recordId;
|
this.recordId = recordId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
|
|
||||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
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)
|
WriteSheet writeSheet = EasyExcel.writerSheet(writeSheetAt)
|
||||||
.registerWriteHandler(new FixsMergeStrategy())
|
.registerWriteHandler(new FixsMergeStrategy())
|
||||||
.registerWriteHandler(new FormulaCellWriteHandler())
|
.registerWriteHandler(new FormulaCellWriteHandler())
|
||||||
|
@ -150,8 +150,13 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected File getTargetFile() {
|
protected File getTargetFile() {
|
||||||
return RebuildConfiguration.getFileOfTemp(String.format("RBREPORT-%d.%s",
|
String suffix = "xls";
|
||||||
System.currentTimeMillis(), template.getName().endsWith(".xlsx") ? "xlsx" : "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 第一个为主记录(若有)
|
* @return 第一个为主记录(若有)
|
||||||
*/
|
*/
|
||||||
protected List<Map<String, Object>> buildData() {
|
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> varsMap = templateExtractor.transformVars(entity);
|
||||||
|
|
||||||
Map<String, String> varsMapOfMain = new HashMap<>();
|
Map<String, String> varsMapOfMain = new HashMap<>();
|
||||||
|
@ -193,7 +198,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
}
|
}
|
||||||
// 无效字段
|
// 无效字段
|
||||||
else if (validField == null) {
|
else if (validField == null) {
|
||||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), this.template);
|
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,9 +224,9 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
entity.getPrimaryField().getName(), entity.getName(), entity.getPrimaryField().getName());
|
entity.getPrimaryField().getName(), entity.getName(), entity.getPrimaryField().getName());
|
||||||
|
|
||||||
Record record = Application.createQuery(sql, this.getUser())
|
Record record = Application.createQuery(sql, this.getUser())
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, recordId)
|
||||||
.record();
|
.record();
|
||||||
Assert.notNull(record, "No record found : " + this.recordId);
|
Assert.notNull(record, "No record found : " + recordId);
|
||||||
|
|
||||||
datas.add(buildData(record, varsMapOfMain));
|
datas.add(buildData(record, varsMapOfMain));
|
||||||
this.hasMain = true;
|
this.hasMain = true;
|
||||||
|
@ -236,7 +241,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
MetadataHelper.getDetailToMainField(entity.getDetailEntity()).getName());
|
MetadataHelper.getDetailToMainField(entity.getDetailEntity()).getName());
|
||||||
|
|
||||||
List<Record> list = Application.createQuery(sql, this.getUser())
|
List<Record> list = Application.createQuery(sql, this.getUser())
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, recordId)
|
||||||
.list();
|
.list();
|
||||||
|
|
||||||
phNumber = 1;
|
phNumber = 1;
|
||||||
|
@ -253,7 +258,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
StringUtils.join(fieldsOfApproval, ","));
|
StringUtils.join(fieldsOfApproval, ","));
|
||||||
|
|
||||||
List<Record> list = Application.createQueryNoFilter(sql)
|
List<Record> list = Application.createQueryNoFilter(sql)
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, recordId)
|
||||||
.list();
|
.list();
|
||||||
|
|
||||||
phNumber = 1;
|
phNumber = 1;
|
||||||
|
@ -273,6 +278,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
*/
|
*/
|
||||||
protected Map<String, Object> buildData(Record record, Map<String, String> varsMap) {
|
protected Map<String, Object> buildData(Record record, Map<String, String> varsMap) {
|
||||||
final Entity entity = record.getEntity();
|
final Entity entity = record.getEntity();
|
||||||
|
final boolean isApproval = entity.getEntityCode() == EntityHelper.RobotApprovalStep;
|
||||||
|
|
||||||
final String invalidFieldTip = Language.L("[无效字段]");
|
final String invalidFieldTip = Language.L("[无效字段]");
|
||||||
final String unsupportFieldTip = Language.L("[暂不支持]");
|
final String unsupportFieldTip = Language.L("[暂不支持]");
|
||||||
|
@ -347,8 +353,12 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
fieldValue = FieldValueHelper.wrapFieldValue(fieldValue, easyField, Boolean.TRUE);
|
fieldValue = FieldValueHelper.wrapFieldValue(fieldValue, easyField, Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.getEntity().getEntityCode() == EntityHelper.RobotApprovalStep && "state".equalsIgnoreCase(fieldName)) {
|
if (isApproval && "state".equalsIgnoreCase(fieldName)) {
|
||||||
fieldValue = Language.L(ApprovalState.valueOf(ObjectUtils.toInt(fieldValue)));
|
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())) {
|
} else if (FieldValueHelper.isUseDesensitized(easyField, this.getUser())) {
|
||||||
fieldValue = FieldValueHelper.desensitized(easyField, fieldValue);
|
fieldValue = FieldValueHelper.desensitized(easyField, fieldValue);
|
||||||
}
|
}
|
||||||
|
@ -371,6 +381,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.put(varName, fieldValue);
|
data.put(varName, fieldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -519,7 +530,8 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
if (recordIds.size() == 1) {
|
if (recordIds.size() == 1) {
|
||||||
return create(reportId, recordId);
|
return create(reportId, recordId);
|
||||||
} else {
|
} 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);
|
return new EasyExcelGenerator33(tt.templateFile, recordIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,7 +542,8 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static EasyExcelGenerator create(ID reportId, ID recordId) {
|
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);
|
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;
|
package com.rebuild.core.service.datareport;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import cn.devezhao.persist4j.Entity;
|
import cn.devezhao.persist4j.Entity;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.privileges.UserService;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
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.Sheet;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||||
|
@ -28,6 +33,7 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -45,23 +51,23 @@ import static com.rebuild.core.service.datareport.TemplateExtractor33.DETAIL_PRE
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
|
|
||||||
final private List<ID> recordIds;
|
final private List<ID> recordIdMultiple;
|
||||||
|
|
||||||
protected EasyExcelGenerator33(File template, ID recordId) {
|
protected EasyExcelGenerator33(File templateFile, ID recordId) {
|
||||||
super(template, recordId);
|
super(templateFile, recordId);
|
||||||
this.recordIds = null;
|
this.recordIdMultiple = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected EasyExcelGenerator33(File template, List<ID> recordIds) {
|
protected EasyExcelGenerator33(File template, List<ID> recordIds) {
|
||||||
super(template, recordIds.get(0));
|
super(template, recordIds.get(0));
|
||||||
this.recordIds = recordIds;
|
this.recordIdMultiple = recordIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Map<String, Object>> buildData() {
|
protected List<Map<String, Object>> buildData() {
|
||||||
final Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
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);
|
final Map<String, String> varsMap = templateExtractor33.transformVars(entity);
|
||||||
|
|
||||||
// 变量
|
// 变量
|
||||||
|
@ -102,7 +108,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
if (TemplateExtractor33.isPlaceholder(varName)) continue;
|
if (TemplateExtractor33.isPlaceholder(varName)) continue;
|
||||||
// 无效字段
|
// 无效字段
|
||||||
if (fieldName == null) {
|
if (fieldName == null) {
|
||||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), template);
|
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +150,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
if (isApproval) {
|
if (isApproval) {
|
||||||
querySql += " and isWaiting = 'F' and isCanceled = 'F' order by createdOn";
|
querySql += " and isWaiting = 'F' and isCanceled = 'F' order by createdOn";
|
||||||
querySql = String.format(querySql, StringUtils.join(e.getValue(), ","),
|
querySql = String.format(querySql, StringUtils.join(e.getValue(), ","),
|
||||||
"stepId", "RobotApprovalStep", "recordId");
|
"createdOn,recordId,state,stepId", "RobotApprovalStep", "recordId");
|
||||||
|
|
||||||
} else if (refName.startsWith(DETAIL_PREFIX)) {
|
} else if (refName.startsWith(DETAIL_PREFIX)) {
|
||||||
Entity de = entity.getDetailEntity();
|
Entity de = entity.getDetailEntity();
|
||||||
|
@ -172,8 +178,30 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
.setParameter(1, recordId)
|
.setParameter(1, recordId)
|
||||||
.list();
|
.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;
|
phNumber = 1;
|
||||||
for (Record c : list) {
|
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)));
|
datas.add(buildData(c, varsMapOfRefs.get(refName)));
|
||||||
phNumber++;
|
phNumber++;
|
||||||
}
|
}
|
||||||
|
@ -182,21 +210,29 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
return datas;
|
return datas;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- V34 多个
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected TemplateExtractor33 buildTemplateExtractor33() {
|
||||||
|
return new TemplateExtractor33(templateFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- V34 支持多记录导出
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File generate() {
|
public File generate() {
|
||||||
if (recordIds == null) return super.generate();
|
if (recordIdMultiple == null) return super.generate();
|
||||||
|
|
||||||
// init
|
// init
|
||||||
File targetFile = super.getTargetFile();
|
File targetFile = super.getTargetFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.copyFile(this.template, targetFile);
|
FileUtils.copyFile(templateFile, targetFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ReportsException(e);
|
throw new ReportsException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ID recordId : this.recordIds) {
|
PrintSetup copyPrintSetup = null;
|
||||||
|
for (ID recordId : recordIdMultiple) {
|
||||||
int newSheetAt;
|
int newSheetAt;
|
||||||
try (Workbook wb = WorkbookFactory.create(Files.newInputStream(targetFile.toPath()))) {
|
try (Workbook wb = WorkbookFactory.create(Files.newInputStream(targetFile.toPath()))) {
|
||||||
// 1.复制模板
|
// 1.复制模板
|
||||||
|
@ -210,7 +246,15 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
wb.setSheetName(newSheetAt, newSheetName);
|
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)) {
|
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
|
||||||
wb.write(fos);
|
wb.write(fos);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +264,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成报表
|
// 生成报表
|
||||||
this.template = targetFile;
|
this.templateFile = targetFile;
|
||||||
this.writeSheetAt = newSheetAt;
|
this.writeSheetAt = newSheetAt;
|
||||||
this.recordId = recordId;
|
this.recordId = recordId;
|
||||||
this.hasMain = false;
|
this.hasMain = false;
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
||||||
@Override
|
@Override
|
||||||
protected List<Map<String, Object>> buildData() {
|
protected List<Map<String, Object>> buildData() {
|
||||||
Entity entity = MetadataHelper.getEntity(queryData.getString("entity"));
|
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);
|
Map<String, String> varsMap = varsExtractor.transformVars(entity);
|
||||||
|
|
||||||
List<String> validFields = new ArrayList<>();
|
List<String> validFields = new ArrayList<>();
|
||||||
|
@ -68,7 +68,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
||||||
if (validField != null && e.getKey().startsWith(NROW_PREFIX)) {
|
if (validField != null && e.getKey().startsWith(NROW_PREFIX)) {
|
||||||
validFields.add(validField);
|
validFields.add(validField);
|
||||||
} else {
|
} 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";
|
protected static final String PH__CURRENTDATETIME = PLACEHOLDER + "CURRENTDATETIME";
|
||||||
|
|
||||||
// v2:{xxx} v1:${xxx}
|
// 变量匹配 v2:{xxx} v1:${xxx}
|
||||||
protected static final Pattern PATT_V2 = Pattern.compile("\\{(.*?)}");
|
protected static final Pattern PATT_V2 = Pattern.compile("\\{(.*?)}");
|
||||||
|
|
||||||
final protected File template;
|
final protected File templateFile;
|
||||||
final private boolean isList;
|
final private boolean isListType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param template
|
* @param template
|
||||||
|
@ -69,12 +69,12 @@ public class TemplateExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param template
|
* @param templateFile
|
||||||
* @param isList 列表模板
|
* @param isList 列表模板
|
||||||
*/
|
*/
|
||||||
public TemplateExtractor(File template, boolean isList) {
|
public TemplateExtractor(File templateFile, boolean isList) {
|
||||||
this.template = template;
|
this.templateFile = templateFile;
|
||||||
this.isList = isList;
|
this.isListType = isList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +86,7 @@ public class TemplateExtractor {
|
||||||
public Map<String, String> transformVars(Entity entity) {
|
public Map<String, String> transformVars(Entity entity) {
|
||||||
final Set<String> vars = extractVars();
|
final Set<String> vars = extractVars();
|
||||||
|
|
||||||
Entity detailEntity = this.isList ? null : entity.getDetailEntity();
|
Entity detailEntity = this.isListType ? null : entity.getDetailEntity();
|
||||||
Entity approvalEntity = MetadataHelper.hasApprovalField(entity)
|
Entity approvalEntity = MetadataHelper.hasApprovalField(entity)
|
||||||
? MetadataHelper.getEntity(EntityHelper.RobotApprovalStep) : null;
|
? MetadataHelper.getEntity(EntityHelper.RobotApprovalStep) : null;
|
||||||
|
|
||||||
|
@ -135,13 +135,16 @@ public class TemplateExtractor {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected Set<String> extractVars() {
|
protected Set<String> extractVars() {
|
||||||
List<Cell[]> rows = ExcelUtils.readExcel(this.template);
|
List<Cell[]> rows = ExcelUtils.readExcel(templateFile);
|
||||||
|
|
||||||
Set<String> vars = new HashSet<>();
|
Set<String> vars = new HashSet<>();
|
||||||
for (Cell[] row : rows) {
|
for (Cell[] row : rows) {
|
||||||
for (Cell cell : row) {
|
for (Cell cell : row) {
|
||||||
if (cell.isEmpty()) continue;
|
if (cell.isEmpty()) continue;
|
||||||
|
|
||||||
|
// 变量套变量无法支持
|
||||||
|
// {.__KEEP:(=IF(ISBLANK({.LimitedCredit}), "", "{.LimitedCredit}天付款"))}
|
||||||
|
|
||||||
String cellText = cell.asString();
|
String cellText = cell.asString();
|
||||||
Matcher matcher = PATT_V2.matcher(cellText);
|
Matcher matcher = PATT_V2.matcher(cellText);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
|
|
@ -34,10 +34,10 @@ public class TemplateExtractor33 extends TemplateExtractor {
|
||||||
private Map<String, String> sortFields = new HashMap<>();
|
private Map<String, String> sortFields = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param template
|
* @param templateFile
|
||||||
*/
|
*/
|
||||||
public TemplateExtractor33(File template) {
|
public TemplateExtractor33(File templateFile) {
|
||||||
super(template, Boolean.FALSE);
|
super(templateFile, Boolean.FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,7 +57,7 @@ public class TemplateExtractor33 extends TemplateExtractor {
|
||||||
for (final String varName : vars) {
|
for (final String varName : vars) {
|
||||||
// 列表型字段
|
// 列表型字段
|
||||||
if (varName.startsWith(NROW_PREFIX)) {
|
if (varName.startsWith(NROW_PREFIX)) {
|
||||||
final String listField = varName.substring(1);
|
final String listField = varName.substring(1).replace("$", ".");
|
||||||
|
|
||||||
if (isPlaceholder(listField)) {
|
if (isPlaceholder(listField)) {
|
||||||
map.put(varName, null);
|
map.put(varName, null);
|
||||||
|
|
|
@ -18,14 +18,25 @@ import java.io.File;
|
||||||
public class TemplateFile {
|
public class TemplateFile {
|
||||||
|
|
||||||
final public File templateFile;
|
final public File templateFile;
|
||||||
|
final public String templateContent;
|
||||||
final public Entity entity;
|
final public Entity entity;
|
||||||
final public boolean isList;
|
final public int type;
|
||||||
final public boolean isV33;
|
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.templateFile = templateFile;
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
this.isList = isList;
|
this.type = type;
|
||||||
this.isV33 = isV33;
|
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.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.metadata.EntityHelper;
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.service.DataSpecificationException;
|
|
||||||
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
||||||
import com.rebuild.core.support.general.BatchOperatorQuery;
|
import com.rebuild.core.support.general.BatchOperatorQuery;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -27,18 +26,18 @@ import org.apache.commons.lang.StringUtils;
|
||||||
* @since 2019/12/2
|
* @since 2019/12/2
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@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);
|
super(context, ges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +83,7 @@ public class BulkBacthUpdate extends BulkOperator {
|
||||||
ges.createOrUpdate(record);
|
ges.createOrUpdate(record);
|
||||||
this.addSucceeded();
|
this.addSucceeded();
|
||||||
|
|
||||||
} catch (DataSpecificationException ex) {
|
} catch (Exception ex) {
|
||||||
log.warn("Cannot update `{}` because : {}", id, ex.getLocalizedMessage());
|
log.warn("Cannot update `{}` because : {}", id, ex.getLocalizedMessage());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -109,6 +108,6 @@ public class BulkBacthUpdate extends BulkOperator {
|
||||||
JSONObject customData = (JSONObject) context.getExtraParams().get("customData");
|
JSONObject customData = (JSONObject) context.getExtraParams().get("customData");
|
||||||
int dataRange = customData.getIntValue("_dataRange");
|
int dataRange = customData.getIntValue("_dataRange");
|
||||||
BatchOperatorQuery query = new BatchOperatorQuery(dataRange, customData.getJSONObject("queryData"));
|
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;
|
package com.rebuild.core.service.general;
|
||||||
|
|
||||||
import cn.devezhao.bizz.privileges.Permission;
|
|
||||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
@ -25,63 +24,56 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public interface EntityService extends ServiceSpec {
|
public interface EntityService extends ServiceSpec {
|
||||||
|
|
||||||
/**
|
|
||||||
* 取消共享,跟随共享权限
|
|
||||||
*
|
|
||||||
* @see BizzPermission
|
|
||||||
*/
|
|
||||||
Permission UNSHARE = new BizzPermission("UNSHARE", 1 << 6, true);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除(带级联)
|
* 删除(带级联)
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param cascades 需要级联删除的实体
|
* @param cascades 需要级联删除的实体
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int delete(ID record, String[] cascades);
|
int delete(ID recordId, String[] cascades);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分配
|
* 分配
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param to
|
* @param to
|
||||||
* @param cascades 需要级联分配的实体
|
* @param cascades 需要级联分配的实体
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int assign(ID record, ID to, String[] cascades);
|
int assign(ID recordId, ID to, String[] cascades);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 共享
|
* 共享
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param to
|
* @param to
|
||||||
* @param cascades
|
* @param cascades
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
default int share(ID record, ID to, String[] cascades) {
|
default int share(ID recordId, ID to, String[] cascades) {
|
||||||
return share(record, to, cascades, BizzPermission.READ.getMask());
|
return share(recordId, to, cascades, BizzPermission.READ.getMask());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 共享
|
* 共享
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param to
|
* @param to
|
||||||
* @param cascades 需要级联分配的实体
|
* @param cascades 需要级联分配的实体
|
||||||
* @param rights 共享权限
|
* @param rights 共享权限
|
||||||
* @return
|
* @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
|
* @param accessId 共享的 AccessID
|
||||||
* @return
|
* @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 checkRecord
|
||||||
* @param limit
|
* @param limit 最大查重返回数量
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<Record> getAndCheckRepeated(Record checkRecord, int limit);
|
List<Record> getAndCheckRepeated(Record checkRecord, int limit);
|
||||||
|
@ -112,11 +104,11 @@ public interface EntityService extends ServiceSpec {
|
||||||
/**
|
/**
|
||||||
* 审批
|
* 审批
|
||||||
*
|
*
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param state 只接受通过或撤销
|
* @param state 只接受通过或撤销
|
||||||
* @param approvalUser 审批人
|
* @param approvalUser 审批人
|
||||||
* @see com.rebuild.core.service.approval.ApprovalStepService
|
* @see com.rebuild.core.service.approval.ApprovalStepService
|
||||||
* @see com.rebuild.core.service.approval.ApprovalProcessor
|
* @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.Permission;
|
||||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||||
|
import cn.devezhao.commons.ReflectUtils;
|
||||||
import cn.devezhao.persist4j.Entity;
|
import cn.devezhao.persist4j.Entity;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.Filter;
|
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.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.MetadataSorter;
|
import com.rebuild.core.metadata.MetadataSorter;
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
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.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
|
@ -52,6 +52,7 @@ import com.rebuild.utils.CommonsUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Observer;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
@ -73,10 +75,8 @@ import java.util.TreeMap;
|
||||||
*
|
*
|
||||||
* <p>如有需要,其他实体可根据自身业务继承并复写</p>
|
* <p>如有需要,其他实体可根据自身业务继承并复写</p>
|
||||||
*
|
*
|
||||||
* FIXME 删除主记录时会关联删除明细记录(持久层实现),但明细记录不会触发业务规则
|
|
||||||
*
|
|
||||||
* @author Zixin (RB)
|
* @author Zixin (RB)
|
||||||
* @since 11/06/2017
|
* @since 11/06/2019
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@ -87,11 +87,18 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
|
|
||||||
protected GeneralEntityService(PersistManagerFactory aPMFactory) {
|
protected GeneralEntityService(PersistManagerFactory aPMFactory) {
|
||||||
super(aPMFactory);
|
super(aPMFactory);
|
||||||
|
}
|
||||||
|
|
||||||
// 通知
|
@Override
|
||||||
addObserver(new NotificationObserver());
|
protected Observer[] getOrderObservers() {
|
||||||
// 触发器
|
Observer[] obs = new Observer[] {
|
||||||
addObserver(new RobotTriggerObserver());
|
// 触发器
|
||||||
|
new RobotTriggerObserver(),
|
||||||
|
// 通知
|
||||||
|
new NotificationObserver(),
|
||||||
|
};
|
||||||
|
obs = ArrayUtils.addAll(obs, super.getOrderObservers());
|
||||||
|
return obs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,49 +126,68 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
// 含明细
|
// 含明细
|
||||||
final boolean hasDetails = details != null && !details.isEmpty();
|
final boolean hasDetails = details != null && !details.isEmpty();
|
||||||
|
|
||||||
boolean lazyAutoApprovalForDetails = false;
|
// 延迟执行触发器,因为明细尚未保存好
|
||||||
boolean lazyAutoTransformForDetails = false;
|
boolean lazyAutoApproval4Details = false;
|
||||||
|
boolean lazyAutoTransform4Details = false;
|
||||||
|
boolean lazyHookUrl4Details = false;
|
||||||
if (hasDetails) {
|
if (hasDetails) {
|
||||||
// fix: v3.2.2
|
|
||||||
|
// 自动审批 fix: v3.2.2
|
||||||
|
|
||||||
TriggerAction[] hasAutoApprovalTriggers = getSpecTriggers(
|
TriggerAction[] hasAutoApprovalTriggers = getSpecTriggers(
|
||||||
record.getEntity(), ActionType.AUTOAPPROVAL, TriggerWhen.CREATE, TriggerWhen.UPDATE);
|
record.getEntity(), ActionType.AUTOAPPROVAL, TriggerWhen.CREATE, TriggerWhen.UPDATE);
|
||||||
lazyAutoApprovalForDetails = hasAutoApprovalTriggers.length > 0;
|
lazyAutoApproval4Details = hasAutoApprovalTriggers.length > 0;
|
||||||
// FIXME 此判断可能无意义,待进一步测试后确定是否保留
|
// FIXME 此判断可能无意义,待进一步测试后确定是否保留
|
||||||
if (!lazyAutoApprovalForDetails) {
|
if (!lazyAutoApproval4Details) {
|
||||||
TriggerAction[] hasOnApprovedTriggers = getSpecTriggers(
|
TriggerAction[] hasOnApprovedTriggers = getSpecTriggers(
|
||||||
record.getEntity().getDetailEntity(), null, TriggerWhen.APPROVED);
|
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(
|
TriggerAction[] hasAutoTransformTriggers = getSpecTriggers(
|
||||||
record.getEntity(), ActionType.AUTOTRANSFORM, TriggerWhen.CREATE, TriggerWhen.UPDATE);
|
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<>();
|
Map<Integer, ID> detaileds = new TreeMap<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
record = record.getPrimary() == null ? create(record) : update(record);
|
record = record.getPrimary() == null ? create(record) : update(record);
|
||||||
if (!hasDetails) return 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 ID mainid = record.getPrimary();
|
||||||
|
|
||||||
final boolean checkDetailsRepeated = rcm == GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS
|
final boolean checkDetailsRepeated = rcm == GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS
|
||||||
|| rcm == GeneralEntityServiceContextHolder.RCM_CHECK_ALL;
|
|| 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++) {
|
for (int i = 0; i < details.size(); i++) {
|
||||||
Record d = details.get(i);
|
Record d = details.get(i);
|
||||||
if (d instanceof DeleteRecord) {
|
if (d instanceof DeleteRecord) {
|
||||||
delete(d.getPrimary());
|
des.delete(d.getPrimary());
|
||||||
detaileds.put(i, d.getPrimary());
|
detaileds.put(i, d.getPrimary());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +200,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
if (checkDetailsRepeated) {
|
if (checkDetailsRepeated) {
|
||||||
d.setID(dtfField, mainid); // for check
|
d.setID(dtfField, mainid); // for check
|
||||||
|
|
||||||
List<Record> repeated = getAndCheckRepeated(d, 20);
|
List<Record> repeated = des.getAndCheckRepeated(d, 20);
|
||||||
if (!repeated.isEmpty()) {
|
if (!repeated.isEmpty()) {
|
||||||
throw new RepeatedRecordsException(repeated);
|
throw new RepeatedRecordsException(repeated);
|
||||||
}
|
}
|
||||||
|
@ -182,9 +208,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
|
|
||||||
if (d.getPrimary() == null) {
|
if (d.getPrimary() == null) {
|
||||||
d.setID(dtfField, mainid);
|
d.setID(dtfField, mainid);
|
||||||
create(d);
|
des.create(d);
|
||||||
} else {
|
} else {
|
||||||
update(d);
|
des.update(d);
|
||||||
}
|
}
|
||||||
detaileds.put(i, d.getPrimary());
|
detaileds.put(i, d.getPrimary());
|
||||||
}
|
}
|
||||||
|
@ -193,12 +219,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
return record;
|
return record;
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (lazyAutoApprovalForDetails) {
|
if (lazyAutoApproval4Details) AutoApproval.executeLazy();
|
||||||
AutoApproval.executeLazyAutoApproval();
|
if (lazyAutoTransform4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#executeLazy");
|
||||||
}
|
if (lazyHookUrl4Details) CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.HookUrl#executeLazy");
|
||||||
if (lazyAutoTransformForDetails) {
|
|
||||||
CommonsUtils.invokeMethod("com.rebuild.rbv.trigger.AutoTransform#executeLazyAutoTransform");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,7 +477,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
|
|
||||||
if (countObservers() > 0) {
|
if (countObservers() > 0) {
|
||||||
setChanged();
|
setChanged();
|
||||||
notifyObservers(OperatingContext.create(currentUser, UNSHARE, unsharedBefore, null));
|
notifyObservers(OperatingContext.create(currentUser, InternalPermission.UNSHARE, unsharedBefore, null));
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -550,11 +573,15 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
return new BulkAssign(context, this);
|
return new BulkAssign(context, this);
|
||||||
} else if (context.getAction() == BizzPermission.SHARE) {
|
} else if (context.getAction() == BizzPermission.SHARE) {
|
||||||
return new BulkShare(context, this);
|
return new BulkShare(context, this);
|
||||||
} else if (context.getAction() == UNSHARE) {
|
} else if (context.getAction() == InternalPermission.UNSHARE) {
|
||||||
return new BulkUnshare(context, this);
|
return new BulkUnshare(context, this);
|
||||||
} else if (context.getAction() == BizzPermission.UPDATE) {
|
} 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());
|
throw new UnsupportedOperationException("Unsupported bulk action : " + context.getAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,10 +700,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
EasyField easyField = EasyMetaFactory.valueOf(field);
|
Object defaultValue = EasyMetaFactory.valueOf(field).exprDefaultValue();
|
||||||
if (easyField.getDisplayType() == DisplayType.SERIES) continue;
|
|
||||||
|
|
||||||
Object defaultValue = easyField.exprDefaultValue();
|
|
||||||
if (defaultValue != null) {
|
if (defaultValue != null) {
|
||||||
recordOfNew.setObjectValue(field.getName(), defaultValue);
|
recordOfNew.setObjectValue(field.getName(), defaultValue);
|
||||||
}
|
}
|
||||||
|
@ -831,4 +855,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
}
|
}
|
||||||
return specTriggers.toArray(new TriggerAction[0]);
|
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) {
|
protected ObservableService(PersistManagerFactory aPMFactory) {
|
||||||
this.delegateService = new BaseService(aPMFactory);
|
this.delegateService = new BaseService(aPMFactory);
|
||||||
|
|
||||||
// 默认监听者
|
for (Observer o : getOrderObservers()) {
|
||||||
addObserver(new RevisionHistoryObserver());
|
log.info("Add observer : {} for [ {} ] ", o, getEntityCode());
|
||||||
addObserver(new AttachmentAwareObserver());
|
addObserver(o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public synchronized void addObserver(Observer o) {
|
* @return
|
||||||
super.addObserver(o);
|
*/
|
||||||
log.info("Add observer : {} for [ {} ] ", o, getEntityCode());
|
protected Observer[] getOrderObservers() {
|
||||||
|
// 默认监听者
|
||||||
|
return new Observer[] {
|
||||||
|
new RevisionHistoryObserver(), // 2
|
||||||
|
new AttachmentAwareObserver(), // 1
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,7 +93,8 @@ public abstract class ObservableService extends Observable implements ServiceSpe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(ID recordId) {
|
public int delete(ID recordId) {
|
||||||
final ID currentUser = UserContextHolder.getUser();
|
ID currentUser = UserContextHolder.getRestoreUser();
|
||||||
|
if (currentUser == null) currentUser = UserContextHolder.getUser();
|
||||||
|
|
||||||
Record deleted = null;
|
Record deleted = null;
|
||||||
if (countObservers() > 0) {
|
if (countObservers() > 0) {
|
||||||
|
|
|
@ -11,6 +11,8 @@ import cn.devezhao.bizz.privileges.Permission;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.rebuild.core.UserContextHolder;
|
import com.rebuild.core.UserContextHolder;
|
||||||
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
|
import com.rebuild.core.service.trigger.ActionContext;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +49,7 @@ public class OperatingContext {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.beforeRecord = beforeRecord;
|
this.beforeRecord = beforeRecord;
|
||||||
this.afterRecord = afterRecord;
|
this.afterRecord = afterRecord;
|
||||||
this.affected = affected == null ? new ID[]{getAnyRecord().getPrimary()} : affected;
|
this.affected = affected == null ? new ID[]{ getFixedRecordId() } : affected;
|
||||||
this.operationIp = operationIp;
|
this.operationIp = operationIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,12 +82,31 @@ public class OperatingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* NOTE!!! 请注意当共享时得到的是共享实体 Record
|
||||||
|
* 如果是为了获取源纪录 ID 推荐使用 {@link #getFixedRecordId()}
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Record getAnyRecord() {
|
public Record getAnyRecord() {
|
||||||
return getAfterRecord() != null ? getAfterRecord() : getBeforeRecord();
|
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);
|
onAssign(ctx);
|
||||||
} else if (ctx.getAction() == BizzPermission.SHARE) {
|
} else if (ctx.getAction() == BizzPermission.SHARE) {
|
||||||
onShare(ctx);
|
onShare(ctx);
|
||||||
} else if (ctx.getAction() == EntityService.UNSHARE) {
|
} else if (ctx.getAction() == InternalPermission.UNSHARE) {
|
||||||
onUnshare(ctx);
|
onUnshare(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.service.general;
|
package com.rebuild.core.service.general;
|
||||||
|
|
||||||
import cn.devezhao.commons.ObjectUtils;
|
|
||||||
import cn.devezhao.persist4j.Entity;
|
import cn.devezhao.persist4j.Entity;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
|
@ -19,11 +18,11 @@ import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.RebuildException;
|
import com.rebuild.core.RebuildException;
|
||||||
import com.rebuild.core.metadata.EntityHelper;
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 两个 Record 的不同
|
* 两个 Record 的不同
|
||||||
|
@ -52,6 +51,13 @@ public class RecordDifference {
|
||||||
return diffMerge(after, false);
|
return diffMerge(after, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取不同
|
||||||
|
*
|
||||||
|
* @param after
|
||||||
|
* @param diffCommons
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
protected JSON diffMerge(Record after, boolean diffCommons) {
|
protected JSON diffMerge(Record after, boolean diffCommons) {
|
||||||
if (record == null && after == null) {
|
if (record == null && after == null) {
|
||||||
throw new RebuildException("Both records cannot be null");
|
throw new RebuildException("Both records cannot be null");
|
||||||
|
@ -67,26 +73,28 @@ public class RecordDifference {
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
JSONObject recordSerialize = (JSONObject) record.serialize();
|
JSONObject recordSerialize = (JSONObject) record.serialize();
|
||||||
for (Map.Entry<String, Object> e : recordSerialize.entrySet()) {
|
for (Map.Entry<String, Object> e : recordSerialize.entrySet()) {
|
||||||
String field = e.getKey();
|
String fieldName = e.getKey();
|
||||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
if (!entity.containsField(fieldName)) continue;
|
||||||
|
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||||
|
|
||||||
Object beforeVal = e.getValue();
|
Object beforeVal = e.getValue();
|
||||||
if (NullValue.is(beforeVal)) beforeVal = null;
|
if (NullValue.is(beforeVal)) beforeVal = null;
|
||||||
|
|
||||||
merged.put(field, new Object[]{beforeVal, null});
|
merged.put(fieldName, new Object[]{beforeVal, null});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (after != null) {
|
if (after != null) {
|
||||||
JSONObject afterSerialize = (JSONObject) after.serialize();
|
JSONObject afterSerialize = (JSONObject) after.serialize();
|
||||||
for (Map.Entry<String, Object> e : afterSerialize.entrySet()) {
|
for (Map.Entry<String, Object> e : afterSerialize.entrySet()) {
|
||||||
String field = e.getKey();
|
String fieldName = e.getKey();
|
||||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
if (!entity.containsField(fieldName)) continue;
|
||||||
|
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||||
|
|
||||||
Object afterVal = e.getValue();
|
Object afterVal = e.getValue();
|
||||||
if (NullValue.is(afterVal)) continue;
|
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;
|
mergedValue[1] = afterVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +104,7 @@ public class RecordDifference {
|
||||||
for (Map.Entry<String, Object[]> e : merged.entrySet()) {
|
for (Map.Entry<String, Object[]> e : merged.entrySet()) {
|
||||||
Object[] vals = e.getValue();
|
Object[] vals = e.getValue();
|
||||||
if (vals[0] == null && vals[1] == null) continue;
|
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(
|
JSON item = JSONUtils.toJSONObject(
|
||||||
new String[]{"field", "before", "after"},
|
new String[]{"field", "before", "after"},
|
||||||
|
@ -107,10 +115,10 @@ public class RecordDifference {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否不同
|
* 是否相同
|
||||||
*
|
*
|
||||||
* @param diff
|
* @param diff
|
||||||
* @param diffCommons
|
* @param diffCommons 是否比较系统共用字段
|
||||||
* @return
|
* @return
|
||||||
* @see #diffMerge(Record)
|
* @see #diffMerge(Record)
|
||||||
*/
|
*/
|
||||||
|
@ -135,21 +143,4 @@ public class RecordDifference {
|
||||||
|| field.getType() == FieldType.PRIMARY
|
|| field.getType() == FieldType.PRIMARY
|
||||||
|| MetadataHelper.isApprovalField(fieldName);
|
|| 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) {
|
private boolean isIgnore(OperatingContext ctx) {
|
||||||
int entity = ctx.getAnyRecord().getEntity().getEntityCode();
|
int entity = ctx.getFixedRecordId().getEntityCode();
|
||||||
return entity == EntityHelper.FeedsComment || entity == EntityHelper.ProjectTaskComment;
|
return entity == EntityHelper.FeedsComment || entity == EntityHelper.ProjectTaskComment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +130,8 @@ public class RevisionHistoryObserver extends OperatingObserver {
|
||||||
TriggerSource triggerSource = RobotTriggerObserver.getTriggerSource();
|
TriggerSource triggerSource = RobotTriggerObserver.getTriggerSource();
|
||||||
if (triggerSource != null) {
|
if (triggerSource != null) {
|
||||||
record.setID("channelWith", triggerSource.getOriginRecord());
|
record.setID("channelWith", triggerSource.getOriginRecord());
|
||||||
|
// v35 系统用户
|
||||||
|
record.setID("revisionBy", UserService.SYSTEM_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.getOperationIp() != null) {
|
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.RebuildConfiguration;
|
||||||
import com.rebuild.core.support.distributed.DistributedJobLock;
|
import com.rebuild.core.support.distributed.DistributedJobLock;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.NamedThreadLocal;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -109,19 +110,52 @@ public class RecycleBinCleanerJob extends DistributedJobLock {
|
||||||
Application.getSqlExecutor().execute(delSql, 60 * 3);
|
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
|
* @return
|
||||||
|
* @see #setSkipRecyclebinOnce()
|
||||||
*/
|
*/
|
||||||
public static boolean isEnableRecycleBin() {
|
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
|
* @return
|
||||||
|
* @see #setSkipRevisionHistoryOnce()
|
||||||
*/
|
*/
|
||||||
public static boolean isEnableRevisionHistory() {
|
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.ConfigurationException;
|
||||||
import com.rebuild.core.configuration.general.TransformManager;
|
import com.rebuild.core.configuration.general.TransformManager;
|
||||||
import com.rebuild.core.metadata.EntityHelper;
|
import com.rebuild.core.metadata.EntityHelper;
|
||||||
|
import com.rebuild.core.metadata.EntityRecordCreator;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
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.general.GeneralEntityServiceContextHolder;
|
||||||
import com.rebuild.core.service.query.FilterRecordChecker;
|
import com.rebuild.core.service.query.FilterRecordChecker;
|
||||||
import com.rebuild.core.support.SetUser;
|
import com.rebuild.core.support.SetUser;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.utils.JSONUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -95,11 +96,11 @@ public class RecordTransfomer extends SetUser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param sourceRecordId
|
* @param sourceRecordId
|
||||||
* @param mainId
|
* @param specMainId 转换明细时需指定主记录 ID
|
||||||
* @return
|
* @return
|
||||||
* @see #checkFilter(ID)
|
* @see #checkFilter(ID)
|
||||||
*/
|
*/
|
||||||
public ID transform(ID sourceRecordId, ID mainId) {
|
public ID transform(ID sourceRecordId, ID specMainId) {
|
||||||
// 检查配置
|
// 检查配置
|
||||||
Entity sourceEntity = MetadataHelper.getEntity(sourceRecordId.getEntityCode());
|
Entity sourceEntity = MetadataHelper.getEntity(sourceRecordId.getEntityCode());
|
||||||
Entity sourceDetailEntity = null;
|
Entity sourceDetailEntity = null;
|
||||||
|
@ -132,13 +133,22 @@ public class RecordTransfomer extends SetUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> dvMap = null;
|
Map<String, Object> dvMap = null;
|
||||||
if (mainId != null) {
|
if (specMainId != null) {
|
||||||
Field targetDtf = MetadataHelper.getDetailToMainField(targetEntity);
|
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);
|
// v3.5 此配置未开放
|
||||||
ID newId;
|
// 在之前的版本中,虽然文档写明非空字段无值会转换失败,但是从来没有做过非空检查
|
||||||
|
// 为保持兼容性,此选项不启用,即入参保持为 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) {
|
if (sourceDetails != null && sourceDetails.length > 0) {
|
||||||
|
@ -146,18 +156,18 @@ public class RecordTransfomer extends SetUser {
|
||||||
List<Record> detailsList = new ArrayList<>();
|
List<Record> detailsList = new ArrayList<>();
|
||||||
for (Object[] d : sourceDetails) {
|
for (Object[] d : sourceDetails) {
|
||||||
detailsList.add(
|
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 {
|
} 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) {
|
private ID saveRecord(Record record, List<Record> detailsList) {
|
||||||
|
@ -198,7 +208,7 @@ public class RecordTransfomer extends SetUser {
|
||||||
|
|
||||||
// 此配置未开放
|
// 此配置未开放
|
||||||
int fillbackMode = transConfig.getIntValue("fillbackMode");
|
int fillbackMode = transConfig.getIntValue("fillbackMode");
|
||||||
if (fillbackMode == 2) {
|
if (fillbackMode == 2 && !EntityHelper.isUnsavedId(newId)) {
|
||||||
GeneralEntityServiceContextHolder.setAllowForceUpdate(updateSource.getPrimary());
|
GeneralEntityServiceContextHolder.setAllowForceUpdate(updateSource.getPrimary());
|
||||||
try {
|
try {
|
||||||
Application.getEntityService(sourceEntity.getEntityCode()).update(updateSource);
|
Application.getEntityService(sourceEntity.getEntityCode()).update(updateSource);
|
||||||
|
@ -206,7 +216,7 @@ public class RecordTransfomer extends SetUser {
|
||||||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FIXME 回填仅更新,无业务规则
|
// 无传播更新
|
||||||
Application.getCommonsService().update(updateSource, false);
|
Application.getCommonsService().update(updateSource, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,19 +224,21 @@ public class RecordTransfomer extends SetUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换
|
* 转换 Record
|
||||||
*
|
*
|
||||||
* @param sourceEntity
|
* @param sourceEntity
|
||||||
* @param targetEntity
|
* @param targetEntity
|
||||||
* @param fieldsMapping
|
* @param fieldsMapping
|
||||||
* @param sourceRecordId
|
* @param sourceRecordId
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @param ignoreUncreateable
|
* @param ignoreUncreateable 忽略不可新建字段
|
||||||
|
* @param forceNullValue v3.5 强制设定空字段值(更新记录时)
|
||||||
|
* @param checkNullable v3.5 检查不允许为空的字段是否都有值
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected Record transformRecord(
|
protected Record transformRecord(
|
||||||
Entity sourceEntity, Entity targetEntity, JSONObject fieldsMapping,
|
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());
|
Record target = EntityHelper.forNew(targetEntity.getEntityCode(), getUser());
|
||||||
|
|
||||||
|
@ -249,9 +261,13 @@ public class RecordTransfomer extends SetUser {
|
||||||
ID specOwningUser = null;
|
ID specOwningUser = null;
|
||||||
|
|
||||||
for (Map.Entry<String, Object> e : fieldsMapping.entrySet()) {
|
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));
|
EasyField targetFieldEasy = EasyMetaFactory.valueOf(targetEntity.getField(targetField));
|
||||||
if (ignoreUncreateable && !targetFieldEasy.isCreatable()) continue;
|
if (ignoreUncreateable && !targetFieldEasy.isCreatable()) continue;
|
||||||
|
|
||||||
|
@ -271,6 +287,9 @@ public class RecordTransfomer extends SetUser {
|
||||||
|
|
||||||
Object targetValue = sourceFieldEasy.convertCompatibleValue(sourceValue, targetFieldEasy);
|
Object targetValue = sourceFieldEasy.convertCompatibleValue(sourceValue, targetFieldEasy);
|
||||||
target.setObjectValue(targetField, targetValue);
|
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());
|
(ID) Application.getUserStore().getUser(specOwningUser).getOwningDept().getIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkNullable) {
|
||||||
|
new EntityRecordCreator(targetEntity, JSONUtils.EMPTY_OBJECT, getUser()).verify(target);
|
||||||
|
}
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class TransformerPreview {
|
||||||
try {
|
try {
|
||||||
for (ID did : ids) {
|
for (ID did : ids) {
|
||||||
Record targetRecord = transfomer.transformRecord(
|
Record targetRecord = transfomer.transformRecord(
|
||||||
sourceEntity, targetEntity, fieldsMapping, did, null, true);
|
sourceEntity, targetEntity, fieldsMapping, did, null, true, false, false);
|
||||||
|
|
||||||
fillLabelOfReference(targetRecord);
|
fillLabelOfReference(targetRecord);
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ public class TransformerPreview {
|
||||||
}
|
}
|
||||||
|
|
||||||
Record targetRecord = transfomer.transformRecord(
|
Record targetRecord = transfomer.transformRecord(
|
||||||
sourceEntity, targetEntity, fieldsMapping, sourceId, null, true);
|
sourceEntity, targetEntity, fieldsMapping, sourceId, null, true, false, false);
|
||||||
fillLabelOfReference(targetRecord);
|
fillLabelOfReference(targetRecord);
|
||||||
|
|
||||||
// 转为明细
|
// 转为明细
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.regex.Pattern;
|
||||||
*
|
*
|
||||||
* @author devezhao zhaofang123@gmail.com
|
* @author devezhao zhaofang123@gmail.com
|
||||||
* @since 2019/07/12
|
* @since 2019/07/12
|
||||||
|
* @see com.rebuild.core.support.general.ContentWithFieldVars
|
||||||
*/
|
*/
|
||||||
public class MessageBuilder {
|
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.metadata.impl.EasyFieldConfigProps;
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.privileges.bizz.Department;
|
import com.rebuild.core.privileges.bizz.Department;
|
||||||
|
import com.rebuild.core.support.License;
|
||||||
import com.rebuild.core.support.SetUser;
|
import com.rebuild.core.support.SetUser;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import com.rebuild.utils.CommonsUtils;
|
import com.rebuild.utils.CommonsUtils;
|
||||||
|
@ -108,12 +109,12 @@ public class AdvFilterParser extends SetUser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param filterExpr
|
* @param filterExpr
|
||||||
* @param varRecord 条件中包含字段变量,将从此记录中提取实际值。注意如果包括字段变量但是字段无值,则过滤项会被忽略,应在配置条件时考虑此问题(设置不允许为空)
|
* @param varRecord 条件中包含字段变量,将从该记录中提取实际值替换
|
||||||
*/
|
*/
|
||||||
public AdvFilterParser(JSONObject filterExpr, ID varRecord) {
|
public AdvFilterParser(JSONObject filterExpr, ID varRecord) {
|
||||||
this.filterExpr = filterExpr;
|
this.filterExpr = filterExpr;
|
||||||
this.rootEntity = MetadataHelper.getEntity(varRecord.getEntityCode());
|
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 PATT_FIELDVAR = "\\{@([\\w.]+)}";
|
||||||
// `当前`变量(当前日期、时间、用户)
|
// `当前`变量(当前日期、时间、用户)
|
||||||
private static final String CURRENT_ANY = "CURRENT";
|
private static final String CURRENT_ANY = "CURRENT";
|
||||||
|
private static final String CURRENT_DATE = "NOW";
|
||||||
|
|
||||||
private String useValueOfVarRecord(String value, Field queryField) {
|
private String useValueOfVarRecord(String value, Field queryField) {
|
||||||
if (StringUtils.isBlank(value) || !value.matches(PATT_FIELDVAR)) return value;
|
if (StringUtils.isBlank(value) || !value.matches(PATT_FIELDVAR)) return value;
|
||||||
|
@ -712,7 +714,7 @@ public class AdvFilterParser extends SetUser {
|
||||||
Object useValue = null;
|
Object useValue = null;
|
||||||
|
|
||||||
// {@CURRENT} DATE
|
// {@CURRENT} DATE
|
||||||
if (CURRENT_ANY.equals(fieldName)) {
|
if (CURRENT_ANY.equals(fieldName) || CURRENT_DATE.equals(fieldName)) {
|
||||||
DisplayType dt = EasyMetaFactory.getDisplayType(queryField);
|
DisplayType dt = EasyMetaFactory.getDisplayType(queryField);
|
||||||
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME || dt == DisplayType.TIME) {
|
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME || dt == DisplayType.TIME) {
|
||||||
useValue = CalendarUtils.now();
|
useValue = CalendarUtils.now();
|
||||||
|
|
|
@ -229,6 +229,9 @@ public class ParseHelper {
|
||||||
|| dt == DisplayType.LOCATION) {
|
|| dt == DisplayType.LOCATION) {
|
||||||
return StringUtils.defaultIfEmpty(fieldPath, field.getName());
|
return StringUtils.defaultIfEmpty(fieldPath, field.getName());
|
||||||
|
|
||||||
|
} else if (dt == DisplayType.ANYREFERENCE) {
|
||||||
|
return fieldPath;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,15 +30,15 @@ import java.util.List;
|
||||||
* 查询服务
|
* 查询服务
|
||||||
*
|
*
|
||||||
* @author Zixin (RB)
|
* @author Zixin (RB)
|
||||||
* @since 05/21/2017
|
* @since 05/21/2019
|
||||||
* @see RoleBaseQueryFilter
|
* @see RoleBaseQueryFilter
|
||||||
* @see QueryHelper
|
* @see QueryHelper
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class QueryFactory {
|
public class QueryFactory {
|
||||||
|
|
||||||
private static final int QUERY_TIMEOUT = 10 * 1000;
|
private static final int QUERY_TIMEOUT = 15; // s
|
||||||
private static final int SLOW_LOGGER_TIME = 1000;
|
private static final int SLOW_LOGGER_TIME = 3 * 1000; // ms
|
||||||
|
|
||||||
private final PersistManagerFactory aPMFactory;
|
private final PersistManagerFactory aPMFactory;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.springframework.util.Assert;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ public class QueryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取明细(完整)记录
|
* 获取明细列表记录
|
||||||
*
|
*
|
||||||
* @param mainId
|
* @param mainId
|
||||||
* @return
|
* @return
|
||||||
|
@ -129,7 +128,7 @@ public class QueryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取明细 ID
|
* 获取明细列表 ID
|
||||||
*
|
*
|
||||||
* @param mainId
|
* @param mainId
|
||||||
* @return
|
* @return
|
||||||
|
@ -217,12 +216,9 @@ public class QueryHelper {
|
||||||
final ID primaryId = base.getPrimary();
|
final ID primaryId = base.getPrimary();
|
||||||
Assert.notNull(primaryId, "Record primary cannot be null");
|
Assert.notNull(primaryId, "Record primary cannot be null");
|
||||||
|
|
||||||
Set<String> fields = new HashSet<>();
|
Set<String> fields = new HashSet<>(base.getAvailableFields());
|
||||||
for (Iterator<String> iter = base.getAvailableFieldIterator(); iter.hasNext(); ) {
|
|
||||||
fields.add(iter.next());
|
|
||||||
}
|
|
||||||
|
|
||||||
fields.add(base.getEntity().getPrimaryField().getName());
|
fields.add(base.getEntity().getPrimaryField().getName());
|
||||||
|
|
||||||
Record snap = Application.getQueryFactory().recordNoFilter(primaryId, fields.toArray(new String[0]));
|
Record snap = Application.getQueryFactory().recordNoFilter(primaryId, fields.toArray(new String[0]));
|
||||||
|
|
||||||
if (snap == null) throw new NoRecordFoundException(primaryId);
|
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
|
* @param when
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public TriggerAction[] getActions(ID record, TriggerWhen... when) {
|
public TriggerAction[] getActions(ID recordId, TriggerWhen... when) {
|
||||||
return filterActions(MetadataHelper.getEntity(record.getEntityCode()), record, 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);
|
private static final ThreadLocal<List<String>> TRIGGERS_CHAIN_4DEBUG = ThreadLocal.withInitial(ArrayList::new);
|
||||||
/**
|
/**
|
||||||
* @param record
|
* @param recordId
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param when
|
* @param when
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private TriggerAction[] filterActions(Entity entity, ID record, TriggerWhen... when) {
|
private TriggerAction[] filterActions(Entity entity, ID recordId, TriggerWhen... when) {
|
||||||
List<TriggerAction> actions = new ArrayList<>();
|
List<TriggerAction> actions = new ArrayList<>();
|
||||||
for (ConfigBean cb : getConfig(entity)) {
|
for (ConfigBean cb : getConfig(entity)) {
|
||||||
// 发生动作
|
// 发生动作
|
||||||
if (allowedWhen(cb, when)) {
|
if (allowedWhen(cb, when)) {
|
||||||
// 附加过滤条件
|
// 附加过滤条件
|
||||||
if (record == null
|
if (recordId == null
|
||||||
|| QueryHelper.isMatchAdvFilter(record, (JSONObject) cb.getJSON("whenFilter"), Boolean.TRUE)) {
|
|| 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);
|
TriggerAction o = ActionFactory.createAction(cb.getString("actionType"), ctx);
|
||||||
if (o.getClass().getName().contains("NoRbv")) {
|
if (o.getClass().getName().contains("NoRbv")) {
|
||||||
log.warn("Trigger action {} is RBV", cb.getString("actionType"));
|
log.warn("Trigger action {} is RBV", cb.getString("actionType"));
|
||||||
|
@ -86,7 +86,7 @@ public class RobotTriggerManager implements ConfigManager {
|
||||||
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
TRIGGERS_CHAIN_4DEBUG.get().add(
|
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()) {
|
if (!TRIGGERS_CHAIN_4DEBUG.get().isEmpty()) {
|
||||||
log.info("Record ({}) triggers chain : \n > {}",
|
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]);
|
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;
|
package com.rebuild.core.service.trigger;
|
||||||
|
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
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.OperatingContext;
|
||||||
import com.rebuild.core.service.general.OperatingObserver;
|
import com.rebuild.core.service.general.OperatingObserver;
|
||||||
import com.rebuild.core.service.general.RepeatedRecordsException;
|
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.CommonsLog;
|
||||||
import com.rebuild.core.support.general.FieldValueHelper;
|
import com.rebuild.core.support.general.FieldValueHelper;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
|
import com.rebuild.web.KnownExceptionConverter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.core.NamedThreadLocal;
|
import org.springframework.core.NamedThreadLocal;
|
||||||
|
@ -76,7 +76,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDeleteBefore(OperatingContext context) {
|
protected void onDeleteBefore(OperatingContext context) {
|
||||||
final ID primary = context.getAnyRecord().getPrimary();
|
final ID primary = context.getFixedRecordId();
|
||||||
|
|
||||||
TriggerAction[] deleteActions = RobotTriggerManager.instance.getActions(primary, TriggerWhen.DELETE);
|
TriggerAction[] deleteActions = RobotTriggerManager.instance.getActions(primary, TriggerWhen.DELETE);
|
||||||
for (TriggerAction action : deleteActions) {
|
for (TriggerAction action : deleteActions) {
|
||||||
|
@ -94,7 +94,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDelete(OperatingContext context) {
|
protected void onDelete(OperatingContext context) {
|
||||||
final ID primary = context.getAnyRecord().getPrimary();
|
final ID primary = context.getFixedRecordId();
|
||||||
try {
|
try {
|
||||||
execAction(context, TriggerWhen.DELETE);
|
execAction(context, TriggerWhen.DELETE);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -109,11 +109,11 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
* @param when
|
* @param when
|
||||||
*/
|
*/
|
||||||
protected void execAction(OperatingContext context, TriggerWhen when) {
|
protected void execAction(OperatingContext context, TriggerWhen when) {
|
||||||
final ID primaryId = context.getAnyRecord().getPrimary();
|
final ID primaryId = context.getFixedRecordId();
|
||||||
|
|
||||||
TriggerAction[] beExecuted = when == TriggerWhen.DELETE
|
TriggerAction[] beExecuted = when == TriggerWhen.DELETE
|
||||||
? DELETE_BEFORE_HOLD.get(primaryId)
|
? DELETE_BEFORE_HOLD.get(primaryId)
|
||||||
: RobotTriggerManager.instance.getActions(getRealRecordId(context), when);
|
: RobotTriggerManager.instance.getActions(context.getFixedRecordId(), when);
|
||||||
if (beExecuted == null || beExecuted.length == 0) return;
|
if (beExecuted == null || beExecuted.length == 0) return;
|
||||||
|
|
||||||
TriggerSource triggerSource = getTriggerSource();
|
TriggerSource triggerSource = getTriggerSource();
|
||||||
|
@ -186,7 +186,8 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
if (ex instanceof TriggerException) {
|
if (ex instanceof TriggerException) {
|
||||||
throw (TriggerException) ex;
|
throw (TriggerException) ex;
|
||||||
} else {
|
} else {
|
||||||
String errMsg = ex.getLocalizedMessage();
|
String errMsg = KnownExceptionConverter.convert2ErrorMsg(ex);
|
||||||
|
if (errMsg == null) errMsg = ex.getLocalizedMessage();
|
||||||
if (ex instanceof RepeatedRecordsException) errMsg = Language.L("存在重复记录");
|
if (ex instanceof RepeatedRecordsException) errMsg = Language.L("存在重复记录");
|
||||||
if (StringUtils.isBlank(errMsg)) errMsg = ex.getClass().getSimpleName().toUpperCase();
|
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 SOURCE_SELF = "$PRIMARY$";
|
||||||
|
/**
|
||||||
|
* 任意实体(字段匹配)
|
||||||
|
*/
|
||||||
|
public static final String TARGET_ANY = "$";
|
||||||
|
|
||||||
final protected ActionContext actionContext;
|
final protected ActionContext actionContext;
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,15 @@ public class TriggerResult implements JSONAware {
|
||||||
return new TriggerResult(1, message, null);
|
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
|
* @param message
|
||||||
* @return
|
* @return
|
||||||
|
@ -144,4 +153,13 @@ public class TriggerResult implements JSONAware {
|
||||||
public static TriggerResult noUpdateFields() {
|
public static TriggerResult noUpdateFields() {
|
||||||
return wran("No update fields");
|
return wran("No update fields");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无效配置
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TriggerResult badConfig() {
|
||||||
|
return wran("Bad config");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class TriggerSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ID getOriginRecord() {
|
public ID getOriginRecord() {
|
||||||
return getOrigin().getAnyRecord().getPrimary();
|
return getOrigin().getFixedRecordId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperatingContext getLast() {
|
public OperatingContext getLast() {
|
||||||
|
@ -66,7 +66,7 @@ public class TriggerSource {
|
||||||
|
|
||||||
public String getLastSourceKey() {
|
public String getLastSourceKey() {
|
||||||
Object[] last = sources.get(sources.size() - 1);
|
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() {
|
public void setSkipOnce() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
package com.rebuild.core.service.trigger;
|
package com.rebuild.core.service.trigger;
|
||||||
|
|
||||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
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 {
|
public class AviatorDate extends AviatorObject {
|
||||||
private static final long serialVersionUID = 2930549924386648595L;
|
private static final long serialVersionUID = 2930549924386648595L;
|
||||||
|
|
||||||
protected static final String DU_YEAR = "Y";
|
public static final String DU_YEAR = "Y";
|
||||||
protected static final String DU_MONTH = "M";
|
public static final String DU_MONTH = "M";
|
||||||
protected static final String DU_DAY = "D";
|
public static final String DU_DAY = "D";
|
||||||
protected static final String DU_HOUR = "H";
|
public static final String DU_HOUR = "H";
|
||||||
protected static final String DU_MINUTE = "I";
|
public static final String DU_MINUTE = "I";
|
||||||
protected static final String DU_SECOND = "S";
|
public static final String DU_SECOND = "S";
|
||||||
|
|
||||||
final private Date dateValue;
|
final private Date dateValue;
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ public class AviatorUtils {
|
||||||
addCustomFunction(new CurrentDateFunction());
|
addCustomFunction(new CurrentDateFunction());
|
||||||
addCustomFunction(new ChineseYuanFunction());
|
addCustomFunction(new ChineseYuanFunction());
|
||||||
addCustomFunction(new TextFunction());
|
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;
|
package com.rebuild.core.service.trigger.aviator;
|
||||||
|
|
||||||
|
import cn.devezhao.persist4j.Entity;
|
||||||
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorJavaType;
|
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.AviatorObject;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorString;
|
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.core.support.general.FieldValueHelper;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
|
import com.rebuild.web.commons.MetadataGetting;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage: TEXT($id|$id[], [$defaultValue], [$separator])
|
* Usage: TEXT($id|$id[], [$defaultValue], [$separator], [$fieldName])
|
||||||
* Return: String
|
* Return: String
|
||||||
*
|
*
|
||||||
* @author RB
|
* @author RB
|
||||||
|
@ -37,37 +45,62 @@ public class TextFunction extends AbstractFunction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
|
public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
|
||||||
return call(env, arg1, BLANK, SEPARATOR);
|
return call(env, arg1, BLANK, SEPARATOR, AviatorNil.NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
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
|
@Override
|
||||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
|
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 $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
|
// 引用 ID
|
||||||
if (ID.isId($id)) {
|
if (ID.isId($id)) {
|
||||||
ID anyid = $id instanceof ID ? (ID) $id : ID.valueOf($id.toString());
|
ID anyid = $id instanceof ID ? (ID) $id : ID.valueOf($id.toString());
|
||||||
String text = FieldValueHelper.getLabel(anyid, null);
|
String text = getLabel(anyid, fieldName);
|
||||||
return text == null ? arg2 : new AviatorString(text);
|
|
||||||
|
if (text == null && $defaultValue != null) text = $defaultValue.toString();
|
||||||
|
return new AviatorString(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 多引用 ID[]
|
// 多引用 ID[]
|
||||||
if ($id instanceof ID[]) {
|
|
||||||
List<String> texts = new ArrayList<>();
|
Object idArray = $id;
|
||||||
for (ID anyid : (ID[]) $id) {
|
if ($id instanceof Collection) {
|
||||||
String t = FieldValueHelper.getLabel(anyid, null);
|
List<ID> list = null;
|
||||||
if (t != null) texts.add(t);
|
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();
|
if (idArray instanceof ID[]) {
|
||||||
return new AviatorString(StringUtils.join(texts, sep));
|
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) {
|
if (arg1 instanceof AviatorJavaType) {
|
||||||
|
@ -81,6 +114,20 @@ public class TextFunction extends AbstractFunction {
|
||||||
return arg2;
|
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
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "TEXT";
|
return "TEXT";
|
||||||
|
|
|
@ -28,9 +28,11 @@ import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -69,9 +71,11 @@ public class AggregationEvaluator {
|
||||||
if ("FORMULA".equalsIgnoreCase(calcMode)) {
|
if ("FORMULA".equalsIgnoreCase(calcMode)) {
|
||||||
return evalFormula();
|
return evalFormula();
|
||||||
}
|
}
|
||||||
// ONLY FieldAggregation
|
// for FieldAggregation/GroupAggregation
|
||||||
else if ("RBJOIN".equalsIgnoreCase(calcMode)) {
|
else if ("RBJOIN".equalsIgnoreCase(calcMode)
|
||||||
return evalRbJoin();
|
|| "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");
|
String sourceField = item.getString("sourceField");
|
||||||
|
@ -193,9 +197,10 @@ public class AggregationEvaluator {
|
||||||
/**
|
/**
|
||||||
* 智能连接
|
* 智能连接
|
||||||
*
|
*
|
||||||
|
* @param mode 去重模式
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Object evalRbJoin() {
|
private Object evalRbJoin(int mode) {
|
||||||
String sourceField = item.getString("sourceField");
|
String sourceField = item.getString("sourceField");
|
||||||
Field field;
|
Field field;
|
||||||
if ((field = MetadataHelper.getLastJoinField(sourceEntity, sourceField)) == null) {
|
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",
|
String ql = String.format("select %s,%s from %s where %s",
|
||||||
sourceField, sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), filterSql);
|
sourceField, sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), filterSql);
|
||||||
Object[][] array = Application.createQueryNoFilter(ql).array();
|
Object[][] array = Application.createQueryNoFilter(ql).array();
|
||||||
|
if (array.length == 0) return new Object[0];
|
||||||
|
|
||||||
EasyField easyField = EasyMetaFactory.valueOf(field);
|
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) {
|
for (Object[] o : array) {
|
||||||
Object n = o[0];
|
Object n = o[0];
|
||||||
if (n == null) continue;
|
if (n == null) continue;
|
||||||
|
@ -216,21 +231,54 @@ public class AggregationEvaluator {
|
||||||
if (n instanceof ID[]) {
|
if (n instanceof ID[]) {
|
||||||
CollectionUtils.addAll(nvList, (ID[]) n);
|
CollectionUtils.addAll(nvList, (ID[]) n);
|
||||||
} else if (n instanceof ID) {
|
} else if (n instanceof ID) {
|
||||||
nvList.add(n);
|
if (field.getType() == FieldType.PRIMARY) {
|
||||||
|
nvList.add(n.toString()); // 保持主键
|
||||||
|
} else {
|
||||||
|
nvList.add(n);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Object v = easyField.wrapValue(n);
|
Object v = easyField.wrapValue(n);
|
||||||
if (v == null) continue;
|
if (v == null) continue;
|
||||||
|
|
||||||
if (easyField.getDisplayType() == DisplayType.MULTISELECT) {
|
DisplayType dt = easyField.getDisplayType();
|
||||||
|
if (dt == DisplayType.MULTISELECT) {
|
||||||
JSONArray a = ((JSONObject) v).getJSONArray("text");
|
JSONArray a = ((JSONObject) v).getJSONArray("text");
|
||||||
CollectionUtils.addAll(nvList, a);
|
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 {
|
} else {
|
||||||
|
// TEXT
|
||||||
nvList.add(v);
|
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
|
// 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.general.OperatingContext;
|
||||||
import com.rebuild.core.service.trigger.ActionContext;
|
import com.rebuild.core.service.trigger.ActionContext;
|
||||||
import com.rebuild.core.service.trigger.ActionType;
|
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.TriggerAction;
|
||||||
import com.rebuild.core.service.trigger.TriggerException;
|
import com.rebuild.core.service.trigger.TriggerException;
|
||||||
import com.rebuild.core.service.trigger.TriggerResult;
|
import com.rebuild.core.service.trigger.TriggerResult;
|
||||||
|
@ -37,10 +38,11 @@ import static com.rebuild.core.support.CommonsLog.TYPE_TRIGGER;
|
||||||
* @since 2020/7/31
|
* @since 2020/7/31
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@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 static final ThreadLocal<List<AutoApproval>> LAZY_AUTOAPPROVAL = new NamedThreadLocal<>("Lazy AutoApproval");
|
||||||
private OperatingContext operatingContext;
|
private OperatingContext keepOperatingContext;
|
||||||
|
|
||||||
public AutoApproval(ActionContext context) {
|
public AutoApproval(ActionContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -58,19 +60,19 @@ public class AutoApproval extends TriggerAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
this.operatingContext = operatingContext;
|
this.keepOperatingContext = operatingContext;
|
||||||
List<AutoApproval> lazyed;
|
List<AutoApproval> lazyed;
|
||||||
if ((lazyed = isLazyAutoApproval(Boolean.FALSE)) != null) {
|
if ((lazyed = isLazy(false)) != null) {
|
||||||
lazyed.add(this);
|
lazyed.add(this);
|
||||||
log.info("Lazy AutoApproval : {}", lazyed);
|
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");
|
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;
|
ID approvalId = ID.isId(useApproval) ? ID.valueOf(useApproval) : null;
|
||||||
|
|
||||||
// v2.10
|
// v2.10
|
||||||
|
@ -89,24 +91,23 @@ public class AutoApproval extends TriggerAction {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String s = super.toString();
|
String s = super.toString();
|
||||||
if (operatingContext != null) s += "#OperatingContext:" + operatingContext;
|
if (keepOperatingContext != null) s += "#OperatingContext:" + keepOperatingContext;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --
|
// --
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳过自动审批
|
|
||||||
* @see #isLazyAutoApproval(boolean)
|
|
||||||
*/
|
*/
|
||||||
public static void setLazyAutoApproval() {
|
public static void setLazy() {
|
||||||
LAZY_AUTOAPPROVAL.set(new ArrayList<>());
|
LAZY_AUTOAPPROVAL.set(new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param once
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static List<AutoApproval> isLazyAutoApproval(boolean once) {
|
public static List<AutoApproval> isLazy(boolean once) {
|
||||||
List<AutoApproval> lazyed = LAZY_AUTOAPPROVAL.get();
|
List<AutoApproval> lazyed = LAZY_AUTOAPPROVAL.get();
|
||||||
if (lazyed != null && once) LAZY_AUTOAPPROVAL.remove();
|
if (lazyed != null && once) LAZY_AUTOAPPROVAL.remove();
|
||||||
return lazyed;
|
return lazyed;
|
||||||
|
@ -115,12 +116,12 @@ public class AutoApproval extends TriggerAction {
|
||||||
/**
|
/**
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static int executeLazyAutoApproval() {
|
public static int executeLazy() {
|
||||||
List<AutoApproval> lazyed = isLazyAutoApproval(Boolean.TRUE);
|
List<AutoApproval> lazyed = isLazy(true);
|
||||||
if (lazyed != null) {
|
if (lazyed != null) {
|
||||||
for (AutoApproval a : lazyed) {
|
for (AutoApproval a : lazyed) {
|
||||||
log.info("Lazy AutoApproval execute : {}", a);
|
log.info("Lazy AutoApproval execute : {}", a);
|
||||||
Object res = a.execute(a.operatingContext);
|
Object res = a.execute(a.keepOperatingContext);
|
||||||
|
|
||||||
CommonsLog.createLog(TYPE_TRIGGER,
|
CommonsLog.createLog(TYPE_TRIGGER,
|
||||||
UserService.SYSTEM_USER, a.getActionContext().getConfigId(), res.toString());
|
UserService.SYSTEM_USER, a.getActionContext().getConfigId(), res.toString());
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class AutoAssign extends TriggerAction {
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
final ID recordId = operatingContext.getFixedRecordId();
|
||||||
|
|
||||||
JSONArray assignTo = content.getJSONArray("assignTo");
|
JSONArray assignTo = content.getJSONArray("assignTo");
|
||||||
Set<ID> toUsers = UserHelper.parseUsers(assignTo, recordId, true);
|
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.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
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.ApprovalHelper;
|
||||||
import com.rebuild.core.service.approval.ApprovalState;
|
import com.rebuild.core.service.approval.ApprovalState;
|
||||||
import com.rebuild.core.service.general.OperatingContext;
|
import com.rebuild.core.service.general.OperatingContext;
|
||||||
|
@ -46,7 +47,7 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
||||||
protected void prepare(OperatingContext operatingContext) throws TriggerException {
|
protected void prepare(OperatingContext operatingContext) throws TriggerException {
|
||||||
if (operatingContext.getAction() == InternalPermission.DELETE_BEFORE) {
|
if (operatingContext.getAction() == InternalPermission.DELETE_BEFORE) {
|
||||||
willRecords = getRelatedRecords(
|
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) {
|
protected Set<ID> getWillRecords(OperatingContext operatingContext) {
|
||||||
if (willRecords == null) {
|
if (willRecords == null) {
|
||||||
willRecords = getRelatedRecords(
|
willRecords = getRelatedRecords(
|
||||||
actionContext, operatingContext.getAnyRecord().getPrimary());
|
actionContext, operatingContext.getFixedRecordId());
|
||||||
}
|
}
|
||||||
return willRecords;
|
return willRecords;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Field refto : fieldsRefto) {
|
for (Field refto : fieldsRefto) {
|
||||||
Entity ownEntity = refto.getOwnEntity();
|
final Entity ownEntity = refto.getOwnEntity();
|
||||||
String sql = String.format("select %s from %s where ",
|
String sql = String.format("select %s from %s where ",
|
||||||
ownEntity.getPrimaryField().getName(), ownEntity.getName());
|
ownEntity.getPrimaryField().getName(), ownEntity.getName());
|
||||||
|
|
||||||
|
@ -153,8 +154,13 @@ public abstract class AutoHoldTriggerAction extends TriggerAction {
|
||||||
* @see ApprovalHelper#getApprovalState(ID)
|
* @see ApprovalHelper#getApprovalState(ID)
|
||||||
*/
|
*/
|
||||||
protected static ApprovalState getApprovalState(ID recordId) {
|
protected static ApprovalState getApprovalState(ID recordId) {
|
||||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
if (MetadataHelper.hasApprovalField(MetadataHelper.getEntity(recordId.getEntityCode()))) {
|
||||||
if (MetadataHelper.hasApprovalField(entity)) return ApprovalHelper.getApprovalState(recordId);
|
try {
|
||||||
else return null;
|
return ApprovalHelper.getApprovalState(recordId);
|
||||||
|
} catch (NoRecordFoundException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class AutoShare extends TriggerAction {
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||||
final ID recordId = operatingContext.getAnyRecord().getPrimary();
|
final ID recordId = operatingContext.getFixedRecordId();
|
||||||
|
|
||||||
JSONArray shareTo = content.getJSONArray("shareTo");
|
JSONArray shareTo = content.getJSONArray("shareTo");
|
||||||
Set<ID> toUsers = UserHelper.parseUsers(shareTo, recordId, true);
|
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.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
|
@ -43,7 +44,9 @@ import org.apache.commons.lang.StringUtils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字段聚合,场景 N>1
|
* 字段聚合,场景 N>1
|
||||||
|
@ -139,7 +142,7 @@ public class FieldAggregation extends TriggerAction {
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
final String chainName = String.format("%s:%s:%s", actionContext.getConfigId(),
|
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);
|
final List<String> tschain = checkTriggerChain(chainName);
|
||||||
if (tschain == null) return TriggerResult.triggerOnce();
|
if (tschain == null) return TriggerResult.triggerOnce();
|
||||||
|
|
||||||
|
@ -159,7 +162,7 @@ public class FieldAggregation extends TriggerAction {
|
||||||
JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter");
|
JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter");
|
||||||
String dataFilterSql = null;
|
String dataFilterSql = null;
|
||||||
if (dataFilter != null && !dataFilter.isEmpty()) {
|
if (dataFilter != null && !dataFilter.isEmpty()) {
|
||||||
dataFilterSql = new AdvFilterParser(dataFilter).toSqlWhere();
|
dataFilterSql = new AdvFilterParser(dataFilter, operatingContext.getFixedRecordId()).toSqlWhere();
|
||||||
}
|
}
|
||||||
|
|
||||||
String filterSql = followSourceWhere;
|
String filterSql = followSourceWhere;
|
||||||
|
@ -190,13 +193,13 @@ public class FieldAggregation extends TriggerAction {
|
||||||
if (evalValue instanceof Date) targetRecord.setDate(targetField, (Date) evalValue);
|
if (evalValue instanceof Date) targetRecord.setDate(targetField, (Date) evalValue);
|
||||||
else targetRecord.setNull(targetField);
|
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;
|
Object[] oArray = (Object[]) evalValue;
|
||||||
|
|
||||||
if (oArray.length == 0) {
|
if (oArray.length == 0) {
|
||||||
targetRecord.setNull(targetField);
|
targetRecord.setNull(targetField);
|
||||||
} else if (dt == DisplayType.NTEXT) {
|
} else if (dt == DisplayType.NTEXT) {
|
||||||
// 使用文本
|
// ID 则使用文本
|
||||||
if (oArray[0] instanceof ID) {
|
if (oArray[0] instanceof ID) {
|
||||||
List<String> labelList = new ArrayList<>();
|
List<String> labelList = new ArrayList<>();
|
||||||
for (Object id : oArray) {
|
for (Object id : oArray) {
|
||||||
|
@ -207,10 +210,16 @@ public class FieldAggregation extends TriggerAction {
|
||||||
|
|
||||||
String join = StringUtils.join(oArray, ", ");
|
String join = StringUtils.join(oArray, ", ");
|
||||||
targetRecord.setString(targetField, join);
|
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 {
|
} else {
|
||||||
List<ID> idList = new ArrayList<>();
|
String join = JSON.toJSONString(oArray);
|
||||||
for (Object id : oArray) idList.add((ID) id);
|
targetRecord.setString(targetField, join);
|
||||||
targetRecord.setIDArray(targetField, idList.toArray(new ID[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,6 +302,13 @@ public class FieldAggregation extends TriggerAction {
|
||||||
targetEntity = MetadataHelper.getEntity(targetFieldEntity[1]);
|
targetEntity = MetadataHelper.getEntity(targetFieldEntity[1]);
|
||||||
|
|
||||||
String followSourceField = targetFieldEntity[0];
|
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)) {
|
if (!sourceEntity.containsField(followSourceField)) {
|
||||||
throw new MissingMetaExcetion(followSourceField, sourceEntity.getName());
|
throw new MissingMetaExcetion(followSourceField, sourceEntity.getName());
|
||||||
}
|
}
|
||||||
|
@ -328,8 +344,7 @@ public class FieldAggregation extends TriggerAction {
|
||||||
protected boolean isCurrentSame(Record record) {
|
protected boolean isCurrentSame(Record record) {
|
||||||
if (!ignoreSame) return false;
|
if (!ignoreSame) return false;
|
||||||
|
|
||||||
Record c = Application.getQueryFactory().recordNoFilter(
|
Record c = QueryHelper.querySnap(record);
|
||||||
record.getPrimary(), record.getAvailableFields().toArray(new String[0]));
|
|
||||||
return new RecordDifference(record).isSame(c, false);
|
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.privileges.UserService;
|
||||||
import com.rebuild.core.service.general.OperatingContext;
|
import com.rebuild.core.service.general.OperatingContext;
|
||||||
import com.rebuild.core.service.trigger.ActionContext;
|
import com.rebuild.core.service.trigger.ActionContext;
|
||||||
|
import com.rebuild.core.service.trigger.TriggerAction;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,9 +37,7 @@ public class FieldAggregationRefresh {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
if (operatingContext.getBeforeRecord() == null || operatingContext.getAfterRecord() == null) {
|
if (operatingContext.getBeforeRecord() == null || operatingContext.getAfterRecord() == null) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID triggerUser = UserService.SYSTEM_USER;
|
ID triggerUser = UserService.SYSTEM_USER;
|
||||||
ActionContext parentAc = parent.getActionContext();
|
ActionContext parentAc = parent.getActionContext();
|
||||||
|
@ -47,6 +46,11 @@ public class FieldAggregationRefresh {
|
||||||
String[] targetFieldEntity = ((JSONObject) parentAc.getActionContent()).getString("targetEntity").split("\\.");
|
String[] targetFieldEntity = ((JSONObject) parentAc.getActionContent()).getString("targetEntity").split("\\.");
|
||||||
String followSourceField = targetFieldEntity[0];
|
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 beforeValue = operatingContext.getBeforeRecord().getID(followSourceField);
|
||||||
final ID afterValue = operatingContext.getAfterRecord().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.core.support.general.N2NReferenceSupport;
|
||||||
import com.rebuild.utils.CommonsUtils;
|
import com.rebuild.utils.CommonsUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
final String chainName = String.format("%s:%s:%s", actionContext.getConfigId(),
|
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);
|
final List<String> tschain = checkTriggerChain(chainName);
|
||||||
if (tschain == null) return TriggerResult.triggerOnce();
|
if (tschain == null) return TriggerResult.triggerOnce();
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
for (ID targetRecordId : targetRecordIds) {
|
for (ID targetRecordId : targetRecordIds) {
|
||||||
// 删除时无需更新自己
|
// 删除时无需更新自己
|
||||||
if (operatingContext.getAction() == BizzPermission.DELETE
|
if (operatingContext.getAction() == BizzPermission.DELETE
|
||||||
&& targetRecordId.equals(operatingContext.getAnyRecord().getPrimary())) {
|
&& targetRecordId.equals(operatingContext.getFixedRecordId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,8 +201,14 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
|
|
||||||
targetRecordIds = new HashSet<>();
|
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());
|
targetRecordIds.add(actionContext.getSourceRecord());
|
||||||
}
|
}
|
||||||
// 1:1
|
// 1:1
|
||||||
|
@ -247,13 +254,13 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
// N:N v3.1
|
// N:N v3.1
|
||||||
Field targetField = targetEntity.getField(targetFieldEntity[0]);
|
Field targetField = targetEntity.getField(targetFieldEntity[0]);
|
||||||
if (targetField.getType() == FieldType.REFERENCE_LIST) {
|
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);
|
targetRecordIds.addAll(set);
|
||||||
} else {
|
} else {
|
||||||
String sql = String.format("select %s from %s where %s = ?",
|
String sql = String.format("select %s from %s where %s = ?",
|
||||||
targetEntity.getPrimaryField().getName(), targetFieldEntity[1], targetFieldEntity[0]);
|
targetEntity.getPrimaryField().getName(), targetFieldEntity[1], targetFieldEntity[0]);
|
||||||
Object[][] array = Application.createQueryNoFilter(sql)
|
Object[][] array = Application.createQueryNoFilter(sql)
|
||||||
.setParameter(1, operatingContext.getAnyRecord().getPrimary())
|
.setParameter(1, operatingContext.getFixedRecordId())
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
for (Object[] o : array) {
|
for (Object[] o : array) {
|
||||||
|
@ -461,7 +468,13 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
} else if (dt == DisplayType.DECIMAL) {
|
} else if (dt == DisplayType.DECIMAL) {
|
||||||
targetRecord.setDouble(targetField, ObjectUtils.toDouble(newValue));
|
targetRecord.setDouble(targetField, ObjectUtils.toDouble(newValue));
|
||||||
} else if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
} 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 {
|
} else {
|
||||||
newValue = checkoutFieldValue(newValue, targetFieldEasy);
|
newValue = checkoutFieldValue(newValue, targetFieldEasy);
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
|
|
|
@ -33,7 +33,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.util.Assert;
|
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
|
* 分组聚合,场景 N[+N]>1
|
||||||
|
@ -137,6 +141,7 @@ public class GroupAggregation extends FieldAggregation {
|
||||||
? null : operatingContext.getBeforeRecord().getObjectValue(sourceField);
|
? null : operatingContext.getBeforeRecord().getObjectValue(sourceField);
|
||||||
Object afterValue = operatingContext.getAfterRecord().getObjectValue(sourceField);
|
Object afterValue = operatingContext.getAfterRecord().getObjectValue(sourceField);
|
||||||
if (NullValue.isNull(beforeValue) && NullValue.isNull(afterValue)) {
|
if (NullValue.isNull(beforeValue) && NullValue.isNull(afterValue)) {
|
||||||
|
// All null
|
||||||
} else {
|
} else {
|
||||||
valueChanged.put(sourceField, new Object[] { beforeValue, afterValue });
|
valueChanged.put(sourceField, new Object[] { beforeValue, afterValue });
|
||||||
}
|
}
|
||||||
|
@ -205,7 +210,7 @@ public class GroupAggregation extends FieldAggregation {
|
||||||
|
|
||||||
// 需要匹配等级的值
|
// 需要匹配等级的值
|
||||||
if (sourceFieldLevel != targetFieldLevel) {
|
if (sourceFieldLevel != targetFieldLevel) {
|
||||||
ID parent = getItemWithLevel((ID) val, targetFieldLevel);
|
ID parent = TargetWithMatchFields.getItemWithLevel((ID) val, targetFieldLevel);
|
||||||
Assert.isTrue(parent != null, Language.L("分类字段等级不兼容"));
|
Assert.isTrue(parent != null, Language.L("分类字段等级不兼容"));
|
||||||
|
|
||||||
val = parent;
|
val = parent;
|
||||||
|
@ -231,7 +236,7 @@ public class GroupAggregation extends FieldAggregation {
|
||||||
if (!valueChanged.isEmpty()) {
|
if (!valueChanged.isEmpty()) {
|
||||||
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
|
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
|
||||||
} else {
|
} else {
|
||||||
log.warn("All group-fields are null, ignored");
|
log.warn("All values of group-fields are null, ignored");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -283,21 +288,4 @@ public class GroupAggregation extends FieldAggregation {
|
||||||
|
|
||||||
targetRecordId = newTargetRecord.getPrimary();
|
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 MTYPE_DINGTALK = 5; // 钉钉群
|
||||||
private static final int UTYPE_USER = 1; // 内部用户
|
private static final int UTYPE_USER = 1; // 内部用户
|
||||||
private static final int UTYPE_ACCOUNT = 2; // 外部人员
|
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_WXWORK = 4; // 企微群
|
||||||
private static final int UTYPE_DINGTALK = 5; // 钉钉群
|
private static final int UTYPE_DINGTALK = 5; // 钉钉群
|
||||||
|
|
||||||
|
@ -87,8 +88,8 @@ public class SendNotification extends TriggerAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Object> s;
|
Set<Object> s;
|
||||||
if (userType == UTYPE_ACCOUNT) {
|
if (userType == UTYPE_ACCOUNT || userType == UTYPE_ACCOUNT20) {
|
||||||
s = sendToAccounts(operatingContext);
|
s = sendToAccounts(operatingContext, userType);
|
||||||
} else if (userType == UTYPE_WXWORK) {
|
} else if (userType == UTYPE_WXWORK) {
|
||||||
s = sendToWxwork(operatingContext);
|
s = sendToWxwork(operatingContext);
|
||||||
} else if (userType == UTYPE_DINGTALK) {
|
} else if (userType == UTYPE_DINGTALK) {
|
||||||
|
@ -115,62 +116,68 @@ public class SendNotification extends TriggerAction {
|
||||||
Set<Object> send = new HashSet<>();
|
Set<Object> send = new HashSet<>();
|
||||||
|
|
||||||
for (ID user : toUsers) {
|
for (ID user : toUsers) {
|
||||||
|
if (send.contains(user)) continue;
|
||||||
|
|
||||||
if (msgType == MTYPE_MAIL) {
|
if (msgType == MTYPE_MAIL) {
|
||||||
String emailAddr = Application.getUserStore().getUser(user).getEmail();
|
String emailAddr = Application.getUserStore().getUser(user).getEmail();
|
||||||
if (RegexUtils.isEMail(emailAddr) && !send.contains(emailAddr)) {
|
if (RegexUtils.isEMail(emailAddr)) {
|
||||||
SMSender.sendMailAsync(emailAddr, message[1], message[0]);
|
String mdHtml = MarkdownUtils.render(message[0]);
|
||||||
|
SMSender.sendMailAsync(emailAddr, message[1], mdHtml);
|
||||||
send.add(emailAddr);
|
send.add(emailAddr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (msgType == MTYPE_SMS) {
|
if (msgType == MTYPE_SMS) {
|
||||||
String mobileAddr = Application.getUserStore().getUser(user).getWorkphone();
|
String mobileAddr = Application.getUserStore().getUser(user).getWorkphone();
|
||||||
if (RegexUtils.isCNMobile(mobileAddr) && !send.contains(mobileAddr)) {
|
if (RegexUtils.isCNMobile(mobileAddr)) {
|
||||||
SMSender.sendSMSAsync(mobileAddr, message[0]);
|
SMSender.sendSMSAsync(mobileAddr, message[0]);
|
||||||
send.add(mobileAddr);
|
send.add(mobileAddr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
if (msgType == MTYPE_NOTIFICATION) {
|
||||||
// TYPE_NOTIFICATION
|
Message m = MessageBuilder.createMessage(user, message[0], Message.TYPE_DEFAULT, actionContext.getSourceRecord());
|
||||||
if (!send.contains(user)) {
|
Application.getNotifications().send(m);
|
||||||
Message m = MessageBuilder.createMessage(user, message[0], Message.TYPE_DEFAULT, actionContext.getSourceRecord());
|
send.add(user);
|
||||||
Application.getNotifications().send(m);
|
|
||||||
send.add(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return send;
|
return send;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Object> sendToAccounts(OperatingContext operatingContext) {
|
private Set<Object> sendToAccounts(OperatingContext operatingContext, int userType) {
|
||||||
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
final JSONObject content = (JSONObject) actionContext.getActionContent();
|
||||||
final int msgType = content.getIntValue("type");
|
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;
|
Object[] to = null;
|
||||||
// v3.4 删除就尝试从快照中取
|
if (userType == UTYPE_ACCOUNT20) {
|
||||||
if (operatingContext.getAction() == BizzPermission.DELETE) {
|
to = content.getString("sendTo").split("[,,;;]");
|
||||||
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 {
|
} else {
|
||||||
to = Application.getQueryFactory().uniqueNoFilter(
|
JSONArray fieldsDef = content.getJSONArray("sendTo");
|
||||||
actionContext.getSourceRecord(), validFields.toArray(new String[0]));
|
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;
|
if (to == null) return null;
|
||||||
|
|
||||||
|
@ -180,7 +187,7 @@ public class SendNotification extends TriggerAction {
|
||||||
for (Object item : to) {
|
for (Object item : to) {
|
||||||
if (item == null) continue;
|
if (item == null) continue;
|
||||||
|
|
||||||
String mobileOrEmail = item.toString();
|
String mobileOrEmail = item.toString().trim();
|
||||||
if (send.contains(mobileOrEmail)) continue;
|
if (send.contains(mobileOrEmail)) continue;
|
||||||
|
|
||||||
if (msgType == MTYPE_SMS && RegexUtils.isCNMobile(mobileOrEmail)) {
|
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,7 +108,9 @@ public enum ConfigurationItem {
|
||||||
PortalBaiduMapAk,
|
PortalBaiduMapAk,
|
||||||
PortalOfficePreviewUrl,
|
PortalOfficePreviewUrl,
|
||||||
PortalUploadMaxSize(200),
|
PortalUploadMaxSize(200),
|
||||||
|
MobileNavStyle(34),
|
||||||
|
PageMourningMode(false),
|
||||||
|
|
||||||
// !!! 命令行适用
|
// !!! 命令行适用
|
||||||
DataDirectory, // 数据目录
|
DataDirectory, // 数据目录
|
||||||
RedisDatabase(0), // Redis DB
|
RedisDatabase(0), // Redis DB
|
||||||
|
@ -118,6 +120,7 @@ public enum ConfigurationItem {
|
||||||
SecurityEnhanced(false), // 安全增强
|
SecurityEnhanced(false), // 安全增强
|
||||||
TrustedAllUrl(false), // 可信外部地址
|
TrustedAllUrl(false), // 可信外部地址
|
||||||
LibreofficeBin, // Libreoffice 命令
|
LibreofficeBin, // Libreoffice 命令
|
||||||
|
UnsafeImgAccess(false), // 不安全图片访问
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -136,6 +139,7 @@ public enum ConfigurationItem {
|
||||||
|| SecurityEnhanced.name().equalsIgnoreCase(name)
|
|| SecurityEnhanced.name().equalsIgnoreCase(name)
|
||||||
|| TrustedAllUrl.name().equalsIgnoreCase(name)
|
|| TrustedAllUrl.name().equalsIgnoreCase(name)
|
||||||
|| LibreofficeBin.name().equalsIgnoreCase(name)
|
|| LibreofficeBin.name().equalsIgnoreCase(name)
|
||||||
|
|| UnsafeImgAccess.name().equals(name)
|
||||||
|| SN.name().equalsIgnoreCase(name);
|
|| SN.name().equalsIgnoreCase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,8 @@ import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
// !!!! 请勿对本文件做任何改动,否则导致的一切损失由您自行承担!
|
||||||
* !!!! 请勿修改或删除本文件
|
// !!!! 请严格遵守《REBUILD 用户服务协议》https://getrebuild.com/legal/service-terms
|
||||||
* !!!! 请严格遵守《REBUILD 用户服务协议》https://getrebuild.com/legal/service-terms
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public final class License {
|
public final class License {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ package com.rebuild.core.support;
|
||||||
import cn.devezhao.commons.CodecUtils;
|
import cn.devezhao.commons.CodecUtils;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.cache.CommonsCache;
|
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) {
|
} else if (level == 2) {
|
||||||
vcode = CodecUtils.randomCode(8);
|
vcode = CodecUtils.randomCode(8);
|
||||||
} else {
|
} else {
|
||||||
vcode = RandomUtils.nextInt(100000, 999999) + "";
|
vcode = CommonsUtils.randomInt(100000, 999999) + "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存 10 分钟
|
// 缓存 10 分钟
|
||||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.support.general;
|
package com.rebuild.core.support.general;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.web.ServletUtils;
|
||||||
import cn.devezhao.persist4j.Entity;
|
import cn.devezhao.persist4j.Entity;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
@ -15,7 +16,9 @@ import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.service.query.ParseHelper;
|
import com.rebuild.core.service.query.ParseHelper;
|
||||||
import com.rebuild.core.support.SetUser;
|
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.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -66,12 +69,14 @@ public class BatchOperatorQuery extends SetUser {
|
||||||
public JSONObject wrapQueryData(int maxRows, boolean clearFields) {
|
public JSONObject wrapQueryData(int maxRows, boolean clearFields) {
|
||||||
if (this.dataRange != DR_PAGED) {
|
if (this.dataRange != DR_PAGED) {
|
||||||
queryData.put("pageNo", 1);
|
queryData.put("pageNo", 1);
|
||||||
queryData.put("pageSize", maxRows); // Max
|
queryData.put("pageSize", maxRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dataRange == DR_SELECTED || this.dataRange == DR_ALL) {
|
if (this.dataRange == DR_SELECTED || this.dataRange == DR_ALL) {
|
||||||
queryData.remove("filter");
|
queryData.remove("filter");
|
||||||
queryData.remove("advFilter");
|
queryData.remove("advFilter");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dataRange == DR_SELECTED) {
|
if (this.dataRange == DR_SELECTED) {
|
||||||
JSONObject idsItem = new JSONObject();
|
JSONObject idsItem = new JSONObject();
|
||||||
idsItem.put("op", ParseHelper.IN);
|
idsItem.put("op", ParseHelper.IN);
|
||||||
|
@ -88,6 +93,7 @@ public class BatchOperatorQuery extends SetUser {
|
||||||
if (clearFields) {
|
if (clearFields) {
|
||||||
queryData.put("fields", new String[]{getEntity().getPrimaryField().getName()});
|
queryData.put("fields", new String[]{getEntity().getPrimaryField().getName()});
|
||||||
}
|
}
|
||||||
|
|
||||||
queryData.put("reload", Boolean.FALSE);
|
queryData.put("reload", Boolean.FALSE);
|
||||||
return queryData;
|
return queryData;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +115,7 @@ public class BatchOperatorQuery extends SetUser {
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ID[] getQueryedRecords() {
|
public ID[] getQueryedRecordIds() {
|
||||||
if (this.dataRange == DR_SELECTED) {
|
if (this.dataRange == DR_SELECTED) {
|
||||||
String selected = queryData.getString("_selected");
|
String selected = queryData.getString("_selected");
|
||||||
|
|
||||||
|
@ -122,14 +128,13 @@ public class BatchOperatorQuery extends SetUser {
|
||||||
return ids.toArray(new ID[0]);
|
return ids.toArray(new ID[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String sql = String.format("select %s %s",
|
String sql = String.format("select %s %s", getEntity().getPrimaryField().getName(), getFromClauseSql());
|
||||||
getEntity().getPrimaryField().getName(), getFromClauseSql());
|
|
||||||
int pageNo = queryData.getIntValue("pageNo");
|
int pageNo = queryData.getIntValue("pageNo");
|
||||||
int pageSize = queryData.getIntValue("pageSize");
|
int pageSize = queryData.getIntValue("pageSize");
|
||||||
|
|
||||||
Object[][] array = Application.createQuery(sql, getUser())
|
Object[][] array = Application.createQuery(sql, getUser())
|
||||||
.setLimit(pageSize, pageNo * pageSize - pageSize)
|
.setLimit(pageSize, pageNo * pageSize - pageSize)
|
||||||
.setTimeout(60)
|
.setTimeout(180)
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
Set<ID> ids = new HashSet<>();
|
Set<ID> ids = new HashSet<>();
|
||||||
|
@ -140,7 +145,20 @@ public class BatchOperatorQuery extends SetUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entity getEntity() {
|
private Entity getEntity() {
|
||||||
String entityName = queryData.getString("entity");
|
return MetadataHelper.getEntity(queryData.getString("entity"));
|
||||||
return MetadataHelper.getEntity(entityName);
|
}
|
||||||
|
|
||||||
|
// --
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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