mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
merge-3.8 (#813)
* Update @rbv * feat: trigger when update-fields approve-nodes (#728) * ** NEED TEST ** * Update metadata-conf.xml * 3.7-dev * bump: echarts v5 * COLOR_PALETTES * be: DlgSpecFields into common * feat: spec approval-node trigger * feat: hasUpdateFields for all * tmp: RobotSopConfig * Update @rbv * enh: TriggerByTimerJob 未完成也可重进 (#731) * enh: TriggerByTimerJob 未完成也可重进 * enh: fileName use#698 * feat: UseDbFullText * !!!@EnableAsync --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * feat: chart CNMAP (#732) * style: chart in datalist * feat: CNMAP * feat: Details auto imports 110 (#733) * js: $.trim, $.isArray, click-on * feat: details import auto * enh: $type, select2 * Add lib react18 * bump lib * Update lint.yml --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update @rbv * Enh auto approval (#735) * rbv: AutoApproval * console: RBAPI ASSISTANT * Update @rbv * print Approval Node * filter: op=HHH * feat: AutoApproval * be: use tags * be: LastLogsViewer.renderLog * Feat list3 card 100 (#737) * style * datalist conf * feat: mode3 * mode23 style * enh: datalist2 * fjs: openModal, getType * Update @rbv * bump: react18, jq (#738) * js: $.trim, $.isArray, click-on * feat: details import auto * enh: $type, select2 * Add lib react18 * enh: js * bump lib * react18 * cnmap style * actions * loadmore style --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * feat: approval step users (#739) * feat: approvalLastTime * feat: approvalStepUsers * img indicator * Update @rbv * be: filter * submail attach --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Notify use sms email (#741) * feat: EmailDistributor, SmsDistributor * feat: ApprovalStepNodeName * isOceanBase --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update @rbv * Update @rbv * feat: Auto create task 119 (#742) feat: CreateTask feat:SMS/EmailDistributor * be: save cb * entity searchbox * feat: REP * Chart axis 120 (#743) * style: CNMAP * feat: Stack Bar * feat: showHorizontal * be --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Approval expires 114 (#745) * feat: expiresAuto * be: approval copy * be:entity view by code * feat: tasks list (#746) * style: icon of chart * feat: project list tasks * be: setEditableFields keep sort Gitee#I9EGJB * be: executeLazy * Nd trans (#747) * enh: getDisksUsed * be: link entity * feat: ND trans * style * be: filter * icon: zmdi-filter-list * style: list badge 12px * detailImports * style project * be: $multipleUploader * feat: sop (#748) * be * be: trans 1>N * enh: sql ver * styles * ps style * feat: sop * enh: useExecManual for all --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update forms.html * Report use cond (#749) * style * be: sop * feat: report useFilter * field image _captureType * TSID error --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Feat datasync 94 (#750) * be: ref-search pageSize=20 * feat: date W Gitee#I9I67Z * style * feat: bar3 * be: charts style * feat: DataSyncer spec * Enh extforms (#752) * dock style * enh: trigger edit code * fix: guide * trubo * fix: Add no-rollback-for=RepeatedRecordsException * 通过》已完成 * be: charts * enh: .detail-form-table.fullscreen * fix: Gitee#I9J3UR https://github.com/alibaba/easyexcel/issues/3432 * ExcelClipboardData * Update README.md --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update @rbv * Feat excel clipboard data (#753) * style * feat: csvdata-rebuild * enh: DataListCategory (Use tree) * be: Nval duplicate * Apidock (#754) * TsetEntity * theme color * be: trigger on update * rm: LazyWaitDetailsFinished * apiman * fix: ApprovalStepNodeName * Enh charts (#755) * enh: axis filter * enh: FunnelChart * feat: SendNotification email attach * AutoGenReport * enh: `_readonly ` for setReadonly * be 3.7 (#756) * be: nodeName * url-safe * md pdf * $cleanNumbern * be: text:关联记录>相关记录 * enh: 级联支持N2N字段 * entity-overview * Dockerfile * fix yj * enh: AutoApproval revoke * Update @rbv --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * feat: List cat ref (#757) * be: DataListCategory * fix: 审批返回上一步有分支节点时错误 * be: 清理备份错误提示 * oshi * Be 3.7 2 (#759) * Update MarkdownUtils.java * be: filter N2N:User * apiman pdf * Be 3.7 3 (#760) * fix * be: webcam * Update field-edit.html * be * Be 3.7 4 (#761) * Update system-cfg.html * ConcatIdFunction * Update FieldWriteback.java * be video * Update media-capturer.js * Update DataImportController.java * lang * be * Update DataImportController.java * Update RebuildWebConfigurer.java * flatpickr * Update rb-base.js * handleChange lazy * fix chart in datalist * Update submail.js * style * Be 3.7 5 (#762) * $hex2rgb * be: checkRefDataFilter * style: feeds * style: file-icon * fix: 记录转换 D>M+D * be install * be: tests * feat: form-formula {NOW} * fix: 日期短格式区间查询 * _readonly37 --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Be 3.7 6 (#764) * form: __LAB_FORMACTION_105, __LAB_FORMACTION_103 * __LAB_MINUTESTEP * fix: 字段更新清空时支持N2N * be: Installer.java * feat: $dropUpload * be: file RbPreview * fix * mde paste * be dropUpload * fix: 不触发 onClientProgress??? * Be 3.7 7 (#765) * fix: num input * style: NTEXT keep empty-line * media-capturer.js * fix: pdf 预览下载文件名不对 * feat: nform * Update KnownExceptionConverter.java * be * Be 3.7 8 (#766) * be NFORM * Update form-design.js * Update charts.js * Update FormsManager.java * Be 3.7 9 (#767) * feat: speclayout * feat: Gitee#I9UJ7N * feat: easyaction * useCode * Be 3.7 10 (#768) * v3.7-hide * lang * be: targetEntityMatchFields * fix: * _StartEntityTypeCode * be * Update README.md * Feat html5 report tinymce (#777) * f-3.8 * tinymce * be: template5 * Update @rbv * Check inst (#778) * bump lib * refactor * feat: stopPropagation * flatpickr * fix: time query * feat: class color * be --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Be easyaction2 (#781) * Update @rbv * open: nform * Update flatpickr.min.js * fix: class color * feat: word 相关项支持 * feat: html5 相关项 * Update report-templates.js * feat: open: __LAB_MATCHFIELDS * feat: class code * feat: ConcatArrayFunction * Update @rbv * be * Update @rbv * fix * Update @rbv * style * Update @rbv * Protable width (#784) * feat: series reset * col-resize * feat: user batch * be: IncreasingVar * be * enh:reports (#785) * feat: report CHECKBOX/CHECKBOX2 * open: _cfParent * enh: 多选字段显示不要边框 * fix:backspace select2 * enh: query maxlength * enh: FeedsSchedule relatedRecord * Update @rbv * File upload cam (#788) * enh: _captureType * bump lib * style: sop * enh: Share2 edit * feat: fp * Update @rbv * Fp details (#789) * enh: CreateFeed feedType * enh: fp for details * enh: stopPropagation=quickMode * enh: html5 recordIdMultiple * be: admin deep=3 * fix: ApprovalFields2Schema complement * Custom lang (#790) * feat: PREV_APPROVER_BACKED * feat; vertical38 * feat: _ProtectedAdmin * List group tab (#791) * feat: advListShowCategory-set * style * kill-session (#792) * be: MultipleSessions feat: kill-session h5 * Be template5 (#793) * fix: CVE * $tagStyle2 * Update rb-forms.append.js * Charts field filter (#796) * be * enh: extform search-filter; HANPINY * be exportReport * Update DataReportManager.java * 变更历史合并显示 * feat: class, ref: code-append * feat: axis filter * be: html5 preview * barcode decode * Update @rbv * Enh easyaction (#798) * enh-easyaction * enh: ValueConvertFunc * Update @rbv * Update FieldPrivileges.java * enh: excel 列表支持值转换(#SIZE不支持) * Update EasyExcelGenerator.java * Update @rbv * Update @rbv * be * rm PrivilegesGuardContextHolder * Update rb-forms.js * be: wrapReturn * 多表单适用新建 --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update @rbv * be:3.8-1 (#799) * be * HowtoPointcut * style * fix SN * be-3.8-2 (#800) * be:weakMode * feat: calcFormulaBackend * feat: groupFields * be: sop * feat: 修改模版文件 * fix * style * Be 3.8 3 (#801) * be * NODE MIRROR * Update @rbv --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Be 3.8 4 (#802) * fix: 通过字段匹配没有 SERIES 字段 * fix: best form-layout * style * fix: sop * MysqldumpBin * style * Update application-dev.yml * Update @rbv * be-3.8-5 (#803) * Update ValueConvertFunc.java * pt fixedWidth * Update AdvFilterParser.java * YYY, MMM * Update @rbv * be: user-delete cascade * fix * RbvMissingController * ROUND * be * be-3.8-6 (#804) * bugfix * Update ValueConvertFunc.java * DECIMAL_ROUNDING, etc * lang * Update @rbv * Be 3.8 7 (#805) * be * style * fix * fix * Update @rbv * Update ValueConvertFunc.java * Be 3.8 8 (#806) * base64 * be * Update @rbv * fix CVE * lang * Be 3.8 9 (#807) * Update @rbv * codemirror hints * be NForms * feat: _expandLine * fix 单字段保存取消后仍有脏数据 * fix: 代码丢失??? * Be 3.8 10 (#808) * be * enh: 字段匹配支持N级 * enh: 补充自增编号顺序 * feat: FromtJS.Query * Be 3.8 10 (#809) * er * Be 3.8 10 (#810) * er * fix: 位置字段数据格式 * be * Be 3.8 11 (#811) * Update charts.js * be:表单回调 * -bosskey-show * report * fix * Update DataListWrapper.java * open:regRowButton * be * Update @rbv * Be 3.8 12 (#812) * be * be * Update DataExporter.java * fix fjs * be * Update @rbv * beta1 * Update README.md * Update media-capturer.js * Update @rbv * be: quick-filter * Update AdvFilterParser.java * Update rb-datalist.common.js * feat: 触发器执行日 * Update @rbv * style * 允许撤回、撤销审批 * Update MetadataGetting.java * AutoGenReport * Update form-design.js * style:sop --------- Co-authored-by: devezhao <zhaofang123@gmail.com> * Update ServerStatus.java --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
82e51fe46c
commit
0478a8e08e
|
@ -145,5 +145,6 @@ module.exports = {
|
||||||
$hex2rgb: true,
|
$hex2rgb: true,
|
||||||
$isImage: true,
|
$isImage: true,
|
||||||
$dropUpload: true,
|
$dropUpload: true,
|
||||||
|
$tagStyle2: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
||||||
Subproject commit fb639d01abceb0dbb85760e475a153e2f69237b5
|
Subproject commit 325da768fde9291914a1132283e62fe8cb83053c
|
16
README.md
16
README.md
|
@ -15,18 +15,18 @@
|
||||||
|
|
||||||
> **福利:加入 REBUILD VIP 用户 QQ 交流群 819865721 1013051587 GET 使用技能**
|
> **福利:加入 REBUILD VIP 用户 QQ 交流群 819865721 1013051587 GET 使用技能**
|
||||||
|
|
||||||
## V3.7 新特性
|
## V3.8 新特性
|
||||||
|
|
||||||
本次更新为你带来众多功能增强与优化。
|
本次更新为你带来众多功能增强与优化。
|
||||||
|
|
||||||
1. [新增] 限时审批
|
1. [新增] HTML 报表模版
|
||||||
2. [新增] 新建任务(触发器)
|
2. [新增] 多表单布局
|
||||||
3. [新增] 地图等多个图表
|
3. [新增] 明细支持 Excel 粘贴录入
|
||||||
4. [新增] 自动明细记录导入(记录转换)
|
4. [新增] 图片/附件支持摄像头上传模式
|
||||||
5. [新增] 数据列表之卡片模式
|
5. [新增] 手机版数据列表可导出报表
|
||||||
6. [新增] 多个 FrontJS 函数
|
6. [新增] 多个 FrontJS 函数
|
||||||
7. [优化] 图表支持多轴显示、横向显示、显示背景
|
7. [新增] 用户支持批量操作
|
||||||
8. [优化] 手机版全新列表搜索组件
|
8. [优化] 20+ 细节/BUG/安全性更新
|
||||||
9. ...
|
9. ...
|
||||||
|
|
||||||
更多更新详情请参见 [更新日志](https://getrebuild.com/docs/dev/changelog)
|
更多更新详情请参见 [更新日志](https://getrebuild.com/docs/dev/changelog)
|
||||||
|
|
38
pom.xml
38
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.rebuild</groupId>
|
<groupId>com.rebuild</groupId>
|
||||||
<artifactId>rebuild</artifactId>
|
<artifactId>rebuild</artifactId>
|
||||||
<version>3.7.7</version>
|
<version>3.8.0-beta1</version>
|
||||||
<name>rebuild</name>
|
<name>rebuild</name>
|
||||||
<description>Building your business-systems freely!</description>
|
<description>Building your business-systems freely!</description>
|
||||||
<url>https://getrebuild.com/</url>
|
<url>https://getrebuild.com/</url>
|
||||||
|
@ -86,6 +86,11 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<nodeVersion>v10.22.0</nodeVersion>
|
<nodeVersion>v10.22.0</nodeVersion>
|
||||||
<workingDirectory>.deploy</workingDirectory>
|
<workingDirectory>.deploy</workingDirectory>
|
||||||
|
<!-- UNCOMMENT USE NODE MIRROR -->
|
||||||
|
<!--
|
||||||
|
<nodeDownloadRoot>https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/</nodeDownloadRoot>
|
||||||
|
<npmRegistryURL>https://registry.npmmirror.com</npmRegistryURL>
|
||||||
|
-->
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- USE COMMERCIAL MODULES IF EXISTS -->
|
<!-- USE COMMERCIAL MODULES IF EXISTS -->
|
||||||
|
@ -187,9 +192,9 @@
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>alimaven</id>
|
<id>alimaven2</id>
|
||||||
<name>public</name>
|
<name>public</name>
|
||||||
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
</repository>
|
</repository>
|
||||||
<!-- Unpublished libs can be found on https://github.com/devezhao/ -->
|
<!-- Unpublished libs can be found on https://github.com/devezhao/ -->
|
||||||
<repository>
|
<repository>
|
||||||
|
@ -298,7 +303,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.devezhao</groupId>
|
<groupId>com.github.devezhao</groupId>
|
||||||
<artifactId>persist4j</artifactId>
|
<artifactId>persist4j</artifactId>
|
||||||
<version>1.7.10</version>
|
<version>1.7.11</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
|
@ -334,12 +339,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>druid</artifactId>
|
<artifactId>druid</artifactId>
|
||||||
<version>1.2.22</version>
|
<version>1.2.23</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.3.0</version>
|
<version>8.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.ehcache</groupId>
|
<groupId>net.sf.ehcache</groupId>
|
||||||
|
@ -349,12 +354,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
<version>4.4.7</version>
|
<version>4.4.8</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.15.0</version>
|
<version>7.15.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.whvcse</groupId>
|
<groupId>com.github.whvcse</groupId>
|
||||||
|
@ -364,7 +369,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.oshi</groupId>
|
<groupId>com.github.oshi</groupId>
|
||||||
<artifactId>oshi-core</artifactId>
|
<artifactId>oshi-core</artifactId>
|
||||||
<version>6.5.0</version>
|
<version>6.6.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jsoup</groupId>
|
<groupId>org.jsoup</groupId>
|
||||||
|
@ -470,7 +475,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.redisson</groupId>
|
<groupId>org.redisson</groupId>
|
||||||
<artifactId>redisson</artifactId>
|
<artifactId>redisson</artifactId>
|
||||||
<version>3.27.2</version>
|
<version>3.33.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
@ -496,7 +501,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>5.8.26</version>
|
<version>5.8.29</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
@ -516,15 +521,10 @@
|
||||||
<artifactId>jansi</artifactId>
|
<artifactId>jansi</artifactId>
|
||||||
<version>2.4.1</version>
|
<version>2.4.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.zwobble.mammoth</groupId>
|
|
||||||
<artifactId>mammoth</artifactId>
|
|
||||||
<version>1.7.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.getui.push</groupId>
|
<groupId>com.getui.push</groupId>
|
||||||
<artifactId>restful-sdk</artifactId>
|
<artifactId>restful-sdk</artifactId>
|
||||||
<version>1.0.0.17</version>
|
<version>1.0.2.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- fix: CVEs -->
|
<!-- fix: CVEs -->
|
||||||
|
@ -536,7 +536,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.26.1</version>
|
<version>1.26.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
|
@ -546,7 +546,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.woodstox</groupId>
|
<groupId>com.fasterxml.woodstox</groupId>
|
||||||
<artifactId>woodstox-core</artifactId>
|
<artifactId>woodstox-core</artifactId>
|
||||||
<version>6.6.1</version>
|
<version>6.6.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
|
|
|
@ -141,25 +141,4 @@ public class AuthTokenManager {
|
||||||
|
|
||||||
return ID.valueOf(descs[1]);
|
return ID.valueOf(descs[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新 AccessToken 延长有效期
|
|
||||||
*
|
|
||||||
* @param token
|
|
||||||
* @return
|
|
||||||
* @see #verifyToken(String, boolean, boolean)
|
|
||||||
* @deprecated Use {@link #verifyToken(String, boolean, boolean)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static ID refreshAccessToken(String token) {
|
|
||||||
Assert.notNull(token, "[token] cannot be null");
|
|
||||||
String desc = Application.getCommonsCache().get(TOKEN_PREFIX + token);
|
|
||||||
if (desc == null) return null;
|
|
||||||
|
|
||||||
String[] descs = desc.split(",");
|
|
||||||
Assert.isTrue(TYPE_ACCESS_TOKEN.equals(descs[0]), "Cannot refresh none access token");
|
|
||||||
|
|
||||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, ACCESSTOKEN_EXPIRES);
|
|
||||||
return ID.valueOf(descs[1]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
||||||
/**
|
/**
|
||||||
* Rebuild Version
|
* Rebuild Version
|
||||||
*/
|
*/
|
||||||
public static final String VER = "3.7.7";
|
public static final String VER = "3.8.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 = 3070713;
|
public static final int BUILD = 3080001;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Driver for DB
|
// Driver for DB
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class BootApplication extends SpringBootServletInitializer {
|
||||||
try {
|
try {
|
||||||
initTomcatPort();
|
initTomcatPort();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.debug("Cannot to get Tomcat port : " + ex.getLocalizedMessage());
|
log.debug("Cannot to get Tomcat port : {}", ex.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Initializing SpringBoot context {}...", devMode() ? "(dev) " : "");
|
log.info("Initializing SpringBoot context {}...", devMode() ? "(dev) " : "");
|
||||||
|
|
|
@ -18,7 +18,7 @@ import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.RandomAccessFile;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -114,22 +114,23 @@ public final class ServerStatus {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
static Status checkCreateFile() {
|
static Status checkCreateFile() {
|
||||||
String name = "Create File";
|
String name = "CreateFile";
|
||||||
FileWriter fw = null;
|
File test = null;
|
||||||
|
RandomAccessFile raf = null;
|
||||||
try {
|
try {
|
||||||
File test = new File(FileUtils.getTempDirectory(), "ServerStatus.test");
|
test = new File(FileUtils.getTempDirectory(), "ServerStatus.test");
|
||||||
fw = new FileWriter(test);
|
raf = new RandomAccessFile(test, "rw");
|
||||||
IOUtils.write(CodecUtils.randomCode(1024), fw);
|
raf.setLength(1024 * 1024 * 5); // 5M
|
||||||
|
|
||||||
if (!test.exists()) {
|
if (!test.exists()) {
|
||||||
return Status.error(name, "Cannot create file in temp-directory");
|
return Status.error(name, "Cannot create file in temp-directory");
|
||||||
} else {
|
|
||||||
FileUtils.deleteQuietly(test);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return Status.error(name, ThrowableUtils.getRootCause(ex).getLocalizedMessage());
|
return Status.error(name, ThrowableUtils.getRootCause(ex).getLocalizedMessage());
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(fw);
|
if (raf != null) IOUtils.closeQuietly(raf);
|
||||||
|
if (test != null) FileUtils.deleteQuietly(test);
|
||||||
}
|
}
|
||||||
return Status.success(name);
|
return Status.success(name);
|
||||||
}
|
}
|
||||||
|
@ -184,9 +185,9 @@ public final class ServerStatus {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
log.debug("Checking " + this);
|
log.debug("Checking {}", this);
|
||||||
} else {
|
} else {
|
||||||
log.error("Checking " + this);
|
log.error("Checking {}", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -516,8 +516,10 @@ public class NavBuilder extends NavManager {
|
||||||
for (Object[] nd : topNav) {
|
for (Object[] nd : topNav) {
|
||||||
String url = AppUtils.getContextPath("/app/home?def=" + nd[0]);
|
String url = AppUtils.getContextPath("/app/home?def=" + nd[0]);
|
||||||
if (nd[1] != null) url += ":" + nd[1];
|
if (nd[1] != null) url += ":" + nd[1];
|
||||||
|
|
||||||
topNavHtml.append(String.format(
|
topNavHtml.append(String.format(
|
||||||
"<li class=\"nav-item\" data-id=\"%s\"><a class=\"nav-link text-ellipsis\" href=\"%s\">%s</a></li>", nd[0], url, nd[2]));
|
"<li class=\"nav-item\" data-id=\"%s\"><a class=\"nav-link text-ellipsis\" href=\"%s\">%s</a></li>",
|
||||||
|
nd[0], url, CommonsUtils.escapeHtml(nd[2])));
|
||||||
}
|
}
|
||||||
return topNavHtml.toString();
|
return topNavHtml.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyN2NReference;
|
import com.rebuild.core.metadata.easymeta.EasyN2NReference;
|
||||||
import com.rebuild.core.metadata.easymeta.MixValue;
|
import com.rebuild.core.metadata.easymeta.MixValue;
|
||||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
|
import com.rebuild.core.support.general.CalcFormulaSupport;
|
||||||
import com.rebuild.core.support.general.FieldValueHelper;
|
import com.rebuild.core.support.general.FieldValueHelper;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -47,7 +48,8 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单自动回填
|
* 表单自动回填。
|
||||||
|
* 请注意此功能的优先级:回填实在构建 Record 时发生,因此相对触发器,其优先级较低
|
||||||
*
|
*
|
||||||
* @author devezhao zhaofang123@gmail.com
|
* @author devezhao zhaofang123@gmail.com
|
||||||
* @since 2019/05/17
|
* @since 2019/05/17
|
||||||
|
@ -237,6 +239,10 @@ public class AutoFillinManager implements ConfigManager {
|
||||||
|
|
||||||
fillin += fillinRecordItem(easyField.getRawMeta(), record.getObjectValue(fieldName), isNew, fillinForce, record);
|
fillin += fillinRecordItem(easyField.getRawMeta(), record.getObjectValue(fieldName), isNew, fillinForce, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v3.8 借用贵宝地
|
||||||
|
CalcFormulaSupport.calcFormulaBackend(record);
|
||||||
|
|
||||||
return fillin;
|
return fillin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,25 +57,43 @@ public class ClassificationManager implements ConfigManager {
|
||||||
return item == null ? null : item.FullName;
|
return item == null ? null : item.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param itemId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getColor(ID itemId) {
|
||||||
|
Item item = getItem(itemId);
|
||||||
|
return item == null ? null : item.Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param itemId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getCode(ID itemId) {
|
||||||
|
Item item = getItem(itemId);
|
||||||
|
return item == null ? null : item.Code;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param itemId
|
* @param itemId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Item getItem(ID itemId) {
|
private Item getItem(ID itemId) {
|
||||||
final String ckey = "ClassificationITEM31-" + itemId;
|
final String ckey = "ClassificationITEM38-" + itemId;
|
||||||
Item ditem = (Item) Application.getCommonsCache().getx(ckey);
|
Item ditem = (Item) Application.getCommonsCache().getx(ckey);
|
||||||
if (ditem != null) {
|
if (ditem != null) {
|
||||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] o = Application.createQueryNoFilter(
|
Object[] o = Application.createQueryNoFilter(
|
||||||
"select name,fullName,code from ClassificationData where itemId = ?")
|
"select name,fullName,code,color from ClassificationData where itemId = ?")
|
||||||
.setParameter(1, itemId)
|
.setParameter(1, itemId)
|
||||||
.unique();
|
.unique();
|
||||||
if (o != null) ditem = new Item((String) o[0], (String) o[1], (String) o[2]);
|
if (o != null) ditem = new Item((String) o[0], (String) o[1], (String) o[2], (String) o[3]);
|
||||||
|
|
||||||
// 可能已删除
|
// 可能已删除
|
||||||
if (ditem == null) ditem = new Item(DELETED_ITEM, null, null);
|
if (ditem == null) ditem = new Item(DELETED_ITEM, null, null, null);
|
||||||
|
|
||||||
Application.getCommonsCache().putx(ckey, ditem);
|
Application.getCommonsCache().putx(ckey, ditem);
|
||||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||||
|
@ -163,7 +181,7 @@ public class ClassificationManager implements ConfigManager {
|
||||||
public void clean(Object cid) {
|
public void clean(Object cid) {
|
||||||
ID id2 = (ID) cid;
|
ID id2 = (ID) cid;
|
||||||
if (id2.getEntityCode() == EntityHelper.ClassificationData) {
|
if (id2.getEntityCode() == EntityHelper.ClassificationData) {
|
||||||
Application.getCommonsCache().evict("ClassificationITEM31-" + cid);
|
Application.getCommonsCache().evict("ClassificationITEM38-" + cid);
|
||||||
} else if (id2.getEntityCode() == EntityHelper.Classification) {
|
} else if (id2.getEntityCode() == EntityHelper.Classification) {
|
||||||
Application.getCommonsCache().evict("ClassificationLEVEL-" + cid);
|
Application.getCommonsCache().evict("ClassificationLEVEL-" + cid);
|
||||||
}
|
}
|
||||||
|
@ -172,14 +190,16 @@ public class ClassificationManager implements ConfigManager {
|
||||||
// Bean
|
// Bean
|
||||||
static class Item implements Serializable {
|
static class Item implements Serializable {
|
||||||
private static final long serialVersionUID = -1903227875771376652L;
|
private static final long serialVersionUID = -1903227875771376652L;
|
||||||
Item(String name, String fullName, String code) {
|
Item(String name, String fullName, String code, String color) {
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
this.FullName = fullName;
|
this.FullName = fullName;
|
||||||
this.Code = code;
|
this.Code = code;
|
||||||
|
this.Color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String Name;
|
final String Name;
|
||||||
final String FullName;
|
final String FullName;
|
||||||
final String Code;
|
final String Code;
|
||||||
|
final String Color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,12 +77,7 @@ public class DataListCategory {
|
||||||
|
|
||||||
// 使用全部
|
// 使用全部
|
||||||
if (dt == DisplayType.MULTISELECT || dt == DisplayType.PICKLIST) {
|
if (dt == DisplayType.MULTISELECT || dt == DisplayType.PICKLIST) {
|
||||||
ConfigBean[] cbs = MultiSelectManager.instance.getPickListRaw(categoryField, true);
|
dataList = datasOptions(categoryField, dt);
|
||||||
for (ConfigBean cb : cbs) {
|
|
||||||
Object id = cb.getID("id");
|
|
||||||
if (dt == DisplayType.MULTISELECT) id = cb.getLong("mask");
|
|
||||||
dataList.add(new Item(id, cb.getString("text")));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (dt == DisplayType.CLASSIFICATION) {
|
} else if (dt == DisplayType.CLASSIFICATION) {
|
||||||
// 分类
|
// 分类
|
||||||
|
@ -153,6 +148,24 @@ public class DataListCategory {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for PICKLIST, MULTISELECT
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* @param dt
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Collection<Item> datasOptions(Field field, DisplayType dt) {
|
||||||
|
Collection<Item> dataList = new LinkedHashSet<>();
|
||||||
|
ConfigBean[] cbs = MultiSelectManager.instance.getPickListRaw(field, true);
|
||||||
|
for (ConfigBean cb : cbs) {
|
||||||
|
Object id = cb.getID("id");
|
||||||
|
if (dt == DisplayType.MULTISELECT) id = cb.getLong("mask");
|
||||||
|
dataList.add(new Item(id, cb.getString("text")));
|
||||||
|
}
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Max. 4L
|
* Max. 4L
|
||||||
* @param field
|
* @param field
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*!
|
||||||
|
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.configuration.general;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
|
import cn.devezhao.commons.ObjectUtils;
|
||||||
|
import cn.devezhao.persist4j.Entity;
|
||||||
|
import cn.devezhao.persist4j.Field;
|
||||||
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.rebuild.core.Application;
|
||||||
|
import com.rebuild.core.configuration.general.DataListCategory.Item;
|
||||||
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
|
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||||
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
|
import com.rebuild.core.support.general.FieldValueHelper;
|
||||||
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据列表分组查询
|
||||||
|
*
|
||||||
|
* @author ZHAO
|
||||||
|
* @since 07/27/2024
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DataListCategory38 {
|
||||||
|
|
||||||
|
public static final DataListCategory38 instance = new DataListCategory38();
|
||||||
|
|
||||||
|
private DataListCategory38() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entity
|
||||||
|
* @param parentValues
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JSON datas(Entity entity, Object[] parentValues) {
|
||||||
|
final String categoryField = getSettings(entity);
|
||||||
|
if (categoryField == null) return null;
|
||||||
|
|
||||||
|
// 查第几个字段
|
||||||
|
int fieldIndex = parentValues == null ? 0 : parentValues.length;
|
||||||
|
// 特殊处理:引用字段父级
|
||||||
|
if (fieldIndex > 0 && categoryField.split(";").length == 1) {
|
||||||
|
String[] ff = categoryField.split(":");
|
||||||
|
Field fieldMeta = entity.getField(ff[0]);
|
||||||
|
if (ff.length == 2
|
||||||
|
&& entity.containsField(ff[0]) && fieldMeta.getReferenceEntity().containsField(ff[1])) {
|
||||||
|
fieldIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String[]> categoryFields = new ArrayList<>();
|
||||||
|
for (String ff : categoryField.split(";")) {
|
||||||
|
String[] fieldAndFormat = ff.split(":");
|
||||||
|
if (!entity.containsField(fieldAndFormat[0])) {
|
||||||
|
throw new MissingMetaExcetion(entity.getName(), fieldAndFormat[0]);
|
||||||
|
}
|
||||||
|
categoryFields.add(fieldAndFormat);
|
||||||
|
}
|
||||||
|
if (fieldIndex + 1 > categoryFields.size()) return null;
|
||||||
|
|
||||||
|
// 使用配置
|
||||||
|
String[] ff = categoryFields.get(fieldIndex);
|
||||||
|
final String useField = ff[0];
|
||||||
|
String useFormat = ff.length > 1 ? ff[1] : null;
|
||||||
|
final Field useFieldMeta = entity.getField(useField);
|
||||||
|
final DisplayType dt = EasyMetaFactory.getDisplayType(useFieldMeta);
|
||||||
|
|
||||||
|
// 单个字段
|
||||||
|
final boolean isSingleLevel = categoryFields.size() == 1;
|
||||||
|
// 分类,引用(父级)支持树状
|
||||||
|
if (!isSingleLevel && useFormat != null) {
|
||||||
|
if (dt == DisplayType.CLASSIFICATION || dt == DisplayType.REFERENCE) {
|
||||||
|
log.warn("When multiple category fields, the format is disabled : {}", categoryField);
|
||||||
|
useFormat = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<Item> dataList;
|
||||||
|
// 是否还有下级需要加载
|
||||||
|
boolean hasChild = false;
|
||||||
|
// 是否排序
|
||||||
|
int sortMode = 0;
|
||||||
|
|
||||||
|
// 单字段适用:选项类的
|
||||||
|
if ((dt == DisplayType.MULTISELECT || dt == DisplayType.PICKLIST)) {
|
||||||
|
dataList = DataListCategory.instance.datasOptions(useFieldMeta, dt);
|
||||||
|
hasChild = categoryFields.size() > fieldIndex + 1;
|
||||||
|
}
|
||||||
|
// 单字段适用:分类字段
|
||||||
|
else if (dt == DisplayType.CLASSIFICATION && isSingleLevel) {
|
||||||
|
dataList = DataListCategory.instance.datasClassification(useFieldMeta, useFormat);
|
||||||
|
// 开放了几级
|
||||||
|
int level = ClassificationManager.instance.getOpenLevel(useFieldMeta);
|
||||||
|
int levelSpec = useFormat == null ? level : ObjectUtils.toInt(useFormat);
|
||||||
|
if (levelSpec < level) level = levelSpec;
|
||||||
|
if (level > 0) hasChild = true;
|
||||||
|
}
|
||||||
|
// 单字段适用:引用字段父级
|
||||||
|
else if (dt == DisplayType.REFERENCE && useFormat != null) {
|
||||||
|
dataList = datasReference(useFieldMeta, useFormat,
|
||||||
|
parentValues == null ? null : parentValues[parentValues.length - 1]);
|
||||||
|
hasChild = true;
|
||||||
|
sortMode = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sortMode = 1;
|
||||||
|
String sql;
|
||||||
|
if (dt == DisplayType.N2NREFERENCE) {
|
||||||
|
sql = String.format(
|
||||||
|
"select distinct referenceId from NreferenceItem where belongEntity = '%s' and belongField = '%s'",
|
||||||
|
entity.getName(), useField);
|
||||||
|
// N级
|
||||||
|
if (parentValues != null) {
|
||||||
|
String nestSql = String.format("select %s from %s where %s",
|
||||||
|
entity.getPrimaryField().getName(), entity.getName(),
|
||||||
|
buildParentFilters(entity, categoryFields, parentValues));
|
||||||
|
sql += String.format(" and recordId in ( %s )", nestSql);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
String wrapField = useField;
|
||||||
|
// 日期格式
|
||||||
|
if (dt == DisplayType.DATETIME || dt == DisplayType.DATE) {
|
||||||
|
// DATE 使用字段设置的
|
||||||
|
if (useFormat == null) {
|
||||||
|
useFormat = EasyMetaFactory.valueOf(useFieldMeta).getExtraAttr(EasyFieldConfigProps.DATE_FORMAT);
|
||||||
|
}
|
||||||
|
if ("yyyy".equalsIgnoreCase(useFormat)) wrapField = String.format("DATE_FORMAT(%s, '%%Y')", useField);
|
||||||
|
else if ("yyyy-MM".equalsIgnoreCase(useFormat)) wrapField = String.format("DATE_FORMAT(%s, '%%Y-%%m')", useField);
|
||||||
|
else wrapField = String.format("DATE_FORMAT(%s, '%%Y-%%m-%%d')", useField);
|
||||||
|
|
||||||
|
sortMode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = MessageFormat.format(
|
||||||
|
"select distinct {0} from {1} where {2} is not null",
|
||||||
|
wrapField, entity.getName(), useField);
|
||||||
|
|
||||||
|
if (parentValues != null) {
|
||||||
|
sql += " and " + buildParentFilters(entity, categoryFields, parentValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[][] array = Application.createQuery(sql).array();
|
||||||
|
|
||||||
|
dataList = new LinkedHashSet<>();
|
||||||
|
for (Object[] o : array) {
|
||||||
|
Object id = o[0];
|
||||||
|
String label;
|
||||||
|
if (id instanceof Date) {
|
||||||
|
String dateFormat = StringUtils.defaultIfBlank(useFormat, CalendarUtils.UTC_DATE_FORMAT);
|
||||||
|
label = CalendarUtils.format(dateFormat, (Date) id);
|
||||||
|
} else if (id instanceof ID) {
|
||||||
|
label = FieldValueHelper.getLabelNotry((ID) id);
|
||||||
|
} else {
|
||||||
|
label = id.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
dataList.add(new Item(id, label));
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChild = categoryFields.size() > fieldIndex + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray res = new JSONArray();
|
||||||
|
for (Item i : dataList) res.add(i.toJSON(0));
|
||||||
|
// 排序
|
||||||
|
if (sortMode == 2 || sortMode == 1) {
|
||||||
|
final boolean isDesc = sortMode == 2;
|
||||||
|
res.sort((o1, o2) -> {
|
||||||
|
String text1 = ((JSONObject) o1).getString("text");
|
||||||
|
String text2 = ((JSONObject) o2).getString("text");
|
||||||
|
return isDesc ? text2.compareTo(text1) : text1.compareTo(text2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSONUtils.toJSONObject(
|
||||||
|
new String[]{ "hasChild", "data" },
|
||||||
|
new Object[]{ hasChild, res });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field
|
||||||
|
* @param parentField
|
||||||
|
* @param parentValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Collection<Item> datasReference(Field field, String parentField, Object parentValue) {
|
||||||
|
Field parentFieldMeta = field.getReferenceEntity().getField(parentField);
|
||||||
|
String sql = MessageFormat.format(
|
||||||
|
"select {0} from {1} where {2}",
|
||||||
|
parentFieldMeta.getOwnEntity().getPrimaryField().getName(),
|
||||||
|
parentFieldMeta.getOwnEntity().getName(),
|
||||||
|
parentFieldMeta.getName());
|
||||||
|
if (parentValue == null) sql += " is null";
|
||||||
|
else sql += " = '" + parentValue + "'";
|
||||||
|
|
||||||
|
Object[][] array = Application.createQuery(sql).array();
|
||||||
|
|
||||||
|
Collection<Item> dataList = new LinkedHashSet<>();
|
||||||
|
for (Object[] o : array) {
|
||||||
|
Object id = o[0];
|
||||||
|
String label = FieldValueHelper.getLabelNotry((ID) id);
|
||||||
|
dataList.add(new Item(id, label));
|
||||||
|
}
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param categoryFields
|
||||||
|
* @param parentValues
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected String buildParentFilters(Entity entity, List<String[]> categoryFields, Object[] parentValues) {
|
||||||
|
List<String> and = new ArrayList<>();
|
||||||
|
for (int i = 0; i < parentValues.length; i++) {
|
||||||
|
String[] ff = categoryFields.get(i);
|
||||||
|
String fieldName = ff[0];
|
||||||
|
String fieldValue = parentValues[i].toString();
|
||||||
|
Field fieldMeta = entity.getField(fieldName);
|
||||||
|
DisplayType dt = EasyMetaFactory.getDisplayType(fieldMeta);
|
||||||
|
|
||||||
|
// 一级树:引用
|
||||||
|
if (categoryFields.size() == 1 && dt == DisplayType.REFERENCE) {
|
||||||
|
fieldValue = parentValues[parentValues.length - 1].toString();
|
||||||
|
return String.format("%s = '%s'", fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
// 一级树:分类
|
||||||
|
if (categoryFields.size() == 1 && dt == DisplayType.CLASSIFICATION) {
|
||||||
|
fieldValue = parentValues[parentValues.length - 1].toString();
|
||||||
|
int level = ClassificationManager.instance.getOpenLevel(fieldMeta);
|
||||||
|
// 用 or 提高数据兼容性
|
||||||
|
List<String> parentSql = new ArrayList<>();
|
||||||
|
parentSql.add(String.format("%s = '%s'", fieldName, fieldValue));
|
||||||
|
if (level > 0) parentSql.add(String.format("%s.parent = '%s'", fieldName, fieldValue));
|
||||||
|
if (level > 1) parentSql.add(String.format("%s.parent.parent = '%s'", fieldName, fieldValue));
|
||||||
|
if (level > 2) parentSql.add(String.format("%s.parent.parent.parent = '%s'", fieldName, fieldValue));
|
||||||
|
return "( " + StringUtils.join(parentSql, " or ") + " )";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dt == DisplayType.DATETIME || dt == DisplayType.DATE) {
|
||||||
|
String s = fieldValue + "0000-01-01 00:00:00".substring(fieldValue.length());
|
||||||
|
String e = fieldValue + "0000-12-31 23:59:59".substring(fieldValue.length());
|
||||||
|
if (dt == DisplayType.DATE) {
|
||||||
|
s = s.substring(0, 10);
|
||||||
|
e = e.substring(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
String filter = MessageFormat.format("({0} >= ''{1}'' and {0} <= ''{2}'')", fieldName, s, e);
|
||||||
|
and.add(filter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dt == DisplayType.MULTISELECT) {
|
||||||
|
String filter = String.format("%s && %d", fieldName, ObjectUtils.toInt(fieldValue));
|
||||||
|
and.add(filter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dt == DisplayType.N2NREFERENCE) {
|
||||||
|
String filter = String.format(
|
||||||
|
"exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId = '%s')",
|
||||||
|
entity.getPrimaryField().getName(), fieldMeta.getName(), fieldValue);
|
||||||
|
and.add(filter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String simple = String.format("%s = '%s'", fieldName, fieldValue);
|
||||||
|
and.add(simple);
|
||||||
|
}
|
||||||
|
return "( " + StringUtils.join(and, " and ") + " )";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entity
|
||||||
|
* @param parentValues
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String buildParentFilters(Entity entity, Object[] parentValues) {
|
||||||
|
final String categoryField = getSettings(entity);
|
||||||
|
if (categoryField == null) return null;
|
||||||
|
|
||||||
|
List<String[]> categoryFields = new ArrayList<>();
|
||||||
|
for (String ff : categoryField.split(";")) {
|
||||||
|
categoryFields.add(ff.split(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildParentFilters(entity, categoryFields, parentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSettings(Entity entity) {
|
||||||
|
int listMode = ObjectUtils.toInt(EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADVLIST_MODE), 1);
|
||||||
|
String categoryField;
|
||||||
|
if (listMode == 3) {
|
||||||
|
categoryField = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADVLIST_MODE3_SHOWCATEGORY);
|
||||||
|
} else {
|
||||||
|
categoryField = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADVLIST_SHOWCATEGORY);
|
||||||
|
}
|
||||||
|
return StringUtils.isBlank(categoryField) ? null : categoryField;
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,4 +77,11 @@ public class EasyActionManager extends BaseLayoutManager {
|
||||||
public ConfigBean getEasyActionRaw(String entity) {
|
public ConfigBean getEasyActionRaw(String entity) {
|
||||||
return getLayout(UserService.SYSTEM_USER, entity, TYPE_EASYACTION, null);
|
return getLayout(UserService.SYSTEM_USER, entity, TYPE_EASYACTION, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clean(Object layoutId) {
|
||||||
|
super.clean(layoutId);
|
||||||
|
|
||||||
|
// TODO JS 支持 ES6 > ES5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ 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.metadata.impl.EasyFieldConfigProps;
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
|
import com.rebuild.core.privileges.FieldPrivileges;
|
||||||
import com.rebuild.core.privileges.UserFilters;
|
import com.rebuild.core.privileges.UserFilters;
|
||||||
import com.rebuild.core.privileges.bizz.Department;
|
import com.rebuild.core.privileges.bizz.Department;
|
||||||
import com.rebuild.core.privileges.bizz.User;
|
import com.rebuild.core.privileges.bizz.User;
|
||||||
|
@ -129,7 +130,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 明细实体有主实体
|
// 明细实体
|
||||||
final Entity hasMainEntity = entityMeta.getMainEntity();
|
final Entity hasMainEntity = entityMeta.getMainEntity();
|
||||||
// 审批流程(状态)
|
// 审批流程(状态)
|
||||||
ApprovalState approvalState;
|
ApprovalState approvalState;
|
||||||
|
@ -142,7 +143,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
if (recordId == null) {
|
if (recordId == null) {
|
||||||
if (hasMainEntity != null) {
|
if (hasMainEntity != null) {
|
||||||
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
||||||
Assert.notNull(mainid, "Call `FormBuilderContextHolder#setMainIdOfDetail` first!");
|
Assert.notNull(mainid, "CALL `FormBuilderContextHolder#setMainIdOfDetail` FIRST!");
|
||||||
|
|
||||||
approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid);
|
approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid);
|
||||||
if ((approvalState == ApprovalState.PROCESSING || approvalState == ApprovalState.APPROVED)) {
|
if ((approvalState == ApprovalState.PROCESSING || approvalState == ApprovalState.APPROVED)) {
|
||||||
|
@ -176,7 +177,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
// 编辑
|
// 编辑
|
||||||
else {
|
else {
|
||||||
if (!Application.getPrivilegesManager().allowUpdate(user, recordId)) {
|
if (!Application.getPrivilegesManager().allowUpdate(user, recordId)) {
|
||||||
return formatModelError(Language.L("你没有修改此记录的权限"));
|
return formatModelError(Language.L("你没有编辑此记录的权限"));
|
||||||
}
|
}
|
||||||
|
|
||||||
approvalState = getHadApproval(entityMeta, recordId);
|
approvalState = getHadApproval(entityMeta, recordId);
|
||||||
|
@ -205,8 +206,8 @@ public class FormsBuilder extends FormsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int applyType = recordId == null ? FormsManager.APPLY_ONNEW : FormsManager.APPLY_ONEDIT;
|
int applyType = recordId == null ? FormsManager.APPLY_NEW : FormsManager.APPLY_EDIT;
|
||||||
if (viewMode) applyType = FormsManager.APPLY_ONVIEW;
|
if (viewMode) applyType = FormsManager.APPLY_VIEW;
|
||||||
|
|
||||||
ConfigBean model = getFormLayout(entity, recordOrLayoutId, applyType);
|
ConfigBean model = getFormLayout(entity, recordOrLayoutId, applyType);
|
||||||
JSONArray elements = (JSONArray) model.getJSON("elements");
|
JSONArray elements = (JSONArray) model.getJSON("elements");
|
||||||
|
@ -353,6 +354,10 @@ public class FormsBuilder extends FormsManager {
|
||||||
final boolean isNew = recordData == null || recordData.getPrimary() == null
|
final boolean isNew = recordData == null || recordData.getPrimary() == null
|
||||||
|| EntityHelper.isUnsavedId(recordData.getPrimary());
|
|| EntityHelper.isUnsavedId(recordData.getPrimary());
|
||||||
|
|
||||||
|
final FieldPrivileges fp = Application.getPrivilegesManager().getFieldPrivileges();
|
||||||
|
// 在共同编辑时,对于明细应该是编辑而非新建
|
||||||
|
final boolean isProTableLayout = FormsBuilderContextHolder.getMainIdOfDetail(false) != null;
|
||||||
|
|
||||||
// Check and clean
|
// Check and clean
|
||||||
for (Iterator<Object> iter = elements.iterator(); iter.hasNext(); ) {
|
for (Iterator<Object> iter = elements.iterator(); iter.hasNext(); ) {
|
||||||
JSONObject el = (JSONObject) iter.next();
|
JSONObject el = (JSONObject) iter.next();
|
||||||
|
@ -462,18 +467,15 @@ public class FormsBuilder extends FormsManager {
|
||||||
el.put("options", ObjectUtils.defaultIfNull(el.remove("tagList"), JSONUtils.EMPTY_ARRAY));
|
el.put("options", ObjectUtils.defaultIfNull(el.remove("tagList"), JSONUtils.EMPTY_ARRAY));
|
||||||
} else if (dt == DisplayType.DATETIME) {
|
} else if (dt == DisplayType.DATETIME) {
|
||||||
String format = StringUtils.defaultIfBlank(
|
String format = StringUtils.defaultIfBlank(
|
||||||
easyField.getExtraAttr(EasyFieldConfigProps.DATETIME_FORMAT),
|
easyField.getExtraAttr(EasyFieldConfigProps.DATETIME_FORMAT), dt.getDefaultFormat());
|
||||||
easyField.getDisplayType().getDefaultFormat());
|
|
||||||
el.put(EasyFieldConfigProps.DATETIME_FORMAT, format);
|
el.put(EasyFieldConfigProps.DATETIME_FORMAT, format);
|
||||||
} else if (dt == DisplayType.DATE) {
|
} else if (dt == DisplayType.DATE) {
|
||||||
String format = StringUtils.defaultIfBlank(
|
String format = StringUtils.defaultIfBlank(
|
||||||
easyField.getExtraAttr(EasyFieldConfigProps.DATE_FORMAT),
|
easyField.getExtraAttr(EasyFieldConfigProps.DATE_FORMAT), dt.getDefaultFormat());
|
||||||
easyField.getDisplayType().getDefaultFormat());
|
|
||||||
el.put(EasyFieldConfigProps.DATE_FORMAT, format);
|
el.put(EasyFieldConfigProps.DATE_FORMAT, format);
|
||||||
} else if (dt == DisplayType.TIME) {
|
} else if (dt == DisplayType.TIME) {
|
||||||
String format = StringUtils.defaultIfBlank(
|
String format = StringUtils.defaultIfBlank(
|
||||||
easyField.getExtraAttr(EasyFieldConfigProps.TIME_FORMAT),
|
easyField.getExtraAttr(EasyFieldConfigProps.TIME_FORMAT), dt.getDefaultFormat());
|
||||||
easyField.getDisplayType().getDefaultFormat());
|
|
||||||
el.put(EasyFieldConfigProps.TIME_FORMAT, format);
|
el.put(EasyFieldConfigProps.TIME_FORMAT, format);
|
||||||
} else if (dt == DisplayType.CLASSIFICATION) {
|
} else if (dt == DisplayType.CLASSIFICATION) {
|
||||||
el.put("openLevel", ClassificationManager.instance.getOpenLevel(fieldMeta));
|
el.put("openLevel", ClassificationManager.instance.getOpenLevel(fieldMeta));
|
||||||
|
@ -529,7 +531,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
if (defaultValue != null) {
|
if (defaultValue != null) {
|
||||||
defaultValue = easyField.wrapValue(defaultValue);
|
defaultValue = easyField.wrapValue(defaultValue);
|
||||||
// `wrapValue` 会添加格式符号
|
// `wrapValue` 会添加格式符号
|
||||||
if (easyField.getDisplayType() == DisplayType.DECIMAL) {
|
if (dt == DisplayType.DECIMAL || dt == DisplayType.NUMBER) {
|
||||||
defaultValue = EasyDecimal.clearFlaged(defaultValue);
|
defaultValue = EasyDecimal.clearFlaged(defaultValue);
|
||||||
}
|
}
|
||||||
el.put("value", defaultValue);
|
el.put("value", defaultValue);
|
||||||
|
@ -570,7 +572,7 @@ public class FormsBuilder extends FormsManager {
|
||||||
Object value = wrapFieldValue(recordData, easyField, user);
|
Object value = wrapFieldValue(recordData, easyField, user);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
// `wrapValue` 会添加格式符号
|
// `wrapValue` 会添加格式符号
|
||||||
if (!viewModel && easyField.getDisplayType() == DisplayType.DECIMAL) {
|
if (!viewModel && (dt == DisplayType.DECIMAL || dt == DisplayType.NUMBER)) {
|
||||||
value = EasyDecimal.clearFlaged(value);
|
value = EasyDecimal.clearFlaged(value);
|
||||||
}
|
}
|
||||||
el.put("value", value);
|
el.put("value", value);
|
||||||
|
@ -595,6 +597,14 @@ public class FormsBuilder extends FormsManager {
|
||||||
if (decimalType != null && decimalType.contains("%s")) {
|
if (decimalType != null && decimalType.contains("%s")) {
|
||||||
el.put("decimalType", decimalType.replace("%s", ""));
|
el.put("decimalType", decimalType.replace("%s", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v3.8
|
||||||
|
if (isNew && !isProTableLayout) {
|
||||||
|
if (!fp.isCreatable(fieldMeta, user)) el.put("readonly", true);
|
||||||
|
} else {
|
||||||
|
if (!fp.isReadble(fieldMeta, user)) iter.remove();
|
||||||
|
else if (!fp.isUpdatable(fieldMeta, user)) el.put("readonly", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -747,8 +757,9 @@ public class FormsBuilder extends FormsManager {
|
||||||
}
|
}
|
||||||
// 其他
|
// 其他
|
||||||
else if (entity.containsField(field)) {
|
else if (entity.containsField(field)) {
|
||||||
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(field));
|
final EasyField easyField = EasyMetaFactory.valueOf(entity.getField(field));
|
||||||
if (easyField.getDisplayType() == DisplayType.REFERENCE || easyField.getDisplayType() == DisplayType.N2NREFERENCE) {
|
final DisplayType dt = easyField.getDisplayType();
|
||||||
|
if (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE) {
|
||||||
// v3.4 如果字段设置了附加过滤条件,从相关项新建时要检查是否符合
|
// v3.4 如果字段设置了附加过滤条件,从相关项新建时要检查是否符合
|
||||||
if (!FieldValueHelper.checkRefDataFilter(easyField, ID.valueOf(value))) {
|
if (!FieldValueHelper.checkRefDataFilter(easyField, ID.valueOf(value))) {
|
||||||
((JSONObject) formModel).put("alertMessage",
|
((JSONObject) formModel).put("alertMessage",
|
||||||
|
@ -758,7 +769,7 @@ 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) {
|
||||||
if (easyField.getDisplayType() == DisplayType.REFERENCE) {
|
if (dt == DisplayType.REFERENCE) {
|
||||||
initialValReady.put(field, mixValue);
|
initialValReady.put(field, mixValue);
|
||||||
} else {
|
} else {
|
||||||
// N2N 是数组
|
// N2N 是数组
|
||||||
|
|
|
@ -35,16 +35,16 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
protected FormsManager() {}
|
protected FormsManager() {}
|
||||||
|
|
||||||
// 表单布局适用于
|
// 表单布局适用于
|
||||||
public static int APPLY_ONNEW = 1;
|
public static int APPLY_NEW = 1;
|
||||||
public static int APPLY_ONEDIT = 2;
|
public static int APPLY_EDIT = 2;
|
||||||
public static int APPLY_ONVIEW = 4;
|
public static int APPLY_VIEW = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param entity
|
* @param entity
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ConfigBean getNewFormLayout(String entity) {
|
public ConfigBean getNewFormLayout(String entity) {
|
||||||
return getFormLayout(entity, null, APPLY_ONNEW);
|
return getFormLayout(entity, null, APPLY_NEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,26 +62,21 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
|
|
||||||
// 1.指定布局
|
// 1.指定布局
|
||||||
if (recordOrLayoutId != null && recordOrLayoutId.getEntityCode() == EntityHelper.LayoutConfig) {
|
if (recordOrLayoutId != null && recordOrLayoutId.getEntityCode() == EntityHelper.LayoutConfig) {
|
||||||
for (Object[] o : alls) {
|
use = findConfigBean(alls, recordOrLayoutId);
|
||||||
if (recordOrLayoutId.equals(o[0])) {
|
|
||||||
use = findConfigBean(alls, (ID) o[0]);;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (use == null) {
|
if (use == null) {
|
||||||
log.warn("Spec layout not longer exists : {}", recordOrLayoutId);
|
log.warn("Spec layout not longer exists : {}", recordOrLayoutId);
|
||||||
recordOrLayoutId = null;
|
recordOrLayoutId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.使用布局
|
// 2.查找布局
|
||||||
if (use == null) {
|
if (use == null) {
|
||||||
|
// 优先使用条件匹配的
|
||||||
for (Object[] o : alls) {
|
for (Object[] o : alls) {
|
||||||
ConfigBean cb = findConfigBean(alls, (ID) o[0]);
|
ConfigBean cb = findConfigBean(alls, (ID) o[0]);
|
||||||
ShareToAttr attr = new ShareToAttr(cb);
|
ShareToAttr attr = new ShareToAttr(cb);
|
||||||
if (recordOrLayoutId == null) {
|
if (recordOrLayoutId == null) {
|
||||||
if (attr.isFallback()) {
|
if (attr.isFallback() || attr.isForNew()) {
|
||||||
use = cb;
|
use = cb;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -92,9 +87,14 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 默认优先级
|
||||||
|
if (recordOrLayoutId == null) {
|
||||||
|
use = findDefault(alls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.默认布局(fallback)
|
// 3.默认布局
|
||||||
if (use == null && recordOrLayoutId != null) {
|
if (use == null && recordOrLayoutId != null) {
|
||||||
for (Object[] o : alls) {
|
for (Object[] o : alls) {
|
||||||
ConfigBean cb = findConfigBean(alls, (ID) o[0]);
|
ConfigBean cb = findConfigBean(alls, (ID) o[0]);
|
||||||
|
@ -121,6 +121,16 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
.set("elements", JSONUtils.EMPTY_ARRAY);
|
.set("elements", JSONUtils.EMPTY_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 默认优先级布局
|
||||||
|
private ConfigBean findDefault(Object[][] alls) {
|
||||||
|
for (Object[] o : alls) {
|
||||||
|
ConfigBean cb = findConfigBean(alls, (ID) o[0]);
|
||||||
|
ShareToAttr attr = new ShareToAttr(cb);
|
||||||
|
if (attr.isFallback() && attr.isForNew()) return cb;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// -- ADMIN
|
// -- ADMIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,6 +140,14 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
*/
|
*/
|
||||||
public ConfigBean getFormLayout(ID formConfigId, String entity) {
|
public ConfigBean getFormLayout(ID formConfigId, String entity) {
|
||||||
final Object[][] alls = getAllConfig(entity, TYPE_FORM);
|
final Object[][] alls = getAllConfig(entity, TYPE_FORM);
|
||||||
|
|
||||||
|
// 高优先级
|
||||||
|
if (formConfigId == null) {
|
||||||
|
ConfigBean best = findDefault(alls);
|
||||||
|
if (best != null) return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 次优先级
|
||||||
for (Object[] o : alls) {
|
for (Object[] o : alls) {
|
||||||
if (formConfigId == null) {
|
if (formConfigId == null) {
|
||||||
return findConfigBean(alls, (ID) o[0]);
|
return findConfigBean(alls, (ID) o[0]);
|
||||||
|
@ -148,12 +166,26 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<ConfigBean> getAllFormsAttr(String entity) {
|
public List<ConfigBean> getAllFormsAttr(String entity) {
|
||||||
|
return getAllFormsAttr(entity, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param entity
|
||||||
|
* @param forNew 仅新建布局
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<ConfigBean> getAllFormsAttr(String entity, boolean forNew) {
|
||||||
final Object[][] alls = getAllConfig(entity, TYPE_FORM);
|
final Object[][] alls = getAllConfig(entity, TYPE_FORM);
|
||||||
|
|
||||||
List<ConfigBean> flist = new ArrayList<>();
|
List<ConfigBean> flist = new ArrayList<>();
|
||||||
for (Object[] o : alls) {
|
for (Object[] o : alls) {
|
||||||
ConfigBean cb = findConfigBean(alls, (ID) o[0]).remove("config");
|
ConfigBean cb = findConfigBean(alls, (ID) o[0]).remove("config");
|
||||||
flist.add(cb.remove("elements"));
|
cb.remove("elements");
|
||||||
|
if (forNew) {
|
||||||
|
if (new ShareToAttr(cb).isForNew()) flist.add(cb.remove("shareTo"));
|
||||||
|
} else {
|
||||||
|
flist.add(cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A-Z
|
// A-Z
|
||||||
|
@ -194,6 +226,7 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
static class ShareToAttr {
|
static class ShareToAttr {
|
||||||
|
|
||||||
private final JSONObject attrs;
|
private final JSONObject attrs;
|
||||||
|
private final boolean sysDefault;
|
||||||
protected ShareToAttr(ConfigBean cb) {
|
protected ShareToAttr(ConfigBean cb) {
|
||||||
Object s = cb.getObject("shareTo");
|
Object s = cb.getObject("shareTo");
|
||||||
if (s instanceof JSON) {
|
if (s instanceof JSON) {
|
||||||
|
@ -202,11 +235,17 @@ public class FormsManager extends BaseLayoutManager {
|
||||||
// shareTo=ALL
|
// shareTo=ALL
|
||||||
this.attrs = JSONUtils.toJSONObject("fallback", true);
|
this.attrs = JSONUtils.toJSONObject("fallback", true);
|
||||||
}
|
}
|
||||||
|
this.sysDefault = cb.getString("name") == null; // 系统默认的
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认
|
// 默认
|
||||||
boolean isFallback() {
|
boolean isFallback() {
|
||||||
return this.attrs.getBooleanValue("fallback");
|
return this.sysDefault || this.attrs.getBooleanValue("fallback");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建
|
||||||
|
boolean isForNew() {
|
||||||
|
return this.sysDefault || this.attrs.getBooleanValue("fornew");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 符合使用条件
|
// 符合使用条件
|
||||||
|
|
|
@ -11,6 +11,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 com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.rebuild.core.DefinedException;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.service.NoRecordFoundException;
|
import com.rebuild.core.service.NoRecordFoundException;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
@ -59,6 +60,10 @@ public class LiteFormBuilder {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public JSONArray build(JSONArray fieldElements) {
|
public JSONArray build(JSONArray fieldElements) {
|
||||||
|
if (fieldElements == null || fieldElements.isEmpty()) {
|
||||||
|
throw new DefinedException("No field elements");
|
||||||
|
}
|
||||||
|
|
||||||
Record recordData = null;
|
Record recordData = null;
|
||||||
if (recordId != null) {
|
if (recordId != null) {
|
||||||
recordData = FormsBuilder.instance.findRecord(recordId, user, fieldElements);
|
recordData = FormsBuilder.instance.findRecord(recordId, user, fieldElements);
|
||||||
|
|
|
@ -8,8 +8,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
package com.rebuild.core.metadata.easymeta;
|
package com.rebuild.core.metadata.easymeta;
|
||||||
|
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
|
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.configuration.general.ClassificationManager;
|
||||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +28,11 @@ public class EasyClassification extends EasyReference {
|
||||||
@Override
|
@Override
|
||||||
public Object wrapValue(Object value) {
|
public Object wrapValue(Object value) {
|
||||||
JSONObject map = (JSONObject) super.wrapValue(value);
|
JSONObject map = (JSONObject) super.wrapValue(value);
|
||||||
if (map != null) map.remove("entity");
|
if (map != null) {
|
||||||
|
map.remove("entity");
|
||||||
|
String color = ClassificationManager.instance.getColor((ID) value);
|
||||||
|
if (color != null) map.put("color", color);
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,18 @@ public class EasyDecimal extends EasyField {
|
||||||
return format.substring(dotIndex).length() - 1;
|
return format.substring(dotIndex).length() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进位模式
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public RoundingMode getRoundingMode() {
|
||||||
|
String rounding = getExtraAttr(EasyFieldConfigProps.DECIMAL_ROUNDING);
|
||||||
|
if ("2".equals(rounding)) return RoundingMode.CEILING;
|
||||||
|
else if ("1".equals(rounding)) return RoundingMode.DOWN;
|
||||||
|
return RoundingMode.HALF_UP;
|
||||||
|
}
|
||||||
|
|
||||||
// --
|
// --
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,12 +120,13 @@ public class EasyDecimal extends EasyField {
|
||||||
*/
|
*/
|
||||||
public static BigDecimal fixedDecimalScale(Object decimalValue, EasyField decimalField) {
|
public static BigDecimal fixedDecimalScale(Object decimalValue, EasyField decimalField) {
|
||||||
int scale = ((EasyDecimal) decimalField).getScale();
|
int scale = ((EasyDecimal) decimalField).getScale();
|
||||||
|
RoundingMode roundingMode = ((EasyDecimal) decimalField).getRoundingMode();
|
||||||
|
|
||||||
if (decimalValue instanceof BigDecimal) {
|
if (decimalValue instanceof BigDecimal) {
|
||||||
return ((BigDecimal) decimalValue).setScale(scale, RoundingMode.HALF_UP);
|
return ((BigDecimal) decimalValue).setScale(scale, roundingMode);
|
||||||
} else {
|
} else {
|
||||||
double d = ObjectUtils.round(ObjectUtils.toDouble(decimalValue), scale);
|
BigDecimal v = BigDecimal.valueOf(ObjectUtils.toDouble(decimalValue));
|
||||||
return BigDecimal.valueOf(d);
|
return v.setScale(scale, roundingMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,15 @@ public class EasyLocation extends EasyField implements MixValue {
|
||||||
@Override
|
@Override
|
||||||
public Object wrapValue(Object value) {
|
public Object wrapValue(Object value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
String[] vals = value.toString().split(MetadataHelper.SPLITER_RE);
|
|
||||||
|
|
||||||
JSONObject mixVal = JSONUtils.toJSONObject("text", vals[0]);
|
// be: v3.8
|
||||||
if (vals.length >= 2) {
|
final String val2str = value.toString();
|
||||||
String[] lnglat = vals[vals.length - 1].split(",");
|
if (JSONUtils.wellFormat(val2str)) return JSONObject.parse(val2str);
|
||||||
|
|
||||||
|
String[] valSplit = val2str.split(MetadataHelper.SPLITER_RE);
|
||||||
|
JSONObject mixVal = JSONUtils.toJSONObject("text", valSplit[0]);
|
||||||
|
if (valSplit.length >= 2) {
|
||||||
|
String[] lnglat = valSplit[valSplit.length - 1].split(",");
|
||||||
mixVal.put("lng", lnglat[0]);
|
mixVal.put("lng", lnglat[0]);
|
||||||
mixVal.put("lat", lnglat.length == 2 ? lnglat[1] : null);
|
mixVal.put("lat", lnglat.length == 2 ? lnglat[1] : null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,4 +53,15 @@ public class EasyNumber extends EasyField {
|
||||||
getExtraAttr(EasyFieldConfigProps.NUMBER_FORMAT), getDisplayType().getDefaultFormat());
|
getExtraAttr(EasyFieldConfigProps.NUMBER_FORMAT), getDisplayType().getDefaultFormat());
|
||||||
return new DecimalFormat(format).format(value);
|
return new DecimalFormat(format).format(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param flagedValue
|
||||||
|
* @return
|
||||||
|
* @see EasyDecimal#clearFlaged(Object)
|
||||||
|
*/
|
||||||
|
public static String clearFlaged(Object flagedValue) {
|
||||||
|
return EasyDecimal.clearFlaged(flagedValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class EasyPickList extends EasyField implements MixValue {
|
||||||
|
|
||||||
ID itemId = PickListManager.instance.findItemByLabel(text, targetField.getRawMeta());
|
ID itemId = PickListManager.instance.findItemByLabel(text, targetField.getRawMeta());
|
||||||
if (itemId == null) {
|
if (itemId == null) {
|
||||||
log.warn("Cannot find value in PickList : " + text + " << " + targetField);
|
log.warn("Cannot find value in PickList : {} << {}", text, targetField);
|
||||||
}
|
}
|
||||||
return itemId;
|
return itemId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,18 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.metadata.easymeta;
|
package com.rebuild.core.metadata.easymeta;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.record.RecordVisitor;
|
import cn.devezhao.persist4j.record.RecordVisitor;
|
||||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
|
import com.rebuild.core.support.general.FieldValueHelper;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.TemporalAccessor;
|
import java.time.temporal.TemporalAccessor;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
|
@ -44,9 +48,15 @@ public class EasyTime extends EasyDateTime {
|
||||||
String valueExpr = (String) getRawMeta().getDefaultValue();
|
String valueExpr = (String) getRawMeta().getDefaultValue();
|
||||||
if (StringUtils.isBlank(valueExpr)) return null;
|
if (StringUtils.isBlank(valueExpr)) return null;
|
||||||
|
|
||||||
if (valueExpr.contains("NOW")) { // {NOW}
|
// 表达式
|
||||||
return LocalTime.now();
|
if (valueExpr.contains(VAR_NOW) || valueExpr.contains("NOW")) {
|
||||||
} else {
|
Date d = FieldValueHelper.parseDateExpr(valueExpr, null);
|
||||||
|
if (d == null) return null;
|
||||||
|
Calendar c = CalendarUtils.getInstance(d);
|
||||||
|
return LocalTime.of(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
|
||||||
|
}
|
||||||
|
// 具体的时间值
|
||||||
|
else {
|
||||||
return RecordVisitor.tryParseTime(valueExpr);
|
return RecordVisitor.tryParseTime(valueExpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,16 +77,19 @@ public class EasyEntityConfigProps {
|
||||||
public static final String ENABLE_RECORD_MERGER = "enableRecordMerger";
|
public static final String ENABLE_RECORD_MERGER = "enableRecordMerger";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 列表模式
|
* 详情模式:字段
|
||||||
*/
|
*/
|
||||||
public static final String ADVLIST_MODE2_SHOWFIELDS = "mode2ShowFields";
|
public static final String ADVLIST_MODE2_SHOWFIELDS = "mode2ShowFields";
|
||||||
|
/**
|
||||||
|
* 卡片模式:字段
|
||||||
|
*/
|
||||||
public static final String ADVLIST_MODE3_SHOWFIELDS = "mode3ShowFields";
|
public static final String ADVLIST_MODE3_SHOWFIELDS = "mode3ShowFields";
|
||||||
/**
|
/**
|
||||||
* @see #ADVLIST_HIDE_FILTERS
|
* @see #ADVLIST_HIDE_FILTERS
|
||||||
*/
|
*/
|
||||||
public static final String ADVLIST_MODE3_SHOWFILTERS = "mode3ShowFilters";
|
public static final String ADVLIST_MODE3_SHOWFILTERS = "mode3ShowFilters";
|
||||||
/**
|
/**
|
||||||
* @see #ADVLIST_SHOWCATEGORY TODO
|
* @see #ADVLIST_SHOWCATEGORY
|
||||||
*/
|
*/
|
||||||
public static final String ADVLIST_MODE3_SHOWCATEGORY = "mode3ShowCategory";
|
public static final String ADVLIST_MODE3_SHOWCATEGORY = "mode3ShowCategory";
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,10 @@ public class EasyFieldConfigProps {
|
||||||
* 表单公式
|
* 表单公式
|
||||||
*/
|
*/
|
||||||
public static final String NUMBER_CALCFORMULA = "calcFormula";
|
public static final String NUMBER_CALCFORMULA = "calcFormula";
|
||||||
|
/**
|
||||||
|
* 表单公式
|
||||||
|
*/
|
||||||
|
public static final String NUMBER_CALCFORMULABACKEND = "calcFormulaBackend";
|
||||||
/**
|
/**
|
||||||
* 是否允许负数
|
* 是否允许负数
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +66,10 @@ public class EasyFieldConfigProps {
|
||||||
* 表单公式
|
* 表单公式
|
||||||
*/
|
*/
|
||||||
public static final String DECIMAL_CALCFORMULA = NUMBER_CALCFORMULA;
|
public static final String DECIMAL_CALCFORMULA = NUMBER_CALCFORMULA;
|
||||||
|
/**
|
||||||
|
* 进位模式(默认四舍五入)
|
||||||
|
*/
|
||||||
|
public static final String DECIMAL_ROUNDING = "decimalRounding";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日期格式
|
* 日期格式
|
||||||
|
@ -102,9 +109,13 @@ public class EasyFieldConfigProps {
|
||||||
public static final String IMAGE_UPLOADNUMBER = FILE_UPLOADNUMBER;
|
public static final String IMAGE_UPLOADNUMBER = FILE_UPLOADNUMBER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片获取方式(仅H5)
|
* 图片获取方式(兼容附件)
|
||||||
*/
|
*/
|
||||||
public static final String IMAGE_CAPTURE = "imageCapture";
|
public static final String IMAGE_CAPTURE = "imageCapture";
|
||||||
|
/**
|
||||||
|
* 图片获取方式(兼容附件)
|
||||||
|
*/
|
||||||
|
public static final String IMAGE_CAPTURE_DEF = "imageCaptureDef";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自动编号规则
|
* 自动编号规则
|
||||||
|
|
|
@ -47,11 +47,10 @@ import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATETIME_CALCFORMULA;
|
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATETIME_FORMAT;
|
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATETIME_FORMAT;
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATE_CALCFORMULA;
|
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATE_FORMAT;
|
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.DATE_FORMAT;
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_CALCFORMULA;
|
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_CALCFORMULA;
|
||||||
|
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_CALCFORMULABACKEND;
|
||||||
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_NOTNEGATIVE;
|
import static com.rebuild.core.metadata.impl.EasyFieldConfigProps.NUMBER_NOTNEGATIVE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -396,7 +395,7 @@ public class Field2Schema extends SetUser {
|
||||||
String identifier = text;
|
String identifier = text;
|
||||||
|
|
||||||
// 全英文直接返回
|
// 全英文直接返回
|
||||||
if (identifier.length() >= 4 && identifier.matches("[a-zA-Z0-9]+")) {
|
if (identifier.length() >= 4 && identifier.matches("[a-zA-Z0-9_]+")) {
|
||||||
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0)) || BlockList.isBlock(identifier)) {
|
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0)) || BlockList.isBlock(identifier)) {
|
||||||
identifier = "rb" + identifier;
|
identifier = "rb" + identifier;
|
||||||
}
|
}
|
||||||
|
@ -458,14 +457,15 @@ public class Field2Schema extends SetUser {
|
||||||
extraAttrs.remove(DATETIME_FORMAT);
|
extraAttrs.remove(DATETIME_FORMAT);
|
||||||
Object notNegative = extraAttrs.remove(NUMBER_NOTNEGATIVE);
|
Object notNegative = extraAttrs.remove(NUMBER_NOTNEGATIVE);
|
||||||
Object calcFormula = extraAttrs.remove(NUMBER_CALCFORMULA);
|
Object calcFormula = extraAttrs.remove(NUMBER_CALCFORMULA);
|
||||||
|
Object calcFormulaBackend = extraAttrs.remove(NUMBER_CALCFORMULABACKEND);
|
||||||
|
|
||||||
extraAttrs.clear();
|
extraAttrs.clear();
|
||||||
if (notNegative != null) extraAttrs.put(NUMBER_NOTNEGATIVE, notNegative);
|
if (notNegative != null) extraAttrs.put(NUMBER_NOTNEGATIVE, notNegative);
|
||||||
if (calcFormula != null) extraAttrs.put(NUMBER_CALCFORMULA, calcFormula);
|
if (calcFormula != null) extraAttrs.put(NUMBER_CALCFORMULA, calcFormula);
|
||||||
|
if (calcFormulaBackend instanceof Boolean && (Boolean) calcFormulaBackend) extraAttrs.put(NUMBER_CALCFORMULABACKEND, true);
|
||||||
|
|
||||||
if (!extraAttrs.isEmpty()) {
|
if (extraAttrs.isEmpty()) fieldMeta.setNull("extConfig");
|
||||||
fieldMeta.setString("extConfig", extraAttrs.toJSONString());
|
else fieldMeta.setString("extConfig", extraAttrs.toJSONString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Application.getCommonsService().update(fieldMeta, false);
|
Application.getCommonsService().update(fieldMeta, false);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class ChangeOwningDeptTask extends HeavyTask<Integer> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Integer exec() {
|
protected Integer exec() {
|
||||||
log.info("Start modifying the `OwningDept` ... " + this.user);
|
log.info("Start modifying the `OwningDept` ... {}:{}", this.user, this.deptNew);
|
||||||
this.setTotal(MetadataHelper.getEntities().length);
|
this.setTotal(MetadataHelper.getEntities().length);
|
||||||
|
|
||||||
final StopWatch sw = new StopWatch("ChangeOwningDeptTask");
|
final StopWatch sw = new StopWatch("ChangeOwningDeptTask");
|
||||||
|
@ -68,7 +68,7 @@ public class ChangeOwningDeptTask extends HeavyTask<Integer> {
|
||||||
sw.stop();
|
sw.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Modify the `OwningDept` to complete : {} > {}\n{}", this.user, changed, sw.prettyPrint());
|
log.info("Modify the `OwningDept` to complete : {}:{} : {}\n{}", this.user, deptNew, changed, sw.prettyPrint());
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*!
|
||||||
|
Copyright (c) Ruifang Tech <http://ruifang-tech.com/> and/or its owners. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.core.privileges;
|
||||||
|
|
||||||
|
import cn.devezhao.persist4j.Field;
|
||||||
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段权限
|
||||||
|
*
|
||||||
|
* @author devezhao
|
||||||
|
* @since 2024/7/23
|
||||||
|
* @see PrivilegesManager
|
||||||
|
*/
|
||||||
|
@ConditionalOnMissingClass("com.rebuild.Rbv")
|
||||||
|
@Component
|
||||||
|
public class FieldPrivileges {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可新建?
|
||||||
|
* @param field
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isCreatable(Field field, ID user) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可读?
|
||||||
|
* @param field
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isReadble(Field field, ID user) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可修改?
|
||||||
|
* @param field
|
||||||
|
* @param user
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isUpdatable(Field field, ID user) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,51 +0,0 @@
|
||||||
/*!
|
|
||||||
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.privileges;
|
|
||||||
|
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.core.NamedThreadLocal;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跳过权限检查
|
|
||||||
*
|
|
||||||
* @author devezhao
|
|
||||||
* @since 2020/9/28
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class PrivilegesGuardContextHolder {
|
|
||||||
|
|
||||||
private static final ThreadLocal<ID> SKIP_GUARD = new NamedThreadLocal<>("Skip some check once");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 允许无权限操作一次
|
|
||||||
*
|
|
||||||
* @param recordId
|
|
||||||
*/
|
|
||||||
public static void setSkipGuard(ID recordId) {
|
|
||||||
Assert.notNull(recordId, "[recordId] cannot be null");
|
|
||||||
|
|
||||||
ID existsWarn = SKIP_GUARD.get();
|
|
||||||
if (existsWarn != null) {
|
|
||||||
log.warn("Not removed skip record : {}", existsWarn);
|
|
||||||
SKIP_GUARD.remove();
|
|
||||||
}
|
|
||||||
SKIP_GUARD.set(recordId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return
|
|
||||||
* @see #setSkipGuard(ID)
|
|
||||||
*/
|
|
||||||
public static ID getSkipGuardOnce() {
|
|
||||||
ID recordId = SKIP_GUARD.get();
|
|
||||||
if (recordId != null) SKIP_GUARD.remove();
|
|
||||||
return recordId;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,6 +23,7 @@ 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;
|
||||||
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
|
@ -110,7 +111,7 @@ public class PrivilegesGuardInterceptor implements MethodInterceptor, Guard {
|
||||||
|
|
||||||
// 跳过
|
// 跳过
|
||||||
ID skipGuardId;
|
ID skipGuardId;
|
||||||
if ((skipGuardId = PrivilegesGuardContextHolder.getSkipGuardOnce()) != null) {
|
if ((skipGuardId = GeneralEntityServiceContextHolder.isSkipGuardOnce()) != null) {
|
||||||
log.debug("Allow no permission({}) passed once : {}", action.getName(), skipGuardId);
|
log.debug("Allow no permission({}) passed once : {}", action.getName(), skipGuardId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,4 +554,11 @@ public class PrivilegesManager {
|
||||||
}
|
}
|
||||||
throw new BizzException("Unknown Permission : " + name);
|
throw new BizzException("Unknown Permission : " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public FieldPrivileges getFieldPrivileges() {
|
||||||
|
return Application.getBean(FieldPrivileges.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -311,13 +311,16 @@ public class UserService extends BaseService {
|
||||||
super.update(record);
|
super.update(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateRoleAppends(user, roleAppends)) changed = true;
|
if (roleAppends != null) {
|
||||||
|
if (updateRoleAppends(user, roleAppends)) changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
Application.getUserStore().refreshUser(user);
|
Application.getUserStore().refreshUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 改变记录的所属部门
|
// 改变记录的所属部门
|
||||||
|
// 并发修改可能导致数据紊乱
|
||||||
if (deptOld != null) {
|
if (deptOld != null) {
|
||||||
TaskExecutors.submit(new ChangeOwningDeptTask(user, deptNew), UserContextHolder.getUser());
|
TaskExecutors.submit(new ChangeOwningDeptTask(user, deptNew), UserContextHolder.getUser());
|
||||||
}
|
}
|
||||||
|
@ -372,15 +375,17 @@ public class UserService extends BaseService {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected boolean updateRoleAppends(ID user, ID[] roleAppends) {
|
protected boolean updateRoleAppends(ID user, ID[] roleAppends) {
|
||||||
|
if (roleAppends == null) return false;
|
||||||
|
|
||||||
Object[][] exists = Application.createQueryNoFilter(
|
Object[][] exists = Application.createQueryNoFilter(
|
||||||
"select memberId,roleId from RoleMember where userId = ?")
|
"select memberId,roleId from RoleMember where userId = ?")
|
||||||
.setParameter(1, user)
|
.setParameter(1, user)
|
||||||
.array();
|
.array();
|
||||||
if (exists.length == 0 && (roleAppends == null || roleAppends.length == 0)) {
|
if (exists.length == 0 && roleAppends.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roleAppends == null || roleAppends.length == 0) {
|
if (roleAppends.length == 0) {
|
||||||
for (Object[] o : exists) {
|
for (Object[] o : exists) {
|
||||||
super.delete((ID) o[0]);
|
super.delete((ID) o[0]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ public class CombinedRole extends Role {
|
||||||
|
|
||||||
// 实体自定义权限
|
// 实体自定义权限
|
||||||
|
|
||||||
final Map<String, JSON> customFilters = new HashMap<>();
|
Map<String, JSON> useCustomFilters = new HashMap<>();
|
||||||
|
|
||||||
for (Permission action : CustomEntityPrivileges.PERMISSION_DEFS) {
|
for (Permission action : CustomEntityPrivileges.PERMISSION_DEFS) {
|
||||||
JSONObject aCustom = ((CustomEntityPrivileges) a).getCustomFilter(action);
|
JSONObject aCustom = ((CustomEntityPrivileges) a).getCustomFilter(action);
|
||||||
|
@ -182,15 +182,22 @@ public class CombinedRole extends Role {
|
||||||
|
|
||||||
int gt = isGreaterThan(action.getMask(), aDefMap, bDefMap);
|
int gt = isGreaterThan(action.getMask(), aDefMap, bDefMap);
|
||||||
if (gt == 1) {
|
if (gt == 1) {
|
||||||
if (aCustom != null) customFilters.put(action.getName(), aCustom);
|
if (aCustom != null) useCustomFilters.put(action.getName(), aCustom);
|
||||||
} else if (gt == 2) {
|
} else if (gt == 2) {
|
||||||
if (bCustom != null) customFilters.put(action.getName(), bCustom);
|
if (bCustom != null) useCustomFilters.put(action.getName(), bCustom);
|
||||||
}
|
}
|
||||||
// gt == 0 无自定义权限
|
// gt == 0 无自定义权限
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 字段权限
|
||||||
|
Map<String, Object> useFpDefinition;
|
||||||
|
Map<String, Object> aFpDefinition = ((CustomEntityPrivileges) a).getFpDefinition();
|
||||||
|
Map<String, Object> bFpDefinition = ((CustomEntityPrivileges) b).getFpDefinition();
|
||||||
|
if (aFpDefinition == null || bFpDefinition == null) useFpDefinition = null;
|
||||||
|
else useFpDefinition = aFpDefinition;
|
||||||
|
|
||||||
String definition = StringUtils.join(defs.iterator(), ",");
|
String definition = StringUtils.join(defs.iterator(), ",");
|
||||||
return new CustomEntityPrivileges(((EntityPrivileges) a).getEntity(), definition, customFilters);
|
return new CustomEntityPrivileges(((EntityPrivileges) a).getEntity(), definition, useCustomFilters, useFpDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Integer> parseDefinitionMasks(String d) {
|
private Map<String, Integer> parseDefinitionMasks(String d) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.service.query.ParseHelper;
|
import com.rebuild.core.service.query.ParseHelper;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -35,6 +36,8 @@ public class CustomEntityPrivileges extends EntityPrivileges {
|
||||||
|
|
||||||
// 自定义权限 <Action, Filter>
|
// 自定义权限 <Action, Filter>
|
||||||
private final Map<String, JSON> customFilters = new HashMap<>();
|
private final Map<String, JSON> customFilters = new HashMap<>();
|
||||||
|
// 字段权限
|
||||||
|
private final Map<String, Object> fpDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param entity
|
* @param entity
|
||||||
|
@ -52,17 +55,21 @@ public class CustomEntityPrivileges extends EntityPrivileges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fpDefinition = rawDefinition.getJSONObject("FP");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param entity
|
* @param entity
|
||||||
* @param definition
|
* @param definition
|
||||||
* @param customFilters
|
* @param customFilters
|
||||||
|
* @param fpDefinition
|
||||||
*/
|
*/
|
||||||
protected CustomEntityPrivileges(Integer entity, String definition, Map<String, JSON> customFilters) {
|
protected CustomEntityPrivileges(Integer entity, String definition, Map<String, JSON> customFilters, Map<String, Object> fpDefinition) {
|
||||||
super(entity, definition);
|
super(entity, definition);
|
||||||
this.customFilters.clear();
|
this.customFilters.clear();
|
||||||
this.customFilters.putAll(customFilters);
|
this.customFilters.putAll(customFilters);
|
||||||
|
this.fpDefinition = fpDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,7 +176,7 @@ public class CustomEntityPrivileges extends EntityPrivileges {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取自定义权限
|
* 获取自定义权限定义
|
||||||
*
|
*
|
||||||
* @param action
|
* @param action
|
||||||
* @return
|
* @return
|
||||||
|
@ -178,6 +185,15 @@ public class CustomEntityPrivileges extends EntityPrivileges {
|
||||||
return (JSONObject) customFilters.getOrDefault(action.getName(), null);
|
return (JSONObject) customFilters.getOrDefault(action.getName(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段权限定义
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getFpDefinition() {
|
||||||
|
return fpDefinition == null ? null : Collections.unmodifiableMap(fpDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getDefinition() + ";" + getCustomFilters();
|
return getDefinition() + ";" + getCustomFilters();
|
||||||
|
|
|
@ -59,6 +59,7 @@ public enum ZeroEntry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 允许撤销审批
|
* 允许撤销审批
|
||||||
|
* v3.8 起添加撤回权限
|
||||||
*/
|
*/
|
||||||
AllowRevokeApproval(false),
|
AllowRevokeApproval(false),
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,10 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础 CRUD 服务,使用请注意:
|
* 基础 CRUD 服务,使用须知:
|
||||||
* <br>- 此类有事物
|
* <br>- 此类有事物
|
||||||
* <br>- 此类不经过用户权限验证 {@link PrivilegesGuardInterceptor}
|
* <br>- 此类不经过用户权限验证 {@link PrivilegesGuardInterceptor}
|
||||||
* <br>- 此类不对多值字段进行处理 {@link BaseService}
|
* <br>- 此类不字段值做任何处理,如多值、附件 {@link BaseService}
|
||||||
* <br>- 此类无任何系统规则,如默认值、重复检查、自动编号等
|
* <br>- 此类无任何系统规则,如默认值、重复检查、自动编号等
|
||||||
* <br>- 有权限的实体使用此类需要指定 `strictMode=false`
|
* <br>- 有权限的实体使用此类需要指定 `strictMode=false`
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,6 +7,9 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.service;
|
package com.rebuild.core.service;
|
||||||
|
|
||||||
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
|
import com.rebuild.core.service.trigger.RobotTriggerObserver;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -34,7 +37,12 @@ public class SafeObservable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyObservers(Object arg) {
|
public void notifyObservers(Object arg) {
|
||||||
|
boolean quickMode = GeneralEntityServiceContextHolder.isQuickMode(false);
|
||||||
for (SafeObserver o : obs) {
|
for (SafeObserver o : obs) {
|
||||||
|
if (quickMode) {
|
||||||
|
if (o instanceof RobotTriggerObserver) continue;
|
||||||
|
}
|
||||||
|
|
||||||
o.update(this, arg);
|
o.update(this, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,21 +47,26 @@ public class ApprovalFields2Schema extends Field2Schema {
|
||||||
* @throws MetadataModificationException
|
* @throws MetadataModificationException
|
||||||
*/
|
*/
|
||||||
public boolean createFields(Entity entity) throws MetadataModificationException {
|
public boolean createFields(Entity entity) throws MetadataModificationException {
|
||||||
|
// 补充后加字段
|
||||||
if (MetadataHelper.hasApprovalField(entity)) {
|
if (MetadataHelper.hasApprovalField(entity)) {
|
||||||
|
List<Field> complement = new ArrayList<>();
|
||||||
if (!entity.containsField(EntityHelper.ApprovalLastUser)) {
|
if (!entity.containsField(EntityHelper.ApprovalLastUser)) {
|
||||||
return schema2DatabaseInternal(entity, buildApporvalLastUser(entity));
|
complement.add(buildApporvalLastUser(entity));
|
||||||
}
|
}
|
||||||
if (!entity.containsField(EntityHelper.ApprovalLastTime)) {
|
if (!entity.containsField(EntityHelper.ApprovalLastTime)) {
|
||||||
return schema2DatabaseInternal(entity, buildApporvalLastTime(entity));
|
complement.add(buildApporvalLastTime(entity));
|
||||||
}
|
}
|
||||||
if (!entity.containsField(EntityHelper.ApprovalLastRemark)) {
|
if (!entity.containsField(EntityHelper.ApprovalLastRemark)) {
|
||||||
return schema2DatabaseInternal(entity, buildApporvalLastRemark(entity));
|
complement.add(buildApporvalLastRemark(entity));
|
||||||
}
|
}
|
||||||
if (!entity.containsField(EntityHelper.ApprovalStepUsers)) {
|
if (!entity.containsField(EntityHelper.ApprovalStepUsers)) {
|
||||||
return schema2DatabaseInternal(entity,
|
complement.add(buildApprovalStepUsers(entity));
|
||||||
buildApprovalStepUsers(entity), buildApprovalStepNodeName(entity));
|
complement.add(buildApprovalStepNodeName(entity));
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
if (complement.isEmpty()) return false;
|
||||||
|
schema2DatabaseInternal(entity, complement.toArray(new Field[0]));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(MetadataHelper.hasPrivilegesField(entity)
|
if (!(MetadataHelper.hasPrivilegesField(entity)
|
||||||
|
@ -157,7 +162,7 @@ public class ApprovalFields2Schema extends Field2Schema {
|
||||||
try {
|
try {
|
||||||
Application.getSqlExecutor().execute(ddl, DDL_TIMEOUT);
|
Application.getSqlExecutor().execute(ddl, DDL_TIMEOUT);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
log.error("DDL ERROR : \n" + ddl, ex);
|
log.error("DDL ERROR : \n{}", ddl, ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ import com.rebuild.core.configuration.ConfigurationException;
|
||||||
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.easymeta.EasyMetaFactory;
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.service.general.EntityService;
|
import com.rebuild.core.service.general.EntityService;
|
||||||
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
import com.rebuild.core.service.notification.MessageBuilder;
|
import com.rebuild.core.service.notification.MessageBuilder;
|
||||||
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;
|
||||||
|
@ -54,11 +54,13 @@ public class ApprovalProcessor extends SetUser {
|
||||||
|
|
||||||
// 最大撤销次数
|
// 最大撤销次数
|
||||||
private static final int MAX_REVOKED = 100;
|
private static final int MAX_REVOKED = 100;
|
||||||
|
// 自主退回
|
||||||
|
private static final String KEY_CANCEL38 = "PREV_APPROVER_BACKED";
|
||||||
|
|
||||||
final private ID recordId;
|
final private ID recordId;
|
||||||
|
|
||||||
// 如未传递,会在需要时根据 record 确定
|
// 如未传递,会在需要时根据 record 确定
|
||||||
private ID approval;
|
private ID approvalId;
|
||||||
// 流程定义
|
// 流程定义
|
||||||
private FlowParser flowParser;
|
private FlowParser flowParser;
|
||||||
|
|
||||||
|
@ -71,11 +73,11 @@ public class ApprovalProcessor extends SetUser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param recordId
|
* @param recordId
|
||||||
* @param approval
|
* @param approvalId
|
||||||
*/
|
*/
|
||||||
public ApprovalProcessor(ID recordId, ID approval) {
|
public ApprovalProcessor(ID recordId, ID approvalId) {
|
||||||
this.recordId = recordId;
|
this.recordId = recordId;
|
||||||
this.approval = approval;
|
this.approvalId = approvalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,7 +109,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
Set<String> ccAccounts = nextNodes.getCcAccounts(this.recordId);
|
Set<String> ccAccounts = nextNodes.getCcAccounts(this.recordId);
|
||||||
|
|
||||||
Record recordOfMain = EntityHelper.forUpdate(this.recordId, this.getUser(), false);
|
Record recordOfMain = EntityHelper.forUpdate(this.recordId, this.getUser(), false);
|
||||||
recordOfMain.setID(EntityHelper.ApprovalId, this.approval);
|
recordOfMain.setID(EntityHelper.ApprovalId, this.approvalId);
|
||||||
recordOfMain.setInt(EntityHelper.ApprovalState, ApprovalState.PROCESSING.getState());
|
recordOfMain.setInt(EntityHelper.ApprovalState, ApprovalState.PROCESSING.getState());
|
||||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNodes.getApprovalNode().getNodeId());
|
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNodes.getApprovalNode().getNodeId());
|
||||||
Application.getBean(ApprovalStepService.class).txSubmit(recordOfMain, ccUsers, ccAccounts, nextApprovers);
|
Application.getBean(ApprovalStepService.class).txSubmit(recordOfMain, ccUsers, ccAccounts, nextApprovers);
|
||||||
|
@ -174,7 +176,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
approvedStep.setString("attrMore", attrMore.toJSONString());
|
approvedStep.setString("attrMore", attrMore.toJSONString());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.approval = (ID) stepApprover[3];
|
this.approvalId = (ID) stepApprover[3];
|
||||||
FlowNodeGroup nextNodes = getNextNodes((String) stepApprover[2]);
|
FlowNodeGroup nextNodes = getNextNodes((String) stepApprover[2]);
|
||||||
|
|
||||||
Set<ID> nextApprovers = null;
|
Set<ID> nextApprovers = null;
|
||||||
|
@ -224,6 +226,34 @@ public class ApprovalProcessor extends SetUser {
|
||||||
this.recordId, status.getApprovalId(), getCurrentNodeId(status), false);
|
this.recordId, status.getApprovalId(), getCurrentNodeId(status), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3. 由(上一步)审批人撤回
|
||||||
|
*
|
||||||
|
* @param approver
|
||||||
|
* @throws ApprovalException
|
||||||
|
* @see #approve(ID, ApprovalState, String, JSONObject)
|
||||||
|
*/
|
||||||
|
public void cancel38(ID approver) throws ApprovalException {
|
||||||
|
ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
||||||
|
this.approvalId = status.getApprovalId();
|
||||||
|
|
||||||
|
String prevNode = status.getPrevStepNode();
|
||||||
|
if (prevNode == null) throw new ApprovalException(Language.L("无效审批状态"));
|
||||||
|
|
||||||
|
// 由当前审批人代理其退回
|
||||||
|
ID proxyApprover = approver;
|
||||||
|
JSONArray current = getCurrentStep(status);
|
||||||
|
for (Object o : current) {
|
||||||
|
JSONObject step = (JSONObject) o;
|
||||||
|
proxyApprover = ID.valueOf(step.getString("approver"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BACKED
|
||||||
|
String key = KEY_CANCEL38 + ":" + approver;
|
||||||
|
approve(proxyApprover, ApprovalState.REJECTED, key, null, null, null, prevNode, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2.1.催审
|
* 2.1.催审
|
||||||
*
|
*
|
||||||
|
@ -231,9 +261,9 @@ public class ApprovalProcessor extends SetUser {
|
||||||
*/
|
*/
|
||||||
public int urge() {
|
public int urge() {
|
||||||
final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
||||||
this.approval = status.getApprovalId();
|
this.approvalId = status.getApprovalId();
|
||||||
|
|
||||||
final String sentKey = String.format("URGE:%s-%s", approval, recordId);
|
final String sentKey = String.format("URGE:%s-%s", approvalId, recordId);
|
||||||
if (Application.getCommonsCache().getx(sentKey) != null) {
|
if (Application.getCommonsCache().getx(sentKey) != null) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -272,7 +302,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
Object[] instepApprover = Application.createQueryNoFilter(
|
Object[] instepApprover = Application.createQueryNoFilter(
|
||||||
"select state from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'")
|
"select state from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'")
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.approval)
|
.setParameter(2, this.approvalId)
|
||||||
.setParameter(3, getCurrentNodeId(null))
|
.setParameter(3, getCurrentNodeId(null))
|
||||||
.setParameter(4, toUser)
|
.setParameter(4, toUser)
|
||||||
.unique();
|
.unique();
|
||||||
|
@ -416,13 +446,13 @@ public class ApprovalProcessor extends SetUser {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private FlowParser getFlowParser() {
|
private FlowParser getFlowParser() {
|
||||||
Assert.notNull(approval, "[approval] cannot be null");
|
Assert.notNull(approvalId, "[approval] cannot be null");
|
||||||
if (flowParser != null) {
|
if (flowParser != null) {
|
||||||
return flowParser;
|
return flowParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowDefinition flowDefinition = RobotApprovalManager.instance.getFlowDefinition(
|
FlowDefinition flowDefinition = RobotApprovalManager.instance.getFlowDefinition(
|
||||||
MetadataHelper.getEntity(this.recordId.getEntityCode()), this.approval);
|
MetadataHelper.getEntity(this.recordId.getEntityCode()), this.approvalId);
|
||||||
flowParser = flowDefinition.createFlowParser();
|
flowParser = flowDefinition.createFlowParser();
|
||||||
return flowParser;
|
return flowParser;
|
||||||
}
|
}
|
||||||
|
@ -435,7 +465,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
try {
|
try {
|
||||||
return getFlowParser().getNode(nodeNo);
|
return getFlowParser().getNode(nodeNo);
|
||||||
} catch (ApprovalException | ConfigurationException ex) {
|
} catch (ApprovalException | ConfigurationException ex) {
|
||||||
log.warn("Cannot parse node : {} with {}", nodeNo, approval, ex);
|
log.warn("Cannot parse node : {} with {}", nodeNo, approvalId, ex);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -456,7 +486,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
" where recordId = ? and approvalId = ? and node = ? and isCanceled = 'F' and isBacked = 'F' order by createdOn desc";
|
" where recordId = ? and approvalId = ? and node = ? and isCanceled = 'F' and isBacked = 'F' order by createdOn desc";
|
||||||
Object[] lastNode = Application.createQueryNoFilter(sql)
|
Object[] lastNode = Application.createQueryNoFilter(sql)
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.approval)
|
.setParameter(2, this.approvalId)
|
||||||
.setParameter(3, currentNode)
|
.setParameter(3, currentNode)
|
||||||
.unique();
|
.unique();
|
||||||
String nodeBatch = lastNode == null || lastNode[0] == null ? null : (String) lastNode[0];
|
String nodeBatch = lastNode == null || lastNode[0] == null ? null : (String) lastNode[0];
|
||||||
|
@ -468,7 +498,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
|
|
||||||
Object[][] array = Application.createQueryNoFilter(sql)
|
Object[][] array = Application.createQueryNoFilter(sql)
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.approval)
|
.setParameter(2, this.approvalId)
|
||||||
.setParameter(3, currentNode)
|
.setParameter(3, currentNode)
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
|
@ -481,13 +511,13 @@ public class ApprovalProcessor extends SetUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已执行步骤
|
* 获取已审批节点
|
||||||
*
|
*
|
||||||
* @return returns [ [S,S], [S], [SSS], [S] ]
|
* @return returns [ [S,S], [S], [SSS], [S] ]
|
||||||
*/
|
*/
|
||||||
public JSONArray getWorkedSteps() {
|
public JSONArray getWorkedSteps() {
|
||||||
final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId);
|
final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId);
|
||||||
this.approval = status.getApprovalId();
|
this.approvalId = status.getApprovalId();
|
||||||
|
|
||||||
Object[][] array = Application.createQueryNoFilter(
|
Object[][] array = Application.createQueryNoFilter(
|
||||||
"select approver,state,remark,approvedTime,createdOn,createdBy,node,prevNode,nodeBatch,ccUsers,ccAccounts,attrMore from RobotApprovalStep" +
|
"select approver,state,remark,approvedTime,createdOn,createdBy,node,prevNode,nodeBatch,ccUsers,ccAccounts,attrMore from RobotApprovalStep" +
|
||||||
|
@ -553,10 +583,10 @@ public class ApprovalProcessor extends SetUser {
|
||||||
if (FlowNode.NODE_AUTOAPPROVAL.equals(nodeNo)) {
|
if (FlowNode.NODE_AUTOAPPROVAL.equals(nodeNo)) {
|
||||||
// No name
|
// No name
|
||||||
} else if (FlowNode.NODE_REVOKED.equals(nodeNo)) {
|
} else if (FlowNode.NODE_REVOKED.equals(nodeNo)) {
|
||||||
String nodeName = Language.L("管理员撤销");
|
String nodeName = Language.L("撤销");
|
||||||
s.put("nodeName", nodeName);
|
s.put("nodeName", nodeName);
|
||||||
} else if (FlowNode.NODE_CANCELED.equals(nodeNo)) {
|
} else if (FlowNode.NODE_CANCELED.equals(nodeNo)) {
|
||||||
String nodeName = Language.L("提交人撤回");
|
String nodeName = Language.L("撤回");
|
||||||
s.put("nodeName", nodeName);
|
s.put("nodeName", nodeName);
|
||||||
} else {
|
} else {
|
||||||
String nodeName = flowNode == null ? null : flowNode.getNodeName();
|
String nodeName = flowNode == null ? null : flowNode.getNodeName();
|
||||||
|
@ -580,6 +610,15 @@ public class ApprovalProcessor extends SetUser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v3.8
|
||||||
|
String remark = s.getString("remark");
|
||||||
|
if (remark != null && remark.startsWith(KEY_CANCEL38 + ":")) {
|
||||||
|
ID realApprover = ID.valueOf(remark.split(":")[1]);
|
||||||
|
s.put("remark", KEY_CANCEL38);
|
||||||
|
s.put("approver", realApprover);
|
||||||
|
s.put("approverName", UserHelper.getName(realApprover));
|
||||||
|
}
|
||||||
|
|
||||||
s.put("node", nodeNo);
|
s.put("node", nodeNo);
|
||||||
step.add(s);
|
step.add(s);
|
||||||
}
|
}
|
||||||
|
@ -635,7 +674,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
*/
|
*/
|
||||||
public JSONArray getBackSteps() {
|
public JSONArray getBackSteps() {
|
||||||
ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId);
|
ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId);
|
||||||
this.approval = status.getApprovalId();
|
this.approvalId = status.getApprovalId();
|
||||||
|
|
||||||
String currentNode = getCurrentNodeId(status);
|
String currentNode = getCurrentNodeId(status);
|
||||||
if (FlowNode.NODE_ROOT.equals(currentNode)) return JSONUtils.EMPTY_ARRAY;
|
if (FlowNode.NODE_ROOT.equals(currentNode)) return JSONUtils.EMPTY_ARRAY;
|
||||||
|
@ -680,6 +719,30 @@ public class ApprovalProcessor extends SetUser {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上一步审批用户
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Set<ID> getPrevApprovedUsers() {
|
||||||
|
ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId);
|
||||||
|
String prevNode = status.getPrevStepNode();
|
||||||
|
if (prevNode == null) return Collections.emptySet();
|
||||||
|
|
||||||
|
String sql = "select approver,stepId from RobotApprovalStep " +
|
||||||
|
"where recordId = ? and approvalId = ? and node = ? and isCanceled = 'F' and state = 10 and isBacked = 'F'";
|
||||||
|
Object[][] prevNodeApprovers = Application.getQueryFactory().createQueryNoFilter(sql)
|
||||||
|
.setParameter(1, this.recordId)
|
||||||
|
.setParameter(2, this.approvalId)
|
||||||
|
.setParameter(3, prevNode)
|
||||||
|
.array();
|
||||||
|
if (prevNodeApprovers.length == 0) return Collections.emptySet();
|
||||||
|
|
||||||
|
Set<ID> approvedUsers = new HashSet<>();
|
||||||
|
for (Object[] o : prevNodeApprovers) approvedUsers.add((ID) o[0]);
|
||||||
|
return approvedUsers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 会签时自选的审批人
|
* 会签时自选的审批人
|
||||||
*
|
*
|
||||||
|
@ -693,7 +756,7 @@ public class ApprovalProcessor extends SetUser {
|
||||||
Object[][] array = Application.createQueryNoFilter(
|
Object[][] array = Application.createQueryNoFilter(
|
||||||
"select approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and isWaiting = 'T' and isCanceled = 'F'")
|
"select approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and isWaiting = 'T' and isCanceled = 'F'")
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.approval)
|
.setParameter(2, this.approvalId)
|
||||||
.setParameter(3, node)
|
.setParameter(3, node)
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
|
@ -716,12 +779,11 @@ public class ApprovalProcessor extends SetUser {
|
||||||
final EntityService es = Application.getEntityService(recordId.getEntityCode());
|
final EntityService es = Application.getEntityService(recordId.getEntityCode());
|
||||||
for (ID user : shareTo) {
|
for (ID user : shareTo) {
|
||||||
if (!Application.getPrivilegesManager().allowRead(user, recordId)) {
|
if (!Application.getPrivilegesManager().allowRead(user, recordId)) {
|
||||||
// force share
|
GeneralEntityServiceContextHolder.setSkipGuard(recordId);
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(recordId);
|
|
||||||
try {
|
try {
|
||||||
es.share(recordId, user, null);
|
es.share(recordId, user, null);
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -737,13 +799,13 @@ public class ApprovalProcessor extends SetUser {
|
||||||
|
|
||||||
private Object[] findProcessingStepApprover(ID approver) {
|
private Object[] findProcessingStepApprover(ID approver) {
|
||||||
final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING);
|
||||||
this.approval = status.getApprovalId();
|
this.approvalId = status.getApprovalId();
|
||||||
|
|
||||||
String currentNodeId = getCurrentNodeId(status);
|
String currentNodeId = getCurrentNodeId(status);
|
||||||
Object[] stepApprover = Application.createQueryNoFilter(
|
Object[] stepApprover = Application.createQueryNoFilter(
|
||||||
"select stepId,state,approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'")
|
"select stepId,state,approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'")
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.approval)
|
.setParameter(2, this.approvalId)
|
||||||
.setParameter(3, currentNodeId)
|
.setParameter(3, currentNodeId)
|
||||||
.setParameter(4, approver)
|
.setParameter(4, approver)
|
||||||
.unique();
|
.unique();
|
||||||
|
|
|
@ -9,7 +9,6 @@ package com.rebuild.core.service.approval;
|
||||||
|
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author devezhao zhaofang123@gmail.com
|
* @author devezhao zhaofang123@gmail.com
|
||||||
|
@ -51,23 +50,17 @@ public class ApprovalStatus {
|
||||||
return currentStepNode;
|
return currentStepNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastComment() {
|
/**
|
||||||
if (currentStepNode == null || lastComment != null) {
|
* @return
|
||||||
return StringUtils.defaultIfBlank(lastComment, null);
|
*/
|
||||||
}
|
public String getPrevStepNode() {
|
||||||
|
if (currentStepNode == null) return null;
|
||||||
|
|
||||||
Object[] last = Application.createQueryNoFilter(
|
Object[] o = Application.createQueryNoFilter(
|
||||||
"select remark from RobotApprovalStep where recordId = ? and node = ? order by modifiedOn desc")
|
"select prevNode from RobotApprovalStep where recordId = ? and node = ? order by modifiedOn desc")
|
||||||
.setParameter(1, this.recordId)
|
.setParameter(1, this.recordId)
|
||||||
.setParameter(2, this.currentStepNode)
|
.setParameter(2, this.currentStepNode)
|
||||||
.unique();
|
.unique();
|
||||||
|
return o == null ? null : (String) o[0];
|
||||||
if (last == null) {
|
|
||||||
lastComment = StringUtils.EMPTY;
|
|
||||||
} else {
|
|
||||||
lastComment = StringUtils.defaultIfBlank((String) last[0], StringUtils.EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringUtils.defaultIfBlank(lastComment, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import com.rebuild.core.privileges.OperationDeniedException;
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||||
import com.rebuild.core.privileges.bizz.ZeroEntry;
|
|
||||||
import com.rebuild.core.service.BaseService;
|
import com.rebuild.core.service.BaseService;
|
||||||
import com.rebuild.core.service.DataSpecificationNoRollbackException;
|
import com.rebuild.core.service.DataSpecificationNoRollbackException;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
|
@ -54,6 +53,8 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.rebuild.core.privileges.bizz.ZeroEntry.AllowRevokeApproval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批流程。此类所有方法不应直接调用,而是通过 ApprovalProcessor 封装类
|
* 审批流程。此类所有方法不应直接调用,而是通过 ApprovalProcessor 封装类
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -324,13 +325,14 @@ public class ApprovalStepService extends BaseService {
|
||||||
final boolean isAdmin = UserHelper.isAdmin(opUser);
|
final boolean isAdmin = UserHelper.isAdmin(opUser);
|
||||||
|
|
||||||
if (isRevoke) {
|
if (isRevoke) {
|
||||||
boolean canRevoke = Application.getPrivilegesManager().allow(opUser, ZeroEntry.AllowRevokeApproval);
|
boolean canRevoke = Application.getPrivilegesManager().allow(opUser, AllowRevokeApproval);
|
||||||
if (!(isAdmin || canRevoke)) {
|
if (!(isAdmin || canRevoke)) {
|
||||||
throw new OperationDeniedException(Language.L("你无权撤销审批"));
|
throw new OperationDeniedException(Language.L("你无权撤销审批"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
boolean canRevoke = Application.getPrivilegesManager().allow(opUser, AllowRevokeApproval);
|
||||||
ID s = ApprovalHelper.getSubmitter(recordId, approvalId);
|
ID s = ApprovalHelper.getSubmitter(recordId, approvalId);
|
||||||
if (!(isAdmin || opUser.equals(s))) {
|
if (!(canRevoke || opUser.equals(s))) {
|
||||||
throw new OperationDeniedException(Language.L("你无权撤回审批"));
|
throw new OperationDeniedException(Language.L("你无权撤回审批"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*!
|
||||||
|
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.dashboard.charts;
|
||||||
|
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author devezhao
|
||||||
|
* @since 8/3/2024
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
public class AxisEntry {
|
||||||
|
|
||||||
|
final private int index;
|
||||||
|
final private Object[] key;
|
||||||
|
final private Object value;
|
||||||
|
|
||||||
|
public AxisEntry(Object[] rowValue, int index) {
|
||||||
|
this.key = ArrayUtils.subarray(rowValue, 0, rowValue.length - 1);
|
||||||
|
this.value = rowValue[rowValue.length - 1];
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Object[] getKeyRaw() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return Arrays.toString(getKeyRaw());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Object getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ public class CNMapChart extends ChartData {
|
||||||
Numerical[] nums = getNumericals();
|
Numerical[] nums = getNumericals();
|
||||||
|
|
||||||
if (nums.length > 0) {
|
if (nums.length > 0) {
|
||||||
return buildSql(dims[0], nums);
|
return buildSql(dims[0], nums, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
String sql = "select {0} from {1} where {2}";
|
String sql = "select {0} from {1} where {2}";
|
||||||
|
|
|
@ -37,8 +37,10 @@ import org.apache.commons.lang.StringUtils;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -205,16 +207,16 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
||||||
/**
|
/**
|
||||||
* 获取过滤 SQL
|
* 获取过滤 SQL
|
||||||
*
|
*
|
||||||
|
* @param withNumericalFilter
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected String getFilterSql(Numerical withAxisFilter) {
|
protected String getFilterSql(Numerical withNumericalFilter) {
|
||||||
String where = getFilterSql();
|
String filterSql = getFilterSql();
|
||||||
if (withAxisFilter != null && ParseHelper.validAdvFilter(withAxisFilter.getFilter())) {
|
if (withNumericalFilter != null && withNumericalFilter.getFilter() != null) {
|
||||||
AdvFilterParser filterParser = new AdvFilterParser(withAxisFilter.getFilter());
|
String filter = new AdvFilterParser(withNumericalFilter.getFilter()).toSqlWhere();
|
||||||
String fieldWhere = filterParser.toSqlWhere();
|
if (filter != null) filterSql = String.format("((%s) and (%s))", filterSql, filter);
|
||||||
if (fieldWhere != null) where = String.format("((%s) and (%s))", where, fieldWhere);
|
|
||||||
}
|
}
|
||||||
return where;
|
return filterSql;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -438,9 +440,10 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
||||||
*
|
*
|
||||||
* @param dim
|
* @param dim
|
||||||
* @param nums
|
* @param nums
|
||||||
|
* @param withFilter
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected String buildSql(Dimension dim, Numerical[] nums) {
|
protected String buildSql(Dimension dim, Numerical[] nums, boolean withFilter) {
|
||||||
List<String> numSqlItems = new ArrayList<>();
|
List<String> numSqlItems = new ArrayList<>();
|
||||||
for (Numerical num : nums) {
|
for (Numerical num : nums) {
|
||||||
numSqlItems.add(num.getSqlName());
|
numSqlItems.add(num.getSqlName());
|
||||||
|
@ -450,7 +453,7 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
||||||
sql = MessageFormat.format(sql,
|
sql = MessageFormat.format(sql,
|
||||||
dim.getSqlName(),
|
dim.getSqlName(),
|
||||||
StringUtils.join(numSqlItems, ", "),
|
StringUtils.join(numSqlItems, ", "),
|
||||||
getSourceEntity().getName(), getFilterSql());
|
getSourceEntity().getName(), getFilterSql(withFilter ? nums[0] : null));
|
||||||
return appendSqlSort(sql);
|
return appendSqlSort(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,25 +498,6 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
||||||
return appendSqlSort(sql);
|
return appendSqlSort(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* [1-9]N
|
|
||||||
*
|
|
||||||
* @param nums
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected String buildSql(Numerical[] nums) {
|
|
||||||
List<String> numSqlItems = new ArrayList<>();
|
|
||||||
for (Numerical num : nums) {
|
|
||||||
numSqlItems.add(num.getSqlName());
|
|
||||||
}
|
|
||||||
|
|
||||||
String sql = "select {0} from {1} where {2}";
|
|
||||||
sql = MessageFormat.format(sql,
|
|
||||||
StringUtils.join(numSqlItems, ", "),
|
|
||||||
getSourceEntity().getName(), getFilterSql());
|
|
||||||
return appendSqlSort(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param num
|
* @param num
|
||||||
* @param withFilter
|
* @param withFilter
|
||||||
|
@ -538,4 +522,53 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|
||||||
if (sorts != null) sql += " order by " + sorts;
|
if (sorts != null) sql += " order by " + sorts;
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nums
|
||||||
|
* @return
|
||||||
|
* @see Numerical#getFilter()
|
||||||
|
*/
|
||||||
|
protected boolean hasNumericalFilter(Numerical[] nums) {
|
||||||
|
for (Numerical num : nums) {
|
||||||
|
if (num.getFilter() != null) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param axisValues
|
||||||
|
* @param indexAndSize
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected Object[][] mergeAxisEntry2Data(List<AxisEntry> axisValues, int indexAndSize) {
|
||||||
|
// 1.同组合并
|
||||||
|
Map<String, AxisEntry[]> merged = new LinkedHashMap<>();
|
||||||
|
for (AxisEntry e : axisValues) {
|
||||||
|
AxisEntry[] eee = merged.computeIfAbsent(e.getKey(), k -> new AxisEntry[indexAndSize]);
|
||||||
|
eee[e.getIndex()] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.数据合并
|
||||||
|
int startIndex = getDimensions().length;
|
||||||
|
List<Object[]> dataRawList = new ArrayList<>();
|
||||||
|
for (AxisEntry[] group : merged.values()) {
|
||||||
|
AxisEntry keyItem = group[0];
|
||||||
|
for (AxisEntry item : group) {
|
||||||
|
if (keyItem != null) break;
|
||||||
|
keyItem = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] data = keyItem.getKeyRaw();
|
||||||
|
data = Arrays.copyOf(data, startIndex + indexAndSize);
|
||||||
|
|
||||||
|
for (AxisEntry item : group) {
|
||||||
|
if (item != null) {
|
||||||
|
data[startIndex + item.getIndex()] = item.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataRawList.add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataRawList.toArray(new Object[0][]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class LineChart extends ChartData {
|
||||||
Numerical[] nums = getNumericals();
|
Numerical[] nums = getNumericals();
|
||||||
final Dimension dim1 = dims[0];
|
final Dimension dim1 = dims[0];
|
||||||
|
|
||||||
// 部分支持
|
// 日期连续仅部分支持
|
||||||
final Type dim1Type = dim1.getField().getType();
|
final Type dim1Type = dim1.getField().getType();
|
||||||
final FormatCalc dim1Calc = dim1.getFormatCalc();
|
final FormatCalc dim1Calc = dim1.getFormatCalc();
|
||||||
boolean dateContinuous = renderOption.getBooleanValue("dateContinuous")
|
boolean dateContinuous = renderOption.getBooleanValue("dateContinuous")
|
||||||
|
@ -58,14 +58,14 @@ public class LineChart extends ChartData {
|
||||||
JSONArray yyyAxis = new JSONArray();
|
JSONArray yyyAxis = new JSONArray();
|
||||||
List<String> dataFlags = new ArrayList<>();
|
List<String> dataFlags = new ArrayList<>();
|
||||||
|
|
||||||
// 2DIM + 1NUM
|
// 模式1: 2-DIM + 1-NUM
|
||||||
// FIXME 多余AXIS会舍弃
|
// FIXME 多余AXIS会舍弃
|
||||||
if (dims.length > 1) {
|
if (dims.length > 1) {
|
||||||
Numerical num1 = nums[0];
|
Numerical num1 = nums[0];
|
||||||
Object[][] dataRaw = createQuery(buildSql(dims, num1)).array();
|
Object[][] dataRaw = createQuery(buildSql(dims, num1)).array();
|
||||||
// 连续日期
|
// 连续日期
|
||||||
if (dateContinuous && dataRaw.length > 0) {
|
if (dateContinuous && dataRaw.length > 0) {
|
||||||
dataRaw = putFullDates2Data(dataRaw, dim1, 2);
|
dataRaw = putContinuousDate2Data(dataRaw, dim1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Object> dim1Set = new ArrayList<>();
|
List<Object> dim1Set = new ArrayList<>();
|
||||||
|
@ -118,12 +118,27 @@ public class LineChart extends ChartData {
|
||||||
dataFlags.add(num1Flag);
|
dataFlags.add(num1Flag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 1DIM + 多NUM
|
// 模式2: 1-DIM + N-NUM
|
||||||
else {
|
else {
|
||||||
Object[][] dataRaw = createQuery(buildSql(dim1, nums)).array();
|
Object[][] dataRaw;
|
||||||
|
if (nums.length > 1 && hasNumericalFilter(nums)) {
|
||||||
|
// 分别查询
|
||||||
|
List<AxisEntry> axisValues = new ArrayList<>();
|
||||||
|
int indexAndSize = 0;
|
||||||
|
for (Numerical num : nums) {
|
||||||
|
Object[][] array = createQuery(buildSql(dim1, new Numerical[]{num}, true)).array();
|
||||||
|
for (Object[] o : array) axisValues.add(new AxisEntry(o, indexAndSize));
|
||||||
|
indexAndSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataRaw = mergeAxisEntry2Data(axisValues, indexAndSize);
|
||||||
|
} else {
|
||||||
|
dataRaw = createQuery(buildSql(dim1, nums, false)).array();
|
||||||
|
}
|
||||||
|
|
||||||
// 连续日期
|
// 连续日期
|
||||||
if (dateContinuous && dataRaw.length > 0) {
|
if (dateContinuous && dataRaw.length > 0) {
|
||||||
dataRaw = putFullDates2Data(dataRaw, dim1, 1);
|
dataRaw = putContinuousDate2Data(dataRaw, dim1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] numsAxis = new Object[nums.length];
|
Object[] numsAxis = new Object[nums.length];
|
||||||
|
@ -161,7 +176,7 @@ public class LineChart extends ChartData {
|
||||||
new Object[]{JSON.toJSON(dimAxis), JSON.toJSON(yyyAxis), renderOption});
|
new Object[]{JSON.toJSON(dimAxis), JSON.toJSON(yyyAxis), renderOption});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[][] putFullDates2Data(Object[][] dataRaw, Dimension date1, int numStartIndex) {
|
private Object[][] putContinuousDate2Data(Object[][] dataRaw, Dimension date1, int numStartIndex) {
|
||||||
Date min = null;
|
Date min = null;
|
||||||
Date max = null;
|
Date max = null;
|
||||||
for (Object[] o : dataRaw) {
|
for (Object[] o : dataRaw) {
|
||||||
|
|
|
@ -9,8 +9,9 @@ package com.rebuild.core.service.dashboard.charts;
|
||||||
|
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
|
import com.rebuild.core.service.query.ParseHelper;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +22,7 @@ import org.apache.commons.lang.StringUtils;
|
||||||
*/
|
*/
|
||||||
public class Numerical extends Axis {
|
public class Numerical extends Axis {
|
||||||
|
|
||||||
private JSONObject filter;
|
private JSONObject filter = null;
|
||||||
private int scale = 2;
|
private int scale = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +38,7 @@ public class Numerical extends Axis {
|
||||||
JSONObject filter, Field parentField) {
|
JSONObject filter, Field parentField) {
|
||||||
super(field, sort, calc, label, parentField);
|
super(field, sort, calc, label, parentField);
|
||||||
if (scale != null) this.scale = scale;
|
if (scale != null) this.scale = scale;
|
||||||
this.filter = filter;
|
if (ParseHelper.validAdvFilter(filter)) this.filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class RadarChart extends ChartData {
|
||||||
Numerical[] nums = getNumericals();
|
Numerical[] nums = getNumericals();
|
||||||
|
|
||||||
Dimension dim1 = dims[0];
|
Dimension dim1 = dims[0];
|
||||||
Object[][] dataRaw = createQuery(buildSql(dim1, nums)).array();
|
Object[][] dataRaw = createQuery(buildSql(dim1, nums, false)).array();
|
||||||
|
|
||||||
JSONArray indicator = new JSONArray();
|
JSONArray indicator = new JSONArray();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ 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.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ public class ScatterChart extends ChartData {
|
||||||
JSONArray series = new JSONArray();
|
JSONArray series = new JSONArray();
|
||||||
List<String> dataFlags = new ArrayList<>();
|
List<String> dataFlags = new ArrayList<>();
|
||||||
|
|
||||||
|
// 模式1: 0-DMI + N-NUM
|
||||||
if (dims.length == 0) {
|
if (dims.length == 0) {
|
||||||
Object[][] dataRaw = createQuery(buildSql(nums)).array();
|
Object[][] dataRaw = createQuery(buildSql(nums)).array();
|
||||||
for (Object[] item : dataRaw) {
|
for (Object[] item : dataRaw) {
|
||||||
|
@ -47,10 +50,11 @@ public class ScatterChart extends ChartData {
|
||||||
new String[]{"data"},
|
new String[]{"data"},
|
||||||
new Object[]{dataRaw});
|
new Object[]{dataRaw});
|
||||||
series.add(item);
|
series.add(item);
|
||||||
|
}
|
||||||
} else {
|
// 模式2: N-DMI + N-NUM
|
||||||
|
else {
|
||||||
for (Dimension dim : dims) {
|
for (Dimension dim : dims) {
|
||||||
Object[][] dataRaw = createQuery(buildSql(dim, nums)).array();
|
Object[][] dataRaw = createQuery(buildSql(dim, nums, false)).array();
|
||||||
for (Object[] item : dataRaw) {
|
for (Object[] item : dataRaw) {
|
||||||
String label = wrapAxisValue(dim, item[0]);
|
String label = wrapAxisValue(dim, item[0]);
|
||||||
for (int i = 1; i < item.length; i++) {
|
for (int i = 1; i < item.length; i++) {
|
||||||
|
@ -80,4 +84,17 @@ public class ScatterChart extends ChartData {
|
||||||
new String[]{"series", "dataLabel", "_renderOption"},
|
new String[]{"series", "dataLabel", "_renderOption"},
|
||||||
new Object[]{series, dataLabel, renderOption});
|
new Object[]{series, dataLabel, renderOption});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildSql(Numerical[] nums) {
|
||||||
|
List<String> numSqlItems = new ArrayList<>();
|
||||||
|
for (Numerical num : nums) {
|
||||||
|
numSqlItems.add(num.getSqlName());
|
||||||
|
}
|
||||||
|
|
||||||
|
String sql = "select {0} from {1} where {2}";
|
||||||
|
sql = MessageFormat.format(sql,
|
||||||
|
StringUtils.join(numSqlItems, ", "),
|
||||||
|
getSourceEntity().getName(), getFilterSql());
|
||||||
|
return appendSqlSort(sql);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,21 @@ public class TableChart extends ChartData {
|
||||||
Dimension[] dims = getDimensions();
|
Dimension[] dims = getDimensions();
|
||||||
Numerical[] nums = getNumericals();
|
Numerical[] nums = getNumericals();
|
||||||
|
|
||||||
Object[][] dataRaw = createQuery(buildSql(dims, nums)).array();
|
Object[][] dataRaw;
|
||||||
|
if (nums.length > 1 && hasNumericalFilter(nums)) {
|
||||||
|
// 分别查询
|
||||||
|
List<AxisEntry> axisValues = new ArrayList<>();
|
||||||
|
int indexAndSize = 0;
|
||||||
|
for (Numerical num : nums) {
|
||||||
|
Object[][] array = createQuery(buildSql(dims, new Numerical[]{num})).array();
|
||||||
|
for (Object[] o : array) axisValues.add(new AxisEntry(o, indexAndSize));
|
||||||
|
indexAndSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataRaw = mergeAxisEntry2Data(axisValues, indexAndSize);
|
||||||
|
} else {
|
||||||
|
dataRaw = createQuery(buildSql(dims, nums)).array();
|
||||||
|
}
|
||||||
|
|
||||||
// 行号
|
// 行号
|
||||||
if (this.showLineNumber && dataRaw.length > 0) {
|
if (this.showLineNumber && dataRaw.length > 0) {
|
||||||
|
@ -130,10 +144,12 @@ public class TableChart extends ChartData {
|
||||||
} else if (numSqlItems.isEmpty()) {
|
} else if (numSqlItems.isEmpty()) {
|
||||||
sql = "select {0} from {2} where {3} group by {0}";
|
sql = "select {0} from {2} where {3} group by {0}";
|
||||||
}
|
}
|
||||||
|
|
||||||
sql = MessageFormat.format(sql,
|
sql = MessageFormat.format(sql,
|
||||||
StringUtils.join(dimSqlItems, ", "),
|
StringUtils.join(dimSqlItems, ", "),
|
||||||
StringUtils.join(numSqlItems, ", "),
|
StringUtils.join(numSqlItems, ", "),
|
||||||
getSourceEntity().getName(), getFilterSql());
|
getSourceEntity().getName(), getFilterSql(nums.length > 0 ? nums[0] : null));
|
||||||
|
System.out.println(sql);
|
||||||
|
|
||||||
return appendSqlSort(sql);
|
return appendSqlSort(sql);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,12 @@ 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;
|
||||||
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.service.dashboard.charts.ChartData;
|
import com.rebuild.core.service.dashboard.charts.ChartData;
|
||||||
import com.rebuild.core.service.notification.MessageBuilder;
|
import com.rebuild.core.service.notification.MessageBuilder;
|
||||||
|
import com.rebuild.core.support.general.FieldValueHelper;
|
||||||
import com.rebuild.core.support.i18n.I18nUtils;
|
import com.rebuild.core.support.i18n.I18nUtils;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
@ -50,28 +54,32 @@ public class FeedsSchedule extends ChartData implements BuiltinChart {
|
||||||
@Override
|
@Override
|
||||||
public JSON build() {
|
public JSON build() {
|
||||||
Object[][] array = Application.createQueryNoFilter(
|
Object[][] array = Application.createQueryNoFilter(
|
||||||
"select feedsId,scheduleTime,content,contentMore from Feeds" +
|
"select feedsId,scheduleTime,content,contentMore,relatedRecord from Feeds" +
|
||||||
" where createdBy = ? and type = 4 and scheduleTime > ? order by scheduleTime")
|
" where createdBy = ? and type = 4 and scheduleTime > ? order by scheduleTime")
|
||||||
.setParameter(1, getUser())
|
.setParameter(1, getUser())
|
||||||
.setParameter(2, CalendarUtils.addDay(-30)) // 忽略30天前的
|
.setParameter(2, CalendarUtils.addDay(-30)) // 忽略30天前的
|
||||||
.setLimit(200)
|
.setLimit(200)
|
||||||
.array();
|
.array();
|
||||||
|
|
||||||
|
final EasyField relatedRecordMeta = EasyMetaFactory.valueOf(
|
||||||
|
MetadataHelper.getField("Feeds", "relatedRecord"));
|
||||||
|
|
||||||
JSONArray list = new JSONArray();
|
JSONArray list = new JSONArray();
|
||||||
for (Object[] o : array) {
|
for (Object[] o : array) {
|
||||||
// 有完成时间表示已完成
|
// 有完成时间表示已完成
|
||||||
JSONObject state = JSON.parseObject((String) o[3]);
|
JSONObject state = JSON.parseObject((String) o[3]);
|
||||||
if (state.getString("finishTime") != null) {
|
if (state.getString("finishTime") != null) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String scheduleTime = I18nUtils.formatDate((Date) o[1]);
|
String scheduleTime = I18nUtils.formatDate((Date) o[1]);
|
||||||
String content = (String) o[2];
|
String content = (String) o[2];
|
||||||
content = MessageBuilder.formatMessage(content);
|
content = MessageBuilder.formatMessage(content);
|
||||||
|
|
||||||
|
Object relatedRecord = o[4] == null
|
||||||
|
? null : FieldValueHelper.wrapFieldValue(o[4], relatedRecordMeta);
|
||||||
|
|
||||||
JSONObject item = JSONUtils.toJSONObject(
|
JSONObject item = JSONUtils.toJSONObject(
|
||||||
new String[]{"id", "scheduleTime", "content"},
|
new String[]{"id", "scheduleTime", "content", "relatedRecord"},
|
||||||
new Object[]{o[0], scheduleTime, content});
|
new Object[]{o[0], scheduleTime, content, relatedRecord});
|
||||||
list.add(item);
|
list.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,8 +105,10 @@ public class DataExporter extends SetUser {
|
||||||
final List<String> head = this.buildHead(builder);
|
final List<String> head = this.buildHead(builder);
|
||||||
|
|
||||||
// Excel
|
// Excel
|
||||||
if ("xls".equalsIgnoreCase(csvOrExcel)) {
|
// xlsx: 无 65535 行数限制
|
||||||
File file = RebuildConfiguration.getFileOfTemp(String.format("RBEXPORT-%d.xls", System.currentTimeMillis()));
|
if ("xls".equalsIgnoreCase(csvOrExcel) || "xlsx".equalsIgnoreCase(csvOrExcel)) {
|
||||||
|
File file = RebuildConfiguration.getFileOfTemp(
|
||||||
|
String.format("RBEXPORT-%d.%s", System.currentTimeMillis(), csvOrExcel.toLowerCase()));
|
||||||
|
|
||||||
List<List<String>> head4Excel = new ArrayList<>();
|
List<List<String>> head4Excel = new ArrayList<>();
|
||||||
for (String h : head) {
|
for (String h : head) {
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class DataReportManager implements ConfigManager {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ConfigBean[] getReportsRaw(Entity entity) {
|
public ConfigBean[] getReportsRaw(Entity entity) {
|
||||||
final String cKey = "DataReportManager35-" + entity.getName();
|
final String cKey = "DataReportManager38-" + entity.getName();
|
||||||
ConfigBean[] cached = (ConfigBean[]) Application.getCommonsCache().getx(cKey);
|
ConfigBean[] cached = (ConfigBean[]) Application.getCommonsCache().getx(cKey);
|
||||||
if (cached != null) return cached;
|
if (cached != null) return cached;
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ public class DataReportManager implements ConfigManager {
|
||||||
|
|
||||||
int type = ObjectUtils.toInt(o[4], TYPE_RECORD);
|
int type = ObjectUtils.toInt(o[4], TYPE_RECORD);
|
||||||
if (type == TYPE_WORD && outputType.contains("excel")) outputType += ",word";
|
if (type == TYPE_WORD && outputType.contains("excel")) outputType += ",word";
|
||||||
|
else if (type == TYPE_HTML5) outputType = "html5";
|
||||||
|
|
||||||
ConfigBean cb = new ConfigBean()
|
ConfigBean cb = new ConfigBean()
|
||||||
.set("id", o[0])
|
.set("id", o[0])
|
||||||
|
@ -116,7 +117,8 @@ public class DataReportManager implements ConfigManager {
|
||||||
.set("outputType", outputType)
|
.set("outputType", outputType)
|
||||||
.set("templateVersion", templateVersion)
|
.set("templateVersion", templateVersion)
|
||||||
.set("useFilter", useFilter)
|
.set("useFilter", useFilter)
|
||||||
.set("templateContent", o[6]);
|
.set("templateContent", o[6])
|
||||||
|
.set("entity", entity.getName());
|
||||||
alist.add(cb);
|
alist.add(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,26 +128,55 @@ public class DataReportManager implements ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param entity
|
* @param reportId
|
||||||
|
* @return
|
||||||
|
* @see #getReportsRaw(Entity)
|
||||||
|
*/
|
||||||
|
private ConfigBean getReportRaw(ID reportId, Entity entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
Object[] o = Application.getQueryFactory().uniqueNoFilter(reportId, "belongEntity");
|
||||||
|
if (o == null || !MetadataHelper.containsEntity((String) o[0])) {
|
||||||
|
throw new ConfigurationException("No config of report found : " + reportId);
|
||||||
|
}
|
||||||
|
entity = MetadataHelper.getEntity((String) o[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigBean[] cbs = getReportsRaw(entity);
|
||||||
|
for (ConfigBean cb : cbs) {
|
||||||
|
if (reportId.equals(cb.getID("id"))) return cb;
|
||||||
|
}
|
||||||
|
throw new ConfigurationException("No config of report found : " + reportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取报表配置
|
||||||
|
*
|
||||||
* @param reportId
|
* @param reportId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public TemplateFile getTemplateFile(Entity entity, ID reportId) {
|
public ConfigBean getReportRaw(ID reportId) {
|
||||||
String templateFile = null;
|
return getReportRaw(reportId, null);
|
||||||
String templateContent = null;
|
}
|
||||||
int type = DataReportManager.TYPE_RECORD;
|
|
||||||
boolean isV33 = false;
|
|
||||||
|
|
||||||
for (ConfigBean e : getReportsRaw(entity)) {
|
/**
|
||||||
if (e.getID("id").equals(reportId)) {
|
* @param reportId
|
||||||
templateFile = e.getString("template");
|
* @param entity
|
||||||
templateContent = e.getString("templateContent");
|
* @return
|
||||||
type = e.getInteger("type");
|
*/
|
||||||
isV33 = e.getInteger("templateVersion") == 3;
|
public TemplateFile buildTemplateFile(ID reportId, Entity entity) {
|
||||||
break;
|
final ConfigBean conf = getReportRaw(reportId, entity);
|
||||||
}
|
String templateFile = conf.getString("template");
|
||||||
|
String templateContent = conf.getString("templateContent");
|
||||||
|
int type = conf.getInteger("type");
|
||||||
|
boolean isV33 = conf.getInteger("templateVersion") == 3;
|
||||||
|
if (type == TYPE_HTML5) {
|
||||||
|
if (templateContent == null) templateContent = "";
|
||||||
|
} else {
|
||||||
|
templateContent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entity == null) entity = MetadataHelper.getEntity(conf.getString("entity"));
|
||||||
|
|
||||||
// v35 HTML5
|
// v35 HTML5
|
||||||
if (templateContent != null) {
|
if (templateContent != null) {
|
||||||
return new TemplateFile(templateContent, entity, reportId);
|
return new TemplateFile(templateContent, entity, reportId);
|
||||||
|
@ -166,20 +197,15 @@ public class DataReportManager implements ConfigManager {
|
||||||
/**
|
/**
|
||||||
* @param reportId
|
* @param reportId
|
||||||
* @return
|
* @return
|
||||||
* @see #getTemplateFile(Entity, ID) 性能好
|
* @see #buildTemplateFile(ID, Entity)
|
||||||
*/
|
*/
|
||||||
public TemplateFile getTemplateFile(ID reportId) {
|
public TemplateFile buildTemplateFile(ID reportId) {
|
||||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(reportId, "belongEntity");
|
return buildTemplateFile(reportId, null);
|
||||||
if (o == null || !MetadataHelper.containsEntity((String) o[0])) {
|
|
||||||
throw new ConfigurationException("No config of report found : " + reportId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return getTemplateFile(MetadataHelper.getEntity((String) o[0]), reportId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clean(Object entity) {
|
public void clean(Object entity) {
|
||||||
final String cKey = "DataReportManager35-" + ((Entity) entity).getName();
|
final String cKey = "DataReportManager38-" + ((Entity) entity).getName();
|
||||||
Application.getCommonsCache().evict(cKey);
|
Application.getCommonsCache().evict(cKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,31 +219,25 @@ public class DataReportManager implements ConfigManager {
|
||||||
* @param fileName
|
* @param fileName
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String getReportName(ID reportId, Object idOrEntity, String fileName) {
|
public static String getPrettyReportName(ID reportId, Object idOrEntity, String fileName) {
|
||||||
final Entity be = idOrEntity instanceof ID
|
final Entity be = idOrEntity instanceof ID
|
||||||
? MetadataHelper.getEntity(((ID) idOrEntity).getEntityCode())
|
? MetadataHelper.getEntity(((ID) idOrEntity).getEntityCode())
|
||||||
: MetadataHelper.getEntity((String) idOrEntity);
|
: MetadataHelper.getEntity((String) idOrEntity);
|
||||||
|
|
||||||
String name = null;
|
ConfigBean conf = DataReportManager.instance.getReportRaw(reportId, be);
|
||||||
for (ConfigBean cb : DataReportManager.instance.getReportsRaw(be)) {
|
String name = conf.getString("name");
|
||||||
if (cb.getID("id").equals(reportId)) {
|
if (ContentWithFieldVars.matchsVars(name).isEmpty()) {
|
||||||
name = cb.getString("name");
|
name = String.format("%s-%s", name, CalendarUtils.getPlainDateFormat().format(CalendarUtils.now()));
|
||||||
if (ContentWithFieldVars.matchsVars(name).isEmpty()) {
|
} else if (idOrEntity instanceof ID) {
|
||||||
name = String.format("%s-%s", name, CalendarUtils.getPlainDateFormat().format(CalendarUtils.now()));
|
name = ContentWithFieldVars.replaceWithRecord(name, (ID) idOrEntity);
|
||||||
} else if (idOrEntity instanceof ID) {
|
|
||||||
name = ContentWithFieldVars.replaceWithRecord(name, (ID) idOrEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suffix
|
|
||||||
if (fileName.endsWith(".pdf")) name += ".pdf";
|
|
||||||
else if (fileName.endsWith(".docx")) name += ".docx";
|
|
||||||
else if (fileName.endsWith(".doc")) name += ".doc";
|
|
||||||
else if (fileName.endsWith(".xlsx")) name += ".xlsx";
|
|
||||||
else if (fileName.endsWith(".xls")) name += ".xls";
|
|
||||||
else if (fileName.endsWith(".csv")) name += ".csv";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Suffix
|
||||||
|
if (fileName.endsWith(".pdf")) name += ".pdf";
|
||||||
|
else if (fileName.endsWith(".docx")) name += ".docx";
|
||||||
|
else if (fileName.endsWith(".doc")) name += ".doc";
|
||||||
|
else if (fileName.endsWith(".xlsx")) name += ".xlsx";
|
||||||
|
else if (fileName.endsWith(".xls")) name += ".xls";
|
||||||
|
else if (fileName.endsWith(".csv")) name += ".csv";
|
||||||
|
|
||||||
return StringUtils.defaultIfBlank(name, "UNTITLE");
|
return StringUtils.defaultIfBlank(name, "UNTITLE");
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,13 +365,17 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
if (dt == DisplayType.BARCODE) {
|
if (dt == DisplayType.BARCODE) {
|
||||||
data.put(varName, buildBarcodeData(easyField.getRawMeta(), record.getPrimary()));
|
data.put(varName, buildBarcodeData(easyField.getRawMeta(), record.getPrimary()));
|
||||||
} else if (fieldValue == null) {
|
} else if (fieldValue == null) {
|
||||||
data.put(varName, StringUtils.EMPTY);
|
// v3.8
|
||||||
|
Object funcValue = ValueConvertFunc.convert(easyField, null, varName);
|
||||||
|
data.put(varName, funcValue == null ? StringUtils.EMPTY : funcValue);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (dt == DisplayType.SIGN) {
|
if (dt == DisplayType.SIGN) {
|
||||||
fieldValue = buildSignData((String) fieldValue);
|
fieldValue = buildSignData((String) fieldValue);
|
||||||
} else if (dt == DisplayType.IMAGE) {
|
} else if (dt == DisplayType.IMAGE) {
|
||||||
fieldValue = buildImageData((String) fieldValue);
|
fieldValue = buildImageData((String) fieldValue);
|
||||||
|
// TODO Excel 指定图片大小(可通过 Excel 单元格大小控制?)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (dt == DisplayType.NUMBER) {
|
if (dt == DisplayType.NUMBER) {
|
||||||
|
@ -522,7 +526,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
return create(reportId, recordId);
|
return create(reportId, recordId);
|
||||||
} else {
|
} else {
|
||||||
TemplateFile tt = DataReportManager.instance
|
TemplateFile tt = DataReportManager.instance
|
||||||
.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
.buildTemplateFile(reportId, MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||||
return new EasyExcelGenerator33(tt.templateFile, recordIds);
|
return new EasyExcelGenerator33(tt.templateFile, recordIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,7 +538,7 @@ public class EasyExcelGenerator extends SetUser {
|
||||||
*/
|
*/
|
||||||
public static EasyExcelGenerator create(ID reportId, ID recordId) {
|
public static EasyExcelGenerator create(ID reportId, ID recordId) {
|
||||||
TemplateFile tt = DataReportManager.instance
|
TemplateFile tt = DataReportManager.instance
|
||||||
.getTemplateFile(MetadataHelper.getEntity(recordId.getEntityCode()), reportId);
|
.buildTemplateFile(reportId, MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||||
return create(tt.templateFile, recordId, tt.isV33);
|
return create(tt.templateFile, recordId, tt.isV33);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ import static com.rebuild.core.service.datareport.TemplateExtractor33.NROW_PREFI
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
||||||
|
|
||||||
|
// 支持多记录导出,会合并到一个 Excel 文件
|
||||||
final private List<ID> recordIdMultiple;
|
final private List<ID> recordIdMultiple;
|
||||||
|
|
||||||
private Set<String> inShapeVars;
|
private Set<String> inShapeVars;
|
||||||
|
|
|
@ -61,9 +61,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
||||||
|
|
||||||
for (Map.Entry<String, String> e : varsMap.entrySet()) {
|
for (Map.Entry<String, String> e : varsMap.entrySet()) {
|
||||||
String varName = e.getKey();
|
String varName = e.getKey();
|
||||||
if (varName.startsWith(NROW_PREFIX + PLACEHOLDER)) {
|
if (varName.startsWith(NROW_PREFIX + PLACEHOLDER)) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String validField = e.getValue();
|
String validField = e.getValue();
|
||||||
if (validField != null && e.getKey().startsWith(NROW_PREFIX)) {
|
if (validField != null && e.getKey().startsWith(NROW_PREFIX)) {
|
||||||
|
@ -113,9 +111,9 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static EasyExcelListGenerator create(ID reportId, JSONObject queryData) {
|
public static EasyExcelListGenerator create(ID reportId, JSONObject queryData) {
|
||||||
TemplateFile tb = DataReportManager.instance.getTemplateFile(
|
TemplateFile tt = DataReportManager.instance.buildTemplateFile(
|
||||||
MetadataHelper.getEntity(queryData.getString("entity")), reportId);
|
reportId, MetadataHelper.getEntity(queryData.getString("entity")));
|
||||||
return create(tb.templateFile, queryData);
|
return create(tt.templateFile, queryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -92,12 +92,15 @@ public class TemplateExtractor {
|
||||||
|
|
||||||
Map<String, String> map = new HashMap<>();
|
Map<String, String> map = new HashMap<>();
|
||||||
for (final String varName : vars) {
|
for (final String varName : vars) {
|
||||||
|
// v3.8
|
||||||
|
String thatName = ValueConvertFunc.splitName(varName);
|
||||||
|
|
||||||
// 列表型字段
|
// 列表型字段
|
||||||
if (varName.startsWith(NROW_PREFIX)) {
|
if (thatName.startsWith(NROW_PREFIX)) {
|
||||||
String listField = varName.substring(1);
|
String listField = thatName.substring(1);
|
||||||
|
|
||||||
// 审批流程
|
// 审批流程
|
||||||
if (!this.isListType && varName.startsWith(APPROVAL_PREFIX)) {
|
if (!this.isListType && thatName.startsWith(APPROVAL_PREFIX)) {
|
||||||
String stepNodeField = listField.substring(APPROVAL_PREFIX.length());
|
String stepNodeField = listField.substring(APPROVAL_PREFIX.length());
|
||||||
if (approvalEntity != null && MetadataHelper.getLastJoinField(approvalEntity, stepNodeField) != null) {
|
if (approvalEntity != null && MetadataHelper.getLastJoinField(approvalEntity, stepNodeField) != null) {
|
||||||
map.put(varName, stepNodeField);
|
map.put(varName, stepNodeField);
|
||||||
|
@ -120,10 +123,10 @@ public class TemplateExtractor {
|
||||||
map.put(varName, transformRealField(entity, listField));
|
map.put(varName, transformRealField(entity, listField));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (MetadataHelper.getLastJoinField(entity, varName) != null) {
|
} else if (MetadataHelper.getLastJoinField(entity, thatName) != null) {
|
||||||
map.put(varName, varName);
|
map.put(varName, thatName);
|
||||||
} else {
|
} else {
|
||||||
map.put(varName, transformRealField(entity, varName));
|
map.put(varName, transformRealField(entity, thatName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
|
|
|
@ -8,14 +8,18 @@ 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.commons.CalendarUtils;
|
||||||
|
import cn.devezhao.commons.ObjectUtils;
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import com.deepoove.poi.data.PictureRenderData;
|
import com.deepoove.poi.data.PictureRenderData;
|
||||||
import com.deepoove.poi.data.Pictures;
|
import com.deepoove.poi.data.Pictures;
|
||||||
|
import com.rebuild.core.configuration.ConfigBean;
|
||||||
|
import com.rebuild.core.configuration.general.MultiSelectManager;
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyDecimal;
|
import com.rebuild.core.metadata.easymeta.EasyDecimal;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||||
import com.rebuild.utils.CommonsUtils;
|
import com.rebuild.utils.CommonsUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
|
||||||
|
@ -25,7 +29,10 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
|
@ -34,8 +41,18 @@ import java.util.Date;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ValueConvertFunc {
|
public class ValueConvertFunc {
|
||||||
|
|
||||||
// # 函数
|
// # 函数分隔符
|
||||||
protected static final String FUNC_SPLITER = "#";
|
private static final String FUNC_SPLITER = "#";
|
||||||
|
private static final String FVAL_SPLITER = ":";
|
||||||
|
// 支持的函数
|
||||||
|
private static final String CHINESE_4DATE_NUM = "CHINESE";
|
||||||
|
private static final String CHINESEYUAN_4NUM = "CHINESEYUAN";
|
||||||
|
private static final String THOUSANDS_4NUM = "THOUSANDS";
|
||||||
|
private static final String CHECKBOX_4OPTION = "CHECKBOX";
|
||||||
|
private static final String CHECKBOX2_4OPTION = "CHECKBOX" + FVAL_SPLITER + "2";
|
||||||
|
private static final String PICKAT_4CLASS_DATE = "PICKAT"; // eg. PICKAT:2
|
||||||
|
private static final String SIZE_4IMG = "SIZE"; // eg. SIZE:100*200, SIZE:100
|
||||||
|
private static final String EMPTY = "EMPTY"; // eg. EMPTY:无
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param field
|
* @param field
|
||||||
|
@ -47,41 +64,102 @@ public class ValueConvertFunc {
|
||||||
String thatFunc = splitFunc(varName);
|
String thatFunc = splitFunc(varName);
|
||||||
if (thatFunc == null) return value;
|
if (thatFunc == null) return value;
|
||||||
|
|
||||||
|
// 默认值
|
||||||
|
if (thatFunc.startsWith(EMPTY)) {
|
||||||
|
if (value == null || StringUtils.isBlank(value.toString())) {
|
||||||
|
return extractFuncValue(thatFunc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final DisplayType type = field.getDisplayType();
|
final DisplayType type = field.getDisplayType();
|
||||||
if (type == DisplayType.NUMBER || type == DisplayType.DECIMAL) {
|
|
||||||
if ("CHINESEYUAN".equals(thatFunc)) {
|
// 空值也处理
|
||||||
return Convert.digitToChinese((Number) value);
|
if (type == DisplayType.MULTISELECT || type == DisplayType.PICKLIST) {
|
||||||
}
|
if (CHECKBOX_4OPTION.equals(thatFunc) || CHECKBOX2_4OPTION.equals(thatFunc)) {
|
||||||
if ("THOUSANDS".equals(thatFunc)) {
|
String[] m = value == null ? new String[0] : value.toString().split(", ");
|
||||||
String format = "##,##0";
|
ConfigBean[] items = MultiSelectManager.instance.getPickListRaw(field.getRawMeta(), false);
|
||||||
if (type == DisplayType.DECIMAL) {
|
|
||||||
int scale = ((EasyDecimal) field).getScale();
|
String[] flags = new String[]{"■", "□"};
|
||||||
if (scale > 0) {
|
if (CHECKBOX2_4OPTION.equals(thatFunc)) flags = new String[]{"●", "○"};
|
||||||
format += "." + StringUtils.leftPad("", scale, "0");
|
|
||||||
}
|
List<String> chk = new ArrayList<>();
|
||||||
|
for (ConfigBean item : items) {
|
||||||
|
String itemText = item.getString("text");
|
||||||
|
if (ArrayUtils.contains(m, itemText)) chk.add(flags[0] + itemText);
|
||||||
|
else chk.add(flags[1] + itemText);
|
||||||
}
|
}
|
||||||
return new DecimalFormat(format).format(value);
|
|
||||||
|
return StringUtils.join(chk, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
if (type == DisplayType.NUMBER || type == DisplayType.DECIMAL) {
|
||||||
|
switch (thatFunc) {
|
||||||
|
case CHINESEYUAN_4NUM:
|
||||||
|
return Convert.digitToChinese((Number) value);
|
||||||
|
case CHINESE_4DATE_NUM:
|
||||||
|
return Convert.numberToChinese(((Number) value).doubleValue(), true);
|
||||||
|
case THOUSANDS_4NUM:
|
||||||
|
String format = "##,##0";
|
||||||
|
if (type == DisplayType.DECIMAL) {
|
||||||
|
int scale = ((EasyDecimal) field).getScale();
|
||||||
|
if (scale > 0) {
|
||||||
|
format += "." + StringUtils.leftPad("", scale, "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new DecimalFormat(format).format(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (type == DisplayType.DATE || type == DisplayType.DATETIME) {
|
} else if (type == DisplayType.DATE || type == DisplayType.DATETIME || type == DisplayType.TIME) {
|
||||||
if ("CHINESEDATE".equals(thatFunc)) {
|
if (CHINESE_4DATE_NUM.equals(thatFunc) || "CHINESEDATE".equals(thatFunc)) {
|
||||||
Date d = CommonsUtils.parseDate(value.toString());
|
if (type == DisplayType.TIME) {
|
||||||
if (d == null) return value;
|
String s = "2024-01-01 " + value;
|
||||||
|
if (s.length() == 13) s += ":00";
|
||||||
|
Date d = CommonsUtils.parseDate(s);
|
||||||
|
if (d == null) return value;
|
||||||
|
|
||||||
int len = field.wrapValue(CalendarUtils.now()).toString().length();
|
int len = field.wrapValue(LocalTime.now()).toString().length() + 1;
|
||||||
if (len <= 10) len += 1; // yyyy-MM-dd
|
String format = CalendarUtils.CN_TIME_FORMAT.substring(0, len);
|
||||||
else len += 2;
|
return CalendarUtils.getDateFormat(format).format(d);
|
||||||
|
} else {
|
||||||
|
Date d = CommonsUtils.parseDate(value.toString());
|
||||||
|
if (d == null) return value;
|
||||||
|
|
||||||
String format = CalendarUtils.CN_DATETIME_FORMAT.substring(0, len);
|
int len = field.wrapValue(CalendarUtils.now()).toString().length();
|
||||||
return CalendarUtils.getDateFormat(format).format(d);
|
if (len <= 10) len += 1; // yyyy-MM-dd
|
||||||
|
else len += 2;
|
||||||
|
|
||||||
|
String format = CalendarUtils.CN_DATETIME_FORMAT.substring(0, len);
|
||||||
|
return CalendarUtils.getDateFormat(format).format(d);
|
||||||
|
}
|
||||||
|
} else if (thatFunc.startsWith(PICKAT_4CLASS_DATE) && type != DisplayType.TIME) {
|
||||||
|
String[] m = value.toString().replace(" ", "-").split("[-:]");
|
||||||
|
int pickIndex = ObjectUtils.toInt(extractFuncValue(thatFunc)) - 1;
|
||||||
|
|
||||||
|
if (pickIndex < 0) return m[0];
|
||||||
|
if (m.length > pickIndex) return m[pickIndex];
|
||||||
|
return m[0]; // first
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (type == DisplayType.CLASSIFICATION) {
|
||||||
|
if (thatFunc.startsWith(PICKAT_4CLASS_DATE)) {
|
||||||
|
int pickIndex = ObjectUtils.toInt(extractFuncValue(thatFunc)) - 1;
|
||||||
|
String[] m = value.toString().split("\\.");
|
||||||
|
|
||||||
|
if (pickIndex < 0) return m[m.length - 1];
|
||||||
|
if (m.length > pickIndex) return m[pickIndex];
|
||||||
|
return m[m.length - 1]; // last
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换图片
|
* 转换图片(WORD 格式)
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
* @param varName
|
* @param varName
|
||||||
|
@ -93,8 +171,8 @@ public class ValueConvertFunc {
|
||||||
String thatFunc = splitFunc(varName);
|
String thatFunc = splitFunc(varName);
|
||||||
if (thatFunc == null) return builder.create();
|
if (thatFunc == null) return builder.create();
|
||||||
|
|
||||||
if (thatFunc.startsWith("SIZE") && thatFunc.length() > 4) {
|
if (thatFunc.startsWith(SIZE_4IMG)) {
|
||||||
String[] wh = thatFunc.substring(4).split("\\*");
|
String[] wh = extractFuncValue(thatFunc).split("\\*");
|
||||||
int width = NumberUtils.toInt(wh[0]);
|
int width = NumberUtils.toInt(wh[0]);
|
||||||
int height = -1;
|
int height = -1;
|
||||||
|
|
||||||
|
@ -116,19 +194,30 @@ public class ValueConvertFunc {
|
||||||
height = NumberUtils.toInt(wh[1]);
|
height = NumberUtils.toInt(wh[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height < 0) height = width;
|
builder = Pictures.ofBytes(value).size(width, height > 0 ? height : width);
|
||||||
builder = Pictures.ofBytes(value).size(width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取函数附加值
|
||||||
|
private static String extractFuncValue(String func) {
|
||||||
|
// v3.7 兼容
|
||||||
|
if (func.startsWith(SIZE_4IMG) && !func.startsWith(SIZE_4IMG + FVAL_SPLITER)) {
|
||||||
|
func = SIZE_4IMG + FVAL_SPLITER + func.substring(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] nn = func.split(FVAL_SPLITER);
|
||||||
|
if (nn.length == 2) return nn[1];
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param varName
|
* @param varName
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String splitName(String varName) {
|
public static String splitName(String varName) {
|
||||||
return varName.split("#")[0].trim();
|
return varName.split(FUNC_SPLITER)[0].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,6 +225,6 @@ public class ValueConvertFunc {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String splitFunc(String varName) {
|
public static String splitFunc(String varName) {
|
||||||
return varName.contains(FUNC_SPLITER) ? varName.split("#")[1].trim() : null;
|
return varName.contains(FUNC_SPLITER) ? varName.split(FUNC_SPLITER)[1].trim() : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public abstract class BaseFeedsService extends ObservableService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Record update(Record record) {
|
public Record update(Record record) {
|
||||||
record = super.update(converContent4Mentions((record)));
|
record = super.update(converContent4Mentions(record));
|
||||||
|
|
||||||
awareMention(record, false);
|
awareMention(record, false);
|
||||||
return record;
|
return record;
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
||||||
addObserver(new RobotTriggerObserver());
|
addObserver(new RobotTriggerObserver());
|
||||||
try {
|
try {
|
||||||
addObserver((SafeObserver) ReflectUtils.newObject("com.rebuild.rbv.sop.RobotSopObserver"));
|
addObserver((SafeObserver) ReflectUtils.newObject("com.rebuild.rbv.sop.RobotSopObserver"));
|
||||||
} catch (Exception ignoredClassNotFound){}
|
} catch (Exception ignoredRbvClassMiss){}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,12 +8,15 @@ 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.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.NamedThreadLocal;
|
import org.springframework.core.NamedThreadLocal;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
* @since 2020/9/29
|
* @since 2020/9/29
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class GeneralEntityServiceContextHolder {
|
public class GeneralEntityServiceContextHolder {
|
||||||
|
|
||||||
private static final ThreadLocal<Boolean> SKIP_SERIES_VALUE = new NamedThreadLocal<>("Skip series value");
|
private static final ThreadLocal<Boolean> SKIP_SERIES_VALUE = new NamedThreadLocal<>("Skip series value");
|
||||||
|
@ -24,6 +27,10 @@ public class GeneralEntityServiceContextHolder {
|
||||||
|
|
||||||
private static final ThreadLocal<ID> FROM_TRIGGERS = new NamedThreadLocal<>("From triggers");
|
private static final ThreadLocal<ID> FROM_TRIGGERS = new NamedThreadLocal<>("From triggers");
|
||||||
|
|
||||||
|
private static final ThreadLocal<Boolean> QUICK_MODE = new NamedThreadLocal<>("Quick mode");
|
||||||
|
|
||||||
|
private static final ThreadLocal<ID> SKIP_GUARD = new NamedThreadLocal<>("Skip some check once");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新建记录时允许跳过自动编号字段
|
* 新建记录时允许跳过自动编号字段
|
||||||
*/
|
*/
|
||||||
|
@ -105,4 +112,48 @@ public class GeneralEntityServiceContextHolder {
|
||||||
if (mode != null) REPEATED_CHECK_MODE.remove();
|
if (mode != null) REPEATED_CHECK_MODE.remove();
|
||||||
return mode == null ? 0 : mode;
|
return mode == null ? 0 : mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略一些操作/触发器的执行,达到快速操作的目的
|
||||||
|
*/
|
||||||
|
public static void setQuickMode() {
|
||||||
|
QUICK_MODE.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param once
|
||||||
|
* @return
|
||||||
|
* @see #setQuickMode()
|
||||||
|
*/
|
||||||
|
public static boolean isQuickMode(boolean once) {
|
||||||
|
Boolean is = QUICK_MODE.get();
|
||||||
|
if (is != null && once) QUICK_MODE.remove();
|
||||||
|
return is != null && is;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许无权限操作一次
|
||||||
|
*
|
||||||
|
* @param recordId
|
||||||
|
*/
|
||||||
|
public static void setSkipGuard(ID recordId) {
|
||||||
|
Assert.notNull(recordId, "[recordId] cannot be null");
|
||||||
|
|
||||||
|
ID existsWarn = SKIP_GUARD.get();
|
||||||
|
if (existsWarn != null) {
|
||||||
|
log.warn("Not removed skip record : {}", existsWarn);
|
||||||
|
SKIP_GUARD.remove();
|
||||||
|
}
|
||||||
|
SKIP_GUARD.set(recordId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* @see #setSkipGuard(ID)
|
||||||
|
*/
|
||||||
|
public static ID isSkipGuardOnce() {
|
||||||
|
ID recordId = SKIP_GUARD.get();
|
||||||
|
if (recordId != null) SKIP_GUARD.remove();
|
||||||
|
return recordId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,8 @@ public class RecentlyUsedHelper {
|
||||||
* @param type
|
* @param type
|
||||||
*/
|
*/
|
||||||
public static void add(ID user, ID id, String type) {
|
public static void add(ID user, ID id, String type) {
|
||||||
|
if (EntityHelper.isUnsavedId(id)) return;
|
||||||
|
|
||||||
final String key = formatKey(user, MetadataHelper.getEntityName(id), type);
|
final String key = formatKey(user, MetadataHelper.getEntityName(id), type);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
LinkedList<ID> cached = (LinkedList<ID>) Application.getCommonsCache().getx(key);
|
LinkedList<ID> cached = (LinkedList<ID>) Application.getCommonsCache().getx(key);
|
||||||
|
|
|
@ -48,6 +48,8 @@ public class SeriesReindexTask extends HeavyTask<Integer> {
|
||||||
if (ONLY_REINDEX_BLANK) {
|
if (ONLY_REINDEX_BLANK) {
|
||||||
sql += String.format(" where %s is null or %s = ''", field.getName(), field.getName());
|
sql += String.format(" where %s is null or %s = ''", field.getName(), field.getName());
|
||||||
}
|
}
|
||||||
|
if (field.getOwnEntity().containsField(EntityHelper.AutoId)) sql += " order by autoId asc";
|
||||||
|
|
||||||
Query query = Application.createQueryNoFilter(sql);
|
Query query = Application.createQueryNoFilter(sql);
|
||||||
Object[][] array = QueryHelper.readArray(query);
|
Object[][] array = QueryHelper.readArray(query);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数字自增系列
|
* 数字自增系列
|
||||||
|
@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
public class IncreasingVar extends SeriesVar {
|
public class IncreasingVar extends SeriesVar {
|
||||||
|
|
||||||
private static final Object INCREASINGS_LOCK = new Object();
|
private static final Object INCREASINGS_LOCK = new Object();
|
||||||
private static final Map<String, AtomicInteger> INCREASINGS = new ConcurrentHashMap<>();
|
private static final Map<String, AtomicLong> INCREASINGS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private Field field;
|
private Field field;
|
||||||
private String zeroFlag;
|
private String zeroFlag;
|
||||||
|
@ -55,6 +55,11 @@ public class IncreasingVar extends SeriesVar {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getNameKey() {
|
||||||
|
Assert.notNull(this.field, "[this.field] cannot be null");
|
||||||
|
return String.format("Series-%s.%s", field.getOwnEntity().getName(), field.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String generate() {
|
public String generate() {
|
||||||
// Preview mode
|
// Preview mode
|
||||||
|
@ -62,19 +67,18 @@ public class IncreasingVar extends SeriesVar {
|
||||||
return StringUtils.leftPad("1", getSymbols().length(), '0');
|
return StringUtils.leftPad("1", getSymbols().length(), '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
final String nameKey = String.format("Series-%s.%s", field.getOwnEntity().getName(), field.getName());
|
final String nameKey = getNameKey();
|
||||||
|
|
||||||
synchronized (INCREASINGS_LOCK) {
|
synchronized (INCREASINGS_LOCK) {
|
||||||
AtomicInteger incr = INCREASINGS.get(nameKey);
|
AtomicLong incr = INCREASINGS.get(nameKey);
|
||||||
if (incr == null) {
|
if (incr == null) {
|
||||||
String val = KVStorage.getCustomValue(nameKey);
|
String val = KVStorage.getCustomValue(nameKey);
|
||||||
int init = val == null ? countFromDb() : ObjectUtils.toInt(val);
|
long init = val == null ? countFromDb() : ObjectUtils.toLong(val);
|
||||||
|
|
||||||
incr = new AtomicInteger(init);
|
incr = new AtomicLong(init);
|
||||||
INCREASINGS.put(nameKey, incr);
|
INCREASINGS.put(nameKey, incr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextValue = incr.incrementAndGet();
|
long nextValue = incr.incrementAndGet();
|
||||||
RebuildConfiguration.setCustomValue(nameKey, nextValue, Boolean.TRUE);
|
RebuildConfiguration.setCustomValue(nameKey, nextValue, Boolean.TRUE);
|
||||||
|
|
||||||
return StringUtils.leftPad(nextValue + "", getSymbols().length(), '0');
|
return StringUtils.leftPad(nextValue + "", getSymbols().length(), '0');
|
||||||
|
@ -82,16 +86,27 @@ public class IncreasingVar extends SeriesVar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空序号缓存
|
* 重置序号
|
||||||
|
*
|
||||||
|
* @param reset
|
||||||
*/
|
*/
|
||||||
protected void clean() {
|
protected void clean(long reset) {
|
||||||
Assert.notNull(this.field, "[this.field] cannot be null");
|
Assert.isTrue(reset >= 0, "[reset] must be greater than 0");
|
||||||
|
final String nameKey = getNameKey();
|
||||||
final String nameKey = String.format("Series-%s.%s", field.getOwnEntity().getName(), field.getName());
|
|
||||||
|
|
||||||
synchronized (INCREASINGS_LOCK) {
|
synchronized (INCREASINGS_LOCK) {
|
||||||
INCREASINGS.remove(nameKey);
|
INCREASINGS.remove(nameKey);
|
||||||
RebuildConfiguration.setCustomValue(nameKey, 0, Boolean.TRUE);
|
RebuildConfiguration.setCustomValue(nameKey, reset, Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getCurrentValue() {
|
||||||
|
final String nameKey = getNameKey();
|
||||||
|
synchronized (INCREASINGS_LOCK) {
|
||||||
|
String val = KVStorage.getCustomValue(nameKey);
|
||||||
|
return val == null ? 0L : ObjectUtils.toLong(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +117,7 @@ public class IncreasingVar extends SeriesVar {
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private int countFromDb() {
|
private long countFromDb() {
|
||||||
String dateLimit = null;
|
String dateLimit = null;
|
||||||
if ("Y".equals(zeroFlag)) {
|
if ("Y".equals(zeroFlag)) {
|
||||||
dateLimit = CalendarUtils.format("yyyy", CalendarUtils.now()) + "-01-01";
|
dateLimit = CalendarUtils.format("yyyy", CalendarUtils.now()) + "-01-01";
|
||||||
|
@ -121,6 +136,6 @@ public class IncreasingVar extends SeriesVar {
|
||||||
String sql = String.format("select count(%s) from %s where %s",
|
String sql = String.format("select count(%s) from %s where %s",
|
||||||
field.getName(), field.getOwnEntity().getName(), dateLimit);
|
field.getName(), field.getOwnEntity().getName(), dateLimit);
|
||||||
Object[] count = Application.createQueryNoFilter(sql).unique();
|
Object[] count = Application.createQueryNoFilter(sql).unique();
|
||||||
return ObjectUtils.toInt(count[0]);
|
return ObjectUtils.toLong(count[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,25 @@ public class SeriesGeneratorFactory {
|
||||||
* @param field
|
* @param field
|
||||||
*/
|
*/
|
||||||
public static void zero(Field field) {
|
public static void zero(Field field) {
|
||||||
new IncreasingVar(field).clean();
|
zero(field, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置序号
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* @param reset
|
||||||
|
*/
|
||||||
|
public static void zero(Field field, long reset) {
|
||||||
|
new IncreasingVar(field).clean(reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前序号
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
*/
|
||||||
|
public static long getCurrentIncreasingVarValue(Field field) {
|
||||||
|
return new IncreasingVar(field).getCurrentValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ 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;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.service.general.GeneralEntityService;
|
import com.rebuild.core.service.general.GeneralEntityService;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
|
@ -174,7 +173,7 @@ public class RecordTransfomer extends SetUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ID saveRecord(Record record, List<Record> detailsList) {
|
protected ID saveRecord(Record record, List<Record> detailsList) {
|
||||||
if (this.skipGuard) PrivilegesGuardContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
if (this.skipGuard) GeneralEntityServiceContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
||||||
|
|
||||||
if (detailsList != null && !detailsList.isEmpty()) {
|
if (detailsList != null && !detailsList.isEmpty()) {
|
||||||
record.setObjectValue(GeneralEntityService.HAS_DETAILS, detailsList);
|
record.setObjectValue(GeneralEntityService.HAS_DETAILS, detailsList);
|
||||||
|
@ -187,8 +186,8 @@ public class RecordTransfomer extends SetUser {
|
||||||
record = Application.getEntityService(targetEntity.getEntityCode()).createOrUpdate(record);
|
record = Application.getEntityService(targetEntity.getEntityCode()).createOrUpdate(record);
|
||||||
return record.getPrimary();
|
return record.getPrimary();
|
||||||
} finally {
|
} finally {
|
||||||
|
if (this.skipGuard) GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
||||||
if (this.skipGuard) PrivilegesGuardContextHolder.getSkipGuardOnce();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@ import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.privileges.bizz.Department;
|
import com.rebuild.core.privileges.bizz.Department;
|
||||||
|
import com.rebuild.core.support.CommandArgs;
|
||||||
import com.rebuild.core.support.License;
|
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;
|
||||||
|
@ -147,26 +147,7 @@ public class AdvFilterParser extends SetUser {
|
||||||
|
|
||||||
// 自动确定查询项
|
// 自动确定查询项
|
||||||
if (MODE_QUICK.equalsIgnoreCase(filterExpr.getString("type"))) {
|
if (MODE_QUICK.equalsIgnoreCase(filterExpr.getString("type"))) {
|
||||||
String quickFields = filterExpr.getString("quickFields");
|
rebuildQuickFilter38();
|
||||||
JSONArray quickItems = buildQuickFilterItems(quickFields, 1);
|
|
||||||
|
|
||||||
// TODO v3.6-b4,3.7 值1|值2 UNTEST
|
|
||||||
// 转义可输入 \|
|
|
||||||
JSONObject values = filterExpr.getJSONObject("values");
|
|
||||||
String[] valuesPlus = values.values().iterator().next().toString().split("(?<!\\\\)\\|");
|
|
||||||
if (valuesPlus.length > 1) {
|
|
||||||
values.clear();
|
|
||||||
values.put("1", valuesPlus[0].trim());
|
|
||||||
|
|
||||||
for (int i = 2; i <= valuesPlus.length; i++) {
|
|
||||||
JSONArray quickItemsPlus = buildQuickFilterItems(quickFields, i);
|
|
||||||
values.put(String.valueOf(i), valuesPlus[i - 1].trim());
|
|
||||||
quickItems.addAll(quickItemsPlus);
|
|
||||||
}
|
|
||||||
filterExpr.put("values", values);
|
|
||||||
}
|
|
||||||
|
|
||||||
filterExpr.put("items", quickItems);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONArray items = filterExpr.getJSONArray("items");
|
JSONArray items = filterExpr.getJSONArray("items");
|
||||||
|
@ -302,6 +283,9 @@ public class AdvFilterParser extends SetUser {
|
||||||
String value = (String) checkValue;
|
String value = (String) checkValue;
|
||||||
String valueEnd = null;
|
String valueEnd = null;
|
||||||
|
|
||||||
|
// v3.8
|
||||||
|
if (useFulltextOp(field) && "LK".equals(op)) op = "FT";
|
||||||
|
|
||||||
if (dt == DisplayType.N2NREFERENCE) {
|
if (dt == DisplayType.N2NREFERENCE) {
|
||||||
String inWhere = null;
|
String inWhere = null;
|
||||||
boolean forceNot = false;
|
boolean forceNot = false;
|
||||||
|
@ -505,6 +489,25 @@ public class AdvFilterParser extends SetUser {
|
||||||
}
|
}
|
||||||
op = ParseHelper.BW;
|
op = ParseHelper.BW;
|
||||||
|
|
||||||
|
} else if (ParseHelper.YYY.equalsIgnoreCase(op)
|
||||||
|
|| ParseHelper.MMM.equalsIgnoreCase(op)) {
|
||||||
|
|
||||||
|
int xValue = NumberUtils.toInt(value);
|
||||||
|
Calendar now = CalendarUtils.getInstance();
|
||||||
|
|
||||||
|
if (ParseHelper.YYY.equalsIgnoreCase(op)) {
|
||||||
|
now.add(Calendar.YEAR, xValue);
|
||||||
|
value = now.get(Calendar.YEAR) + "-01-01";
|
||||||
|
valueEnd = now.get(Calendar.YEAR) + "-12-31";
|
||||||
|
} else {
|
||||||
|
now.set(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
now.add(Calendar.MONTH, xValue);
|
||||||
|
|
||||||
|
value = CalendarUtils.getUTCDateFormat().format(now.getTime());
|
||||||
|
Moment last = Moment.moment(now.getTime()).endOf(Moment.UNIT_MONTH);
|
||||||
|
valueEnd = CalendarUtils.getUTCDateFormat().format(last.date());
|
||||||
|
}
|
||||||
|
op = ParseHelper.BW;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (dt == DisplayType.TIME) {
|
} else if (dt == DisplayType.TIME) {
|
||||||
|
@ -631,6 +634,10 @@ public class AdvFilterParser extends SetUser {
|
||||||
// LIKE
|
// LIKE
|
||||||
if (op.equalsIgnoreCase(ParseHelper.LK) || op.equalsIgnoreCase(ParseHelper.NLK)) {
|
if (op.equalsIgnoreCase(ParseHelper.LK) || op.equalsIgnoreCase(ParseHelper.NLK)) {
|
||||||
value = '%' + value + '%';
|
value = '%' + value + '%';
|
||||||
|
} else if (op.equalsIgnoreCase(ParseHelper.LK1)) {
|
||||||
|
value = '%' + value;
|
||||||
|
} else if (op.equalsIgnoreCase(ParseHelper.LK2)) {
|
||||||
|
value = value + '%';
|
||||||
}
|
}
|
||||||
sb.append(quoteValue(value, lastFieldMeta.getType()));
|
sb.append(quoteValue(value, lastFieldMeta.getType()));
|
||||||
}
|
}
|
||||||
|
@ -687,21 +694,18 @@ public class AdvFilterParser extends SetUser {
|
||||||
value += (fullTime ? ParseHelper.FULL_TIME : ParseHelper.ZERO_TIME); // 含当日
|
value += (fullTime ? ParseHelper.FULL_TIME : ParseHelper.ZERO_TIME); // 含当日
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 修正月、日
|
// v3.8 不修正了,否则因为格式问题(如日期带日、不带日)就带来不同的查询结果,这很怪异
|
||||||
else if (field.getType() == FieldType.DATE && valueLen == 10) {
|
// // 修正月、日
|
||||||
String dateFormat = StringUtils.defaultIfBlank(
|
// else if (field.getType() == FieldType.DATE && valueLen == 10) {
|
||||||
EasyMetaFactory.valueOf(field).getExtraAttr(EasyFieldConfigProps.DATE_FORMAT),
|
// String dateFormat = StringUtils.defaultIfBlank(
|
||||||
DisplayType.DATE.getDefaultFormat());
|
// EasyMetaFactory.valueOf(field).getExtraAttr(EasyFieldConfigProps.DATE_FORMAT),
|
||||||
|
// DisplayType.DATE.getDefaultFormat());
|
||||||
// v3.7 BW 无需修正,值由用户提供
|
// if (dateFormat.length() == 4) {
|
||||||
if (ParseHelper.BW.equals(op)) dateFormat = "0";
|
// value = value.substring(0, 4) + "-01-01";
|
||||||
|
// } else if (dateFormat.length() == 7) {
|
||||||
if (dateFormat.length() == 4) {
|
// value = value.substring(0, 7) + "-01";
|
||||||
value = value.substring(0, 4) + "-01-01";
|
// }
|
||||||
} else if (dateFormat.length() == 7) {
|
// }
|
||||||
value = value.substring(0, 7) + "-01";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多个值的情况下,兼容 | 号分割
|
// 多个值的情况下,兼容 | 号分割
|
||||||
if (op.equalsIgnoreCase(ParseHelper.IN) || op.equalsIgnoreCase(ParseHelper.NIN)
|
if (op.equalsIgnoreCase(ParseHelper.IN) || op.equalsIgnoreCase(ParseHelper.NIN)
|
||||||
|
@ -750,25 +754,69 @@ public class AdvFilterParser extends SetUser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param date
|
* @param date
|
||||||
* @param paddingType 0=无, 1=FULLTIME, 2=ZEROTIME
|
* @param paddingTimeType 0=无, 1=FULLTIME, 2=ZEROTIME
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String formatDate(Date date, int paddingType) {
|
private String formatDate(Date date, int paddingTimeType) {
|
||||||
String s = CalendarUtils.getUTCDateFormat().format(date);
|
String s = CalendarUtils.getUTCDateFormat().format(date);
|
||||||
if (paddingType == 1) s += ParseHelper.FULL_TIME;
|
if (paddingTimeType == 1) s += ParseHelper.FULL_TIME;
|
||||||
else if (paddingType == 2) s += ParseHelper.ZERO_TIME;
|
else if (paddingTimeType == 2) s += ParseHelper.ZERO_TIME;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 快速查询
|
* 快速查询
|
||||||
*
|
*/
|
||||||
|
private void rebuildQuickFilter38() {
|
||||||
|
String quickFields = filterExpr.getString("quickFields");
|
||||||
|
JSONArray quickItems = buildQuickFilterItems(quickFields, 1);
|
||||||
|
|
||||||
|
JSONObject values = filterExpr.getJSONObject("values");
|
||||||
|
final String quickValue = values.values().iterator().next().toString();
|
||||||
|
|
||||||
|
// eg: =完全相等, *后匹配, 前匹配*
|
||||||
|
if (quickValue.length() > 2
|
||||||
|
&& (quickValue.startsWith("=") || quickValue.startsWith("*") || quickValue.endsWith("*"))) {
|
||||||
|
String op2 = ParseHelper.EQ;
|
||||||
|
String value2;
|
||||||
|
if (quickValue.startsWith("*")) op2 = ParseHelper.LK1;
|
||||||
|
else if (quickValue.endsWith("*")) op2 = ParseHelper.LK2;
|
||||||
|
if (quickValue.endsWith("*")) value2 = quickValue.substring(0, quickValue.length() - 1);
|
||||||
|
else value2 = quickValue.substring(1);
|
||||||
|
|
||||||
|
for (Object o : quickItems) {
|
||||||
|
JSONObject item = (JSONObject) o;
|
||||||
|
item.put("op", op2);
|
||||||
|
item.put("value", value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// v3.6-b4,3.7: 多值查询(转义可输入 \|)。eg: 值1|值2
|
||||||
|
String[] m = quickValue.split("(?<!\\\\)\\|");
|
||||||
|
if (m.length > 1) {
|
||||||
|
values.clear();
|
||||||
|
values.put("1", m[0].trim());
|
||||||
|
|
||||||
|
for (int i = 2; i <= m.length; i++) {
|
||||||
|
JSONArray quickItemsPlus = buildQuickFilterItems(quickFields, i);
|
||||||
|
values.put(String.valueOf(i), m[i - 1].trim());
|
||||||
|
quickItems.addAll(quickItemsPlus);
|
||||||
|
}
|
||||||
|
filterExpr.put("values", values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 覆盖
|
||||||
|
filterExpr.put("items", quickItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @param quickFields
|
* @param quickFields
|
||||||
|
* @param valueIndex
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private JSONArray buildQuickFilterItems(String quickFields, int valueIndex) {
|
private JSONArray buildQuickFilterItems(String quickFields, int valueIndex) {
|
||||||
Set<String> usesFields = ParseHelper.buildQuickFields(rootEntity, quickFields);
|
Set<String> usesFields = ParseHelper.buildQuickFields(rootEntity, quickFields);
|
||||||
|
|
||||||
JSONArray items = new JSONArray();
|
JSONArray items = new JSONArray();
|
||||||
for (String field : usesFields) {
|
for (String field : usesFields) {
|
||||||
items.add(JSON.parseObject("{ op:'LK', value:'{" + valueIndex + "}', field:'" + field + "' }"));
|
items.add(JSON.parseObject("{ op:'LK', value:'{" + valueIndex + "}', field:'" + field + "' }"));
|
||||||
|
@ -776,6 +824,22 @@ public class AdvFilterParser extends SetUser {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用全文索引查詢
|
||||||
|
*
|
||||||
|
* @param fieldName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean useFulltextOp(String fieldName) {
|
||||||
|
if (!CommandArgs.getBoolean(CommandArgs._UseDbFullText)) return false;
|
||||||
|
|
||||||
|
Set<String> canFulltexts = new HashSet<>();
|
||||||
|
canFulltexts.add("40#content");
|
||||||
|
|
||||||
|
String key = rootEntity.getEntityCode() + "#" + fieldName;
|
||||||
|
return canFulltexts.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
// 字段变量 {@FIELD}
|
// 字段变量 {@FIELD}
|
||||||
private static final String PATT_FIELDVAR = "\\{@([\\w.]+)}";
|
private static final String PATT_FIELDVAR = "\\{@([\\w.]+)}";
|
||||||
// `当前`变量(当前日期、时间、用户)
|
// `当前`变量(当前日期、时间、用户)
|
||||||
|
|
|
@ -43,6 +43,8 @@ public class ParseHelper {
|
||||||
public static final String NL = "NL";
|
public static final String NL = "NL";
|
||||||
public static final String NT = "NT";
|
public static final String NT = "NT";
|
||||||
public static final String LK = "LK";
|
public static final String LK = "LK";
|
||||||
|
public static final String LK1 = "LK1"; // *后匹配
|
||||||
|
public static final String LK2 = "LK2"; // 前匹配*
|
||||||
public static final String NLK = "NLK";
|
public static final String NLK = "NLK";
|
||||||
public static final String IN = "IN";
|
public static final String IN = "IN";
|
||||||
public static final String NIN = "NIN";
|
public static final String NIN = "NIN";
|
||||||
|
@ -95,6 +97,8 @@ public class ParseHelper {
|
||||||
public static final String PUY = "PUY"; // 本年-
|
public static final String PUY = "PUY"; // 本年-
|
||||||
public static final String CUY = "CUY"; // 本年
|
public static final String CUY = "CUY"; // 本年
|
||||||
public static final String NUY = "NUY"; // 本年+
|
public static final String NUY = "NUY"; // 本年+
|
||||||
|
public static final String YYY = "YYY"; // 指定年+-
|
||||||
|
public static final String MMM = "MMM"; // 指定月+-
|
||||||
public static final String DDD = "DDD"; // 指定天+-
|
public static final String DDD = "DDD"; // 指定天+-
|
||||||
public static final String HHH = "HHH"; // 指定时+-
|
public static final String HHH = "HHH"; // 指定时+-
|
||||||
public static final String EVW = "EVW"; // 每周几
|
public static final String EVW = "EVW"; // 每周几
|
||||||
|
@ -130,7 +134,7 @@ public class ParseHelper {
|
||||||
return "is null";
|
return "is null";
|
||||||
} else if (NT.equalsIgnoreCase(token)) {
|
} else if (NT.equalsIgnoreCase(token)) {
|
||||||
return "is not null";
|
return "is not null";
|
||||||
} else if (LK.equalsIgnoreCase(token)) {
|
} else if (LK.equalsIgnoreCase(token) || LK1.equalsIgnoreCase(token) || LK2.equalsIgnoreCase(token)) {
|
||||||
return "like";
|
return "like";
|
||||||
} else if (NLK.equalsIgnoreCase(token)) {
|
} else if (NLK.equalsIgnoreCase(token)) {
|
||||||
return "not like";
|
return "not like";
|
||||||
|
@ -185,7 +189,8 @@ public class ParseHelper {
|
||||||
} else if (
|
} else if (
|
||||||
CUW.equalsIgnoreCase(token) || CUM.equalsIgnoreCase(token) || CUQ.equalsIgnoreCase(token) || CUY.equalsIgnoreCase(token) ||
|
CUW.equalsIgnoreCase(token) || CUM.equalsIgnoreCase(token) || CUQ.equalsIgnoreCase(token) || CUY.equalsIgnoreCase(token) ||
|
||||||
PUW.equalsIgnoreCase(token) || PUM.equalsIgnoreCase(token) || PUQ.equalsIgnoreCase(token) || PUY.equalsIgnoreCase(token) ||
|
PUW.equalsIgnoreCase(token) || PUM.equalsIgnoreCase(token) || PUQ.equalsIgnoreCase(token) || PUY.equalsIgnoreCase(token) ||
|
||||||
NUW.equalsIgnoreCase(token) || NUM.equalsIgnoreCase(token) || NUQ.equalsIgnoreCase(token) || NUY.equalsIgnoreCase(token)) {
|
NUW.equalsIgnoreCase(token) || NUM.equalsIgnoreCase(token) || NUQ.equalsIgnoreCase(token) || NUY.equalsIgnoreCase(token) ||
|
||||||
|
YYY.equalsIgnoreCase(token) || MMM.equalsIgnoreCase(token)) {
|
||||||
return "between";
|
return "between";
|
||||||
} else if (DDD.equalsIgnoreCase(token) || HHH.equalsIgnoreCase(token)
|
} else if (DDD.equalsIgnoreCase(token) || HHH.equalsIgnoreCase(token)
|
||||||
|| EVW.equalsIgnoreCase(token) || EVM.equalsIgnoreCase(token)) {
|
|| EVW.equalsIgnoreCase(token) || EVM.equalsIgnoreCase(token)) {
|
||||||
|
@ -211,7 +216,7 @@ public class ParseHelper {
|
||||||
if (dt == DisplayType.REFERENCE) {
|
if (dt == DisplayType.REFERENCE) {
|
||||||
Field nameField = field.getReferenceEntity().getNameField();
|
Field nameField = field.getReferenceEntity().getNameField();
|
||||||
if (nameField.getType() == FieldType.REFERENCE) {
|
if (nameField.getType() == FieldType.REFERENCE) {
|
||||||
log.warn("Quick field cannot be circular-reference : " + nameField);
|
log.warn("Quick field cannot be circular-reference : {}", nameField);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +271,7 @@ public class ParseHelper {
|
||||||
if (can != null) usesFields.add(can);
|
if (can != null) usesFields.add(can);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("No field found in `quickFields` : " + field + " in " + entity.getName());
|
log.warn("No field found in `quickFields` : {} in {}", field, entity.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,7 +304,7 @@ public class ParseHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usesFields.isEmpty()) {
|
if (usesFields.isEmpty()) {
|
||||||
log.warn("No fields of search found : " + usesFields);
|
log.warn("No fields of search found : {}", usesFields);
|
||||||
}
|
}
|
||||||
return usesFields;
|
return usesFields;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,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.persist4j.engine.ID;
|
||||||
import com.rebuild.core.service.DataSpecificationException;
|
import com.rebuild.core.service.DataSpecificationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,18 +20,28 @@ import com.rebuild.core.service.DataSpecificationException;
|
||||||
public class DataValidateException extends DataSpecificationException {
|
public class DataValidateException extends DataSpecificationException {
|
||||||
private static final long serialVersionUID = 4178910284594338317L;
|
private static final long serialVersionUID = 4178910284594338317L;
|
||||||
|
|
||||||
private boolean weakMode = false;
|
final private boolean weakMode;
|
||||||
|
final private ID weakModeTriggerId;
|
||||||
|
|
||||||
public DataValidateException(String msg) {
|
public DataValidateException(String msg) {
|
||||||
super(msg);
|
this(msg, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataValidateException(String msg, boolean weakMode) {
|
public DataValidateException(String msg, boolean weakMode) {
|
||||||
|
this(msg, weakMode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataValidateException(String msg, boolean weakMode, ID triggerId) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.weakMode = weakMode;
|
this.weakMode = weakMode;
|
||||||
|
this.weakModeTriggerId = triggerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWeakMode() {
|
public boolean isWeakMode() {
|
||||||
return weakMode;
|
return weakMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ID getWeakModeTriggerId() {
|
||||||
|
return weakModeTriggerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,10 +97,6 @@ public class RobotTriggerManager implements ConfigManager {
|
||||||
recordId, StringUtils.join(TRIGGERS_CHAIN_4DEBUG.get(), "\n > "));
|
recordId, StringUtils.join(TRIGGERS_CHAIN_4DEBUG.get(), "\n > "));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionContext ctx = new ActionContext(recordId, entity,
|
|
||||||
// JSONUtils.EMPTY_OBJECT, EntityHelper.newUnsavedId(EntityHelper.RobotTriggerConfig));
|
|
||||||
// actions.add(new GeneralTriggerAction(ctx));
|
|
||||||
|
|
||||||
return actions.toArray(new TriggerAction[0]);
|
return actions.toArray(new TriggerAction[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ 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;
|
||||||
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
import com.rebuild.core.service.trigger.impl.FieldAggregation;
|
||||||
|
import com.rebuild.core.support.CommandArgs;
|
||||||
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;
|
||||||
|
@ -48,6 +49,9 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
|
|
||||||
private static final ThreadLocal<String> ALLOW_TRIGGERS_ONAPPROVED = new NamedThreadLocal<>("Allow triggers on approve-node");
|
private static final ThreadLocal<String> ALLOW_TRIGGERS_ONAPPROVED = new NamedThreadLocal<>("Allow triggers on approve-node");
|
||||||
|
|
||||||
|
// 少量触发器日志
|
||||||
|
public static final boolean _TriggerLessLog = CommandArgs.getBoolean(CommandArgs._TriggerLessLog);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return 4;
|
return 4;
|
||||||
|
@ -191,7 +195,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
|
|
||||||
final int t = triggerSource.incrTriggerTimes();
|
final int t = triggerSource.incrTriggerTimes();
|
||||||
final String w = String.format("Trigger.%s.%d [ %s ] executing on record (%s) : %s", sourceId, t, action, when, primaryId);
|
final String w = String.format("Trigger.%s.%d [ %s ] executing on record (%s) : %s", sourceId, t, action, when, primaryId);
|
||||||
log.info(w);
|
if (!_TriggerLessLog) log.info(w);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Object res = action.execute(context);
|
Object res = action.execute(context);
|
||||||
|
@ -247,7 +251,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (originTriggerSource) {
|
if (originTriggerSource) {
|
||||||
log.info("Clear trigger-source : {}", getTriggerSource());
|
if (!_TriggerLessLog) log.info("Clear trigger-source : {}", getTriggerSource());
|
||||||
TRIGGER_SOURCE.remove();
|
TRIGGER_SOURCE.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,7 +300,7 @@ public class RobotTriggerObserver extends OperatingObserver {
|
||||||
if (ctx == null) return 0;
|
if (ctx == null) return 0;
|
||||||
LAZY_TRIGGERS_CTX.remove();
|
LAZY_TRIGGERS_CTX.remove();
|
||||||
|
|
||||||
log.info("Will execute lazy triggers : {}", ctx);
|
if (!_TriggerLessLog) log.info("Will execute lazy triggers : {}", ctx);
|
||||||
RobotTriggerObserver observer = new RobotTriggerObserver();
|
RobotTriggerObserver observer = new RobotTriggerObserver();
|
||||||
for (Object context : ctx) {
|
for (Object context : ctx) {
|
||||||
observer.update(o, context);
|
observer.update(o, context);
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class AviatorDate extends AviatorObject {
|
||||||
return dateValue.compareTo((Date) $date);
|
return dateValue.compareTo((Date) $date);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn("Could not compare " + desc(env) + " with " + other.desc(env));
|
log.warn("Could not compare {} with {}", desc(env), other.desc(env));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*!
|
|
||||||
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.persist4j.engine.ID;
|
|
||||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
|
||||||
import com.googlecode.aviator.runtime.type.AviatorType;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap {@link ID[]}
|
|
||||||
*
|
|
||||||
* @author devezhao
|
|
||||||
* @since 2024/5/14
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class AviatorIdArray extends AviatorObject {
|
|
||||||
private static final long serialVersionUID = 7227725706972057447L;
|
|
||||||
|
|
||||||
final private ID[] idArray;
|
|
||||||
|
|
||||||
public AviatorIdArray(ID[] value) {
|
|
||||||
super();
|
|
||||||
this.idArray = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AviatorIdArray(Collection<ID> value) {
|
|
||||||
this(value.toArray(new ID[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int innerCompare(AviatorObject other, Map<String, Object> env) {
|
|
||||||
log.warn("Could not compare {} with {}", desc(env), other.desc(env));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AviatorType getAviatorType() {
|
|
||||||
return AviatorType.JavaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue(Map<String, Object> env) {
|
|
||||||
return this.idArray;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,19 +7,24 @@ 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.engine.ID;
|
||||||
import com.googlecode.aviator.AviatorEvaluator;
|
import com.googlecode.aviator.AviatorEvaluator;
|
||||||
import com.googlecode.aviator.AviatorEvaluatorInstance;
|
import com.googlecode.aviator.AviatorEvaluatorInstance;
|
||||||
import com.googlecode.aviator.Options;
|
import com.googlecode.aviator.Options;
|
||||||
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
|
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
|
||||||
import com.googlecode.aviator.lexer.token.OperatorType;
|
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||||
|
import com.googlecode.aviator.runtime.function.FunctionUtils;
|
||||||
import com.googlecode.aviator.runtime.function.system.AssertFunction;
|
import com.googlecode.aviator.runtime.function.system.AssertFunction;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorFunction;
|
import com.googlecode.aviator.runtime.type.AviatorFunction;
|
||||||
|
import com.googlecode.aviator.runtime.type.AviatorNil;
|
||||||
|
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||||
import com.googlecode.aviator.runtime.type.Sequence;
|
import com.googlecode.aviator.runtime.type.Sequence;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -132,7 +137,7 @@ public class AviatorUtils {
|
||||||
* @param function
|
* @param function
|
||||||
*/
|
*/
|
||||||
public static void addCustomFunction(final AviatorFunction function) {
|
public static void addCustomFunction(final AviatorFunction function) {
|
||||||
log.info("Add custom function : {}", function);
|
log.info("Add custom function : {}", function.getName());
|
||||||
AVIATOR.addFunction(function);
|
AVIATOR.addFunction(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,4 +159,16 @@ public class AviatorUtils {
|
||||||
|
|
||||||
throw new UnsupportedOperationException("Unsupport type : " + value);
|
throw new UnsupportedOperationException("Unsupport type : " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ret
|
||||||
|
* @return
|
||||||
|
* @see FunctionUtils#wrapReturn(Object)
|
||||||
|
*/
|
||||||
|
public static AviatorObject wrapReturn(final Object ret) {
|
||||||
|
if (ret == null) return AviatorNil.NIL;
|
||||||
|
if (ret instanceof Date) return new AviatorDate((Date) ret);
|
||||||
|
if (ret instanceof ID) return new AviatorId((ID) ret);
|
||||||
|
return FunctionUtils.wrapReturn(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,11 @@ package com.rebuild.core.service.trigger.aviator;
|
||||||
|
|
||||||
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.AviatorNil;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.UserContextHolder;
|
import com.rebuild.core.UserContextHolder;
|
||||||
|
import com.rebuild.core.privileges.bizz.User;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -27,9 +29,10 @@ public class CurrentBizunitFunction extends AbstractFunction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AviatorObject call(Map<String, Object> env) {
|
public AviatorObject call(Map<String, Object> env) {
|
||||||
ID user = UserContextHolder.getUser();
|
ID user = UserContextHolder.getReplacedUser();
|
||||||
ID bizunit = (ID) Application.getUserStore().getUser(user).getOwningBizUnit().getIdentity();
|
User ub = Application.getUserStore().getUser(user);
|
||||||
return new AviatorId(bizunit);
|
if (ub.getOwningDept() == null) return AviatorNil.NIL;
|
||||||
|
return AviatorUtils.wrapReturn(ub.getOwningDept().getIdentity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,8 +26,8 @@ public class CurrentUserFunction extends AbstractFunction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AviatorObject call(Map<String, Object> env) {
|
public AviatorObject call(Map<String, Object> env) {
|
||||||
ID user = UserContextHolder.getUser();
|
ID user = UserContextHolder.getReplacedUser();
|
||||||
return new AviatorId(user);
|
return AviatorUtils.wrapReturn(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,7 +13,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.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
import com.rebuild.core.service.general.OperatingContext;
|
import com.rebuild.core.service.general.OperatingContext;
|
||||||
|
@ -103,7 +102,7 @@ public class AutoAssign extends TriggerAction {
|
||||||
cascades = hasCascades.split(",");
|
cascades = hasCascades.split(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(recordId);
|
GeneralEntityServiceContextHolder.setSkipGuard(recordId);
|
||||||
GeneralEntityServiceContextHolder.setFromTrigger(recordId);
|
GeneralEntityServiceContextHolder.setFromTrigger(recordId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -116,7 +115,7 @@ public class AutoAssign extends TriggerAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
GeneralEntityServiceContextHolder.isFromTrigger(true);
|
GeneralEntityServiceContextHolder.isFromTrigger(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.service.general.EntityService;
|
import com.rebuild.core.service.general.EntityService;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
|
@ -78,13 +77,13 @@ public class AutoShare extends TriggerAction {
|
||||||
|
|
||||||
final EntityService es = Application.getEntityService(actionContext.getSourceEntity().getEntityCode());
|
final EntityService es = Application.getEntityService(actionContext.getSourceEntity().getEntityCode());
|
||||||
for (ID toUser : toUsers) {
|
for (ID toUser : toUsers) {
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(recordId);
|
GeneralEntityServiceContextHolder.setSkipGuard(recordId);
|
||||||
GeneralEntityServiceContextHolder.setFromTrigger(recordId);
|
GeneralEntityServiceContextHolder.setFromTrigger(recordId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
es.share(recordId, toUser, cascades, shareRights);
|
es.share(recordId, toUser, cascades, shareRights);
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import com.rebuild.core.metadata.EntityHelper;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
import com.rebuild.core.service.general.OperatingContext;
|
import com.rebuild.core.service.general.OperatingContext;
|
||||||
|
@ -32,6 +31,7 @@ import com.rebuild.core.service.query.ParseHelper;
|
||||||
import com.rebuild.core.service.query.QueryHelper;
|
import com.rebuild.core.service.query.QueryHelper;
|
||||||
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.RobotTriggerObserver;
|
||||||
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;
|
||||||
|
@ -123,7 +123,7 @@ public class FieldAggregation extends TriggerAction {
|
||||||
// 在整个触发链上只触发1次,避免循环调用
|
// 在整个触发链上只触发1次,避免循环调用
|
||||||
// FIXME 20220804 某些场景是否允许2次,而非1次???
|
// FIXME 20220804 某些场景是否允许2次,而非1次???
|
||||||
if (tschain.contains(chainName)) {
|
if (tschain.contains(chainName)) {
|
||||||
log.warn(w + "!!! TRIGGER ONCE ONLY");
|
log.warn("{}!!! TRIGGER ONCE ONLY", w);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
log.info(w);
|
log.info(w);
|
||||||
|
@ -230,13 +230,13 @@ public class FieldAggregation extends TriggerAction {
|
||||||
|
|
||||||
// 有需要才执行
|
// 有需要才执行
|
||||||
if (targetRecord.isEmpty()) {
|
if (targetRecord.isEmpty()) {
|
||||||
log.info("No data of target record : {}", targetRecordId);
|
if (!RobotTriggerObserver._TriggerLessLog) log.info("No data of target record : {}", targetRecordId);
|
||||||
return TriggerResult.targetEmpty();
|
return TriggerResult.targetEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相等则不更新
|
// 相等则不更新
|
||||||
if (isCurrentSame(targetRecord)) {
|
if (isCurrentSame(targetRecord)) {
|
||||||
log.info("Ignore execution because the record are same : {}", targetRecordId);
|
if (!RobotTriggerObserver._TriggerLessLog) log.info("Ignore execution because the record are same : {}", targetRecordId);
|
||||||
return TriggerResult.targetSame();
|
return TriggerResult.targetSame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,28 +244,30 @@ public class FieldAggregation extends TriggerAction {
|
||||||
final boolean stopPropagation = ((JSONObject) actionContext.getActionContent()).getBooleanValue("stopPropagation");
|
final boolean stopPropagation = ((JSONObject) actionContext.getActionContent()).getBooleanValue("stopPropagation");
|
||||||
|
|
||||||
// 跳过权限
|
// 跳过权限
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(targetRecordId);
|
GeneralEntityServiceContextHolder.setSkipGuard(targetRecordId);
|
||||||
|
|
||||||
// 强制更新 (v2.9)
|
// 强制更新 (v2.9)
|
||||||
if (forceUpdate) {
|
if (forceUpdate) {
|
||||||
GeneralEntityServiceContextHolder.setAllowForceUpdate(targetRecordId);
|
GeneralEntityServiceContextHolder.setAllowForceUpdate(targetRecordId);
|
||||||
}
|
}
|
||||||
|
// 快速模式 (v3.8)
|
||||||
|
if (stopPropagation) {
|
||||||
|
GeneralEntityServiceContextHolder.setQuickMode();
|
||||||
|
}
|
||||||
|
|
||||||
tschain.add(chainName);
|
tschain.add(chainName);
|
||||||
TRIGGER_CHAIN.set(tschain);
|
TRIGGER_CHAIN.set(tschain);
|
||||||
|
|
||||||
targetRecord.setDate(EntityHelper.ModifiedOn, CalendarUtils.now());
|
targetRecord.setDate(EntityHelper.ModifiedOn, CalendarUtils.now());
|
||||||
|
targetRecord.setID(EntityHelper.ModifiedBy, UserService.SYSTEM_USER);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (stopPropagation) {
|
Application.getBestService(targetEntity).update(targetRecord);
|
||||||
Application.getCommonsService().update(targetRecord, false);
|
|
||||||
} else {
|
|
||||||
Application.getBestService(targetEntity).update(targetRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
if (forceUpdate) GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
if (forceUpdate) GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||||
|
if (stopPropagation) GeneralEntityServiceContextHolder.isQuickMode(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operatingContext.getAction() == BizzPermission.UPDATE && this.getClass() == FieldAggregation.class) {
|
if (operatingContext.getAction() == BizzPermission.UPDATE && this.getClass() == FieldAggregation.class) {
|
||||||
|
|
|
@ -28,7 +28,6 @@ import com.rebuild.core.metadata.easymeta.EasyDateTime;
|
||||||
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;
|
||||||
import com.rebuild.core.metadata.easymeta.MultiValue;
|
import com.rebuild.core.metadata.easymeta.MultiValue;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||||
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
|
@ -36,6 +35,7 @@ import com.rebuild.core.service.general.OperatingContext;
|
||||||
import com.rebuild.core.service.query.QueryHelper;
|
import com.rebuild.core.service.query.QueryHelper;
|
||||||
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.RobotTriggerObserver;
|
||||||
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;
|
||||||
import com.rebuild.core.service.trigger.aviator.AviatorUtils;
|
import com.rebuild.core.service.trigger.aviator.AviatorUtils;
|
||||||
|
@ -50,7 +50,6 @@ import org.apache.commons.lang.StringUtils;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -60,6 +59,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字段更新,场景 1>1 1>N
|
* 字段更新,场景 1>1 1>N
|
||||||
|
@ -72,6 +72,8 @@ import java.util.Set;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FieldWriteback extends FieldAggregation {
|
public class FieldWriteback extends FieldAggregation {
|
||||||
|
|
||||||
|
private static final ReentrantLock LOCK = new ReentrantLock();
|
||||||
|
|
||||||
private FieldWritebackRefresh fieldWritebackRefresh;
|
private FieldWritebackRefresh fieldWritebackRefresh;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,6 +111,26 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
public Object execute(OperatingContext operatingContext) throws TriggerException {
|
||||||
|
boolean lockMode38 = ((JSONObject) actionContext.getActionContent()).getBooleanValue("lockMode");
|
||||||
|
if (lockMode38) {
|
||||||
|
long s = System.currentTimeMillis();
|
||||||
|
log.info("Lock resources for {}", actionContext.getConfigId());
|
||||||
|
LOCK.lock();
|
||||||
|
|
||||||
|
s = System.currentTimeMillis() - s;
|
||||||
|
if (s > 1000) log.warn("Lock acquired use {}ms. {}", s, actionContext.getConfigId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return this.execute38(operatingContext);
|
||||||
|
} finally {
|
||||||
|
LOCK.unlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.execute38(operatingContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object execute38(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.getFixedRecordId(), operatingContext.getAction().getName());
|
operatingContext.getFixedRecordId(), operatingContext.getAction().getName());
|
||||||
final List<String> tschain = checkTriggerChain(chainName);
|
final List<String> tschain = checkTriggerChain(chainName);
|
||||||
|
@ -122,7 +144,7 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetRecordData.isEmpty()) {
|
if (targetRecordData.isEmpty()) {
|
||||||
log.info("No data of target record : {}", targetRecordIds);
|
if (!RobotTriggerObserver._TriggerLessLog) log.info("No data of target record : {}", targetRecordIds);
|
||||||
return TriggerResult.targetEmpty();
|
return TriggerResult.targetEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,21 +170,26 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
Record targetRecord = targetRecordData.clone();
|
Record targetRecord = targetRecordData.clone();
|
||||||
targetRecord.setID(targetEntity.getPrimaryField().getName(), targetRecordId);
|
targetRecord.setID(targetEntity.getPrimaryField().getName(), targetRecordId);
|
||||||
targetRecord.setDate(EntityHelper.ModifiedOn, CalendarUtils.now());
|
targetRecord.setDate(EntityHelper.ModifiedOn, CalendarUtils.now());
|
||||||
|
targetRecord.setID(EntityHelper.ModifiedBy, UserService.SYSTEM_USER);
|
||||||
|
|
||||||
// 相等则不更新
|
// 相等则不更新
|
||||||
if (isCurrentSame(targetRecord)) {
|
if (isCurrentSame(targetRecord)) {
|
||||||
log.info("Ignore execution because the record are same : {}", targetRecordId);
|
if (!RobotTriggerObserver._TriggerLessLog) log.info("Ignore execution because the record are same : {}", targetRecordId);
|
||||||
targetSame = true;
|
targetSame = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳过权限
|
// 跳过权限
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(targetRecordId);
|
GeneralEntityServiceContextHolder.setSkipGuard(targetRecordId);
|
||||||
|
|
||||||
// 强制更新 (v2.9)
|
// 强制更新 (v2.9)
|
||||||
if (forceUpdate) {
|
if (forceUpdate) {
|
||||||
GeneralEntityServiceContextHolder.setAllowForceUpdate(targetRecordId);
|
GeneralEntityServiceContextHolder.setAllowForceUpdate(targetRecordId);
|
||||||
}
|
}
|
||||||
|
// 快速模式 (v3.8)
|
||||||
|
if (stopPropagation) {
|
||||||
|
GeneralEntityServiceContextHolder.setQuickMode();
|
||||||
|
}
|
||||||
|
|
||||||
// 重复检查模式
|
// 重复检查模式
|
||||||
GeneralEntityServiceContextHolder.setRepeatedCheckMode(GeneralEntityServiceContextHolder.RCM_CHECK_MAIN);
|
GeneralEntityServiceContextHolder.setRepeatedCheckMode(GeneralEntityServiceContextHolder.RCM_CHECK_MAIN);
|
||||||
|
@ -173,16 +200,13 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
if (CommonsUtils.DEVLOG) System.out.println("[dev] Use current-loop tschain : " + tschainCurrentLoop);
|
if (CommonsUtils.DEVLOG) System.out.println("[dev] Use current-loop tschain : " + tschainCurrentLoop);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (stopPropagation) {
|
Application.getBestService(targetEntity).createOrUpdate(targetRecord);
|
||||||
Application.getCommonsService().update(targetRecord, false);
|
|
||||||
} else {
|
|
||||||
Application.getBestService(targetEntity).createOrUpdate(targetRecord);
|
|
||||||
}
|
|
||||||
affected.add(targetRecord.getPrimary());
|
affected.add(targetRecord.getPrimary());
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
if (forceUpdate) GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
if (forceUpdate) GeneralEntityServiceContextHolder.isAllowForceUpdateOnce();
|
||||||
|
if (stopPropagation) GeneralEntityServiceContextHolder.isQuickMode(true);
|
||||||
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
GeneralEntityServiceContextHolder.getRepeatedCheckModeOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +232,7 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
// v35
|
// v35
|
||||||
if (TARGET_ANY.equals(targetFieldEntity[0])) {
|
if (TARGET_ANY.equals(targetFieldEntity[0])) {
|
||||||
TargetWithMatchFields targetWithMatchFields = new TargetWithMatchFields();
|
TargetWithMatchFields targetWithMatchFields = new TargetWithMatchFields();
|
||||||
ID[] ids = targetWithMatchFields.matchMulti(actionContext);
|
ID[] ids = targetWithMatchFields.matchMultiple(actionContext);
|
||||||
CollectionUtils.addAll(targetRecordIds, ids);
|
CollectionUtils.addAll(targetRecordIds, ids);
|
||||||
}
|
}
|
||||||
// 自己更新自己
|
// 自己更新自己
|
||||||
|
@ -545,19 +569,16 @@ public class FieldWriteback extends FieldAggregation {
|
||||||
|
|
||||||
// v3.7 增强兼容
|
// v3.7 增强兼容
|
||||||
Object[] ids;
|
Object[] ids;
|
||||||
if (value instanceof Collection) {
|
if (value instanceof String) ids = value.toString().split(",");
|
||||||
//noinspection unchecked
|
else ids = CommonsUtils.toArray(value);
|
||||||
ids = ((Collection<Object>) value).toArray(new Object[0]);
|
|
||||||
} else if (value instanceof Object[]) {
|
|
||||||
ids = (Object[]) value;
|
|
||||||
} else {
|
|
||||||
ids = value.toString().split(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<ID> idsSet = new LinkedHashSet<>();
|
Set<ID> idsSet = new LinkedHashSet<>();
|
||||||
for (Object id : ids) {
|
for (Object id : ids) {
|
||||||
id = id.toString().trim();
|
if (id instanceof ID) idsSet.add((ID) id);
|
||||||
if (ID.isId(id)) idsSet.add(ID.valueOf(id.toString()));
|
else {
|
||||||
|
id = id.toString().trim();
|
||||||
|
if (ID.isId(id)) idsSet.add(ID.valueOf(id.toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// v3.5.5: 目标值为多引用时保持 `ID[]`
|
// v3.5.5: 目标值为多引用时保持 `ID[]`
|
||||||
newValue = idsSet.toArray(new ID[0]);
|
newValue = idsSet.toArray(new ID[0]);
|
||||||
|
|
|
@ -22,8 +22,8 @@ import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
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;
|
||||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
|
||||||
import com.rebuild.core.privileges.UserService;
|
import com.rebuild.core.privileges.UserService;
|
||||||
|
import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
|
||||||
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;
|
||||||
|
@ -281,12 +281,12 @@ public class GroupAggregation extends FieldAggregation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivilegesGuardContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
GeneralEntityServiceContextHolder.setSkipGuard(EntityHelper.UNSAVED_ID);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Application.getBestService(targetEntity).create(newTargetRecord);
|
Application.getBestService(targetEntity).create(newTargetRecord);
|
||||||
} finally {
|
} finally {
|
||||||
PrivilegesGuardContextHolder.getSkipGuardOnce();
|
GeneralEntityServiceContextHolder.isSkipGuardOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
targetRecordId = newTargetRecord.getPrimary();
|
targetRecordId = newTargetRecord.getPrimary();
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class TargetWithMatchFields {
|
||||||
@Getter
|
@Getter
|
||||||
private Object targetRecordId;
|
private Object targetRecordId;
|
||||||
|
|
||||||
protected TargetWithMatchFields() {
|
public TargetWithMatchFields() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ public class TargetWithMatchFields {
|
||||||
* @param actionContext
|
* @param actionContext
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ID[] matchMulti(ActionContext actionContext) {
|
public ID[] matchMultiple(ActionContext actionContext) {
|
||||||
Object o = match(actionContext, true);
|
Object o = match(actionContext, true);
|
||||||
return o == null ? new ID[0] : (ID[]) o;
|
return o == null ? new ID[0] : (ID[]) o;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public class TargetWithMatchFields {
|
||||||
if (MetadataHelper.getLastJoinField(sourceEntity, sourceField) == null) {
|
if (MetadataHelper.getLastJoinField(sourceEntity, sourceField) == null) {
|
||||||
throw new MissingMetaExcetion(sourceField, sourceEntity.getName());
|
throw new MissingMetaExcetion(sourceField, sourceEntity.getName());
|
||||||
}
|
}
|
||||||
if (!targetEntity.containsField(targetField)) {
|
if (MetadataHelper.getLastJoinField(targetEntity, targetField) == null) {
|
||||||
throw new MissingMetaExcetion(targetField, targetEntity.getName());
|
throw new MissingMetaExcetion(targetField, targetEntity.getName());
|
||||||
}
|
}
|
||||||
matchFieldsMapping.put(sourceField, targetField);
|
matchFieldsMapping.put(sourceField, targetField);
|
||||||
|
@ -136,7 +136,7 @@ public class TargetWithMatchFields {
|
||||||
String targetField = e.getValue();
|
String targetField = e.getValue();
|
||||||
// @see Dimension#getSqlName
|
// @see Dimension#getSqlName
|
||||||
EasyField sourceFieldEasy = EasyMetaFactory.valueOf(MetadataHelper.getLastJoinField(sourceEntity, sourceField));
|
EasyField sourceFieldEasy = EasyMetaFactory.valueOf(MetadataHelper.getLastJoinField(sourceEntity, sourceField));
|
||||||
EasyField targetFieldEasy = EasyMetaFactory.valueOf(targetEntity.getField(targetField));
|
EasyField targetFieldEasy = EasyMetaFactory.valueOf(MetadataHelper.getLastJoinField(targetEntity, targetField));
|
||||||
|
|
||||||
// fix: 3.7.1
|
// fix: 3.7.1
|
||||||
boolean isDateField = sourceFieldEasy.getDisplayType() == DisplayType.DATE
|
boolean isDateField = sourceFieldEasy.getDisplayType() == DisplayType.DATE
|
||||||
|
|
|
@ -32,6 +32,8 @@ public class CommandArgs {
|
||||||
public static final String _StartEntityTypeCode = "_StartEntityTypeCode";
|
public static final String _StartEntityTypeCode = "_StartEntityTypeCode";
|
||||||
public static final String _UseFrontJSAnywhere = "_UseFrontJSAnywhere";
|
public static final String _UseFrontJSAnywhere = "_UseFrontJSAnywhere";
|
||||||
public static final String _TriggerMaxDepth = "_TriggerMaxDepth";
|
public static final String _TriggerMaxDepth = "_TriggerMaxDepth";
|
||||||
|
public static final String _ProtectedAdmin = "_ProtectedAdmin";
|
||||||
|
public static final String _TriggerLessLog = "_TriggerLessLog";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param name
|
* @param name
|
||||||
|
|
|
@ -124,6 +124,7 @@ public enum ConfigurationItem {
|
||||||
SecurityEnhanced(false), // 安全增强
|
SecurityEnhanced(false), // 安全增强
|
||||||
TrustedAllUrl(false), // 可信外部地址
|
TrustedAllUrl(false), // 可信外部地址
|
||||||
LibreofficeBin, // Libreoffice 命令
|
LibreofficeBin, // Libreoffice 命令
|
||||||
|
MysqldumpBin, // mysqldump 命令
|
||||||
UnsafeImgAccess(false), // 不安全图片访问
|
UnsafeImgAccess(false), // 不安全图片访问
|
||||||
|
|
||||||
;
|
;
|
||||||
|
@ -143,8 +144,9 @@ 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)
|
||||||
|
|| MysqldumpBin.name().equalsIgnoreCase(name)
|
||||||
|| UnsafeImgAccess.name().equals(name)
|
|| UnsafeImgAccess.name().equals(name)
|
||||||
|| SN.name().equalsIgnoreCase(name);
|
|| SN.name().equals(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object defaultVal;
|
private Object defaultVal;
|
||||||
|
|
|
@ -119,6 +119,12 @@ public class KVStorage {
|
||||||
*/
|
*/
|
||||||
protected static String getValue(final String key, boolean noCache, Object defaultValue) {
|
protected static String getValue(final String key, boolean noCache, Object defaultValue) {
|
||||||
String value = null;
|
String value = null;
|
||||||
|
// be:v3.8
|
||||||
|
if (ConfigurationItem.SN.name().equals(key)) {
|
||||||
|
value = BootEnvironmentPostProcessor.getProperty(key);
|
||||||
|
} else if (ConfigurationItem.inJvmArgs(key)) {
|
||||||
|
return BootEnvironmentPostProcessor.getProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
if (Application.isReady()) {
|
if (Application.isReady()) {
|
||||||
// 0. 从缓存
|
// 0. 从缓存
|
||||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.support;
|
package com.rebuild.core.support;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import cn.devezhao.commons.CodecUtils;
|
import cn.devezhao.commons.CodecUtils;
|
||||||
import cn.devezhao.commons.ExpiresMap;
|
import cn.devezhao.commons.ExpiresMap;
|
||||||
import cn.devezhao.commons.identifier.ComputerIdentifier;
|
import cn.devezhao.commons.identifier.ComputerIdentifier;
|
||||||
|
@ -19,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -53,11 +55,12 @@ public final class License {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SN == null) {
|
if (SN == null) {
|
||||||
SN = String.format("RB%s%s-%s-%s",
|
SN = String.format("RB%s%s-%s%s%s",
|
||||||
Application.VER.charAt(0),
|
Application.VER.charAt(0),
|
||||||
Locale.getDefault().getCountry().substring(0, 2),
|
Locale.getDefault().getCountry().substring(0, 2),
|
||||||
ComputerIdentifier.generateIdentifierKey(),
|
ComputerIdentifier.generateIdentifierKey(),
|
||||||
CodecUtils.randomCode(9)).toUpperCase();
|
CalendarUtils.format("wwyy", new Date()),
|
||||||
|
CodecUtils.randomCode(6)).toUpperCase();
|
||||||
RebuildConfiguration.setValue(ConfigurationItem.SN.name(), SN);
|
RebuildConfiguration.setValue(ConfigurationItem.SN.name(), SN);
|
||||||
siteApiNoCache("api/authority/query");
|
siteApiNoCache("api/authority/query");
|
||||||
}
|
}
|
||||||
|
@ -160,4 +163,5 @@ public final class License {
|
||||||
return JSONUtils.toJSONObject("error", "Call site api failed");
|
return JSONUtils.toJSONObject("error", "Call site api failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,15 +45,18 @@ public class SysbaseSupport {
|
||||||
}
|
}
|
||||||
log.warn(confLog.append("----------").toString());
|
log.warn(confLog.append("----------").toString());
|
||||||
|
|
||||||
StringBuilder osLog = new StringBuilder("OS/VM Info :\n----------\n");
|
StringBuilder osLog = new StringBuilder("OS/VM INFO :\n----------\n");
|
||||||
osLog.append(StringUtils.rightPad("OS", 31)).append(" : ").append(SystemUtils.OS_NAME).append("/").append(SystemUtils.OS_VERSION).append("\n");
|
osLog.append(StringUtils.rightPad("OS", 31)).append(" : ").append(SystemUtils.OS_NAME).append("/").append(SystemUtils.OS_VERSION).append("\n");
|
||||||
osLog.append(StringUtils.rightPad("VM", 31)).append(" : ").append(SystemUtils.JAVA_VM_NAME).append("/").append(SystemUtils.JAVA_VERSION).append(SystemUtils.OS_VERSION).append("\n");
|
osLog.append(StringUtils.rightPad("VM", 31)).append(" : ").append(SystemUtils.JAVA_VM_NAME).append("/").append(SystemUtils.JAVA_VERSION).append(SystemUtils.OS_VERSION).append("\n");
|
||||||
osLog.append(StringUtils.rightPad("TimeZone", 31)).append(" : ").append(CalendarUtils.DEFAULT_TIME_ZONE).append("\n");
|
osLog.append(StringUtils.rightPad("TimeZone", 31)).append(" : ").append(CalendarUtils.DEFAULT_TIME_ZONE).append("\n");
|
||||||
osLog.append(StringUtils.rightPad("Date", 31)).append(" : ").append(CalendarUtils.now()).append("\n");
|
osLog.append(StringUtils.rightPad("Date", 31)).append(" : ").append(CalendarUtils.now()).append("\n");
|
||||||
osLog.append(StringUtils.rightPad("Headless", 31)).append(" : ").append(SystemUtils.isJavaAwtHeadless()).append("\n");
|
osLog.append(StringUtils.rightPad("Headless", 31)).append(" : ").append(SystemUtils.isJavaAwtHeadless()).append("\n");
|
||||||
|
|
||||||
log.warn(osLog.append("----------").toString());
|
log.warn(osLog.append("----------").toString());
|
||||||
|
|
||||||
|
StringBuilder vmLog = new StringBuilder("VM ARGUMENTS :\n----------\n");
|
||||||
|
vmLog.append(System.getProperties());
|
||||||
|
log.warn(vmLog.append("----------").toString());
|
||||||
|
|
||||||
File logFile = SysbaseHeartbeat.getLogbackFile();
|
File logFile = SysbaseHeartbeat.getLogbackFile();
|
||||||
|
|
||||||
JSONObject resJson;
|
JSONObject resJson;
|
||||||
|
|
|
@ -10,11 +10,16 @@ package com.rebuild.core.support.general;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.BinaryBitmap;
|
||||||
import com.google.zxing.EncodeHintType;
|
import com.google.zxing.EncodeHintType;
|
||||||
|
import com.google.zxing.MultiFormatReader;
|
||||||
import com.google.zxing.MultiFormatWriter;
|
import com.google.zxing.MultiFormatWriter;
|
||||||
|
import com.google.zxing.Result;
|
||||||
import com.google.zxing.WriterException;
|
import com.google.zxing.WriterException;
|
||||||
|
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||||
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||||
import com.google.zxing.common.BitMatrix;
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
import com.google.zxing.oned.Code128Writer;
|
import com.google.zxing.oned.Code128Writer;
|
||||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
import com.rebuild.core.RebuildException;
|
import com.rebuild.core.RebuildException;
|
||||||
|
@ -25,6 +30,7 @@ import com.rebuild.utils.AppUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
|
@ -250,4 +256,26 @@ public class BarCodeSupport {
|
||||||
|
|
||||||
return fm;
|
return fm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 识别。支持条码与二维码
|
||||||
|
*
|
||||||
|
* @param image
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String decode(File image) {
|
||||||
|
try {
|
||||||
|
BufferedImage bufferedImage = ImageIO.read(image);
|
||||||
|
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage)));
|
||||||
|
MultiFormatReader reader = new MultiFormatReader();
|
||||||
|
|
||||||
|
Result result = reader.decode(bitmap);
|
||||||
|
bufferedImage.flush();
|
||||||
|
return result.getText();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Cannot decode image : {}", image);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*!
|
||||||
|
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.support.general;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
|
import cn.devezhao.commons.ObjectUtils;
|
||||||
|
import cn.devezhao.persist4j.Entity;
|
||||||
|
import cn.devezhao.persist4j.Field;
|
||||||
|
import cn.devezhao.persist4j.Record;
|
||||||
|
import com.rebuild.core.Application;
|
||||||
|
import com.rebuild.core.configuration.general.AutoFillinManager;
|
||||||
|
import com.rebuild.core.metadata.MetadataSorter;
|
||||||
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyDateTime;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyDecimal;
|
||||||
|
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.aviator.AviatorUtils;
|
||||||
|
import com.rebuild.utils.CommonsUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单计算公式
|
||||||
|
*
|
||||||
|
* @author ZHAO
|
||||||
|
* @since 2024/8/19
|
||||||
|
* @see AutoFillinManager
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CalcFormulaSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单计算公式,后端计算。
|
||||||
|
* FIXME 字段计算存在路径依赖:例如字段 B=A+1, 但 A 也是计算字段
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int calcFormulaBackend(Record record) {
|
||||||
|
// 从数据库访问
|
||||||
|
Record recordInDb = null;
|
||||||
|
if (record.getPrimary() != null) {
|
||||||
|
recordInDb = Application.getQueryFactory().recordNoFilter(record.getPrimary());
|
||||||
|
}
|
||||||
|
|
||||||
|
int calc = 0;
|
||||||
|
for (Field field
|
||||||
|
: MetadataSorter.sortFields(record.getEntity(), DisplayType.DECIMAL, DisplayType.NUMBER, DisplayType.DATE, DisplayType.DATETIME)) {
|
||||||
|
final EasyField targetField = EasyMetaFactory.valueOf(field);
|
||||||
|
String formula = targetField.getExtraAttr(EasyFieldConfigProps.NUMBER_CALCFORMULA);
|
||||||
|
String backend = targetField.getExtraAttr(EasyFieldConfigProps.NUMBER_CALCFORMULABACKEND);
|
||||||
|
if (StringUtils.isBlank(formula)) continue;
|
||||||
|
if (!BooleanUtils.toBoolean(backend)) continue;
|
||||||
|
|
||||||
|
Map<String, Object> varsInFormula = new HashMap<>();
|
||||||
|
Set<String> fieldVars = ContentWithFieldVars.matchsVars(formula);
|
||||||
|
for (String fieldName : fieldVars) {
|
||||||
|
Object v = record.getObjectValue(fieldName);
|
||||||
|
if (v == null && recordInDb != null) {
|
||||||
|
v = recordInDb.getObjectValue(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
varsInFormula = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
varsInFormula.put(fieldName, v);
|
||||||
|
}
|
||||||
|
if (varsInFormula == null) continue;
|
||||||
|
|
||||||
|
Object evalVal = evalValue(formula, varsInFormula, targetField, false);
|
||||||
|
// 无值忽略
|
||||||
|
if (evalVal == null) continue;
|
||||||
|
// 同值忽略
|
||||||
|
if (recordInDb != null) {
|
||||||
|
Object dbVal = recordInDb.getObjectValue(field.getName());
|
||||||
|
if (CommonsUtils.isSame(evalVal, dbVal)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setObjectValue(field.getName(), evalVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return calc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算
|
||||||
|
*
|
||||||
|
* @param targetField
|
||||||
|
* @param varsInFormula
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object evalCalcFormula(Field targetField, Map<String, Object> varsInFormula) {
|
||||||
|
final Entity entity = targetField.getOwnEntity();
|
||||||
|
final EasyField easyField = EasyMetaFactory.valueOf(targetField);
|
||||||
|
final String formula = easyField.getExtraAttr(EasyFieldConfigProps.NUMBER_CALCFORMULA);
|
||||||
|
|
||||||
|
boolean calcReady = true;
|
||||||
|
Set<String> fieldVars = ContentWithFieldVars.matchsVars(formula);
|
||||||
|
for (String fieldName : fieldVars) {
|
||||||
|
if (EasyDateTime.VAR_NOW.equals(fieldName) || "NOW".equals(fieldName)) {
|
||||||
|
varsInFormula.put(fieldName, CalendarUtils.now());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.containsField(fieldName)) {
|
||||||
|
calcReady = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object fieldValue = varsInFormula.get(fieldName);
|
||||||
|
if (fieldValue == null) {
|
||||||
|
calcReady = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String val2str = fieldValue.toString();
|
||||||
|
DisplayType dt = EasyMetaFactory.valueOf(entity.getField(fieldName)).getDisplayType();
|
||||||
|
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||||
|
fieldValue = CalendarUtils.parse(val2str, CalendarUtils.UTC_DATETIME_FORMAT.substring(0, val2str.length()));
|
||||||
|
} else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) {
|
||||||
|
if (StringUtils.isNotBlank(val2str)) {
|
||||||
|
// v3.6.3 整数/小数强制使用 BigDecimal 高精度
|
||||||
|
if (dt == DisplayType.NUMBER) fieldValue = BigDecimal.valueOf(ObjectUtils.toLong(fieldValue));
|
||||||
|
else fieldValue = BigDecimal.valueOf(ObjectUtils.toDouble(fieldValue));
|
||||||
|
} else {
|
||||||
|
fieldValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldValue == null) {
|
||||||
|
calcReady = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
varsInFormula.put(fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return calcReady ? evalValue(formula, varsInFormula, easyField, true) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行计算
|
||||||
|
*
|
||||||
|
* @param formula
|
||||||
|
* @param varsInFormula
|
||||||
|
* @param targetField
|
||||||
|
* @param wrapValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static Object evalValue(String formula, Map<String, Object> varsInFormula, EasyField targetField, boolean wrapValue) {
|
||||||
|
String clearFormula = formula
|
||||||
|
.replace("{", "").replace("}", "")
|
||||||
|
.replace("×", "*").replace("÷", "/");
|
||||||
|
|
||||||
|
Object evalVal = AviatorUtils.eval(clearFormula, varsInFormula, true);
|
||||||
|
if (evalVal == null) return null;
|
||||||
|
if (!wrapValue) return evalVal;
|
||||||
|
|
||||||
|
DisplayType dt = targetField.getDisplayType();
|
||||||
|
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||||
|
if (evalVal instanceof Date) {
|
||||||
|
return targetField.wrapValue(evalVal);
|
||||||
|
}
|
||||||
|
} else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) {
|
||||||
|
if (evalVal instanceof Number) {
|
||||||
|
evalVal = targetField.wrapValue(evalVal);
|
||||||
|
evalVal = EasyDecimal.clearFlaged(evalVal);
|
||||||
|
return evalVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,9 @@ 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;
|
||||||
|
import com.rebuild.core.UserContextHolder;
|
||||||
import com.rebuild.core.configuration.ConfigBean;
|
import com.rebuild.core.configuration.ConfigBean;
|
||||||
|
import com.rebuild.core.configuration.general.ClassificationManager;
|
||||||
import com.rebuild.core.configuration.general.MultiSelectManager;
|
import com.rebuild.core.configuration.general.MultiSelectManager;
|
||||||
import com.rebuild.core.configuration.general.PickListManager;
|
import com.rebuild.core.configuration.general.PickListManager;
|
||||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
|
@ -31,6 +33,7 @@ import org.apache.commons.lang.ObjectUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -56,6 +59,8 @@ public class DataListWrapper {
|
||||||
|
|
||||||
private boolean mixWrapper = true;
|
private boolean mixWrapper = true;
|
||||||
|
|
||||||
|
private Map<ID, Object> cacheRefValue = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param total
|
* @param total
|
||||||
* @param data
|
* @param data
|
||||||
|
@ -111,6 +116,10 @@ public class DataListWrapper {
|
||||||
|
|
||||||
Object nameValue = null;
|
Object nameValue = null;
|
||||||
for (int colIndex = 0; colIndex < selectFieldsLen; colIndex++) {
|
for (int colIndex = 0; colIndex < selectFieldsLen; colIndex++) {
|
||||||
|
if (!checkHasFieldPrivileges(selectFields[colIndex])) {
|
||||||
|
row[colIndex] = FieldValueHelper.NO_READ_PRIVILEGES;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!checkHasJoinFieldPrivileges(selectFields[colIndex], raw)) {
|
if (!checkHasJoinFieldPrivileges(selectFields[colIndex], raw)) {
|
||||||
row[colIndex] = FieldValueHelper.NO_READ_PRIVILEGES;
|
row[colIndex] = FieldValueHelper.NO_READ_PRIVILEGES;
|
||||||
continue;
|
continue;
|
||||||
|
@ -169,6 +178,11 @@ public class DataListWrapper {
|
||||||
|
|
||||||
final DisplayType dt = easyField.getDisplayType();
|
final DisplayType dt = easyField.getDisplayType();
|
||||||
final Object originValue = value;
|
final Object originValue = value;
|
||||||
|
final boolean isCacheRefValue = dt == DisplayType.REFERENCE && value instanceof ID;
|
||||||
|
|
||||||
|
if (isCacheRefValue) {
|
||||||
|
if (cacheRefValue.containsKey((ID) value)) return cacheRefValue.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
boolean unpack = dt == DisplayType.CLASSIFICATION || dt == DisplayType.PICKLIST
|
boolean unpack = dt == DisplayType.CLASSIFICATION || dt == DisplayType.PICKLIST
|
||||||
|| dt == DisplayType.STATE || dt == DisplayType.BOOL;
|
|| dt == DisplayType.STATE || dt == DisplayType.BOOL;
|
||||||
|
@ -232,9 +246,17 @@ public class DataListWrapper {
|
||||||
new String[]{ "name", "color" }, new Object[]{ name, colorNames.get(name) }));
|
new String[]{ "name", "color" }, new Object[]{ name, colorNames.get(name) }));
|
||||||
}
|
}
|
||||||
value = colorValue;
|
value = colorValue;
|
||||||
|
|
||||||
|
} else if (easyField.getDisplayType() == DisplayType.CLASSIFICATION) {
|
||||||
|
String color = ClassificationManager.instance.getColor((ID) originValue);
|
||||||
|
if (StringUtils.isNotBlank(color)) {
|
||||||
|
value = JSONUtils.toJSONObject(
|
||||||
|
new String[]{ "text", "color" }, new Object[]{ value, color });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCacheRefValue) cacheRefValue.put((ID) originValue, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,6 +273,7 @@ public class DataListWrapper {
|
||||||
* @param field
|
* @param field
|
||||||
* @param original
|
* @param original
|
||||||
* @return
|
* @return
|
||||||
|
* @see #checkHasFieldPrivileges(SelectItem)
|
||||||
*/
|
*/
|
||||||
protected boolean checkHasJoinFieldPrivileges(SelectItem field, Object[] original) {
|
protected boolean checkHasJoinFieldPrivileges(SelectItem field, Object[] original) {
|
||||||
if (this.queryJoinFields == null || UserHelper.isAdmin(user)) {
|
if (this.queryJoinFields == null || UserHelper.isAdmin(user)) {
|
||||||
|
@ -267,6 +290,15 @@ public class DataListWrapper {
|
||||||
return check == null || Application.getPrivilegesManager().allowRead(user, (ID) check);
|
return check == null || Application.getPrivilegesManager().allowRead(user, (ID) check);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param field
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected boolean checkHasFieldPrivileges(SelectItem field) {
|
||||||
|
ID u = user == null ? UserContextHolder.getUser() : user;
|
||||||
|
return Application.getPrivilegesManager().getFieldPrivileges().isReadble(field.getField(), u);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进一步封装查询结果
|
* 进一步封装查询结果
|
||||||
*
|
*
|
||||||
|
|
|
@ -130,7 +130,6 @@ public class FieldValueHelper {
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
log.error("Bad JSONArray format : {} < {}", value, field.getRawMeta());
|
log.error("Bad JSONArray format : {} < {}", value, field.getRawMeta());
|
||||||
return JSONUtils.EMPTY_ARRAY;
|
return JSONUtils.EMPTY_ARRAY;
|
||||||
// throw new RebuildException("BAD VALUE FORMAT:" + value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,19 @@ 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.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.dialect.FieldType;
|
import cn.devezhao.persist4j.dialect.FieldType;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
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.Application;
|
||||||
import com.rebuild.core.configuration.ConfigBean;
|
import com.rebuild.core.configuration.ConfigBean;
|
||||||
import com.rebuild.core.configuration.general.AdvFilterManager;
|
import com.rebuild.core.configuration.general.AdvFilterManager;
|
||||||
import com.rebuild.core.configuration.general.ClassificationManager;
|
import com.rebuild.core.configuration.general.DataListCategory38;
|
||||||
import com.rebuild.core.configuration.general.DataListCategory;
|
|
||||||
import com.rebuild.core.configuration.general.ViewAddonsManager;
|
import com.rebuild.core.configuration.general.ViewAddonsManager;
|
||||||
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.easymeta.DisplayType;
|
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
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.service.dashboard.ChartManager;
|
import com.rebuild.core.service.dashboard.ChartManager;
|
||||||
|
@ -33,7 +31,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -232,45 +229,13 @@ public class ProtocolFilterParser {
|
||||||
* @param value
|
* @param value
|
||||||
* @return
|
* @return
|
||||||
* @see #P_CATEGORY
|
* @see #P_CATEGORY
|
||||||
* @see DataListCategory
|
* @see DataListCategory38#buildParentFilters(Entity, Object[])
|
||||||
*/
|
*/
|
||||||
protected String parseCategory(String entity, String value) {
|
protected String parseCategory(String entity, String value) {
|
||||||
Entity rootEntity = MetadataHelper.getEntity(entity);
|
String[] filterValues = value.split(CommonsUtils.COMM_SPLITER_RE);
|
||||||
Field categoryField = DataListCategory.instance.getFieldOfCategory(rootEntity);
|
String where = DataListCategory38.instance.buildParentFilters(MetadataHelper.getEntity(entity), filterValues);
|
||||||
if (categoryField == null || StringUtils.isBlank(value)) return "(9=9)";
|
if (Application.devMode()) log.info("[dev] Parse category : {} >> {}", value, where);
|
||||||
|
return where;
|
||||||
DisplayType dt = EasyMetaFactory.getDisplayType(categoryField);
|
|
||||||
value = CommonsUtils.escapeSql(value);
|
|
||||||
|
|
||||||
if (dt == DisplayType.MULTISELECT) {
|
|
||||||
return String.format("%s && %d", categoryField.getName(), ObjectUtils.toInt(value));
|
|
||||||
|
|
||||||
} else if (dt == DisplayType.N2NREFERENCE) {
|
|
||||||
return String.format(
|
|
||||||
"exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId = '%s')",
|
|
||||||
rootEntity.getPrimaryField().getName(), categoryField.getName(), value);
|
|
||||||
|
|
||||||
} else if (dt == DisplayType.DATETIME || dt == DisplayType.DATE) {
|
|
||||||
String s = value + "0000-01-01 00:00:00".substring(value.length());
|
|
||||||
String e = value + "0000-12-31 23:59:59".substring(value.length());
|
|
||||||
if (dt == DisplayType.DATE) {
|
|
||||||
s = s.substring(0, 10);
|
|
||||||
e = e.substring(0, 10);
|
|
||||||
}
|
|
||||||
return MessageFormat.format("({0} >= ''{1}'' and {0} <= ''{2}'')", categoryField.getName(), s, e);
|
|
||||||
|
|
||||||
} else if (dt == DisplayType.CLASSIFICATION) {
|
|
||||||
int level = ClassificationManager.instance.getOpenLevel(categoryField);
|
|
||||||
List<String> parentSql = new ArrayList<>();
|
|
||||||
parentSql.add(String.format("%s = '%s'", categoryField.getName(), value));
|
|
||||||
if (level > 0) parentSql.add(String.format("%s.parent = '%s'", categoryField.getName(), value));
|
|
||||||
if (level > 1) parentSql.add(String.format("%s.parent.parent = '%s'", categoryField.getName(), value));
|
|
||||||
if (level > 2) parentSql.add(String.format("%s.parent.parent.parent = '%s'", categoryField.getName(), value));
|
|
||||||
|
|
||||||
return "( " + StringUtils.join(parentSql, " or ") + " )";
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.format("%s = '%s'", categoryField.getName(), value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,7 +243,6 @@ public class ProtocolFilterParser {
|
||||||
* @param mainid
|
* @param mainid
|
||||||
* @return
|
* @return
|
||||||
* @see #P_RELATED
|
* @see #P_RELATED
|
||||||
* @see com.rebuild.web.general.RelatedListController#buildBaseSql(ID, String, String, boolean, ID)
|
|
||||||
*/
|
*/
|
||||||
public String parseRelated(String relatedExpr, ID mainid) {
|
public String parseRelated(String relatedExpr, ID mainid) {
|
||||||
// format: Entity.Field
|
// format: Entity.Field
|
||||||
|
@ -297,7 +261,7 @@ public class ProtocolFilterParser {
|
||||||
relatedField.getOwnEntity().getPrimaryField().getName(), relatedField.getName(), mainid);
|
relatedField.getOwnEntity().getPrimaryField().getName(), relatedField.getName(), mainid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 附件过滤条件
|
// 附加过滤条件
|
||||||
|
|
||||||
Map<String, JSONObject> vtabFilters = ViewAddonsManager.instance.getViewTabFilters(
|
Map<String, JSONObject> vtabFilters = ViewAddonsManager.instance.getViewTabFilters(
|
||||||
MetadataHelper.getEntity(mainid.getEntityCode()).getName());
|
MetadataHelper.getEntity(mainid.getEntityCode()).getName());
|
||||||
|
|
|
@ -277,6 +277,7 @@ public class SMSender {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected static Element getMailTemplate() {
|
protected static Element getMailTemplate() {
|
||||||
|
if (Application.devMode()) MT_CACHE = null;
|
||||||
if (MT_CACHE != null) return MT_CACHE.clone();
|
if (MT_CACHE != null) return MT_CACHE.clone();
|
||||||
|
|
||||||
String content = CommonsUtils.getStringOfRes("i18n/email.zh_CN.html");
|
String content = CommonsUtils.getStringOfRes("i18n/email.zh_CN.html");
|
||||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.core.support.setup;
|
||||||
|
|
||||||
import cn.devezhao.commons.CalendarUtils;
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import com.rebuild.core.BootEnvironmentPostProcessor;
|
import com.rebuild.core.BootEnvironmentPostProcessor;
|
||||||
|
import com.rebuild.core.support.ConfigurationItem;
|
||||||
import com.rebuild.core.support.RebuildConfiguration;
|
import com.rebuild.core.support.RebuildConfiguration;
|
||||||
import com.rebuild.utils.CommandUtils;
|
import com.rebuild.utils.CommandUtils;
|
||||||
import com.rebuild.utils.CompressUtils;
|
import com.rebuild.utils.CompressUtils;
|
||||||
|
@ -70,12 +71,13 @@ public class DatabaseBackup {
|
||||||
String destName = "backup_database." + CalendarUtils.getPlainDateTimeFormat().format(CalendarUtils.now());
|
String destName = "backup_database." + CalendarUtils.getPlainDateTimeFormat().format(CalendarUtils.now());
|
||||||
File dest = new File(backups, destName);
|
File dest = new File(backups, destName);
|
||||||
|
|
||||||
|
String mysqldump = RebuildConfiguration.get(ConfigurationItem.MysqldumpBin);
|
||||||
|
if (StringUtils.isBlank(mysqldump)) mysqldump = SystemUtils.IS_OS_WINDOWS ? "mysqldump.exe" : "mysqldump";
|
||||||
// https://blog.csdn.net/liaowenxiong/article/details/120587358
|
// https://blog.csdn.net/liaowenxiong/article/details/120587358
|
||||||
// --master-data --flush-logs
|
// --master-data --flush-logs
|
||||||
String cmd = String.format(
|
String cmd = String.format(
|
||||||
"%s -u%s -p\"%s\" -h%s -P%s --default-character-set=utf8 --opt --extended-insert=true --triggers --hex-blob --single-transaction -R %s>%s",
|
"%s -u%s -p\"%s\" -h%s -P%s --default-character-set=utf8 --opt --extended-insert=true --triggers --hex-blob --single-transaction -R %s>\"%s\"",
|
||||||
SystemUtils.IS_OS_WINDOWS ? "mysqldump.exe" : "mysqldump",
|
mysqldump, user, passwd, host, port, dbname, dest.getAbsolutePath());
|
||||||
user, passwd, host, port, dbname, dest.getAbsolutePath());
|
|
||||||
|
|
||||||
if (ignoreTables != null) {
|
if (ignoreTables != null) {
|
||||||
String igPrefix = " --ignore-table=" + dbname + ".";
|
String igPrefix = " --ignore-table=" + dbname + ".";
|
||||||
|
@ -83,7 +85,7 @@ public class DatabaseBackup {
|
||||||
cmd = cmd.replaceFirst(" -R ", " -R" + ig + " ");
|
cmd = cmd.replaceFirst(" -R ", " -R" + ig + " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
String echo = CommandUtils.execFor(cmd);
|
String echo = CommandUtils.execFor(cmd, true);
|
||||||
boolean isGotError = echo.contains("Got error");
|
boolean isGotError = echo.contains("Got error");
|
||||||
if (isGotError) throw new RuntimeException(echo);
|
if (isGotError) throw new RuntimeException(echo);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import com.rebuild.core.support.ConfigurationItem;
|
||||||
import com.rebuild.core.support.RebuildConfiguration;
|
import com.rebuild.core.support.RebuildConfiguration;
|
||||||
import com.rebuild.core.support.i18n.LanguageBundle;
|
import com.rebuild.core.support.i18n.LanguageBundle;
|
||||||
import com.rebuild.web.admin.AdminVerfiyController;
|
import com.rebuild.web.admin.AdminVerfiyController;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -27,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
* @author Zixin (RB)
|
* @author Zixin (RB)
|
||||||
* @since 05/19/2018
|
* @since 05/19/2018
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class AppUtils {
|
public class AppUtils {
|
||||||
|
|
||||||
// Token 认证
|
// Token 认证
|
||||||
|
@ -91,7 +93,13 @@ public class AppUtils {
|
||||||
* @see #getRequestUserViaToken(HttpServletRequest, boolean)
|
* @see #getRequestUserViaToken(HttpServletRequest, boolean)
|
||||||
*/
|
*/
|
||||||
public static ID getRequestUser(HttpServletRequest request, boolean refreshToken) {
|
public static ID getRequestUser(HttpServletRequest request, boolean refreshToken) {
|
||||||
Object user = request.getSession().getAttribute(WebUtils.CURRENT_USER);
|
Object user = null;
|
||||||
|
try {
|
||||||
|
user = request.getSession().getAttribute(WebUtils.CURRENT_USER);
|
||||||
|
} catch (Exception resHasBeenCommitted) {
|
||||||
|
log.warn("resHasBeenCommitted", resHasBeenCommitted);
|
||||||
|
}
|
||||||
|
|
||||||
if (user == null) user = getRequestUserViaToken(request, refreshToken);
|
if (user == null) user = getRequestUserViaToken(request, refreshToken);
|
||||||
return user == null ? null : (ID) user;
|
return user == null ? null : (ID) user;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,15 @@ public class CommandUtils {
|
||||||
* 执行命令行
|
* 执行命令行
|
||||||
*
|
*
|
||||||
* @param cmd
|
* @param cmd
|
||||||
|
* @param secure
|
||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static String execFor(String cmd) throws IOException {
|
public static String execFor(String cmd, boolean secure) throws IOException {
|
||||||
ProcessBuilder builder = new ProcessBuilder();
|
ProcessBuilder builder = new ProcessBuilder();
|
||||||
String encoding = "UTF-8";
|
String encoding = "UTF-8";
|
||||||
|
|
||||||
log.info("CMD : {}", cmd);
|
if (!secure) log.info("CMD : {}", cmd);
|
||||||
|
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
builder.command("cmd.exe", "/c", cmd);
|
builder.command("cmd.exe", "/c", cmd);
|
||||||
|
|
|
@ -32,9 +32,11 @@ import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -256,6 +258,7 @@ public class CommonsUtils {
|
||||||
|
|
||||||
Class<?>[] paramTypes = new Class<?>[args.length];
|
Class<?>[] paramTypes = new Class<?>[args.length];
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (args[i] == null) args[i] = new Object();
|
||||||
paramTypes[i] = args[i].getClass();
|
paramTypes[i] = args[i].getClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,4 +374,23 @@ public class CommonsUtils {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为数组
|
||||||
|
*
|
||||||
|
* @param o
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object[] toArray(Object o) {
|
||||||
|
if (o == null) return new Object[0];
|
||||||
|
if (o instanceof Object[]) return (Object[]) o;
|
||||||
|
if (o instanceof Collection) return ((Collection<?>) o).toArray();
|
||||||
|
|
||||||
|
if (o instanceof Iterable) {
|
||||||
|
List<Object> c = new ArrayList<>();
|
||||||
|
for (Object item : (Iterable<?>) o) c.add(item);
|
||||||
|
return c.toArray();
|
||||||
|
}
|
||||||
|
return new Object[]{o};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,8 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
*/
|
*/
|
||||||
public class Etag {
|
public class Etag {
|
||||||
|
|
||||||
private static final String DIRECTIVE_NO_STORE = "no-store";
|
final private String responseEtag;
|
||||||
|
transient final private HttpServletResponse response;
|
||||||
private String responseETag;
|
|
||||||
transient private HttpServletResponse response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param etag
|
* @param etag
|
||||||
|
@ -34,10 +32,10 @@ public class Etag {
|
||||||
public Etag(String etag, HttpServletResponse response) {
|
public Etag(String etag, HttpServletResponse response) {
|
||||||
// whether the generated ETag should be weak
|
// whether the generated ETag should be weak
|
||||||
// SPEC: length of W/ + " + 0 + 32bits md5 hash + "
|
// SPEC: length of W/ + " + 0 + 32bits md5 hash + "
|
||||||
String responseETag = String.format("W/\"0%s\"", etag);
|
String responseEtag = String.format("W/\"0%s\"", etag);
|
||||||
response.setHeader(HttpHeaders.ETAG, responseETag);
|
response.setHeader(HttpHeaders.ETAG, responseEtag);
|
||||||
|
|
||||||
this.responseETag = responseETag;
|
this.responseEtag = responseEtag;
|
||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +46,7 @@ public class Etag {
|
||||||
*/
|
*/
|
||||||
protected boolean isForceNoCache() {
|
protected boolean isForceNoCache() {
|
||||||
String cacheControl = response.getHeader(HttpHeaders.CACHE_CONTROL);
|
String cacheControl = response.getHeader(HttpHeaders.CACHE_CONTROL);
|
||||||
return cacheControl != null && cacheControl.contains(DIRECTIVE_NO_STORE);
|
return cacheControl != null && cacheControl.contains("no-store");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,14 +57,11 @@ public class Etag {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected boolean isMatchEtag(HttpServletRequest request, boolean writeStatusIfMatch) {
|
protected boolean isMatchEtag(HttpServletRequest request, boolean writeStatusIfMatch) {
|
||||||
String requestETag = request.getHeader(HttpHeaders.IF_NONE_MATCH);
|
String requestEtag = request.getHeader(HttpHeaders.IF_NONE_MATCH);
|
||||||
if (requestETag != null &&
|
if (requestEtag != null &&
|
||||||
("*".equals(requestETag) || responseETag.equals(requestETag) ||
|
("*".equals(requestEtag) || responseEtag.equals(requestEtag) ||
|
||||||
responseETag.replaceFirst("^W/", "").equals(requestETag.replaceFirst("^W/", "")))) {
|
responseEtag.replaceFirst("^W/", "").equals(requestEtag.replaceFirst("^W/", "")))) {
|
||||||
if (writeStatusIfMatch) {
|
if (writeStatusIfMatch) response.setStatus(HttpStatus.NOT_MODIFIED.value());
|
||||||
response.setStatus(HttpStatus.NOT_MODIFIED.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -8,30 +8,20 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
package com.rebuild.utils;
|
package com.rebuild.utils;
|
||||||
|
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.service.datareport.ReportsException;
|
|
||||||
import com.rebuild.core.support.ConfigurationItem;
|
import com.rebuild.core.support.ConfigurationItem;
|
||||||
import com.rebuild.core.support.RebuildConfiguration;
|
import com.rebuild.core.support.RebuildConfiguration;
|
||||||
import com.rebuild.utils.poi.ToHtml;
|
|
||||||
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.commons.lang.SystemUtils;
|
import org.apache.commons.lang.SystemUtils;
|
||||||
import org.apache.poi.ss.usermodel.Sheet;
|
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
|
||||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.springframework.util.Assert;
|
import org.jsoup.nodes.Element;
|
||||||
import org.zwobble.mammoth.DocumentConverter;
|
|
||||||
import org.zwobble.mammoth.Result;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Iterator;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Office 文件转换
|
* Office 文件转换
|
||||||
|
@ -93,7 +83,6 @@ public class PdfConverter {
|
||||||
|
|
||||||
final String pathFileName = path.getFileName().toString();
|
final String pathFileName = path.getFileName().toString();
|
||||||
final boolean isExcel = pathFileName.endsWith(".xlsx") || pathFileName.endsWith(".xls");
|
final boolean isExcel = pathFileName.endsWith(".xlsx") || pathFileName.endsWith(".xls");
|
||||||
final boolean isWord = pathFileName.endsWith(".docx") || pathFileName.endsWith(".doc");
|
|
||||||
// Excel 公式生效
|
// Excel 公式生效
|
||||||
if (isExcel) {
|
if (isExcel) {
|
||||||
ExcelUtils.reSaveAndCalcFormula(path);
|
ExcelUtils.reSaveAndCalcFormula(path);
|
||||||
|
@ -108,97 +97,55 @@ public class PdfConverter {
|
||||||
else return dest.toPath();
|
else return dest.toPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TYPE_HTML.equalsIgnoreCase(type)) {
|
|
||||||
if (isWord) {
|
|
||||||
convertWord2Html(path, dest);
|
|
||||||
return dest.toPath();
|
|
||||||
}
|
|
||||||
if (isExcel) {
|
|
||||||
convertExcel2Html(path, dest);
|
|
||||||
return dest.toPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ReportsException("CANNOT CONVERT TO HTML : " + pathFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// alias
|
// alias
|
||||||
String soffice = RebuildConfiguration.get(ConfigurationItem.LibreofficeBin);
|
String soffice = RebuildConfiguration.get(ConfigurationItem.LibreofficeBin);
|
||||||
if (StringUtils.isBlank(soffice)) soffice = SystemUtils.IS_OS_WINDOWS ? "soffice.exe" : "libreoffice";
|
if (StringUtils.isBlank(soffice)) soffice = SystemUtils.IS_OS_WINDOWS ? "soffice.exe" : "libreoffice";
|
||||||
String cmd = String.format("%s --headless --convert-to %s \"%s\" --outdir \"%s\"", soffice, type, path, outDir);
|
String cmd = String.format("%s --headless --convert-to %s \"%s\" --outdir \"%s\"", soffice, type, path, outDir);
|
||||||
|
|
||||||
String echo = CommandUtils.execFor(cmd);
|
String echo = CommandUtils.execFor(cmd, false);
|
||||||
if (!echo.isEmpty()) log.info(echo);
|
if (!echo.isEmpty()) log.info(echo);
|
||||||
|
|
||||||
if (dest.exists()) return dest.toPath();
|
if (dest.exists()) {
|
||||||
|
if (TYPE_HTML.equalsIgnoreCase(type)) fixHtml(dest, null);
|
||||||
|
return dest.toPath();
|
||||||
|
}
|
||||||
|
|
||||||
throw new PdfConverterException("Cannot convert to PDF : " + StringUtils.defaultIfBlank(echo, "<empty>"));
|
throw new PdfConverterException("Cannot convert to <" + type + "> : " + StringUtils.defaultIfBlank(echo, "<empty>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String TEMPALTE_HTML;
|
private static String TEMPALTE_HTML;
|
||||||
/**
|
/**
|
||||||
* Word to HTML
|
* @param sourceHtml
|
||||||
*
|
* @param title
|
||||||
* @param source
|
|
||||||
* @param dest
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected static void convertWord2Html(Path source, File dest) throws IOException {
|
private static void fixHtml(File sourceHtml, String title) throws IOException {
|
||||||
if (TEMPALTE_HTML == null || Application.devMode()) {
|
if (TEMPALTE_HTML == null || Application.devMode()) TEMPALTE_HTML = CommonsUtils.getStringOfRes("i18n/html-report.html");
|
||||||
TEMPALTE_HTML = CommonsUtils.getStringOfRes("i18n/html-report.html");
|
if (TEMPALTE_HTML == null) return;
|
||||||
}
|
|
||||||
Assert.notNull(TEMPALTE_HTML, "TEMPALTE_HTML MISSING");
|
|
||||||
|
|
||||||
DocumentConverter converter = new DocumentConverter();
|
final Document template = Jsoup.parse(TEMPALTE_HTML);
|
||||||
Result<String> result = converter.convertToHtml(source.toFile());
|
final Element body = template.body();
|
||||||
String cHtml = result.getValue();
|
|
||||||
Set<String> cWarnings = result.getWarnings();
|
final Document source = Jsoup.parse(sourceHtml);
|
||||||
if (!cWarnings.isEmpty()) {
|
|
||||||
log.warn("HTML convert warnings : {}", cWarnings);
|
// 提取表格
|
||||||
|
for (Element table : source.select("body>table")) {
|
||||||
|
Element page = body.appendElement("div").addClass("page");
|
||||||
|
page.appendChild(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
Document html = Jsoup.parse(TEMPALTE_HTML);
|
// 图片添加 temp=yes
|
||||||
html.body().append("<div class=\"paper word\">" + cHtml + "</div>");
|
for (Element img : body.select("img")) {
|
||||||
html.title(source.getFileName().toString());
|
String src = img.attr("src");
|
||||||
|
if (!src.startsWith("data:")) {
|
||||||
FileUtils.writeStringToFile(dest, html.html(), AppUtils.UTF8);
|
img.attr("src", src + "?temp=yes");
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Excel to HTML.
|
|
||||||
* 1. 不能合并
|
|
||||||
*
|
|
||||||
* @param source
|
|
||||||
* @param dest
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
protected static void convertExcel2Html(Path source, File dest) throws IOException {
|
|
||||||
if (TEMPALTE_HTML == null || Application.devMode()) {
|
|
||||||
TEMPALTE_HTML = CommonsUtils.getStringOfRes("i18n/html-report.html");
|
|
||||||
}
|
|
||||||
Assert.notNull(TEMPALTE_HTML, "TEMPALTE_HTML MISSING");
|
|
||||||
|
|
||||||
StringWriter output = new StringWriter();
|
|
||||||
try (Workbook wb = WorkbookFactory.create(Files.newInputStream(source))) {
|
|
||||||
ToHtml toHtml = ToHtml.create(wb, output);
|
|
||||||
output.append("<style>");
|
|
||||||
toHtml.printStyles();
|
|
||||||
output.append("</style>");
|
|
||||||
|
|
||||||
for (Iterator<Sheet> iter = wb.sheetIterator(); iter.hasNext(); ) {
|
|
||||||
final Sheet sheet = iter.next();
|
|
||||||
String paperClass = "paper excel";
|
|
||||||
if (sheet.getPrintSetup().getLandscape()) paperClass += " landscape"; // 横向
|
|
||||||
|
|
||||||
output.append("<div class=\"").append(paperClass).append("\">");
|
|
||||||
toHtml.printSheet(sheet);
|
|
||||||
output.append("</div>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Document html = Jsoup.parse(TEMPALTE_HTML);
|
// TITLE
|
||||||
html.body().append(output.toString());
|
if (title == null) title = sourceHtml.getName();
|
||||||
html.title(source.getFileName().toString());
|
Objects.requireNonNull(template.head().selectFirst("title")).text(title);
|
||||||
|
|
||||||
FileUtils.writeStringToFile(dest, html.html(), AppUtils.UTF8);
|
FileUtils.writeStringToFile(sourceHtml, template.html(), "UTF-8");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,10 +22,9 @@ public class RbAssert {
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
public static void isCommercial(String message) {
|
public static void isCommercial(String message) {
|
||||||
if (!License.isCommercial()) {
|
if (License.isRbvAttached()) return;
|
||||||
if (message == null) message = Language.L("免费版不支持此功能");
|
if (message == null) message = Language.L("免费版不支持此功能");
|
||||||
throw new NeedRbvException(message);
|
throw new NeedRbvException(message);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
/* ====================================================================
|
|
||||||
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.
|
|
||||||
==================================================================== */
|
|
||||||
package com.rebuild.utils.poi;
|
|
||||||
|
|
||||||
import java.util.Formatter;
|
|
||||||
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFPalette;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.hssf.util.HSSFColor;
|
|
||||||
import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
|
|
||||||
import org.apache.poi.ss.usermodel.CellStyle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of {@link HtmlHelper} for HSSF files.
|
|
||||||
*/
|
|
||||||
public class HSSFHtmlHelper implements HtmlHelper {
|
|
||||||
private final HSSFWorkbook wb;
|
|
||||||
private final HSSFPalette colors;
|
|
||||||
|
|
||||||
private static final HSSFColor HSSF_AUTO = HSSFColorPredefined.AUTOMATIC.getColor();
|
|
||||||
|
|
||||||
public HSSFHtmlHelper(HSSFWorkbook wb) {
|
|
||||||
this.wb = wb;
|
|
||||||
// If there is no custom palette, then this creates a new one that is
|
|
||||||
// a copy of the default
|
|
||||||
colors = wb.getCustomPalette();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void colorStyles(CellStyle style, Formatter out) {
|
|
||||||
HSSFCellStyle cs = (HSSFCellStyle) style;
|
|
||||||
out.format(" /* fill pattern = %d */%n", cs.getFillPattern().getCode());
|
|
||||||
styleColor(out, "background-color", cs.getFillForegroundColor());
|
|
||||||
styleColor(out, "color", cs.getFont(wb).getColor());
|
|
||||||
styleColor(out, "border-left-color", cs.getLeftBorderColor());
|
|
||||||
styleColor(out, "border-right-color", cs.getRightBorderColor());
|
|
||||||
styleColor(out, "border-top-color", cs.getTopBorderColor());
|
|
||||||
styleColor(out, "border-bottom-color", cs.getBottomBorderColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void styleColor(Formatter out, String attr, short index) {
|
|
||||||
HSSFColor color = colors.getColor(index);
|
|
||||||
if (index == HSSF_AUTO.getIndex() || color == null) {
|
|
||||||
out.format(" /* %s: index = %d */%n", attr, index);
|
|
||||||
} else {
|
|
||||||
short[] rgb = color.getTriplet();
|
|
||||||
out.format(" %s: #%02x%02x%02x; /* index = %d */%n", attr, rgb[0], rgb[1], rgb[2], index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue