diff --git a/@rbv b/@rbv
index 0632edc28c..55386d909e 160000
--- a/@rbv
+++ b/@rbv
@@ -1 +1 @@
-Subproject commit 0632edc28ced726d01dcc7d949932aca976db5f7
+Subproject commit 55386d909e5ed6afe1fe7a0793c460451b491296
diff --git a/Dockerfile b/Dockerfile
index ea89fee2b2..b1ef765821 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,11 @@
 FROM amazoncorretto:11-alpine
+#docker pull amazoncorretto:11-alpine
 
 RUN apk add ttf-dejavu
 
 EXPOSE 18080
 
 COPY ./target/rebuild.jar /app/rebuild/rebuild-boot.jar
-
 #COPY ./.deploy/SourceHanSansK-Regular.ttf /app/rebuild/.rebuild/
 
 WORKDIR /app/rebuild/
diff --git a/pom.xml b/pom.xml
index f9f488bbad..5945c2c155 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
     </parent>
     <groupId>com.rebuild</groupId>
     <artifactId>rebuild</artifactId>
-    <version>3.9.0-beta3</version>
+    <version>3.9.0</version>
     <name>rebuild</name>
     <description>Building your business-systems freely!</description>
     <url>https://getrebuild.com/</url>
diff --git a/src/main/java/com/rebuild/core/Application.java b/src/main/java/com/rebuild/core/Application.java
index 6859a830f8..c85dcce519 100644
--- a/src/main/java/com/rebuild/core/Application.java
+++ b/src/main/java/com/rebuild/core/Application.java
@@ -74,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
     /**
      * Rebuild Version
      */
-    public static final String VER = "3.9.0-beta3";
+    public static final String VER = "3.9.0";
     /**
      * Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
      */
-    public static final int BUILD = 3090003;
+    public static final int BUILD = 3090005;
 
     static {
         // Driver for DB
diff --git a/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java b/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java
index fdf53193d5..dee5cc809b 100644
--- a/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java
+++ b/src/main/java/com/rebuild/core/metadata/impl/EasyFieldConfigProps.java
@@ -102,6 +102,10 @@ public class EasyFieldConfigProps {
      * 允许上传文件类型
      */
     public static final String FILE_SUFFIX = "fileSuffix";
+    /**
+     * 指定上传路径
+     */
+    public static final String FILE_UPDIR = "fileUpdir";
 
     /**
      * 允许上传数量
diff --git a/src/main/java/com/rebuild/core/service/dashboard/charts/builtin/ApprovalList.java b/src/main/java/com/rebuild/core/service/dashboard/charts/builtin/ApprovalList.java
index 4994aae621..ed129ae969 100644
--- a/src/main/java/com/rebuild/core/service/dashboard/charts/builtin/ApprovalList.java
+++ b/src/main/java/com/rebuild/core/service/dashboard/charts/builtin/ApprovalList.java
@@ -121,6 +121,7 @@ public JSON build() {
         Map<String, Object> ret = new HashMap<>();
         ret.put("data", rearray);
         ret.put("stats", stats);
+        ret.put("overLimit", array.length >= 500);
         return (JSON) JSON.toJSON(ret);
     }
 }
diff --git a/src/main/java/com/rebuild/core/service/general/QuickCodeReindexTask.java b/src/main/java/com/rebuild/core/service/general/QuickCodeReindexTask.java
index 3c65259dd5..0c72f2b0e8 100644
--- a/src/main/java/com/rebuild/core/service/general/QuickCodeReindexTask.java
+++ b/src/main/java/com/rebuild/core/service/general/QuickCodeReindexTask.java
@@ -168,7 +168,7 @@ public static String generateQuickCode(String nameVal) {
             try {
                 quickCode = HanLP.convertToPinyinString(nameVal, "", Boolean.FALSE);
             } catch (Exception e) {
-                log.error("QuickCode shorting error : " + nameVal, e);
+                log.error("QuickCode shorting error : {}", nameVal, e);
                 quickCode = StringUtils.EMPTY;
             }
         }
diff --git a/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java b/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java
index 362d0cbe94..65022034a2 100644
--- a/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java
+++ b/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java
@@ -344,8 +344,8 @@ public static String formatFileKey(String fileName, boolean keepName, String upd
         String dt = CalendarUtils.getDateFormat("yyyyMMddHHmmssSSS").format(CalendarUtils.now());
         String subdir = dt.substring(0, 8);
         String filePrefix = dt.substring(8);
-        // remove unsafe /\.
-        if (StringUtils.isNotBlank(updir)) subdir = updir.replaceAll("[./\\\\\\s]", "");
+        // remove unsafe flags
+        if (StringUtils.isNotBlank(updir)) subdir = updir.replaceAll("[%./\\\\\\s]", "");
 
         return String.format("rb/%s/%s__%s", subdir, filePrefix, fileName);
     }
diff --git a/src/main/java/com/rebuild/web/robot/trigger/TriggerAdminController.java b/src/main/java/com/rebuild/web/robot/trigger/TriggerAdminController.java
index d2a311d6b4..4514c1e34b 100644
--- a/src/main/java/com/rebuild/web/robot/trigger/TriggerAdminController.java
+++ b/src/main/java/com/rebuild/web/robot/trigger/TriggerAdminController.java
@@ -18,6 +18,7 @@
 import com.rebuild.core.metadata.MetadataSorter;
 import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
 import com.rebuild.core.service.approval.RobotApprovalManager;
+import com.rebuild.core.service.datareport.DataReportManager;
 import com.rebuild.core.service.trigger.ActionFactory;
 import com.rebuild.core.service.trigger.ActionType;
 import com.rebuild.core.service.trigger.TriggerAction;
@@ -158,24 +159,32 @@ public static String[] tryParseTargetEntity(String config, String sourceEntity)
         // 自动记录转换
         String useTransform = configJson.getString("useTransform");
         if (ID.isId(useTransform)) {
-            ConfigBean cb;
             try {
-                cb = TransformManager.instance.getTransformConfig(ID.valueOf(useTransform), sourceEntity);
-            } catch (Exception ignored) {
-                return null;
+                ConfigBean cb = TransformManager.instance.getTransformConfig(ID.valueOf(useTransform), sourceEntity);
+                return new String[]{ useTransform, cb.getString("name") };
+            } catch (Exception deleted) {
+                return new String[]{ null, String.format("[%s]", useTransform.toUpperCase()) };
             }
-            return new String[]{ useTransform, cb.getString("name") };
         }
         // 自动审批
         String useApproval = configJson.getString("useApproval");
         if (ID.isId(useApproval)) {
-            ConfigBean cb;
             try {
-                cb = RobotApprovalManager.instance.getFlowDefinition(ID.valueOf(useApproval));
-            } catch (Exception ignored) {
-                return null;
+                ConfigBean cb = RobotApprovalManager.instance.getFlowDefinition(ID.valueOf(useApproval));
+                return new String[]{ useApproval, cb.getString("name") };
+            } catch (Exception deleted) {
+                return new String[]{ null, String.format("[%s]", useApproval.toUpperCase()) };
+            }
+        }
+        // 导出报表
+        String useTemplate = configJson.getString("useTemplate");
+        if (ID.isId(useTemplate)) {
+            try {
+                ConfigBean cb = DataReportManager.instance.getReportRaw(ID.valueOf(useTemplate));
+                return new String[]{ useTemplate, cb.getString("name") };
+            } catch (Exception deleted) {
+                return new String[]{ null, String.format("[%s]", useTemplate.toUpperCase()) };
             }
-            return new String[]{ useApproval, cb.getString("name") };
         }
 
         return null;
diff --git a/src/main/resources/i18n/lang.zh_CN.json b/src/main/resources/i18n/lang.zh_CN.json
index f4b5bd9ac3..9cc251da0a 100644
--- a/src/main/resources/i18n/lang.zh_CN.json
+++ b/src/main/resources/i18n/lang.zh_CN.json
@@ -3343,5 +3343,7 @@
 	"立即备份":"立即备份",
 	"数据库":"数据库",
 	"未备份":"未备份",
-	"选择要备份哪些数据":"选择要备份哪些数据"
+	"选择要备份哪些数据":"选择要备份哪些数据",
+	"分配与共享":"分配与共享",
+	"最多显示最近 500 条记录":"最多显示最近 500 条记录"
 }
\ No newline at end of file
diff --git a/src/main/resources/web/admin/metadata/field-edit.html b/src/main/resources/web/admin/metadata/field-edit.html
index 236eb99448..1d4158dbad 100644
--- a/src/main/resources/web/admin/metadata/field-edit.html
+++ b/src/main/resources/web/admin/metadata/field-edit.html
@@ -171,6 +171,12 @@ <h5>[[${bundle.L('常用')}]]</h5>
                     </label>
                   </div>
                 </div>
+                <div th:if="${fieldType == 'FILE'}" class="form-group row J_for-FILE bosskey-show">
+                  <label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('上传目录')}]]</label>
+                  <div class="col-md-12 col-xl-6 col-lg-8">
+                    <input type="text" class="form-control form-control-sm" id="fileUpdir" th:placeholder="${bundle.L('默认')}" />
+                  </div>
+                </div>
                 <div th:if="${fieldType == 'PICKLIST' or fieldType == 'MULTISELECT'}" class="form-group row J_for-PICKLIST J_for-MULTISELECT">
                   <label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('选项列表')}]]</label>
                   <div class="col-md-12 col-xl-6 col-lg-8">
diff --git a/src/main/resources/web/assets/css/chart-design.css b/src/main/resources/web/assets/css/chart-design.css
index b5d3c474ce..02a84e7443 100644
--- a/src/main/resources/web/assets/css/chart-design.css
+++ b/src/main/resources/web/assets/css/chart-design.css
@@ -101,12 +101,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
   cursor: default;
 }
 
-.axis .axis-head > a {
-  display: inline-block;
-  padding: 0 3px;
-  cursor: default;
-}
-
 .axis .axis-target {
   margin-left: 80px;
   min-height: 30px;
diff --git a/src/main/resources/web/assets/js/admin/system-cfg.js b/src/main/resources/web/assets/js/admin/system-cfg.js
index 36d1aeb63f..9073b15c58 100644
--- a/src/main/resources/web/assets/js/admin/system-cfg.js
+++ b/src/main/resources/web/assets/js/admin/system-cfg.js
@@ -358,7 +358,7 @@ class DlgBackup extends RbAlert {
           <div className="text-warning mb-1" ref={(c) => (this._$tips = c)}>
             <i className="mdi-alert-outline mdi" /> {$L('请勿在业务高峰时段执行备份')}
           </div>
-          <button type="button" className="btn btn-space btn-primary" onClick={this.confirm} disabled={this.state.disable}>
+          <button type="button" className="btn btn-space btn-primary" onClick={this.confirm} ref={(c) => (this._$btn = c)} data-spinner>
             {$L('开始备份')}
           </button>
         </div>
@@ -371,6 +371,7 @@ class DlgBackup extends RbAlert {
     if (type === 0) return
 
     this.disabled(true, true)
+    const $btn = $(this._$btn).button('loading')
     $.post(`systems/backup?type=${type}`, (res) => {
       if (res.error_code === 0) {
         const data = res.data || {}
@@ -380,6 +381,7 @@ class DlgBackup extends RbAlert {
         RbHighbar.error(res.error_msg)
       }
       this.disabled(false, false)
+      $btn.button('reset')
     })
   }
 }
diff --git a/src/main/resources/web/assets/js/charts/charts.js b/src/main/resources/web/assets/js/charts/charts.js
index 163abde834..9c9cb8690c 100644
--- a/src/main/resources/web/assets/js/charts/charts.js
+++ b/src/main/resources/web/assets/js/charts/charts.js
@@ -413,12 +413,18 @@ const ECHART_LEGEND_VOPT = {
   textStyle: { fontSize: 12 },
 }
 
-// K=千 M=百万
+// K=千 M=百万 B=亿
 const shortNumber = function (num) {
-  if (rb.locale === 'zh_CN' && (num > 10000 || num < -10000)) return (num / 10000).toFixed(1) + '万'
-  if (num > 1000000 || num < -1000000) return (num / 1000000).toFixed(1) + 'M'
+  if (rb.locale === 'zh_CN') {
+    if (num > 100000000 || num < -100000000) return (num / 100000000).toFixed(1) + '亿'
+    else if (num > 1000000 || num < -1000000) return (num / 1000000).toFixed(1) + '百万'
+    else if (num > 10000 || num < -10000) return (num / 10000).toFixed(1) + '万'
+    return num
+  }
+  if (num > 100000000 || num < -100000000) return (num / 100000000).toFixed(1) + 'B'
+  else if (num > 1000000 || num < -1000000) return (num / 1000000).toFixed(1) + 'M'
   else if (num > 10000 || num < -10000) return (num / 1000).toFixed(1) + 'K'
-  else return num
+  return num
 }
 
 // 千分位
@@ -918,6 +924,11 @@ class ApprovalList extends BaseChart {
               })}
             </tbody>
           </table>
+          {data.overLimit && (
+            <div className="m-2 text-center text-warning">
+              <i className="mdi mdi-information-outline" /> {$L('最多显示最近 500 条记录')}
+            </div>
+          )}
         </div>
       )
 
diff --git a/src/main/resources/web/assets/js/general/rb-forms.js b/src/main/resources/web/assets/js/general/rb-forms.js
index e6d92c8387..74a4af4d51 100644
--- a/src/main/resources/web/assets/js/general/rb-forms.js
+++ b/src/main/resources/web/assets/js/general/rb-forms.js
@@ -1857,7 +1857,15 @@ class RbFormFile extends RbFormImage {
           )
         })}
         <div className={`file-select ${showUpload ? '' : 'hide'}`}>
-          <input type="file" className="inputfile" ref={(c) => (this._fieldValue__input = c)} id={this._htmlid} accept={this.props.fileSuffix || null} multiple />
+          <input
+            type="file"
+            className="inputfile"
+            ref={(c) => (this._fieldValue__input = c)}
+            id={this._htmlid}
+            accept={this.props.fileSuffix || null}
+            multiple
+            data-updir={this.props.fileUpdir || null}
+          />
           <label htmlFor={this._htmlid} title={$L('拖动或点击选择文件。需要 %s 个', `${this.__minUpload}~${this.__maxUpload}`)} className="btn-secondary" onClick={(e) => this._fileClick(e)}>
             {this._captureType === 2 ? <span className="mdi mdi-camera" /> : <span className="zmdi zmdi-upload" />}
             <span className="ml-1">{$L('上传文件')}</span>
diff --git a/src/main/resources/web/assets/js/rb-base.js b/src/main/resources/web/assets/js/rb-base.js
index 9e17d85687..f8b4e83702 100644
--- a/src/main/resources/web/assets/js/rb-base.js
+++ b/src/main/resources/web/assets/js/rb-base.js
@@ -192,6 +192,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
     this.$search.closest('.select2-search--inline').css('width', widthParent)
   }
 })(jQuery)
+// fix:select2:https://stackoverflow.com/questions/18487056/select2-doesnt-work-when-embedded-in-a-bootstrap-modal
+$.fn.modal.Constructor.prototype._enforceFocus = function () {}
 
 // extends Array
 Array.prototype.remove = function (item) {
diff --git a/src/main/resources/web/assets/js/trigger/trigger-list.js b/src/main/resources/web/assets/js/trigger/trigger-list.js
index f7d9cc269f..4affc8f926 100644
--- a/src/main/resources/web/assets/js/trigger/trigger-list.js
+++ b/src/main/resources/web/assets/js/trigger/trigger-list.js
@@ -70,19 +70,28 @@ class TriggerList extends ConfigList {
       <RF>
         {(this.state.data || []).map((item) => {
           let targetRef = item[9]
+          // [ID, NAME]
           if (targetRef) {
-            if (targetRef[0] && targetRef[0].startsWith('028-')) {
+            if (!targetRef[0]) {
+              targetRef = <a className="text-danger">{targetRef[1]}</a>
+            } else if (targetRef[0].startsWith('028-')) {
               targetRef = (
                 <a href={`${rb.baseUrl}/admin/robot/approval/${targetRef[0]}`} className="light-link" target={`_${targetRef[0]}`}>
                   {targetRef[1]}
                 </a>
               )
-            } else if (targetRef[0] && targetRef[0].startsWith('037-')) {
+            } else if (targetRef[0].startsWith('037-')) {
               targetRef = (
                 <a href={`${rb.baseUrl}/admin/robot/transform/${targetRef[0]}`} className="light-link" target={`_${targetRef[0]}`}>
                   {targetRef[1]}
                 </a>
               )
+            } else if (targetRef[0].startsWith('032-')) {
+              targetRef = (
+                <a href={`${rb.baseUrl}/admin/data/report-templates#gs=${targetRef[0]}`} className="light-link" target={`_${targetRef[0]}`}>
+                  {targetRef[1]}
+                </a>
+              )
             } else {
               targetRef = (
                 <a href={`${rb.baseUrl}/admin/entity/${targetRef[0]}/base`} className="light-link" target={`_${targetRef[0]}`}>
diff --git a/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js b/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js
index 2c80d86d39..c3962deb6d 100644
--- a/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js
+++ b/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js
@@ -347,26 +347,26 @@ class ContentFieldAggregation extends ActionContentSpec {
           $s2sf.trigger('change')
 
           // 回填
-          const $fbf = $(this._$fillbackField).select2({ placeholder: $L('(可选)'), allowClear: true })
-          this.__select2.push($fbf)
+          const $fbField = $(this._$fillbackField).select2({ placeholder: $L('(可选)'), allowClear: true })
+          $fbField.val(null).trigger('change')
+          this.__select2.push($fbField)
 
           this.__select2.push($s2sf)
           this.__select2.push($s2cm)
           this.__select2.push($s2tf)
-        })
 
-        const content = this.props.content
-        if (content) {
-          this.setState({ items: content.items || [] })
-          if (content.targetEntityMatchFields) {
-            setTimeout(() => this._MatchFields && this._MatchFields.setState({ groupFields: content.targetEntityMatchFields }), 200)
-          }
-          setTimeout(() => {
+          // init
+          const content = this.props.content
+          if (content) {
+            this.setState({ items: content.items || [] })
+            if (content.targetEntityMatchFields) {
+              this._MatchFields && this._MatchFields.setState({ groupFields: content.targetEntityMatchFields })
+            }
             $(this._$fillbackField)
               .val(content.fillbackField || null)
               .trigger('change')
-          }, 200)
-        }
+          }
+        })
       }
 
       // v3.7
diff --git a/src/main/resources/web/dashboard/chart-design.html b/src/main/resources/web/dashboard/chart-design.html
index 9b71f3946c..9cdc91b157 100644
--- a/src/main/resources/web/dashboard/chart-design.html
+++ b/src/main/resources/web/dashboard/chart-design.html
@@ -214,14 +214,12 @@ <h5>[[${bundle.L('图表选项')}]]</h5>
             <div class="axis J_dimension">
               <div class="axis-head">
                 <span>[[${bundle.L('维度')}]]</span>
-                <a><i class="zmdi zmdi-edit"></i></a>
               </div>
               <div class="axis-target J_axis-dim"></div>
             </div>
             <div class="axis J_numerical">
               <div class="axis-head">
                 <span>[[${bundle.L('数值')}]]</span>
-                <a><i class="zmdi zmdi-edit"></i></a>
               </div>
               <div class="axis-target J_axis-num"></div>
             </div>
diff --git a/src/main/resources/web/entity/approval/approval-view.html b/src/main/resources/web/entity/approval/approval-view.html
index d25640d1a7..4fba28e20d 100644
--- a/src/main/resources/web/entity/approval/approval-view.html
+++ b/src/main/resources/web/entity/approval/approval-view.html
@@ -23,7 +23,6 @@
       <h3 class="title">[[${bundle.L('审批流程')}]]</h3>
       <span>
         <a class="close J_close" th:title="${bundle.L('关闭')}"><i class="zmdi zmdi-close"></i></a>
-        <a class="close sm J_reload" th:title="${bundle.L('刷新')}"><i class="zmdi zmdi-refresh"></i></a>
         <a class="close sm J_back hide" th:title="${bundle.L('回退')}"><i class="zmdi zmdi-arrow-left"></i></a>
       </span>
     </div>
diff --git a/src/main/resources/web/notification/messages.html b/src/main/resources/web/notification/messages.html
index 0e6d76464f..f05564fb3f 100644
--- a/src/main/resources/web/notification/messages.html
+++ b/src/main/resources/web/notification/messages.html
@@ -131,9 +131,6 @@
                         </a>
                       </div>
                       <div class="list-group">
-                        <a href="#read" data-type="2" class="hide list-group-item d-flex list-group-item-action">
-                          <span class="text">[[${bundle.L('已读消息')}]]</span>
-                        </a>
                         <a href="#feeds" data-type="30" class="list-group-item d-flex list-group-item-action">
                           <span class="text">[[${bundle.L('动态')}]]</span>
                         </a>
@@ -144,7 +141,7 @@
                           <span class="text">[[${bundle.L('审批')}]]</span>
                         </a>
                         <a href="#assigns" data-type="10" class="list-group-item d-flex list-group-item-action">
-                          <span class="text">[[${bundle.L('分配&共享')}]]</span>
+                          <span class="text">[[${bundle.L('分配与共享')}]]</span>
                         </a>
                       </div>
                     </div>