diff --git a/pom.xml b/pom.xml index 4424d5885..6e9859fb7 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.rebuild rebuild - 2.0.0 + 2.0.1 rebuild RB V2 use SpringBoot diff --git a/src/main/java/com/rebuild/core/Application.java b/src/main/java/com/rebuild/core/Application.java index d984c9f74..08e6c0fd5 100644 --- a/src/main/java/com/rebuild/core/Application.java +++ b/src/main/java/com/rebuild/core/Application.java @@ -14,7 +14,9 @@ import cn.devezhao.persist4j.Query; import cn.devezhao.persist4j.engine.ID; import cn.devezhao.persist4j.engine.StandardRecord; import cn.devezhao.persist4j.query.QueryedRecord; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.ToStringSerializer; import com.rebuild.core.cache.CommonsCache; import com.rebuild.core.metadata.impl.DynamicMetadataFactory; @@ -65,11 +67,11 @@ public class Application implements ApplicationListener /** * Rebuild Version */ - public static final String VER = "2.0.0"; + public static final String VER = "2.0.1"; /** * Rebuild Build */ - public static final int BUILD = 20000; + public static final int BUILD = 20001; static { // Driver for DB @@ -87,6 +89,9 @@ public class Application implements ApplicationListener SerializeConfig.getGlobalInstance().put(Date.class, RbDateCodec.instance); SerializeConfig.getGlobalInstance().put(StandardRecord.class, RbRecordCodec.instance); SerializeConfig.getGlobalInstance().put(QueryedRecord.class, RbRecordCodec.instance); + + JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask(); + JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask(); } // 服务启动状态 @@ -175,7 +180,7 @@ public class Application implements ApplicationListener // 版本升级会清除缓存 int lastBuild = ObjectUtils.toInt(RebuildConfiguration.get(ConfigurationItem.AppBuild, true), 0); if (lastBuild < BUILD) { - LOG.warn("Clear all cache after the first upgrade : " + BUILD); + LOG.warn("CLEAR ALL CACHE AFTER THE FIRST UPGRADE : " + BUILD); Installer.clearAllCache(); RebuildConfiguration.set(ConfigurationItem.AppBuild, BUILD); } diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/CompatibleValueConversion.java b/src/main/java/com/rebuild/core/service/trigger/impl/CompatibleValueConversion.java index 3ea5d3624..0766c085b 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/CompatibleValueConversion.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/CompatibleValueConversion.java @@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information. package com.rebuild.core.service.trigger.impl; import cn.devezhao.commons.CalendarUtils; +import cn.devezhao.commons.ObjectUtils; import cn.devezhao.persist4j.Field; import cn.devezhao.persist4j.engine.ID; import cn.devezhao.persist4j.engine.NullValue; @@ -19,6 +20,7 @@ import com.rebuild.core.support.general.FieldValueWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigDecimal; import java.util.Date; /** @@ -130,8 +132,14 @@ public class CompatibleValueConversion { } } else if (is2Text) { compatibleValue = FieldValueWrapper.instance.wrapFieldValue(sourceValue, sourceField); + + } else if (sourceType == DisplayType.NUMBER && targetType == DisplayType.DECIMAL) { + compatibleValue = BigDecimal.valueOf((Long) sourceValue); + + } else if (sourceType == DisplayType.DECIMAL && targetType == DisplayType.NUMBER) { + compatibleValue = ObjectUtils.toLong(sourceValue); + } - // 整数/浮点数无需转换,因为持久层框架已有兼容处理 return compatibleValue; } diff --git a/src/main/java/com/rebuild/core/support/RebuildConfiguration.java b/src/main/java/com/rebuild/core/support/RebuildConfiguration.java index 932692f47..d4c064df2 100644 --- a/src/main/java/com/rebuild/core/support/RebuildConfiguration.java +++ b/src/main/java/com/rebuild/core/support/RebuildConfiguration.java @@ -187,8 +187,7 @@ public class RebuildConfiguration extends KVStorage { * @return */ public static String get(ConfigurationItem name, boolean noCache) { - if (name == ConfigurationItem.AppName && !License.isCommercial()) return "REBUILD"; - else return getValue(name.name(), noCache, name.getDefaultValue()); + return getValue(name.name(), noCache, name.getDefaultValue()); } /** diff --git a/src/main/java/com/rebuild/utils/AppUtils.java b/src/main/java/com/rebuild/utils/AppUtils.java index 7e511c79e..8cfaa2aaf 100644 --- a/src/main/java/com/rebuild/utils/AppUtils.java +++ b/src/main/java/com/rebuild/utils/AppUtils.java @@ -67,7 +67,7 @@ public class AppUtils { * @see #getRequestUserViaRbMobile(HttpServletRequest, boolean) */ public static ID getRequestUser(HttpServletRequest request) { - Object user = request.getSession(true).getAttribute(WebUtils.CURRENT_USER); + Object user = request.getSession().getAttribute(WebUtils.CURRENT_USER); return user == null ? null : (ID) user; } diff --git a/src/main/java/com/rebuild/web/commons/HeavyTaskController.java b/src/main/java/com/rebuild/web/commons/HeavyTaskController.java index 94f44c501..31feb995a 100644 --- a/src/main/java/com/rebuild/web/commons/HeavyTaskController.java +++ b/src/main/java/com/rebuild/web/commons/HeavyTaskController.java @@ -77,6 +77,7 @@ public class HeavyTaskController extends BaseController { */ private JSON formatTaskState(HeavyTask task) { JSONObject state = new JSONObject(); + state.put("total", task.getTotal()); state.put("progress", task.getCompletedPercent()); state.put("completed", task.getCompleted()); state.put("succeeded", task.getSucceeded()); diff --git a/src/main/resources/i18n/language.zh_CN.json b/src/main/resources/i18n/language.zh_CN.json index 9e54285d4..629caea63 100644 --- a/src/main/resources/i18n/language.zh_CN.json +++ b/src/main/resources/i18n/language.zh_CN.json @@ -1195,7 +1195,7 @@ "s.ApprovalState.REVOKED": "撤销", "s.HowtoState.DRAFT": "草稿", "s.HowtoState.PENDING": "处理中", - "s.HowtoState.SOLVED": "已驳回", + "s.HowtoState.SOLVED": "已解决", "s.HowtoState.REJECTED": "已驳回", "t.__": "字段类型", "t.NUMBER": "数字", diff --git a/src/main/resources/web/admin/admin-verify.html b/src/main/resources/web/admin/admin-verify.html index 91c10426f..1268b24a1 100644 --- a/src/main/resources/web/admin/admin-verify.html +++ b/src/main/resources/web/admin/admin-verify.html @@ -54,7 +54,7 @@ } $btn.button('loading') - $.post('admin-verify?passwd=' + passwd, function (res) { + $.post('admin-verify?passwd=' + $encode(passwd), function (res) { if (res.error_code == 0) location.replace(nexturl) else { RbHighbar.create(res.error_msg) diff --git a/src/main/resources/web/admin/data/data-imports.html b/src/main/resources/web/admin/data/data-imports.html index 28a751447..a9769a890 100644 --- a/src/main/resources/web/admin/data/data-imports.html +++ b/src/main/resources/web/admin/data/data-imports.html @@ -75,16 +75,13 @@
-
-
- - -
+
+ +
-
+
-
  • [[${bundle.L('ImportDataTips1')}]]
  • @@ -123,7 +120,7 @@
    - +
    [[${bundle.L('RecordOwningUserTips')}]]
    @@ -166,7 +163,9 @@
    [[${bundle.L('DataReading')}]]
    [[${bundle.L('TimeConsuming')}]] 00:00:00
    -
    +
    +
    +
    [[${bundle.L('ContinueImport')}]] diff --git a/src/main/resources/web/assets/js/admin/data-imports.js b/src/main/resources/web/assets/js/admin/data-imports.js index 32a14841f..2a19d5bc7 100644 --- a/src/main/resources/web/assets/js/admin/data-imports.js +++ b/src/main/resources/web/assets/js/admin/data-imports.js @@ -5,7 +5,8 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3). See LICENSE and COMMERCIAL in the project root for license information. */ -const ientry = { +// 导入规则 +const _Config = { file: null, entity: null, repeat_opt: 1, @@ -19,31 +20,6 @@ let import_inprogress = false let import_taskid $(document).ready(() => { - init_upload() - - const fileds_render = (entity) => { - if (!entity) return - const $el = $('#repeatFields').empty() - $.get(`/admin/data/data-imports/import-fields?entity=${entity}`, (res) => { - $(res.data).each(function () { - if (this.name === 'createdBy' || this.name === 'createdOn' || this.name === 'modifiedOn' || this.name === 'modifiedBy') return - $('').appendTo($el) - }) - - $el - .select2({ - maximumSelectionLength: 3, - placeholder: $L('SelectSome,Field'), - }) - .on('change', function () { - ientry.repeat_fields = $(this).val() - }) - - fields_cached = res.data - ientry.entity = entity - }) - } - $.get('/commons/metadata/entities?detail=true', (res) => { $(res.data).each(function () { $('').appendTo('#toEntity') @@ -54,122 +30,73 @@ $(document).ready(() => { allowClear: false, }) .on('change', function () { - fileds_render($(this).val()) - check_user() + _renderRepeatFields($(this).val()) + _checkUserPrivileges() }) if ($urlp('entity')) $toe.val($urlp('entity')) $toe.trigger('change') }) + $createUploader('#upload-input', null, (res) => { + _Config.file = res.key + $('.J_upload-input').text($fileCutName(_Config.file)) + }) + $('input[name=repeatOpt]').click(function () { - ientry.repeat_opt = ~~$(this).val() - if (ientry.repeat_opt === 3) $('.J_repeatFields').hide() + _Config.repeat_opt = ~~$(this).val() + if (_Config.repeat_opt === 3) $('.J_repeatFields').hide() else $('.J_repeatFields').show() }) - $('#toUser') - .select2({ - placeholder: $L('Default'), - minimumInputLength: 1, - ajax: { - url: '/commons/search/search', - delay: 300, - data: function (params) { - const query = { - entity: 'User', - quickFields: 'loginName,fullName,email,quickCode', - q: params.term, - } - return query - }, - processResults: function (data) { - const rs = data.data.map((item) => { - return item - }) - return { results: rs } - }, - }, - }) - .on('change', function () { - ientry.owning_user = $(this).val() || null - check_user() - }) + const _onSelectUser = function (s, isRemove) { + if (isRemove || !s) _Config.owning_user = null + else _Config.owning_user = s.id + } + renderRbcomp( + _onSelectUser(s, isRemove)} onClearSelection={() => _onSelectUser()} />, + 'toUser' + ) $('.J_step1-btn').click(step_mapping) $('.J_step2-btn').click(step_import) $('.J_step2-return').click(step_upload) $('.J_step3-cancel').click(import_cancel) - window.onbeforeunload = function () { - if (import_inprogress === true) return false - } import_taskid = $urlp('task', location.hash) if (import_taskid) { step_import_show() import_inprogress = true import_state(import_taskid, true) } + + window.onbeforeunload = function () { + if (import_inprogress === true) return false + } }) -const init_upload = () => { - $('#upload-input').html5Uploader({ - postUrl: rb.baseUrl + '/filex/upload?temp=yes', - onSelectError: function (field, error) { - if (error === 'ErrorType') RbHighbar.create($L('PlsUploadSomeFile').replace('%s', 'EXCEL/CSV ')) - else if (error === 'ErrorMaxSize') RbHighbar.create($L('ExceedMaxLimit') + ' (50MB)') - }, - onClientLoad: function () { - $mp.start() - }, - onSuccess: function (d) { - $mp.end() - d = JSON.parse(d.currentTarget.response) - if (d.error_code === 0) { - ientry.file = d.data - $('.J_upload-input').text($fileCutName(ientry.file)) - } else { - RbHighbar.error($L('ErrorUpload')) - } - }, - }) -} - -// 检查所属用户权限 -const check_user = () => $setTimeout(check_user0, 200, 'check_user') -const check_user0 = () => { - if (!ientry.entity || !ientry.owning_user) return - $.get(`/admin/data/data-imports/check-user?user=${ientry.owning_user}&entity=${ientry.entity}`, (res) => { - let hasError = [] - if (res.data.canCreate !== true) hasError.push($L('Create')) - if (res.data.canUpdate !== true) hasError.push($L('Update')) - if (hasError.length > 0) { - renderRbcomp(, 'user-warn') - } else { - $('#user-warn').empty() - } - }) -} - +// 1. 初始导入 const step_upload = () => { $('.steps li, .step-content .step-pane').removeClass('active complete') $('.steps li[data-step=1], .step-content .step-pane[data-step=1]').addClass('active') } + +// 2. 字段映射 const step_mapping = () => { - if (!ientry.entity) { + if (!_Config.entity) { RbHighbar.create($L('PlsSelectSome,ImportEntity')) return } - if (!ientry.file) { + if (!_Config.file) { RbHighbar.create($L('PlsUploadSome,DataFile')) return } - if (ientry.repeat_opt !== 3 && (!ientry.repeat_fields || ientry.repeat_fields.length === 0)) { + if (_Config.repeat_opt !== 3 && (!_Config.repeat_fields || _Config.repeat_fields.length === 0)) { RbHighbar.create($L('PlsSelectSome,DuplicateFields')) return } const $btn = $('.J_step1-btn').button('loading') - $.get(`/admin/data/data-imports/check-file?file=${$encode(ientry.file)}`, (res) => { + $.get(`/admin/data/data-imports/check-file?file=${$encode(_Config.file)}`, (res) => { $btn.button('reset') if (res.error_code > 0) { RbHighbar.create(res.error_msg) @@ -188,6 +115,8 @@ const step_mapping = () => { $('.steps li[data-step=2], .step-content .step-pane[data-step=2]').addClass('active') }) } + +// 3. 开始导入 const step_import = () => { let fm = {} $('#fieldsMapping tbody>tr').each(function () { @@ -206,12 +135,12 @@ const step_import = () => { } }) if (!fm) return - ientry.fields_mapping = fm + _Config.fields_mapping = fm RbAlert.create($L('DataImportConfirm'), { confirm: function () { this.disabled(true) - $.post('/admin/data/data-imports/import-submit', JSON.stringify(ientry), (res) => { + $.post('/admin/data/data-imports/import-submit', JSON.stringify(_Config), (res) => { if (res.error_code === 0) { this.hide() step_import_show() @@ -224,11 +153,15 @@ const step_import = () => { }, }) } + +// 3.1. 开始导入 const step_import_show = () => { $('.steps li, .step-content .step-pane').removeClass('active complete') $('.steps li[data-step=1], .steps li[data-step=2]').addClass('complete') $('.steps li[data-step=3], .step-content .step-pane[data-step=3]').addClass('active') } + +// 3.2. 导入状态 const import_state = (taskid, inLoad) => { $.get(`/commons/task/state?taskid=${taskid}`, (res) => { if (res.error_code !== 0) { @@ -243,7 +176,7 @@ const import_state = (taskid, inLoad) => { } const _data = res.data - $('.J_import_time').text(sec_to_time(~~_data.elapsedTime / 1000)) + $('.J_import_time').text(_secToTime(~~_data.elapsedTime / 1000)) if (_data.isCompleted === true) { $('.J_import-bar').css('width', '100%') @@ -251,6 +184,7 @@ const import_state = (taskid, inLoad) => { } else if (_data.isInterrupted === true) { $('.J_import_state').text($L('ImportInterrupttedTips').replace('%d', _data.succeeded)) } + if (_data.isCompleted === true || _data.isInterrupted === true) { $('.J_step3-cancel').attr('disabled', true).text($L('ImportFinshed')) $('.J_step3-logs').removeClass('hide') @@ -258,15 +192,18 @@ const import_state = (taskid, inLoad) => { return } - if (_data.total > -1) { + if (_data.progress > 0) { $('.J_import_state').text($L('DataImporting') + ' ' + _data.completed + '/' + _data.total) $('.J_import-bar').css('width', _data.progress * 100 + '%') } + setTimeout(() => { import_state(taskid) }, 500) }) } + +// 3.3. 中断导入 const import_cancel = () => { RbAlert.create($L('ImportInterrupttedConfirm'), { type: 'danger', @@ -319,7 +256,8 @@ const render_fieldsMapping = (columns, fields) => { }) } -const sec_to_time = function (s) { +// 格式化秒显示 +function _secToTime(s) { if (!s || s <= 0) return '00:00:00' let hh = Math.floor(s / 3600) let mm = Math.floor(s / 60) % 60 @@ -329,3 +267,42 @@ const sec_to_time = function (s) { if (ss < 10) ss = '0' + ss return hh + ':' + mm + ':' + ss } + +// 检查所属用户权限 +function _checkUserPrivileges() { + if (!_Config.entity || !_Config.owning_user) return + $.get(`/admin/data/data-imports/check-user?user=${_Config.owning_user}&entity=${_Config.entity}`, (res) => { + let hasError = [] + if (res.data.canCreate !== true) hasError.push($L('Create')) + if (res.data.canUpdate !== true) hasError.push($L('Update')) + if (hasError.length > 0) { + renderRbcomp(, 'user-warn') + } else { + $('#user-warn').empty() + } + }) +} + +// 渲染重复判断字段 +function _renderRepeatFields(entity) { + if (!entity) return + const $el = $('#repeatFields').empty() + $.get(`/admin/data/data-imports/import-fields?entity=${entity}`, (res) => { + $(res.data).each(function () { + if (this.name === 'createdBy' || this.name === 'createdOn' || this.name === 'modifiedOn' || this.name === 'modifiedBy') return + $('').appendTo($el) + }) + + $el + .select2({ + maximumSelectionLength: 3, + placeholder: $L('SelectSome,Field'), + }) + .on('change', function () { + _Config.repeat_fields = $(this).val() + }) + + fields_cached = res.data + _Config.entity = entity + }) +} diff --git a/src/main/resources/web/assets/js/bizuser/user-view.js b/src/main/resources/web/assets/js/bizuser/user-view.js index ce7a14c25..a7f08724b 100644 --- a/src/main/resources/web/assets/js/bizuser/user-view.js +++ b/src/main/resources/web/assets/js/bizuser/user-view.js @@ -205,7 +205,7 @@ class DlgEnableUser extends RbModalHandler { } if (this._roleAppends) { data.roleAppends = this._roleAppends.val().join(',') - if (data.roleAppends && rb.commercial > 0) { + if (data.roleAppends && rb.commercial < 1) { return RbHighbar.error($L('FreeVerNotSupportted,AppendRoles')) } } diff --git a/src/main/resources/web/assets/js/rb-components.js b/src/main/resources/web/assets/js/rb-components.js index cc59f9c54..ef6c74dc5 100644 --- a/src/main/resources/web/assets/js/rb-components.js +++ b/src/main/resources/web/assets/js/rb-components.js @@ -557,7 +557,9 @@ class UserSelector extends React.Component { } clearSelection() { - this.setState({ selected: [] }) + this.setState({ selected: [] }, () => { + typeof this.props.onClearSelection === 'function' && this.props.onClearSelection() + }) } switchTab(type) { @@ -587,7 +589,7 @@ class UserSelector extends React.Component { const st = $(this._scroller).scrollTop() const et = $el.position().top if (et >= 0) { - const top = et + st - (222 - 36) // maxHeight - elementHeight + const top = et + st - (222 - 36) // maxHeight - elementHeight if (top > 0) $(this._scroller).scrollTop(top) } else { const top = st + et @@ -620,14 +622,14 @@ class UserSelector extends React.Component { }) } - clickItem(e) { + clickItem(e, isRemove) { const $target = $(e.currentTarget) const id = $target.data('id') || $target.parents('.select2-results__option').data('id') let exists = false let ns = [] // 单选 - if (this.props.multiple !== false) { + if (this.props.multiple !== false || isRemove) { ns = this.state.selected.filter((x) => { if (x.id === id) { exists = true @@ -646,7 +648,7 @@ class UserSelector extends React.Component { } this.setState({ selected: ns }, () => { - typeof this.props.onSelectItem === 'function' && this.props.onSelectItem(selected) + typeof this.props.onSelectItem === 'function' && this.props.onSelectItem(selected, isRemove) }) }