Merge pull request #240 from getrebuild/fix-2.0.1

Fix 2.0.1
This commit is contained in:
RB 2020-11-06 01:08:39 +08:00 committed by GitHub
commit d2014a03af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 124 additions and 133 deletions

View file

@ -10,7 +10,7 @@
</parent>
<groupId>com.rebuild</groupId>
<artifactId>rebuild</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
<name>rebuild</name>
<description>RB V2 use SpringBoot</description>
<!-- UNCOMMENT USE TOMCAT -->

View file

@ -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<ApplicationStartedEvent>
/**
* 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<ApplicationStartedEvent>
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<ApplicationStartedEvent>
// 版本升级会清除缓存
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);
}

View file

@ -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;
}

View file

@ -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());
}
/**

View file

@ -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;
}

View file

@ -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());

View file

@ -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": "数字",

View file

@ -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)

View file

@ -75,16 +75,13 @@
<div class="form-group row">
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('UploadSome,DataFile')}]]</label>
<div class="col-md-12 col-xl-6 col-lg-8">
<div class="float-left">
<div class="file-select">
<input type="file" class="inputfile" id="upload-input" accept=".xlsx,.xls,.csv" data-maxsize="52428800" />
<label for="upload-input" class="btn-secondary"><i class="zmdi zmdi-upload"></i><span>[[${bundle.L('UploadFile')}]]</span></label>
</div>
<div class="file-select">
<input type="file" class="inputfile" id="upload-input" accept=".xlsx,.xls,.csv" data-temp="true" data-maxsize="52428800" />
<label for="upload-input" class="btn-secondary"><i class="zmdi zmdi-upload"></i><span>[[${bundle.L('UploadFile')}]]</span></label>
</div>
<div class="float-left ml-2 pt-1">
<div>
<u class="text-bold J_upload-input"></u>
</div>
<div class="clearfix"></div>
<div class="form-text mb-0">
<ul class="mb-0 pl-4">
<li>[[${bundle.L('ImportDataTips1')}]]</li>
@ -123,7 +120,7 @@
<div class="form-group row">
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('RecordOwningUser')}]]</label>
<div class="col-md-12 col-xl-6 col-lg-8">
<select class="form-control form-control-sm" id="toUser"></select>
<div id="toUser"></div>
<div class="form-text mb-0">[[${bundle.L('RecordOwningUserTips')}]]</div>
<div id="user-warn"></div>
</div>
@ -166,7 +163,9 @@
<div class="col-6"><h5 class="text-bold m-0 p-0 J_import_state">[[${bundle.L('DataReading')}]]</h5></div>
<div class="col-6 text-right text-muted">[[${bundle.L('TimeConsuming')}]] <span class="J_import_time">00:00:00</span></div>
</div>
<div class="progress"></div>
<div class="progress">
<div class="progress-bar J_import-bar"></div>
</div>
<div class="mt-3">
<button class="btn btn-danger btn-space J_step3-cancel" type="button">[[${bundle.L('InterruptImport')}]]</button>
<a class="btn btn-link btn-space J_step3-logs hide" href="data-imports">[[${bundle.L('ContinueImport')}]]</a>

View file

@ -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
$('<option value="' + this.name + '">' + this.label + '</option>').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 () {
$('<option value="' + this.name + '">' + this.label + '</option>').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(
<UserSelector hideDepartment={true} hideRole={true} hideTeam={true} multiple={false} onSelectItem={(s, isRemove) => _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(<RbAlertBox message={$L('SelectUserNoPermissionConfirm').replace('%s', hasError.join('/'))} />, '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(<RbAlertBox message={$L('SelectUserNoPermissionConfirm').replace('%s', hasError.join('/'))} />, '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
$('<option value="' + this.name + '">' + this.label + '</option>').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
})
}

View file

@ -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'))
}
}

View file

@ -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)
})
}