mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 15:35:55 +08:00
commit
348dbe53c3
14
pom.xml
14
pom.xml
|
@ -252,7 +252,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>commons</artifactId>
|
||||
<version>1.3.4</version>
|
||||
<version>1.3.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>httpclient</artifactId>
|
||||
|
@ -267,12 +267,12 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>bizz</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>persist4j</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<version>28559f7dea</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
|
@ -316,13 +316,13 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
<artifactId>EasyCaptcha</artifactId>
|
||||
<artifactId>easy-captcha</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-system</artifactId>
|
||||
<version>5.7.9</version>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>5.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.rebuild.core.support.i18n.Language;
|
|||
import com.rebuild.core.support.setup.Installer;
|
||||
import com.rebuild.core.support.setup.UpgradeDatabase;
|
||||
import com.rebuild.utils.JSONable;
|
||||
import com.rebuild.utils.OshiUtils;
|
||||
import com.rebuild.utils.RebuildBanner;
|
||||
import com.rebuild.utils.codec.RbDateCodec;
|
||||
import com.rebuild.utils.codec.RbRecordCodec;
|
||||
|
@ -131,7 +132,7 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
" License : " + License.queryAuthority(false).values(),
|
||||
"Access URLs : ",
|
||||
" Local : " + localUrl,
|
||||
" External : " + localUrl.replace("localhost", ServerStatus.getLocalIp()),
|
||||
" External : " + localUrl.replace("localhost", OshiUtils.getLocalIp()),
|
||||
" Public : " + RebuildConfiguration.getHomeUrl());
|
||||
log.info(banner);
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ package com.rebuild.core;
|
|||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.ThrowableUtils;
|
||||
import cn.devezhao.commons.runtime.MemoryInformationBean;
|
||||
import cn.hutool.core.util.RuntimeUtil;
|
||||
import cn.hutool.system.HostInfo;
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import com.rebuild.core.cache.CommonsCache;
|
||||
import com.rebuild.core.support.setup.Installer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -188,40 +183,4 @@ public final class ServerStatus {
|
|||
return new Status(name, false, error);
|
||||
}
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
* JVM 内存用量
|
||||
*
|
||||
* @return [总计M, 已用%]
|
||||
*/
|
||||
public static double[] getJvmMemoryUsed() {
|
||||
long maxMemory = RuntimeUtil.getMaxMemory();
|
||||
long usableMemory = RuntimeUtil.getUsableMemory();
|
||||
return new double[] {
|
||||
(int) (maxMemory / MemoryInformationBean.MEGABYTES),
|
||||
ObjectUtils.round(100 - (usableMemory * 100d / maxMemory), 2)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO CPU 负载
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static double getSystemLoad() {
|
||||
return 0d;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本机 IP
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getLocalIp() {
|
||||
HostInfo host = SystemUtil.getHostInfo();
|
||||
if (host == null || host.getAddress() == null) return "127.0.0.1";
|
||||
else return host.getAddress();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.rebuild.core.metadata.easymeta.EasyField;
|
|||
import com.rebuild.core.metadata.easymeta.EasyFile;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.easymeta.MixValue;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -50,16 +51,40 @@ public class AutoFillinManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public JSONArray getFillinValue(Field field, ID source) {
|
||||
// @see field-edit.html 内置字段无配置
|
||||
if (EasyMetaFactory.valueOf(field).isBuiltin()) {
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
final EasyField easyField = EasyMetaFactory.valueOf(field);
|
||||
|
||||
// 内置字段无配置 @see field-edit.html
|
||||
if (easyField.isBuiltin()) return JSONUtils.EMPTY_ARRAY;
|
||||
|
||||
final List<ConfigBean> config = getConfig(field);
|
||||
if (config.isEmpty()) {
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
|
||||
// 父级级联
|
||||
// 利用表单回填做父级级联字段回填
|
||||
String cascadingField = easyField.getExtraAttr(EasyFieldConfigProps.REFERENCE_CASCADINGFIELD);
|
||||
if (StringUtils.isNotBlank(cascadingField)) {
|
||||
String[] fs = cascadingField.split(MetadataHelper.SPLITER2);
|
||||
ConfigBean fake = new ConfigBean()
|
||||
.set("source", fs[1])
|
||||
.set("target", fs[0])
|
||||
.set("whenCreate", true)
|
||||
.set("whenUpdate", true)
|
||||
.set("fillinForce", true);
|
||||
|
||||
// 移除冲突的表单回填配置
|
||||
for (Iterator<ConfigBean> iter = config.iterator(); iter.hasNext(); ) {
|
||||
ConfigBean cb = iter.next();
|
||||
if (cb.getString("source").equals(fake.getString("source"))
|
||||
&& cb.getString("target").equals(fake.getString("target"))) {
|
||||
iter.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config.add(fake);
|
||||
}
|
||||
|
||||
if (config.isEmpty()) return JSONUtils.EMPTY_ARRAY;
|
||||
|
||||
Entity sourceEntity = MetadataHelper.getEntity(source.getEntityCode());
|
||||
Entity targetEntity = field.getOwnEntity();
|
||||
Set<String> sourceFields = new HashSet<>();
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.metadata.impl.DynamicMetadataFactory;
|
||||
import com.rebuild.core.metadata.impl.GhostEntity;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -36,6 +37,10 @@ import java.util.List;
|
|||
@Slf4j
|
||||
public class MetadataHelper {
|
||||
|
||||
// 通用分隔符
|
||||
public static final String SPLITER = "$$$$";
|
||||
public static final String SPLITER2 = "\\$\\$\\$\\$";
|
||||
|
||||
/**
|
||||
* 元数据工厂
|
||||
*
|
||||
|
@ -365,11 +370,9 @@ public class MetadataHelper {
|
|||
* @return
|
||||
*/
|
||||
public static boolean checkAndWarnField(Entity entity, String fieldName) {
|
||||
if (entity.containsField(fieldName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log.warn("Unknown field `" + fieldName + "` in `" + entity.getName() + "`");
|
||||
if (entity.containsField(fieldName)) return true;
|
||||
log.warn("Unknown field `{}` in `{}`", fieldName, entity.getName());
|
||||
CommonsUtils.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,12 @@ import org.apache.commons.lang.StringUtils;
|
|||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.rebuild.core.metadata.MetadataHelper.SPLITER;
|
||||
import static com.rebuild.core.metadata.MetadataHelper.SPLITER2;
|
||||
|
||||
/**
|
||||
* @author zhaofang123@gmail.com
|
||||
* @since 08/04/2018
|
||||
|
@ -93,16 +99,18 @@ public class DynamicMetadataFactory extends ConfigurationMetadataFactory {
|
|||
entity.addAttribute("extra-attrs", extraAttrs.toJSONString());
|
||||
}
|
||||
|
||||
Set<String> cascadingFieldsChild = new HashSet<>();
|
||||
|
||||
Object[][] customFields = Application.createQueryNoFilter(
|
||||
"select belongEntity,fieldName,physicalName,fieldLabel,displayType,nullable,creatable,updatable,"
|
||||
+ "maxLength,defaultValue,refEntity,cascade,fieldId,comments,extConfig,repeatable,queryable from MetaField")
|
||||
.array();
|
||||
for (Object[] c : customFields) {
|
||||
String entityName = (String) c[0];
|
||||
String fieldName = (String) c[1];
|
||||
final String entityName = (String) c[0];
|
||||
final String fieldName = (String) c[1];
|
||||
Element entityElement = (Element) rootElement.selectSingleNode("entity[@name='" + entityName + "']");
|
||||
if (entityElement == null) {
|
||||
log.warn("No entity found : " + entityName + "." + fieldName);
|
||||
log.warn("No entity `{}` found for field `{}`", entityName, fieldName);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -159,12 +167,34 @@ public class DynamicMetadataFactory extends ConfigurationMetadataFactory {
|
|||
extraAttrs.put("metaId", c[12]);
|
||||
extraAttrs.put("comments", c[13]);
|
||||
extraAttrs.put("displayType", dt.name());
|
||||
|
||||
String cascadingField = extraAttrs.getString(EasyFieldConfigProps.REFERENCE_CASCADINGFIELD);
|
||||
if (StringUtils.isNotBlank(cascadingField)
|
||||
&& (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) {
|
||||
extraAttrs.put("_cascadingFieldParent", cascadingField);
|
||||
String[] fs = cascadingField.split(SPLITER2);
|
||||
cascadingFieldsChild.add(entityName + SPLITER + fs[0] + SPLITER + fieldName + SPLITER + fs[1]);
|
||||
}
|
||||
|
||||
field.addAttribute("extra-attrs", extraAttrs.toJSONString());
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
XmlHelper.dump(rootElement);
|
||||
// 处理父级级联的父子级关系
|
||||
for (String child : cascadingFieldsChild) {
|
||||
String[] fs = child.split(SPLITER2);
|
||||
Element fieldElement = (Element) rootElement.selectSingleNode(
|
||||
String.format("entity[@name='%s']/field[@name='%s']", fs[0], fs[1]));
|
||||
if (fieldElement == null) {
|
||||
log.warn("No field found: {}.{}", fs[0], fs[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
JSONObject extraAttrs = JSON.parseObject(fieldElement.valueOf("@extra-attrs"));
|
||||
extraAttrs.put("_cascadingFieldChild", fs[2] + SPLITER + fs[3]);
|
||||
fieldElement.addAttribute("extra-attrs", extraAttrs.toJSONString());
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) XmlHelper.dump(rootElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,7 @@ public class EasyFieldConfigProps {
|
|||
/**
|
||||
* 表单公式
|
||||
*/
|
||||
public static final String NUMBER_CALC_FORMULA = "calcFormula";
|
||||
public static final String NUMBER_CALCFORMULA = "calcFormula";
|
||||
|
||||
/**
|
||||
* 是否允许负数
|
||||
|
@ -39,7 +39,7 @@ public class EasyFieldConfigProps {
|
|||
/**
|
||||
* 表单公式
|
||||
*/
|
||||
public static final String DECIMAL_CALC_FORMULA = NUMBER_CALC_FORMULA;
|
||||
public static final String DECIMAL_CALCFORMULA = NUMBER_CALCFORMULA;
|
||||
|
||||
/**
|
||||
* 日期格式
|
||||
|
@ -89,17 +89,26 @@ public class EasyFieldConfigProps {
|
|||
* 引用字段数据过滤
|
||||
*/
|
||||
public static final String REFERENCE_DATAFILTER = "referenceDataFilter";
|
||||
/**
|
||||
* 父级级联字段
|
||||
*/
|
||||
public static final String REFERENCE_CASCADINGFIELD = "referenceCascadingField";
|
||||
|
||||
/**
|
||||
* 多引用字段数据过滤
|
||||
* @see #REFERENCE_DATAFILTER
|
||||
*/
|
||||
public static final String N2NREFERENCE_DATAFILTER = REFERENCE_DATAFILTER;
|
||||
/**
|
||||
* 父级级联字段
|
||||
* @see #REFERENCE_CASCADINGFIELD
|
||||
*/
|
||||
public static final String N2NREFERENCE_CASCADINGFIELD = REFERENCE_CASCADINGFIELD;
|
||||
|
||||
/**
|
||||
* 多行文本使用 MD 编辑器
|
||||
*/
|
||||
public static final String NTEXT_USE_MDEDIT = "useMdedit";
|
||||
public static final String NTEXT_USEMDEDIT = "useMdedit";
|
||||
|
||||
/**
|
||||
* 信息脱敏
|
||||
|
|
|
@ -1,293 +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.metadata.impl;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.dialect.Type;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.BaseMeta;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.service.trigger.RobotTriggerManager;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 元数据(Entity/Field)封装
|
||||
*
|
||||
* @author zhaofang123@gmail.com
|
||||
* @since 08/13/2018
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated
|
||||
public class EasyMeta implements BaseMeta {
|
||||
private static final long serialVersionUID = -6463919098111506968L;
|
||||
|
||||
final private BaseMeta baseMeta;
|
||||
|
||||
public EasyMeta(BaseMeta baseMeta) {
|
||||
this.baseMeta = baseMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns Entity or Field
|
||||
*/
|
||||
public BaseMeta getBaseMeta() {
|
||||
return baseMeta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return baseMeta.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPhysicalName() {
|
||||
return baseMeta.getPhysicalName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link #getLabel()}
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return baseMeta.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getExtraAttrs() {
|
||||
return getExtraAttrs(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clean
|
||||
* @return
|
||||
*/
|
||||
public JSONObject getExtraAttrs(boolean clean) {
|
||||
// see DynamicMetadataFactory
|
||||
if (clean) {
|
||||
JSONObject clone = (JSONObject) JSONUtils.clone(baseMeta.getExtraAttrs());
|
||||
clone.remove("metaId");
|
||||
clone.remove("comments");
|
||||
clone.remove("icon");
|
||||
clone.remove("displayType");
|
||||
return clone;
|
||||
}
|
||||
return baseMeta.getExtraAttrs() == null ? JSONUtils.EMPTY_OBJECT : baseMeta.getExtraAttrs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreatable() {
|
||||
return baseMeta.isCreatable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatable() {
|
||||
if (isField()) {
|
||||
if (!baseMeta.isUpdatable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Field field = (Field) baseMeta;
|
||||
Set<String> set = RobotTriggerManager.instance.getAutoReadonlyFields(field.getOwnEntity().getName());
|
||||
return !set.contains(field.getName());
|
||||
}
|
||||
|
||||
return baseMeta.isUpdatable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueryable() {
|
||||
return baseMeta.isQueryable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展属性
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @see EasyFieldConfigProps
|
||||
*/
|
||||
public String getExtraAttr(String name) {
|
||||
return getExtraAttrs().getString(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统内置字段/实体,不可更改
|
||||
*
|
||||
* @return
|
||||
* @see MetadataHelper#isCommonsField(Field)
|
||||
*/
|
||||
public boolean isBuiltin() {
|
||||
if (this.getMetaId() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isField()) {
|
||||
Field field = (Field) baseMeta;
|
||||
if (MetadataHelper.isCommonsField(field)) {
|
||||
return true;
|
||||
} else if (getDisplayType() == DisplayType.REFERENCE) {
|
||||
// 明细-引用主记录的字段也是内置
|
||||
// @see MetadataHelper#getDetailToMainField
|
||||
Entity hasMain = field.getOwnEntity().getMainEntity();
|
||||
return hasMain != null && hasMain.equals(field.getReferenceEntity()) && !field.isCreatable();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see #getDescription()
|
||||
*/
|
||||
public String getLabel() {
|
||||
if (isField() && ((Field) baseMeta).getType() == FieldType.PRIMARY) {
|
||||
return "ID";
|
||||
}
|
||||
return Language.L(this.baseMeta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义实体/字段 ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ID getMetaId() {
|
||||
String metaId = getExtraAttr("metaId");
|
||||
return metaId == null ? null : ID.valueOf(metaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取代 persist4j 中的 description,而 persist4j 中的 description 则表示 label
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getComments() {
|
||||
String comments = getExtraAttr("comments");
|
||||
if (getMetaId() != null) {
|
||||
return comments;
|
||||
}
|
||||
return StringUtils.defaultIfBlank(comments, Language.L("系统内置"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EASY#" + baseMeta.toString();
|
||||
}
|
||||
|
||||
// -- ENTITY
|
||||
|
||||
/**
|
||||
* 实体图标
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getIcon() {
|
||||
Assert.isTrue(!isField(), "Entity supports only");
|
||||
return StringUtils.defaultIfBlank(getExtraAttr("icon"), "texture");
|
||||
}
|
||||
|
||||
/**
|
||||
* 具有和业务实体一样的特性(除权限以外(因为无权限字段))
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isPlainEntity() {
|
||||
Assert.isTrue(!isField(), "Entity supports only");
|
||||
return getExtraAttrs().getBooleanValue("plainEntity");
|
||||
}
|
||||
|
||||
// -- FIELD
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private boolean isField() {
|
||||
return baseMeta instanceof Field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fullName
|
||||
* @return
|
||||
*/
|
||||
public String getDisplayType(boolean fullName) {
|
||||
DisplayType dt = getDisplayType();
|
||||
if (fullName) {
|
||||
return dt.getDisplayName() + " (" + dt.name() + ")";
|
||||
} else {
|
||||
return dt.name();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public DisplayType getDisplayType() {
|
||||
Assert.isTrue(isField(), "Field supports only");
|
||||
|
||||
String displayType = getExtraAttr("displayType");
|
||||
DisplayType dt = displayType != null
|
||||
? DisplayType.valueOf(displayType) : converBuiltinFieldType((Field) baseMeta);
|
||||
if (dt != null) {
|
||||
return dt;
|
||||
}
|
||||
throw new RebuildException("Unsupported field type : " + baseMeta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字段类型转成 DisplayType
|
||||
*
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
private DisplayType converBuiltinFieldType(Field field) {
|
||||
Type ft = field.getType();
|
||||
if (ft == FieldType.PRIMARY) {
|
||||
return DisplayType.ID;
|
||||
} else if (ft == FieldType.REFERENCE) {
|
||||
int typeCode = field.getReferenceEntity().getEntityCode();
|
||||
if (typeCode == EntityHelper.PickList) {
|
||||
return DisplayType.PICKLIST;
|
||||
} else if (typeCode == EntityHelper.Classification) {
|
||||
return DisplayType.CLASSIFICATION;
|
||||
} else {
|
||||
return DisplayType.REFERENCE;
|
||||
}
|
||||
} else if (ft == FieldType.ANY_REFERENCE) {
|
||||
return DisplayType.ANYREFERENCE;
|
||||
} else if (ft == FieldType.REFERENCE_LIST) {
|
||||
return DisplayType.N2NREFERENCE;
|
||||
} else if (ft == FieldType.TIMESTAMP) {
|
||||
return DisplayType.DATETIME;
|
||||
} else if (ft == FieldType.DATE) {
|
||||
return DisplayType.DATE;
|
||||
} else if (ft == FieldType.STRING) {
|
||||
return DisplayType.TEXT;
|
||||
} else if (ft == FieldType.TEXT || ft == FieldType.NTEXT) {
|
||||
return DisplayType.NTEXT;
|
||||
} else if (ft == FieldType.BOOL) {
|
||||
return DisplayType.BOOL;
|
||||
} else if (ft == FieldType.INT || ft == FieldType.SMALL_INT || ft == FieldType.LONG) {
|
||||
return DisplayType.NUMBER;
|
||||
} else if (ft == FieldType.DOUBLE || ft == FieldType.DECIMAL) {
|
||||
return DisplayType.DECIMAL;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -153,7 +153,7 @@ public class FeedsHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* URL 提取
|
||||
* URL 提取 FIXME 会匹配 " 符号
|
||||
*/
|
||||
public static final Pattern URL_PATTERN = Pattern.compile("((www|https?://)[-a-zA-Z0-9+&@#/%?=~_|!:,.;]{5,300})");
|
||||
|
||||
|
@ -164,7 +164,7 @@ public class FeedsHelper {
|
|||
* @return
|
||||
*/
|
||||
public static String formatContent(String content) {
|
||||
return formatContent(content, false);
|
||||
return formatContent(content, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,7 @@ public abstract class BaseTaskService extends ObservableService {
|
|||
Assert.notNull(taskOrProject, "taskOrProject");
|
||||
|
||||
ConfigBean c = taskOrProject.getEntityCode() == EntityHelper.ProjectTask
|
||||
? ProjectManager.instance.getProjectByTask(taskOrProject, null)
|
||||
? ProjectManager.instance.getProjectByX(taskOrProject, null)
|
||||
: ProjectManager.instance.getProject(taskOrProject, null);
|
||||
if (c != null && c.get("members", Set.class).contains(user)) return true;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public class ProjectHelper {
|
|||
taskOrComment = convert2Task(taskOrComment);
|
||||
|
||||
// 能访问就有读取权限
|
||||
ProjectManager.instance.getProjectByTask(taskOrComment, user);
|
||||
ProjectManager.instance.getProjectByX(taskOrComment, user);
|
||||
return true;
|
||||
} catch (ConfigurationException | AccessDeniedException ex) {
|
||||
return false;
|
||||
|
@ -60,9 +60,9 @@ public class ProjectHelper {
|
|||
Object[] projectId = Application.getQueryFactory().uniqueNoFilter(taskOrCommentOrTag, "projectId");
|
||||
pcfg = ProjectManager.instance.getProject((ID) projectId[0], null);
|
||||
} else {
|
||||
pcfg = ProjectManager.instance.getProjectByTask(convert2Task(taskOrCommentOrTag), null);
|
||||
pcfg = ProjectManager.instance.getProjectByX(convert2Task(taskOrCommentOrTag), null);
|
||||
}
|
||||
|
||||
|
||||
// 负责人
|
||||
if (user.equals(pcfg.getID("principal"))) return true;
|
||||
// 非成员
|
||||
|
|
|
@ -38,8 +38,8 @@ public class ProjectManager implements ConfigManager {
|
|||
}
|
||||
|
||||
private static final String CKEY_PROJECTS = "ProjectManager";
|
||||
private static final String CKEY_PLAN = "ProjectPlan-";
|
||||
private static final String CKEY_TASK = "Task2Project-";
|
||||
private static final String CKEY_PLANS = "ProjectPlan-";
|
||||
private static final String CKEY_TP2P = "TP2Project-";
|
||||
|
||||
/**
|
||||
* @param user
|
||||
|
@ -148,40 +148,6 @@ public class ProjectManager implements ConfigManager {
|
|||
throw new ConfigurationException(Language.L("无权访问该项目或项目已删除"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param taskId
|
||||
* @param user
|
||||
* @return
|
||||
* @throws ConfigurationException
|
||||
* @throws AccessDeniedException
|
||||
*/
|
||||
public ConfigBean getProjectByTask(ID taskId, ID user) throws ConfigurationException, AccessDeniedException {
|
||||
final String ckey = CKEY_TASK + taskId;
|
||||
ID projectId = (ID) Application.getCommonsCache().getx(ckey);
|
||||
|
||||
if (projectId == null) {
|
||||
Object[] task = Application.createQueryNoFilter(
|
||||
"select projectId from ProjectTask where taskId = ?")
|
||||
.setParameter(1, taskId)
|
||||
.unique();
|
||||
|
||||
projectId = task == null ? null : (ID) task[0];
|
||||
if (projectId != null) {
|
||||
Application.getCommonsCache().putx(ckey, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectId == null) {
|
||||
throw new ConfigurationException(Language.L("任务不存在或已被删除"));
|
||||
}
|
||||
|
||||
try {
|
||||
return getProject(projectId, user);
|
||||
} catch (ConfigurationException ex) {
|
||||
throw new AccessDeniedException(Language.L("无权访问该任务"), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目的任务面板
|
||||
*
|
||||
|
@ -191,7 +157,7 @@ public class ProjectManager implements ConfigManager {
|
|||
public ConfigBean[] getPlansOfProject(ID projectId) {
|
||||
Assert.notNull(projectId, "[projectId] cannot be null");
|
||||
|
||||
final String ckey = CKEY_PLAN + projectId;
|
||||
final String ckey = CKEY_PLANS + projectId;
|
||||
ConfigBean[] cache = (ConfigBean[]) Application.getCommonsCache().getx(ckey);
|
||||
|
||||
if (cache == null) {
|
||||
|
@ -240,20 +206,50 @@ public class ProjectManager implements ConfigManager {
|
|||
throw new ConfigurationException(Language.L("无效任务面板 (%s)", planId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param taskOrPlan
|
||||
* @param checkUser
|
||||
* @return
|
||||
*/
|
||||
public ConfigBean getProjectByX(ID taskOrPlan, ID checkUser) {
|
||||
final String ckey = CKEY_TP2P + taskOrPlan;
|
||||
ID projectId = (ID) Application.getCommonsCache().getx(ckey);
|
||||
|
||||
if (projectId == null) {
|
||||
Object[] x = Application.getQueryFactory().uniqueNoFilter(taskOrPlan, "projectId");
|
||||
projectId = x == null ? null : (ID) x[0];
|
||||
if (projectId != null) {
|
||||
Application.getCommonsCache().putx(ckey, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectId == null) {
|
||||
throw new ConfigurationException(Language.L("任务/面板不存在或已被删除"));
|
||||
}
|
||||
|
||||
try {
|
||||
return getProject(projectId, checkUser);
|
||||
} catch (ConfigurationException ex) {
|
||||
throw new AccessDeniedException(Language.L("无权访问该项目"), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clean(Object nullOrAnyProjectId) {
|
||||
int ec = nullOrAnyProjectId == null ? -1 : ((ID) nullOrAnyProjectId).getEntityCode();
|
||||
int e = nullOrAnyProjectId == null ? -1 : ((ID) nullOrAnyProjectId).getEntityCode();
|
||||
// 清理项目
|
||||
if (ec == -1) {
|
||||
if (e == -1) {
|
||||
Application.getCommonsCache().evict(CKEY_PROJECTS);
|
||||
}
|
||||
// 清理面板
|
||||
else if (ec == EntityHelper.ProjectConfig) {
|
||||
Application.getCommonsCache().evict(CKEY_PLAN + nullOrAnyProjectId);
|
||||
else if (e == EntityHelper.ProjectConfig) {
|
||||
Application.getCommonsCache().evict(CKEY_PLANS + nullOrAnyProjectId);
|
||||
}
|
||||
// 清理任务
|
||||
else if (ec == EntityHelper.ProjectTask) {
|
||||
Application.getCommonsCache().evict(CKEY_TASK + nullOrAnyProjectId);
|
||||
else if (e == EntityHelper.ProjectTask || e == EntityHelper.ProjectPlanConfig) {
|
||||
Application.getCommonsCache().evict(CKEY_TP2P + nullOrAnyProjectId);
|
||||
Application.getCommonsCache().evict(CKEY_TP2P + nullOrAnyProjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class AggregationEvaluator {
|
|||
|
||||
List<String[]> fields = new ArrayList<>();
|
||||
for (String m : matchsVars) {
|
||||
String[] fieldAndFunc = m.split("\\$\\$\\$\\$");
|
||||
String[] fieldAndFunc = m.split(MetadataHelper.SPLITER2);
|
||||
if (MetadataHelper.getLastJoinField(sourceEntity, fieldAndFunc[0]) == null) {
|
||||
throw new MissingMetaExcetion(fieldAndFunc[0], sourceEntity.getName());
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ public class AggregationEvaluator {
|
|||
String[] field = fields.get(i);
|
||||
Object value = useSourceData[i] == null ? "0" : useSourceData[i];
|
||||
|
||||
String replace = "{" + StringUtils.join(field, "$$$$") + "}";
|
||||
String replace = "{" + StringUtils.join(field, MetadataHelper.SPLITER) + "}";
|
||||
clearFormual = clearFormual.replace(replace.toUpperCase(), value.toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ public class EvaluatorUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// 日期加 `date = dateadd(date, interval[H|D|M|Y])`
|
||||
// 日期加 `date = DATEADD(date, interval[H|D|M|Y])`
|
||||
static class DateAddFunction extends AbstractFunction {
|
||||
private static final long serialVersionUID = 8286269123891483078L;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.support.general;
|
||||
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Field;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
@ -21,9 +22,13 @@ import com.rebuild.core.service.dashboard.ChartManager;
|
|||
import com.rebuild.core.service.query.AdvFilterParser;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 解析已知的个性化过滤条件
|
||||
|
@ -31,12 +36,13 @@ import java.util.Collections;
|
|||
* @author devezhao
|
||||
* @since 2020/6/13
|
||||
*/
|
||||
@Slf4j
|
||||
public class ProtocolFilterParser {
|
||||
|
||||
final private String protocolExpr;
|
||||
|
||||
/**
|
||||
* @param protocolExpr via:xxx ref:xxx
|
||||
* @param protocolExpr via:xxx:[field] ref:xxx:[id]
|
||||
*/
|
||||
public ProtocolFilterParser(String protocolExpr) {
|
||||
this.protocolExpr = protocolExpr;
|
||||
|
@ -52,10 +58,12 @@ public class ProtocolFilterParser {
|
|||
return parseVia(ps[1], ps.length > 2 ? ps[2] : null);
|
||||
}
|
||||
case "ref": {
|
||||
return parseRef(ps[1]);
|
||||
return parseRef(ps[1], ps.length > 2 ? ps[2] : null);
|
||||
}
|
||||
default:
|
||||
default: {
|
||||
log.warn("Unknown protocol expr : {}", protocolExpr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +88,7 @@ public class ProtocolFilterParser {
|
|||
ConfigBean filter = AdvFilterManager.instance.getAdvFilter(anyId);
|
||||
if (filter != null) filterExp = (JSONObject) filter.getJSON("filter");
|
||||
}
|
||||
// via others
|
||||
// via OTHERS
|
||||
else if (refField != null) {
|
||||
String[] entityAndField = refField.split("\\.");
|
||||
Assert.isTrue(entityAndField.length == 2, "Bad `via` filter defined");
|
||||
|
@ -98,24 +106,53 @@ public class ProtocolFilterParser {
|
|||
|
||||
/**
|
||||
* @param content
|
||||
* @param cascadingValue
|
||||
* @return
|
||||
*/
|
||||
public String parseRef(String content) {
|
||||
public String parseRef(String content, String cascadingValue) {
|
||||
String[] fieldAndEntity = content.split("\\.");
|
||||
if (fieldAndEntity.length != 2 || !MetadataHelper.checkAndWarnField(fieldAndEntity[1], fieldAndEntity[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Field field = MetadataHelper.getField(fieldAndEntity[1], fieldAndEntity[0]);
|
||||
final Entity entity = MetadataHelper.getEntity(fieldAndEntity[1]);
|
||||
final Field field = entity.getField(fieldAndEntity[0]);
|
||||
|
||||
List<String> sqls = new ArrayList<>();
|
||||
|
||||
JSONObject advFilter = getFieldDataFilter(field);
|
||||
return advFilter == null ? null : new AdvFilterParser(advFilter).toSqlWhere();
|
||||
if (advFilter != null) sqls.add(new AdvFilterParser(advFilter).toSqlWhere());
|
||||
|
||||
if (hasFieldCascadingField(field) && ID.isId(cascadingValue)) {
|
||||
String cascadingFieldParent = field.getExtraAttrs().getString("_cascadingFieldParent");
|
||||
String cascadingFieldChild = field.getExtraAttrs().getString("_cascadingFieldChild");
|
||||
|
||||
if (StringUtils.isNotBlank(cascadingFieldParent)) {
|
||||
String[] fs = cascadingFieldParent.split(MetadataHelper.SPLITER2);
|
||||
sqls.add(String.format("%s = '%s'", fs[1], cascadingValue));
|
||||
}
|
||||
if (StringUtils.isNotBlank(cascadingFieldChild)) {
|
||||
String[] fs = cascadingFieldChild.split(MetadataHelper.SPLITER2);
|
||||
Entity refEntity = entity.getField(fs[0]).getReferenceEntity();
|
||||
|
||||
String sql = String.format("exists (select %s from %s where ^%s = %s and %s = '%s')",
|
||||
fs[1], refEntity.getName(),
|
||||
field.getReferenceEntity().getPrimaryField().getName(), fs[1],
|
||||
refEntity.getPrimaryField().getName(), cascadingValue);
|
||||
sqls.add(sql);
|
||||
}
|
||||
}
|
||||
|
||||
return sqls.isEmpty() ? null
|
||||
: "( " + StringUtils.join(sqls, " and ") + " )";
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用了数据过滤
|
||||
* 附加过滤条件
|
||||
*
|
||||
* @param field
|
||||
* @return
|
||||
* @see #parseRef(String, String)
|
||||
*/
|
||||
public static JSONObject getFieldDataFilter(Field field) {
|
||||
String dataFilter = EasyMetaFactory.valueOf(field).getExtraAttr(EasyFieldConfigProps.REFERENCE_DATAFILTER);
|
||||
|
@ -127,4 +164,16 @@ public class ProtocolFilterParser {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否级联字段
|
||||
*
|
||||
* @param field
|
||||
* @return
|
||||
* @see #parseRef(String, String)
|
||||
*/
|
||||
public static boolean hasFieldCascadingField(Field field) {
|
||||
return field.getExtraAttrs().containsKey("_cascadingFieldParent")
|
||||
|| field.getExtraAttrs().containsKey("_cascadingFieldChild");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ import com.rebuild.core.support.RebuildConfiguration;
|
|||
import com.rebuild.core.support.i18n.LanguageBundle;
|
||||
import com.rebuild.web.admin.AdminVerfiyController;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
@ -149,32 +147,6 @@ public class AppUtils {
|
|||
return UA != null && UA.startsWith("RB/Mobile-");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求类型
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @see MimeTypeUtils#parseMimeType(String)
|
||||
*/
|
||||
public static MimeType parseMimeType(HttpServletRequest request) {
|
||||
try {
|
||||
String acceptType = request.getHeader("Accept");
|
||||
if (acceptType == null || "*/*".equals(acceptType)) acceptType = request.getContentType();
|
||||
|
||||
// Via Spider?
|
||||
if (StringUtils.isBlank(acceptType)) return MimeTypeUtils.TEXT_HTML;
|
||||
|
||||
acceptType = acceptType.split("[,;]")[0];
|
||||
// Accpet ALL?
|
||||
if ("*/*".equals(acceptType)) return MimeTypeUtils.TEXT_HTML;
|
||||
|
||||
return MimeTypeUtils.parseMimeType(acceptType);
|
||||
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否 IE11(加载 polyfill)
|
||||
*
|
||||
|
|
|
@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.utils;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import com.rebuild.core.Application;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -145,9 +146,11 @@ public class CommonsUtils {
|
|||
* @return
|
||||
*/
|
||||
public static void printStackTrace() {
|
||||
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement traceElement : trace) {
|
||||
System.err.println("\tat " + traceElement);
|
||||
if (Application.devMode() || log.isDebugEnabled()) {
|
||||
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement traceElement : trace) {
|
||||
System.err.println("\tat " + traceElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,9 +110,7 @@ public class JSONUtils {
|
|||
* @return
|
||||
*/
|
||||
public static boolean wellFormat(String text) {
|
||||
if (StringUtils.isBlank(text)) {
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.isBlank(text)) return false;
|
||||
text = text.trim();
|
||||
return (text.startsWith("{") && text.endsWith("}")) || (text.startsWith("[") && text.endsWith("]"));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ package com.rebuild.utils;
|
|||
import com.vladsch.flexmark.ext.tables.TablesExtension;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.parser.ParserEmulationProfile;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import com.vladsch.flexmark.util.data.MutableDataSet;
|
||||
|
||||
|
@ -26,20 +27,21 @@ public class MarkdownUtils {
|
|||
private static final MutableDataSet OPTIONS = new MutableDataSet();
|
||||
|
||||
static {
|
||||
OPTIONS.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
|
||||
// OPTIONS.set(HtmlRenderer.SOFT_BREAK, "<br/>");
|
||||
OPTIONS.setFrom(ParserEmulationProfile.MARKDOWN)
|
||||
.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
|
||||
}
|
||||
|
||||
private static final Parser PARSER = Parser.builder(OPTIONS).build();
|
||||
private static final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build();
|
||||
|
||||
/**
|
||||
* MD 渲染,支持表格
|
||||
* MD 渲染,支持表格,HTML 代码会转义
|
||||
*
|
||||
* @param md
|
||||
* @return
|
||||
*/
|
||||
public static String render(String md) {
|
||||
md = CommonsUtils.escapeHtml(md);
|
||||
Node document = PARSER.parse(md);
|
||||
return RENDERER.render(document);
|
||||
}
|
||||
|
|
74
src/main/java/com/rebuild/utils/OshiUtils.java
Normal file
74
src/main/java/com/rebuild/utils/OshiUtils.java
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
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.utils;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.runtime.MemoryInformationBean;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.NetworkIF;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author devezhao
|
||||
* @since 2021/9/22
|
||||
*/
|
||||
public class OshiUtils {
|
||||
|
||||
private static SystemInfo SI;
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
synchronized public static SystemInfo getSI() {
|
||||
if (SI == null) SI = new SystemInfo();
|
||||
return SI;
|
||||
}
|
||||
|
||||
/**
|
||||
* OS 内存
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static double[] getOsMemoryUsed() {
|
||||
GlobalMemory memory = getSI().getHardware().getMemory();
|
||||
long memoryTotal = memory.getTotal();
|
||||
double memoryUsage = (memoryTotal - memory.getAvailable()) * 1.0 / memoryTotal;
|
||||
return new double[]{
|
||||
(int) (memoryTotal / MemoryInformationBean.MEGABYTES),
|
||||
ObjectUtils.round(memoryUsage * 100, 2)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* CPU 负载
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static double getSystemLoad() {
|
||||
double[] loadAverages = getSI().getHardware().getProcessor().getSystemLoadAverage(2);
|
||||
return ObjectUtils.round(loadAverages[1], 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 本机 IP
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getLocalIp() {
|
||||
List<NetworkIF> nets = getSI().getHardware().getNetworkIFs();
|
||||
if (nets == null || nets.isEmpty()) return "localhost";
|
||||
|
||||
for (NetworkIF net : nets) {
|
||||
String[] ipsv4 = net.getIPv4addr();
|
||||
if (ipsv4 != null && ipsv4.length > 0) return ipsv4[0];
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
@ -67,13 +68,9 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
final RequestEntry requestEntry = new RequestEntry(request, locale);
|
||||
REQUEST_ENTRY.set(requestEntry);
|
||||
|
||||
// 页面变量
|
||||
// 请求页面时如果是非 HTML 请求头可能导致失败,因为页面中需要用到语言包变量
|
||||
if (requestEntry.isHtmlRequest()) {
|
||||
// Lang
|
||||
request.setAttribute(WebConstants.LOCALE, requestEntry.getLocale());
|
||||
request.setAttribute(WebConstants.$BUNDLE, Application.getLanguage().getBundle(requestEntry.getLocale()));
|
||||
}
|
||||
// Lang
|
||||
request.setAttribute(WebConstants.LOCALE, requestEntry.getLocale());
|
||||
request.setAttribute(WebConstants.$BUNDLE, Application.getLanguage().getBundle(requestEntry.getLocale()));
|
||||
|
||||
final String requestUri = requestEntry.getRequestUri();
|
||||
|
||||
|
@ -108,7 +105,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|
||||
// 管理中心
|
||||
if (requestUri.contains("/admin/") && !AppUtils.isAdminVerified(request)) {
|
||||
if (requestEntry.isHtmlRequest()) {
|
||||
if (isHtmlRequest(request)) {
|
||||
sendRedirect(response, "/user/admin-verify", requestUri);
|
||||
} else {
|
||||
ServletUtils.writeJson(response, RespBody.error(HttpStatus.FORBIDDEN.value()).toJSONString());
|
||||
|
@ -119,7 +116,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
UserContextHolder.setUser(requestUser);
|
||||
|
||||
// 页面变量(登录后)
|
||||
if (requestEntry.isHtmlRequest()) {
|
||||
if (isHtmlRequest(request)) {
|
||||
// Last active
|
||||
Application.getSessionStore().storeLastActive(request);
|
||||
|
||||
|
@ -148,7 +145,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|
||||
log.warn("Unauthorized access {}", RebuildWebConfigurer.getRequestUrls(request));
|
||||
|
||||
if (requestEntry.isHtmlRequest()) {
|
||||
if (isHtmlRequest(request)) {
|
||||
sendRedirect(response, "/user/login", requestUri);
|
||||
} else {
|
||||
ServletUtils.writeJson(response, RespBody.error(HttpStatus.UNAUTHORIZED.value()).toJSONString());
|
||||
|
@ -228,19 +225,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
}
|
||||
|
||||
/**
|
||||
* @param response
|
||||
* @param url
|
||||
* @param nexturl
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendRedirect(HttpServletResponse response, String url, String nexturl) throws IOException {
|
||||
String fullUrl = AppUtils.getContextPath() + url;
|
||||
if (nexturl != null) fullUrl += "?nexturl=" + CodecUtils.urlEncode(nexturl);
|
||||
response.sendRedirect(fullUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略认证
|
||||
* 忽略认证的资源
|
||||
*
|
||||
* @param requestUri
|
||||
* @return
|
||||
|
@ -270,27 +255,54 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|| requestUri.startsWith("/rbmob/env");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否 HTML 请求
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private boolean isHtmlRequest(HttpServletRequest request) {
|
||||
if (ServletUtils.isAjaxRequest(request) || request.getRequestURI().contains("/assets/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
MimeType mimeType = MimeTypeUtils.parseMimeType(
|
||||
StringUtils.defaultIfBlank(request.getHeader("Accept"), MimeTypeUtils.TEXT_HTML_VALUE).split("[,;]")[0]);
|
||||
return MimeTypeUtils.TEXT_HTML.equals(mimeType);
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param response
|
||||
* @param url
|
||||
* @param nexturl
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendRedirect(HttpServletResponse response, String url, String nexturl) throws IOException {
|
||||
String fullUrl = AppUtils.getContextPath(url);
|
||||
if (nexturl != null) fullUrl += "?nexturl=" + CodecUtils.urlEncode(nexturl);
|
||||
response.sendRedirect(fullUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数封装
|
||||
*/
|
||||
@Data
|
||||
private static class RequestEntry {
|
||||
final long requestTime;
|
||||
final ID requestUser;
|
||||
final String requestUri;
|
||||
final boolean htmlRequest;
|
||||
final ID requestUser;
|
||||
final String locale;
|
||||
|
||||
RequestEntry(HttpServletRequest request, String locale) {
|
||||
this.requestTime = System.currentTimeMillis();
|
||||
|
||||
this.requestUri = request.getRequestURI()
|
||||
+ (request.getQueryString() != null ? ("?" + request.getQueryString()) : "");
|
||||
this.htmlRequest = !ServletUtils.isAjaxRequest(request)
|
||||
&& MimeTypeUtils.TEXT_HTML.equals(AppUtils.parseMimeType(request));
|
||||
|
||||
this.locale = locale;
|
||||
this.requestUser = AppUtils.getRequestUser(request, true);
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -272,7 +272,7 @@ public class ConfigurationController extends BaseController {
|
|||
ConfigurationItem item = ConfigurationItem.valueOf(e.getKey());
|
||||
RebuildConfiguration.set(item, e.getValue());
|
||||
} catch (Exception ex) {
|
||||
log.error("Invalid item : " + e.getKey() + " = " + e.getValue());
|
||||
log.error("Invalid item : {} = {}", e.getKey(), e.getValue(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author zhaofang123@gmail.com
|
||||
|
@ -124,7 +121,13 @@ public class MetaFieldController extends BaseController {
|
|||
}
|
||||
|
||||
// 扩展配置
|
||||
mv.getModel().put("fieldExtConfig", easyField.getExtraAttrs(true));
|
||||
JSONObject extraAttrs = new JSONObject();
|
||||
for (Map.Entry<String, Object> e : easyField.getExtraAttrs(true).entrySet()) {
|
||||
String name = e.getKey();
|
||||
// 排除私有
|
||||
if (!name.startsWith("_")) extraAttrs.put(name, e.getValue());
|
||||
}
|
||||
mv.getModel().put("fieldExtConfig", extraAttrs);
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
@ -190,4 +193,32 @@ public class MetaFieldController extends BaseController {
|
|||
boolean drop = new Field2Schema(user).dropField(field, false);
|
||||
return drop ? RespBody.ok() : RespBody.error();
|
||||
}
|
||||
|
||||
@RequestMapping("field-cascading-fields")
|
||||
public RespBody fieldCascadingFields(@EntityParam Entity currentEntity, HttpServletRequest request) {
|
||||
Field refField = currentEntity.getField(getParameterNotNull(request, "field"));
|
||||
Entity referenceEntity = refField.getReferenceEntity();
|
||||
|
||||
// 找到共同的引用字段
|
||||
Field[] currentEntityFields = MetadataSorter.sortFields(currentEntity, DisplayType.REFERENCE);
|
||||
Field[] referenceEntityFields = MetadataSorter.sortFields(referenceEntity, DisplayType.REFERENCE);
|
||||
|
||||
List<JSONObject> together = new ArrayList<>();
|
||||
for (Field foo : currentEntityFields) {
|
||||
if (MetadataHelper.isCommonsField(foo)) continue;
|
||||
|
||||
Entity fooEntity = foo.getReferenceEntity();
|
||||
for (Field bar : referenceEntityFields) {
|
||||
if (fooEntity.equals(bar.getReferenceEntity())) {
|
||||
// 当前实体字段$$$$引用实体字段
|
||||
String name = foo.getName() + MetadataHelper.SPLITER + bar.getName();
|
||||
String label = String.format("%s (%s)", EasyMetaFactory.getLabel(foo), EasyMetaFactory.getLabel(bar));
|
||||
together.add(JSONUtils.toJSONObject(
|
||||
new String[] { "name", "label" }, new String[] { name, label } ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RespBody.ok(together);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.ServerStatus;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.OshiUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
@ -57,8 +58,8 @@ public class ErrorPageView extends BaseController {
|
|||
ModelAndView mv = createModelAndView("/error/server-status");
|
||||
mv.getModel().put("ok", ServerStatus.isStatusOK() && Application.isReady());
|
||||
mv.getModel().put("status", ServerStatus.getLastStatus(realtime));
|
||||
mv.getModel().put("MemoryUsage", ServerStatus.getJvmMemoryUsed());
|
||||
mv.getModel().put("SystemLoad", ServerStatus.getSystemLoad());
|
||||
mv.getModel().put("MemoryUsage", OshiUtils.getOsMemoryUsed());
|
||||
mv.getModel().put("SystemLoad", OshiUtils.getSystemLoad());
|
||||
mv.getModelMap().put("isAdminVerified", AppUtils.isAdminVerified(request));
|
||||
return mv;
|
||||
}
|
||||
|
@ -76,8 +77,8 @@ public class ErrorPageView extends BaseController {
|
|||
for (ServerStatus.Status item : ServerStatus.getLastStatus(realtime)) {
|
||||
status.put(item.name, item.success ? true : item.error);
|
||||
}
|
||||
status.put("MemoryUsage", ServerStatus.getJvmMemoryUsed()[1]);
|
||||
status.put("SystemLoad", ServerStatus.getSystemLoad());
|
||||
status.put("MemoryUsage", OshiUtils.getOsMemoryUsed()[1]);
|
||||
status.put("SystemLoad", OshiUtils.getSystemLoad());
|
||||
|
||||
ServletUtils.writeJson(response, s.toJSONString());
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
// 引用字段数据过滤
|
||||
// 启用数据过滤后最近搜索将不可用
|
||||
String protocolFilter = new ProtocolFilterParser(null).parseRef(field + "." + entity.getName());
|
||||
String protocolFilter = new ProtocolFilterParser(null)
|
||||
.parseRef(field + "." + entity.getName(), request.getParameter("cascadingValue"));
|
||||
|
||||
String q = getParameter(request, "q");
|
||||
// 为空则加载最近使用的
|
||||
|
@ -272,8 +273,12 @@ public class ReferenceSearchController extends EntityController {
|
|||
mv.getModel().put("canCreate",
|
||||
Application.getPrivilegesManager().allowCreate(user, searchEntity.getEntityCode()));
|
||||
|
||||
if (ProtocolFilterParser.getFieldDataFilter(field) != null) {
|
||||
mv.getModel().put("referenceFilter", "ref:" + getParameter(request, "field"));
|
||||
if (ProtocolFilterParser.getFieldDataFilter(field) != null
|
||||
|| ProtocolFilterParser.hasFieldCascadingField(field)) {
|
||||
String protocolExpr = String.format("ref:%s:%s",
|
||||
getParameterNotNull(request, "field"),
|
||||
StringUtils.defaultString(getParameter(request, "cascadingValue"), ""));
|
||||
mv.getModel().put("referenceFilter", protocolExpr);
|
||||
} else {
|
||||
mv.getModel().put("referenceFilter", StringUtils.EMPTY);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class ProjectAdminController extends BaseController {
|
|||
}
|
||||
|
||||
Object[] p = Application.createQuery(
|
||||
"select projectName,scope,principal,members from ProjectConfig where configId = ?")
|
||||
"select projectName,scope,principal,members,extraDefinition from ProjectConfig where configId = ?")
|
||||
.setParameter(1, projectId2)
|
||||
.unique();
|
||||
|
||||
|
@ -59,6 +59,7 @@ public class ProjectAdminController extends BaseController {
|
|||
mv.getModelMap().put("scope", p[1]);
|
||||
mv.getModelMap().put("principal", p[2]);
|
||||
mv.getModelMap().put("members", p[3]);
|
||||
mv.getModelMap().put("extraDefinition", p[4]);
|
||||
return mv;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.web.project;
|
|||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.persist4j.Query;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
|
@ -41,6 +42,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -73,7 +75,7 @@ public class ProjectTaskController extends BaseController {
|
|||
return null;
|
||||
}
|
||||
|
||||
ConfigBean project = ProjectManager.instance.getProjectByTask(taskId2, user);
|
||||
ConfigBean project = ProjectManager.instance.getProjectByX(taskId2, user);
|
||||
|
||||
ModelAndView mv = createModelAndView("/project/task-view");
|
||||
mv.getModel().put("id", taskId2.toLiteral());
|
||||
|
@ -85,7 +87,7 @@ public class ProjectTaskController extends BaseController {
|
|||
@RequestMapping("tasks/list")
|
||||
public JSON taskList(@IdParam(name = "plan") ID planId, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
String queryWhere = "projectPlanId = ?";
|
||||
String queryWhere = "projectPlanId = '" + planId + "'";
|
||||
|
||||
// 关键词搜索
|
||||
String search = getParameter(request, "search");
|
||||
|
@ -108,9 +110,7 @@ public class ProjectTaskController extends BaseController {
|
|||
int count = -1;
|
||||
if (pageNo == 1) {
|
||||
String countSql = "select count(taskId) from ProjectTask where " + queryWhere;
|
||||
Object[] count2 = Application.createQueryNoFilter(countSql)
|
||||
.setParameter(1, planId)
|
||||
.unique();
|
||||
Object[] count2 = Application.createQueryNoFilter(countSql).unique();
|
||||
count = ObjectUtils.toInt(count2[0]);
|
||||
|
||||
if (count == 0) {
|
||||
|
@ -119,19 +119,7 @@ public class ProjectTaskController extends BaseController {
|
|||
}
|
||||
|
||||
queryWhere += " order by " + buildQuerySort(request);
|
||||
String querySql = "select " + BASE_FIELDS + " from ProjectTask where " + queryWhere;
|
||||
|
||||
Object[][] tasks = Application.createQueryNoFilter(querySql)
|
||||
.setParameter(1, planId)
|
||||
.setLimit(pageSize, pageNo * pageSize - pageSize)
|
||||
.array();
|
||||
|
||||
JSONArray alist = new JSONArray();
|
||||
for (Object[] o : tasks) {
|
||||
JSONObject item = formatTask(o, user);
|
||||
item.remove("planName");
|
||||
alist.add(item);
|
||||
}
|
||||
JSONArray alist = queryCardDatas(planId, user, queryWhere, new int[] { pageSize, pageNo * pageSize - pageSize });
|
||||
|
||||
return JSONUtils.toJSONObject(
|
||||
new String[] { "count", "tasks" },
|
||||
|
@ -139,12 +127,72 @@ public class ProjectTaskController extends BaseController {
|
|||
}
|
||||
|
||||
@GetMapping("tasks/get")
|
||||
public JSON taskGet(@IdParam(name = "task") ID taskId) {
|
||||
Object[] task = Application.createQueryNoFilter(
|
||||
"select " + BASE_FIELDS + " from ProjectTask where taskId = ?")
|
||||
.setParameter(1, taskId)
|
||||
.unique();
|
||||
return formatTask(task, null);
|
||||
public JSON taskGet(@IdParam(name = "task") ID taskId, HttpServletRequest request) {
|
||||
String where = "taskId = '" + taskId + "'";
|
||||
JSONArray a = queryCardDatas(taskId, getRequestUser(request), where, null);
|
||||
return (JSON) a.get(0);
|
||||
}
|
||||
|
||||
private JSONArray queryCardDatas(ID taskOrPlan, ID user, String queryWhere, int[] limits) {
|
||||
// 卡片显示字段
|
||||
ConfigBean project = ProjectManager.instance.getProjectByX(taskOrPlan, user);
|
||||
JSON cardFields = project.getJSON("cardFields");
|
||||
|
||||
final Set<String> fields2show = new HashSet<>();
|
||||
if (cardFields == null) {
|
||||
fields2show.add("createdOn");
|
||||
fields2show.add("endTime");
|
||||
fields2show.add("_tag");
|
||||
} else {
|
||||
for (Object o : (JSONArray) cardFields) {
|
||||
fields2show.add(o.toString());
|
||||
}
|
||||
}
|
||||
|
||||
String queryFields = FMT_FIELDS11 + ",";
|
||||
if (fields2show.contains("createdBy")) queryFields += "createdBy,";
|
||||
else queryFields += "taskId,";
|
||||
if (fields2show.contains("modifiedOn")) queryFields += "modifiedOn,";
|
||||
else queryFields += "taskId,";
|
||||
if (fields2show.contains("description")) queryFields += "description,";
|
||||
else queryFields += "taskId,";
|
||||
if (fields2show.contains("attachments")) queryFields += "attachments,";
|
||||
else queryFields += "taskId,";
|
||||
|
||||
queryFields = queryFields.substring(0, queryFields.length() - 1);
|
||||
String querySql = String.format("select %s from ProjectTask where %s", queryFields, queryWhere);
|
||||
Query query = Application.createQueryNoFilter(querySql);
|
||||
if (limits != null) query.setLimit(limits[0], limits[1]);
|
||||
|
||||
Object[][] tasks = query.array();
|
||||
|
||||
JSONArray alist = new JSONArray();
|
||||
for (Object[] o : tasks) {
|
||||
JSONObject item = formatTask(o, user, fields2show.contains("_tag"));
|
||||
|
||||
if (fields2show.contains("createdBy")) {
|
||||
item.put("createdBy", new Object[] { o[12], UserHelper.getName((ID) o[12]) });
|
||||
}
|
||||
if (!fields2show.contains("createdOn")) {
|
||||
item.remove("createdOn");
|
||||
}
|
||||
if (fields2show.contains("modifiedOn")) {
|
||||
item.put("modifiedOn", I18nUtils.formatDate((Date) o[13]));
|
||||
}
|
||||
if (!fields2show.contains("endTime")) {
|
||||
item.remove("endTime");
|
||||
}
|
||||
if (fields2show.contains("description")) {
|
||||
item.put("description", StringUtils.isNotBlank((String) o[14]));
|
||||
}
|
||||
if (fields2show.contains("attachments")) {
|
||||
item.put("attachments", o[15] != null && ((String) o[15]).length() > 10);
|
||||
}
|
||||
|
||||
item.remove("planName");
|
||||
alist.add(item);
|
||||
}
|
||||
return alist;
|
||||
}
|
||||
|
||||
@GetMapping("tasks/details")
|
||||
|
@ -152,10 +200,10 @@ public class ProjectTaskController extends BaseController {
|
|||
final ID user = getRequestUser(request);
|
||||
|
||||
Object[] task = Application.createQueryNoFilter(
|
||||
"select " + BASE_FIELDS + ",description,attachments,relatedRecord from ProjectTask where taskId = ?")
|
||||
String.format("select %s,description,attachments,relatedRecord from ProjectTask where taskId = ?", FMT_FIELDS11))
|
||||
.setParameter(1, taskId)
|
||||
.unique();
|
||||
JSONObject details = formatTask(task, user);
|
||||
JSONObject details = formatTask(task, user, true);
|
||||
|
||||
details.put("description", task[12]);
|
||||
String attachments = (String) task[13];
|
||||
|
@ -172,30 +220,33 @@ public class ProjectTaskController extends BaseController {
|
|||
return details;
|
||||
}
|
||||
|
||||
private static final String BASE_FIELDS =
|
||||
private static final String FMT_FIELDS11 =
|
||||
"projectId,projectPlanId,taskNumber,taskId,taskName,createdOn,deadline,executor,status,seq,priority,endTime";
|
||||
|
||||
/**
|
||||
* @param o
|
||||
* @param user
|
||||
* @param putTags
|
||||
* @return
|
||||
* @throws ConfigurationException 如果指定用户无权限
|
||||
* @see #FMT_FIELDS11
|
||||
*/
|
||||
private JSONObject formatTask(Object[] o, ID user) throws ConfigurationException {
|
||||
private JSONObject formatTask(Object[] o, ID user, boolean putTags) throws ConfigurationException {
|
||||
final ConfigBean project = ProjectManager.instance.getProject((ID) o[0], user);
|
||||
|
||||
String taskNumber = String.format("%s-%s", project.getString("projectCode"), o[2]);
|
||||
String createdOn = I18nUtils.formatDate((Date) o[5]);
|
||||
String deadline = I18nUtils.formatDate((Date) o[6]);
|
||||
String endTime = I18nUtils.formatDate((Date) o[11]);
|
||||
|
||||
Object[] executor = o[7] == null ? null : new Object[]{o[7], UserHelper.getName((ID) o[7])};
|
||||
|
||||
JSONObject data = JSONUtils.toJSONObject(
|
||||
new String[] { "id", "taskNumber", "taskName", "createdOn", "deadline", "executor", "status", "seq", "priority", "endTime", "projectId" },
|
||||
new Object[] { o[3], taskNumber, o[4], createdOn, deadline, executor, o[8], o[9], o[10], endTime, o[0] });
|
||||
|
||||
// 标签
|
||||
data.put("tags", TaskTagController.getTaskTags((ID) o[3]));
|
||||
if (putTags) {
|
||||
data.put("tags", TaskTagController.getTaskTags((ID) o[3]));
|
||||
}
|
||||
|
||||
if (user != null) {
|
||||
// 项目信息
|
||||
|
@ -219,7 +270,8 @@ public class ProjectTaskController extends BaseController {
|
|||
return sort;
|
||||
}
|
||||
|
||||
// for View of Entity
|
||||
// -- for General Entity
|
||||
|
||||
@GetMapping("alist")
|
||||
public RespBody getProjectAndPlans(HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
|
@ -269,15 +321,15 @@ public class ProjectTaskController extends BaseController {
|
|||
queryWhere = String.format("taskId = '%s'", taskId);
|
||||
}
|
||||
|
||||
String querySql = "select " + BASE_FIELDS + " from ProjectTask where " + queryWhere;
|
||||
Object[][] tasks = Application.createQueryNoFilter(querySql)
|
||||
Object[][] tasks = Application.createQueryNoFilter(
|
||||
String.format("select %s from ProjectTask where %s", FMT_FIELDS11, queryWhere))
|
||||
.setLimit(pageSize, pageNo * pageSize - pageSize)
|
||||
.array();
|
||||
|
||||
JSONArray array = new JSONArray();
|
||||
for (Object[] o : tasks) {
|
||||
try {
|
||||
array.add(formatTask(o, user));
|
||||
array.add(formatTask(o, user, false));
|
||||
} catch (ConfigurationException ex) {
|
||||
// FIXME 无项目权限会报错(考虑任务在相关项中是否无权限也显示)
|
||||
log.warn(ex.getLocalizedMessage());
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>X.509 Certificate</td>
|
||||
<td data-id="SamlIdPCert" th:data-value="${SamlIdPCert ?:''}">[[${SamlIdPCert ?:bundle.L('未设置')}]]</td>
|
||||
<td data-id="SamlIdPCert" th:data-value="${SamlIdPCert}"><pre class="unstyle">[[${SamlIdPCert ?:bundle.L('未设置')}]]</pre></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<th:block th:replace="~{/_include/header}" />
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/assets/lib/widget/bootstrap-slider.min.css}" />
|
||||
<meta name="page-help" content="https://getrebuild.com/docs/admin/meta-entity#%E5%BC%95%E7%94%A8%E5%AD%97%E6%AE%B5%E7%9A%84%E6%9B%B4%E5%A4%9A%E8%AF%B4%E6%98%8E" />
|
||||
<meta name="page-help" content="https://getrebuild.com/docs/admin/meta-entity#%E5%BC%95%E7%94%A8%E5%AD%97%E6%AE%B5" />
|
||||
<title>[[${bundle.L('表单回填')}]]</title>
|
||||
<style type="text/css">
|
||||
.dataTables_wrapper .rb-datatable-header {
|
||||
|
|
|
@ -84,6 +84,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('父级级联字段')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<select class="form-control form-control-sm" id="referenceCascadingField">
|
||||
<option value="" selected>[[${bundle.L('无')}]]</option>
|
||||
</select>
|
||||
<p class="form-text">[[${bundle.L('选择的字段将与本字段产生级联关系')}]]</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row pt-0 pb-2 J_for-REFERENCE-filter">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('附加过滤条件')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
|
|
|
@ -31,6 +31,13 @@
|
|||
#members {
|
||||
min-height: 37px;
|
||||
}
|
||||
#cardFields {
|
||||
margin-top: 6px;
|
||||
}
|
||||
#cardFields > label {
|
||||
min-width: 130px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -84,6 +91,41 @@
|
|||
<button class="btn btn-secondary btn-sm J_add-plan" type="button"><i class="zmdi zmdi-plus"></i> [[${bundle.L('添加')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-lg-3 col-form-label text-lg-right">[[${bundle.L('任务卡显示字段')}]]</label>
|
||||
<div class="col-12 col-lg-9">
|
||||
<div id="cardFields">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="createdBy" />
|
||||
<span class="custom-control-label">[[${bundle.L('创建人')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="createdOn" checked />
|
||||
<span class="custom-control-label">[[${bundle.L('创建时间')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="modifiedOn" />
|
||||
<span class="custom-control-label">[[${bundle.L('更新时间')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="endTime" checked />
|
||||
<span class="custom-control-label">[[${bundle.L('完成时间')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="description" />
|
||||
<span class="custom-control-label">[[${bundle.L('详情')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="attachments" />
|
||||
<span class="custom-control-label">[[${bundle.L('附件')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" value="_tag" checked />
|
||||
<span class="custom-control-label">[[${bundle.L('标签')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row footer">
|
||||
<label class="col-12 col-lg-3 col-form-label text-lg-right"> </label>
|
||||
<div class="col-12 col-lg-9">
|
||||
|
@ -103,6 +145,7 @@
|
|||
scope: ~~'[[${scope}]]',
|
||||
principal: '[[${principal}]]',
|
||||
members: '[[${members}]]',
|
||||
extraDefinition: [(${extraDefinition ?:'null'})],
|
||||
}
|
||||
</script>
|
||||
<script th:src="@{/assets/js/project/project-editor.js}" type="text/babel"></script>
|
||||
|
|
|
@ -5,23 +5,29 @@
|
|||
<meta name="page-help" content="https://getrebuild.com/docs/admin/projects" />
|
||||
<title>[[${bundle.L('项目管理')}]]</title>
|
||||
<style type="text/css">
|
||||
.project-icon {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background-color: #e3e3e3;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.project-icon .icon,
|
||||
.project-icon > .icon,
|
||||
.card-body > .icon {
|
||||
font-size: 26px;
|
||||
color: #555;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.project-icon > .icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
background-color: #e3e3e3;
|
||||
}
|
||||
.project-icon:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.card-body > .icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
margin-right: -9px;
|
||||
}
|
||||
.card-body {
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
[[${bundle.L('页脚')}]]
|
||||
<p class="link">[(${bundle.L('仅适用于外部页面,支持 [MD 语法](https://getrebuild.com/docs/markdown-guide)')})]</p>
|
||||
</td>
|
||||
<td data-id="PageFooter" th:data-value="${PageFooter}" data-optional="true">[(${PageFooter ?:bundle.L('无')})]</td>
|
||||
<td data-id="PageFooter" th:data-value="${PageFooter}" data-optional="true"><pre class="unstyle">[[${PageFooter ?:bundle.L('无')}]]</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('默认语言')}]]</td>
|
||||
|
|
|
@ -184,11 +184,23 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.task-content .task-extras > .avatar,
|
||||
.task-content .task-extras > .icon {
|
||||
float: left;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.task-content .task-extras > .avatar img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.task-content .task-extras > .icon {
|
||||
font-size: 1.3rem;
|
||||
color: #999;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.task-content .task-extras > .badge {
|
||||
background-color: #f7f7f7;
|
||||
color: #595959;
|
||||
|
|
|
@ -2670,9 +2670,8 @@ form {
|
|||
}
|
||||
|
||||
.card-list .card-body > a {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
padding-top: 3px;
|
||||
display: block;
|
||||
padding: 3px 0 5px;
|
||||
}
|
||||
|
||||
.card-list .card-body > p {
|
||||
|
@ -4348,4 +4347,12 @@ html.external-auth .auth-body.must-center .login {
|
|||
content: '\f26b' !important;
|
||||
color: #34a853;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
pre.unstyle {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
}
|
|
@ -29,7 +29,7 @@ $(document).ready(() => {
|
|||
const __data = {}
|
||||
const changeValue = function (e) {
|
||||
const name = e.target.name
|
||||
__data[name] = e.target.value
|
||||
__data[name] = (e.target.value || '').trim()
|
||||
}
|
||||
|
||||
// 激活编辑模式
|
||||
|
|
|
@ -343,6 +343,15 @@ const _handleClassification = function (useClassification) {
|
|||
const _handleReference = function (isN2N) {
|
||||
const referenceEntity = $('.J_referenceEntity').data('refentity')
|
||||
|
||||
// 父级字段
|
||||
$.get(`/admin/entity/field-cascading-fields?entity=${wpc.entityName}&field=${wpc.fieldName}`, (res) => {
|
||||
res.data &&
|
||||
res.data.forEach((item) => {
|
||||
$(`<option value="${item.name}">${item.label}</option>`).appendTo('#referenceCascadingField')
|
||||
})
|
||||
wpc.extConfig.referenceCascadingField && $('#referenceCascadingField').val(wpc.extConfig.referenceCascadingField)
|
||||
})
|
||||
|
||||
// 数据过滤
|
||||
let dataFilter = (wpc.extConfig || {}).referenceDataFilter
|
||||
const saveFilter = function (res) {
|
||||
|
|
|
@ -24,9 +24,19 @@ $(document).ready(() => {
|
|||
renderRbcomp(<PlanList />, 'plans', function () {
|
||||
_PlanList = this
|
||||
})
|
||||
$('.J_add-plan').click(() => renderRbcomp(<PlanEdit projectId={wpc.id} flowNexts={_PlanList.getPlans()} seq={_PlanList.getMaxSeq() + 1000} />))
|
||||
|
||||
const $btn = $('.J_save').click(() => {
|
||||
$('.J_add-plan').on('click', () => {
|
||||
renderRbcomp(<PlanEdit projectId={wpc.id} flowNexts={_PlanList.getPlans()} seq={_PlanList.getMaxSeq() + 1000} />)
|
||||
})
|
||||
|
||||
if (wpc.extraDefinition && wpc.extraDefinition.cardFields) {
|
||||
$('#cardFields input').each(function () {
|
||||
const $chk = $(this)
|
||||
$chk.attr('checked', wpc.extraDefinition.cardFields.includes($chk.val()))
|
||||
})
|
||||
}
|
||||
|
||||
const $btn = $('.J_save').on('click', () => {
|
||||
const data = {
|
||||
scope: $('#scope_2').prop('checked') ? 2 : 1,
|
||||
principal: _Principal.val().join(','),
|
||||
|
@ -35,6 +45,14 @@ $(document).ready(() => {
|
|||
}
|
||||
if (!data.members) return RbHighbar.create($L('请选择成员'))
|
||||
|
||||
const fs = []
|
||||
$('#cardFields input:checked').each(function () {
|
||||
fs.push($(this).val())
|
||||
})
|
||||
const extra = wpc.extraDefinition || {}
|
||||
extra.cardFields = fs
|
||||
data.extraDefinition = extra
|
||||
|
||||
$btn.button('loading')
|
||||
$.post('/admin/projects/post', JSON.stringify(data), (res) => {
|
||||
if (res.error_code === 0) location.href = '../projects'
|
||||
|
@ -62,10 +80,10 @@ class PlanList extends React.Component {
|
|||
</div>
|
||||
<div className="card-footer card-footer-contrast">
|
||||
<a onClick={() => this._handleEdit(item)}>
|
||||
<i className="zmdi zmdi-edit"></i>
|
||||
<i className="zmdi zmdi-edit" />
|
||||
</a>
|
||||
<a onClick={() => this._handleDelete(item[0])} className="danger danger-hover">
|
||||
<i className="zmdi zmdi-delete"></i>
|
||||
<i className="zmdi zmdi-delete" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -162,55 +180,26 @@ class PlanEdit extends RbFormHandler {
|
|||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('面板名称')}</label>
|
||||
<div className="col-sm-7">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
name="planName"
|
||||
value={this.state.planName || ''}
|
||||
onChange={this.handleChange}
|
||||
maxLength="60"
|
||||
autoFocus
|
||||
/>
|
||||
<input type="text" className="form-control form-control-sm" name="planName" value={this.state.planName || ''} onChange={this.handleChange} maxLength="60" autoFocus />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('工作流状态')}</label>
|
||||
<div className="col-sm-7">
|
||||
<label className="custom-control custom-control-sm custom-radio mb-1 mt-1">
|
||||
<input
|
||||
className="custom-control-input"
|
||||
type="radio"
|
||||
name="flowStatus"
|
||||
value="1"
|
||||
checked={~~this.state.flowStatus === 1}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<input className="custom-control-input" type="radio" name="flowStatus" value="1" checked={~~this.state.flowStatus === 1} onChange={this.handleChange} />
|
||||
<span className="custom-control-label">
|
||||
{$L('开始状态')} <p className="text-muted mb-0 fs-12">{$L('该状态下可新建任务')}</p>
|
||||
</span>
|
||||
</label>
|
||||
<label className="custom-control custom-control-sm custom-radio mb-1">
|
||||
<input
|
||||
className="custom-control-input"
|
||||
type="radio"
|
||||
name="flowStatus"
|
||||
value="2"
|
||||
checked={~~this.state.flowStatus === 2}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<input className="custom-control-input" type="radio" name="flowStatus" value="2" checked={~~this.state.flowStatus === 2} onChange={this.handleChange} />
|
||||
<span className="custom-control-label">
|
||||
{$L('进行中')} <p className="text-muted mb-0 fs-12">{$L('该状态下不可新建任务,不可完成任务')}</p>
|
||||
</span>
|
||||
</label>
|
||||
<label className="custom-control custom-control-sm custom-radio mb-1">
|
||||
<input
|
||||
className="custom-control-input"
|
||||
type="radio"
|
||||
name="flowStatus"
|
||||
value="3"
|
||||
checked={~~this.state.flowStatus === 3}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<input className="custom-control-input" type="radio" name="flowStatus" value="3" checked={~~this.state.flowStatus === 3} onChange={this.handleChange} />
|
||||
<span className="custom-control-label">
|
||||
{$L('结束状态')} <p className="text-muted mb-0 fs-12">{$L('该状态下任务自动标记完成')}</p>
|
||||
</span>
|
||||
|
|
|
@ -93,13 +93,7 @@ class DlgEdit extends RbFormHandler {
|
|||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('项目名称')}</label>
|
||||
<div className="col-sm-7">
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
value={this.state.projectName || ''}
|
||||
data-id="projectName"
|
||||
onChange={this.handleChange}
|
||||
maxLength="60"
|
||||
/>
|
||||
<input className="form-control form-control-sm" value={this.state.projectName || ''} data-id="projectName" onChange={this.handleChange} maxLength="60" />
|
||||
</div>
|
||||
</div>
|
||||
{!this.props.id && (
|
||||
|
@ -107,13 +101,7 @@ class DlgEdit extends RbFormHandler {
|
|||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('项目 ID')}</label>
|
||||
<div className="col-sm-7">
|
||||
<input
|
||||
className="form-control form-control-sm "
|
||||
value={this.state.projectCode || ''}
|
||||
data-id="projectCode"
|
||||
onChange={this.handleChange}
|
||||
maxLength="6"
|
||||
/>
|
||||
<input className="form-control form-control-sm " value={this.state.projectCode || ''} data-id="projectCode" onChange={this.handleChange} maxLength="6" />
|
||||
<div className="form-text">{$L('任务编号将以项目 ID 作为前缀,用以区别不同项目。支持 2-6 位字母')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -149,7 +137,7 @@ class DlgEdit extends RbFormHandler {
|
|||
that.setState({ iconName: s })
|
||||
RbModal.hide()
|
||||
}
|
||||
RbModal.create('/p/common/search-icon', $L('选择图标'))
|
||||
RbModal.create('/p/common/search-icon', $L('选择图标'), { zIndex: 1051 })
|
||||
}
|
||||
|
||||
save = () => {
|
||||
|
|
|
@ -289,7 +289,7 @@ class PlanBox extends React.Component {
|
|||
const $boxes = document.getElementById('plan-boxes')
|
||||
$addResizeHandler(() => {
|
||||
let mh = $(window).height() - 210 + (this.creatableTask ? 0 : 44)
|
||||
if ($boxes.scrollWidth > $boxes.clientWidth) mh -= 5 // 横向滚动条高度
|
||||
if ($boxes.scrollWidth > $boxes.clientWidth) mh -= 13 // 横向滚动条高度
|
||||
|
||||
$scroller.css({ 'max-height': mh })
|
||||
$scroller.perfectScrollbar('update')
|
||||
|
@ -395,13 +395,14 @@ class PlanBox extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
// 任务
|
||||
// 任务卡
|
||||
class Task extends React.Component {
|
||||
state = { ...this.props }
|
||||
|
||||
render() {
|
||||
let deadlineState = -1
|
||||
if (!this.state.endTime && this.state.deadline) {
|
||||
// 未完成且设置了到期时间
|
||||
if (this.state.status !== 1 && this.state.deadline) {
|
||||
if ($expired(this.state.deadline)) deadlineState = 2
|
||||
else if ($expired(this.state.deadline, -60 * 60 * 24)) deadlineState = 1
|
||||
else deadlineState = 0
|
||||
|
@ -424,21 +425,29 @@ class Task extends React.Component {
|
|||
defaultChecked={this.state.status === 1}
|
||||
onChange={(e) => this._toggleStatus(e)}
|
||||
disabled={!this.props.$$$parent.performableTask}
|
||||
ref={(c) => (this._status = c)}
|
||||
ref={(c) => (this._$status = c)}
|
||||
/>
|
||||
<span className="custom-control-label" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="task-content">
|
||||
<div className="task-title text-wrap">{this.state.taskName}</div>
|
||||
|
||||
{this.state.endTime && (
|
||||
<div className="task-time">
|
||||
{$L('完成时间')} <DateShow date={this.state.endTime} />
|
||||
</div>
|
||||
)}
|
||||
<div className="task-time">
|
||||
{$L('创建时间')} <DateShow date={this.state.createdOn} />
|
||||
</div>
|
||||
{this.state.modifiedOn && (
|
||||
<div className="task-time">
|
||||
{$L('更新时间')} <DateShow date={this.state.modifiedOn} />
|
||||
</div>
|
||||
)}
|
||||
{this.state.createdOn && (
|
||||
<div className="task-time">
|
||||
{$L('创建时间')} <DateShow date={this.state.createdOn} />
|
||||
</div>
|
||||
)}
|
||||
{deadlineState > -1 && (
|
||||
<div className="task-time">
|
||||
<span className={`badge badge-${deadlineState === 2 ? 'danger' : deadlineState === 1 ? 'warning' : 'primary'}`}>
|
||||
|
@ -446,6 +455,7 @@ class Task extends React.Component {
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(this.state.tags || []).length > 0 && (
|
||||
<div className="task-tags">
|
||||
{this.state.tags.map((item) => {
|
||||
|
@ -459,12 +469,22 @@ class Task extends React.Component {
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="task-extras">
|
||||
{this.state.createdBy && (
|
||||
<a className="avatar mr-1" title={`${$L('创建人')} ${this.state.createdBy[1]}`}>
|
||||
<img src={`${rb.baseUrl}/account/user-avatar/${this.state.createdBy[0]}`} alt="Avatar" />
|
||||
</a>
|
||||
)}
|
||||
{this.state.executor && (
|
||||
<a className="avatar float-left" title={`${$L('执行人')} ${this.state.executor[1]}`}>
|
||||
<a className="avatar mr-1" title={`${$L('执行人')} ${this.state.executor[1]}`}>
|
||||
<img src={`${rb.baseUrl}/account/user-avatar/${this.state.executor[0]}`} alt="Avatar" />
|
||||
</a>
|
||||
)}
|
||||
|
||||
{this.state.description && <i className="ml-2 icon zmdi zmdi-comment-more" title={$L('详情')} />}
|
||||
{this.state.attachments && <i className="ml-3 icon zmdi zmdi-attachment-alt zmdi-hc-rotate-45" title={$L('附件')} />}
|
||||
|
||||
<span className="badge float-right">{this.state.taskNumber}</span>
|
||||
<div className="clearfix" />
|
||||
</div>
|
||||
|
@ -481,7 +501,7 @@ class Task extends React.Component {
|
|||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState && prevState.status !== this.state.status) {
|
||||
$(this._status).prop('checked', this.state.status === 1)
|
||||
$(this._$status).prop('checked', this.state.status === 1)
|
||||
}
|
||||
// __TaskRefs[this.props.id] = this
|
||||
}
|
||||
|
|
|
@ -15,9 +15,13 @@ class RbModal extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const inFrame = !this.props.children
|
||||
const styles = {}
|
||||
if (this.props.zIndex) styles.zIndex = this.props.zIndex
|
||||
|
||||
const iframe = !this.props.children // No child
|
||||
|
||||
return (
|
||||
<div className={`modal rbmodal colored-header colored-header-${this.props.colored || 'primary'}`} ref={(c) => (this._rbmodal = c)}>
|
||||
<div className={`modal rbmodal colored-header colored-header-${this.props.colored || 'primary'}`} style={styles} ref={(c) => (this._rbmodal = c)}>
|
||||
<div className="modal-dialog" style={{ maxWidth: `${this.props.width || 680}px` }}>
|
||||
<div className="modal-content">
|
||||
<div className="modal-header modal-header-colored">
|
||||
|
@ -26,9 +30,9 @@ class RbModal extends React.Component {
|
|||
<span className="zmdi zmdi-close" />
|
||||
</button>
|
||||
</div>
|
||||
<div className={`modal-body ${inFrame ? 'iframe rb-loading' : ''} ${inFrame && this.state.frameLoad !== false ? 'rb-loading-active' : ''}`}>
|
||||
<div className={`modal-body ${iframe ? 'iframe rb-loading' : ''} ${iframe && this.state.frameLoad !== false ? 'rb-loading-active' : ''}`}>
|
||||
{this.props.children || <iframe src={this.props.url} frameBorder="0" scrolling="no" onLoad={() => this.resize()} />}
|
||||
{inFrame && <RbSpinner />}
|
||||
{iframe && <RbSpinner />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -100,7 +104,7 @@ class RbModal extends React.Component {
|
|||
that.__HOLDER.show()
|
||||
that.__HOLDER.resize()
|
||||
} else {
|
||||
renderRbcomp(<RbModal url={url} title={title} width={options.width} disposeOnHide={options.disposeOnHide} />, null, function () {
|
||||
renderRbcomp(<RbModal url={url} title={title} width={options.width} disposeOnHide={options.disposeOnHide} zIndex={options.zIndex} />, null, function () {
|
||||
that.__HOLDER = this
|
||||
if (options.disposeOnHide === false) that.__HOLDERs[url] = this
|
||||
})
|
||||
|
|
|
@ -224,15 +224,10 @@ class DeleteConfirm extends RbAlert {
|
|||
{!this.props.entity ? null : (
|
||||
<div className="mt-2">
|
||||
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mb-2">
|
||||
<input
|
||||
className="custom-control-input"
|
||||
type="checkbox"
|
||||
checked={this.state.enableCascade === true}
|
||||
onChange={() => this.enableCascade()}
|
||||
/>
|
||||
<input className="custom-control-input" type="checkbox" checked={this.state.enableCascade === true} onChange={() => this.enableCascade()} />
|
||||
<span className="custom-control-label"> {$L('同时删除关联记录')}</span>
|
||||
</label>
|
||||
<div className={' ' + (this.state.enableCascade ? '' : 'hide')}>
|
||||
<div className={this.state.enableCascade ? '' : 'hide'}>
|
||||
<select className="form-control form-control-sm" ref={(c) => (this._cascades = c)} multiple>
|
||||
{(this.state.cascadesEntity || []).map((item) => {
|
||||
return (
|
||||
|
|
|
@ -6,6 +6,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
*/
|
||||
/* global SimpleMDE */
|
||||
|
||||
const TYPE_DIVIDER = '$DIVIDER$'
|
||||
|
||||
// ~~ 表单窗口
|
||||
class RbFormModal extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -205,7 +207,7 @@ class RbForm extends React.Component {
|
|||
<div className="rbform">
|
||||
<div className="form" ref={(c) => (this._form = c)}>
|
||||
{this.props.children.map((fieldComp) => {
|
||||
const refid = `fieldcomp-${fieldComp.props.field}`
|
||||
const refid = fieldComp.props.field === TYPE_DIVIDER ? null : `fieldcomp-${fieldComp.props.field}`
|
||||
return React.cloneElement(fieldComp, { $$$parent: this, ref: refid })
|
||||
})}
|
||||
{this.renderFormAction()}
|
||||
|
@ -278,13 +280,12 @@ class RbForm extends React.Component {
|
|||
// 表单回填
|
||||
setAutoFillin(data) {
|
||||
if (!data || data.length === 0) return
|
||||
const that = this
|
||||
data.forEach((item) => {
|
||||
// eslint-disable-next-line react/no-string-refs
|
||||
const fieldComp = that.refs[`fieldcomp-${item.target}`]
|
||||
const fieldComp = this.refs[`fieldcomp-${item.target}`]
|
||||
if (fieldComp) {
|
||||
if (!item.fillinForce && fieldComp.getValue()) return
|
||||
if ((that.isNew && item.whenCreate) || (!that.isNew && item.whenUpdate)) fieldComp.setValue(item.value)
|
||||
if ((this.isNew && item.whenCreate) || (!this.isNew && item.whenUpdate)) fieldComp.setValue(item.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1198,6 +1199,7 @@ class RbFormReference extends RbFormElement {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
this._hasDataFilter = props.referenceDataFilter && (props.referenceDataFilter.items || []).length > 0
|
||||
this._hasCascadingField = !!(props._cascadingFieldParent || props._cascadingFieldChild)
|
||||
}
|
||||
|
||||
renderElement() {
|
||||
|
@ -1255,20 +1257,20 @@ class RbFormReference extends RbFormElement {
|
|||
label: this.props.label,
|
||||
entity: this.props.$$$parent.props.entity,
|
||||
// appendClass: this._hasDataFilter ? 'data-filter-tip' : null,
|
||||
wrapQuery: (query) => {
|
||||
const cascadingValue = this._getCascadingFieldValue()
|
||||
return cascadingValue ? { cascadingValue, ...query } : query
|
||||
},
|
||||
})
|
||||
|
||||
const val = this.state.value
|
||||
if (val) {
|
||||
this.setValue(val)
|
||||
// const o = new Option(val.text, val.id, true, true)
|
||||
// this.__select2.append(o).trigger('change')
|
||||
}
|
||||
if (val) this.setValue(val)
|
||||
|
||||
const that = this
|
||||
this.__select2.on('change', function (e) {
|
||||
const v = $(e.target).val()
|
||||
if (v && typeof v === 'string') {
|
||||
$.post(`/commons/search/recently-add?id=${v}`)
|
||||
__addRecentlyUse(v)
|
||||
that.triggerAutoFillin(v)
|
||||
}
|
||||
that.handleChange({ target: { value: v } }, true)
|
||||
|
@ -1278,12 +1280,31 @@ class RbFormReference extends RbFormElement {
|
|||
}
|
||||
}
|
||||
|
||||
_getCascadingFieldValue() {
|
||||
let cascadingField
|
||||
if (this.props._cascadingFieldParent) {
|
||||
cascadingField = this.props._cascadingFieldParent.split('$$$$')[0]
|
||||
} else if (this.props._cascadingFieldChild) {
|
||||
cascadingField = this.props._cascadingFieldChild.split('$$$$')[0]
|
||||
}
|
||||
if (!cascadingField) return null
|
||||
|
||||
if (this.props.onView) {
|
||||
const v = (this.props.$$$parent.__ViewData || {})[cascadingField]
|
||||
return v ? v.id : null
|
||||
} else {
|
||||
const fieldComp = this.props.$$$parent.refs[`fieldcomp-${cascadingField}`]
|
||||
return fieldComp ? fieldComp.getValue() : null
|
||||
}
|
||||
}
|
||||
|
||||
// 字段回填
|
||||
triggerAutoFillin(value) {
|
||||
if (this.props.onView) return
|
||||
|
||||
const $$$parent = this.props.$$$parent
|
||||
$.get(`/app/entity/extras/fillin-value?entity=${$$$parent.props.entity}&field=${this.props.field}&source=${value}`, (res) => {
|
||||
const url = `/app/entity/extras/fillin-value?entity=${$$$parent.props.entity}&field=${this.props.field}&source=${value}`
|
||||
$.get(url, (res) => {
|
||||
res.error_code === 0 && res.data.length > 0 && $$$parent.setAutoFillin(res.data)
|
||||
})
|
||||
}
|
||||
|
@ -1296,8 +1317,7 @@ class RbFormReference extends RbFormElement {
|
|||
setValue(val) {
|
||||
if (val) {
|
||||
const o = new Option(val.text, val.id, true, true)
|
||||
this.__select2.append(o)
|
||||
this.handleChange({ target: { value: val.id } }, true)
|
||||
this.__select2.append(o).trigger('change')
|
||||
} else {
|
||||
this.__select2.val(null).trigger('change')
|
||||
}
|
||||
|
@ -1307,27 +1327,27 @@ class RbFormReference extends RbFormElement {
|
|||
const that = this
|
||||
window.referenceSearch__call = function (selected) {
|
||||
that.showSearcher_call(selected, that)
|
||||
that.__searcher.hide()
|
||||
that._ReferenceSearcher.hide()
|
||||
}
|
||||
|
||||
if (this.__searcher) {
|
||||
this.__searcher.show()
|
||||
if (this._ReferenceSearcher && !this._hasCascadingField) {
|
||||
this._ReferenceSearcher.show()
|
||||
} else {
|
||||
const searchUrl = `${rb.baseUrl}/commons/search/reference-search?field=${this.props.field}.${this.props.$$$parent.props.entity}`
|
||||
const url = `${rb.baseUrl}/commons/search/reference-search?field=${this.props.field}.${this.props.$$$parent.props.entity}&cascadingValue=${this._getCascadingFieldValue() || ''}`
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
renderRbcomp(<ReferenceSearcher url={searchUrl} title={$L('选择%s', this.props.label)} />, function () {
|
||||
that.__searcher = this
|
||||
renderRbcomp(<ReferenceSearcher url={url} title={$L('选择%s', this.props.label)} disposeOnHide={this._hasCascadingField} />, function () {
|
||||
that._ReferenceSearcher = this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
showSearcher_call(selected, that) {
|
||||
const first = selected[0]
|
||||
if ($(that._fieldValue).find(`option[value="${first}"]`).length > 0) {
|
||||
that.__select2.val(first).trigger('change')
|
||||
const s = selected[0]
|
||||
if ($(that._fieldValue).find(`option[value="${s}"]`).length > 0) {
|
||||
that.__select2.val(s).trigger('change')
|
||||
} else {
|
||||
$.get(`/commons/search/read-labels?ids=${first}`, (res) => {
|
||||
const o = new Option(res.data[first], first, true, true)
|
||||
$.get(`/commons/search/read-labels?ids=${s}`, (res) => {
|
||||
const o = new Option(res.data[s], s, true, true)
|
||||
that.__select2.append(o).trigger('change')
|
||||
})
|
||||
}
|
||||
|
@ -1402,20 +1422,15 @@ class RbFormN2NReference extends RbFormReference {
|
|||
}
|
||||
that.setValue(val, true)
|
||||
})
|
||||
this._recentlyAdd(ids)
|
||||
__addRecentlyUse(ids)
|
||||
}
|
||||
|
||||
onEditModeChanged(destroy) {
|
||||
super.onEditModeChanged(destroy)
|
||||
if (!destroy && this.__select2) {
|
||||
this.__select2.on('select2:select', (e) => this._recentlyAdd(e.params.data.id))
|
||||
this.__select2.on('select2:select', (e) => __addRecentlyUse(e.params.data.id))
|
||||
}
|
||||
}
|
||||
|
||||
_recentlyAdd(id) {
|
||||
if (!id) return
|
||||
$.post(`/commons/search/recently-add?id=${id}`)
|
||||
}
|
||||
}
|
||||
|
||||
class RbFormClassification extends RbFormElement {
|
||||
|
@ -1761,8 +1776,6 @@ class RbFormDivider extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const TYPE_DIVIDER = '$DIVIDER$'
|
||||
|
||||
// 确定元素类型
|
||||
var detectElement = function (item) {
|
||||
if (!item.key) item.key = 'field-' + (item.field === TYPE_DIVIDER ? $random() : item.field)
|
||||
|
@ -1831,6 +1844,12 @@ const __findMultiTexts = function (options, maskValue) {
|
|||
return texts
|
||||
}
|
||||
|
||||
// 最近使用
|
||||
const __addRecentlyUse = function (id) {
|
||||
if (!id) return
|
||||
$.post(`/commons/search/recently-add?id=${id}`)
|
||||
}
|
||||
|
||||
// ~ 重复记录查看
|
||||
class RepeatedViewer extends RbModalHandler {
|
||||
constructor(props) {
|
||||
|
|
|
@ -580,18 +580,24 @@ var $unmount = function (container, delay, keepContainer) {
|
|||
/**
|
||||
* 初始化引用字段(搜索)
|
||||
*/
|
||||
var $initReferenceSelect2 = function (el, field) {
|
||||
var $initReferenceSelect2 = function (el, options) {
|
||||
var search_input = null
|
||||
return $(el).select2({
|
||||
placeholder: field.placeholder || $L('选择%s', field.label),
|
||||
placeholder: options.placeholder || $L('选择%s', options.label),
|
||||
minimumInputLength: 0,
|
||||
maximumSelectionLength: $(el).attr('multiple') ? 999 : 2,
|
||||
ajax: {
|
||||
url: '/commons/search/' + (field.searchType || 'reference'),
|
||||
url: '/commons/search/' + (options.searchType || 'reference'),
|
||||
delay: 300,
|
||||
data: function (params) {
|
||||
search_input = params.term
|
||||
return { entity: field.entity, field: field.name, q: params.term }
|
||||
const query = {
|
||||
entity: options.entity,
|
||||
field: options.name,
|
||||
q: params.term,
|
||||
}
|
||||
if (options && typeof options.wrapQuery === 'function') return options.wrapQuery(query)
|
||||
else return query
|
||||
},
|
||||
processResults: function (data) {
|
||||
return { results: data.data }
|
||||
|
@ -614,7 +620,7 @@ var $initReferenceSelect2 = function (el, field) {
|
|||
return $L('清除')
|
||||
},
|
||||
},
|
||||
theme: 'default ' + (field.appendClass || ''),
|
||||
theme: 'default ' + (options.appendClass || ''),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
*/
|
||||
|
||||
const wpc = window.__PageConfig || {}
|
||||
const TYPE_DIVIDER = '$DIVIDER$'
|
||||
|
||||
//~~ 视图
|
||||
class RbViewForm extends React.Component {
|
||||
|
@ -41,11 +42,13 @@ class RbViewForm extends React.Component {
|
|||
hadApproval = null
|
||||
}
|
||||
|
||||
const viewData = {}
|
||||
const VFORM = (
|
||||
<div>
|
||||
{hadApproval && <ApprovalProcessor id={this.props.id} entity={this.props.entity} />}
|
||||
<div className="row">
|
||||
{res.data.elements.map((item) => {
|
||||
if (item.field !== TYPE_DIVIDER) viewData[item.field] = item.value
|
||||
item.$$$parent = this
|
||||
return detectViewElement(item)
|
||||
})}
|
||||
|
@ -58,6 +61,8 @@ class RbViewForm extends React.Component {
|
|||
window.FrontJS.View._trigger('open', [res.data])
|
||||
}
|
||||
})
|
||||
|
||||
this.__ViewData = viewData
|
||||
this.__lastModified = res.data.lastModified || 0
|
||||
})
|
||||
}
|
||||
|
@ -124,11 +129,12 @@ class RbViewForm extends React.Component {
|
|||
[fieldName]: fieldValue.value,
|
||||
}
|
||||
|
||||
const $btns = $(fieldComp._fieldText).find('.edit-oper .btn').button('loading')
|
||||
const $btn = $(fieldComp._fieldText).find('.edit-oper .btn').button('loading')
|
||||
$.post('/app/entity/record-save?single=true', JSON.stringify(data), (res) => {
|
||||
$btns.button('reset')
|
||||
$btn.button('reset')
|
||||
if (res.error_code === 0) {
|
||||
this.setFieldUnchanged(fieldName)
|
||||
this.__ViewData[fieldName] = res.data[fieldName]
|
||||
fieldComp.toggleEditMode(false, res.data[fieldName])
|
||||
// 刷新列表
|
||||
parent && parent.RbListPage && parent.RbListPage.reload()
|
||||
|
@ -147,7 +153,7 @@ const detectViewElement = function (item) {
|
|||
if (!window.detectElement) throw 'detectElement undef'
|
||||
item.onView = true
|
||||
item.editMode = false
|
||||
item.key = `col-${item.field === '$DIVIDER$' ? $random() : item.field}`
|
||||
item.key = `col-${item.field === TYPE_DIVIDER ? $random() : item.field}`
|
||||
return (
|
||||
<div className={`col-12 col-sm-${item.isFull ? 12 : 6}`} key={item.key}>
|
||||
{window.detectElement(item)}
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th>Local IP</th>
|
||||
<td>[[${T(com.rebuild.core.ServerStatus).getLocalIp()}]]</td>
|
||||
<td>[[${T(com.rebuild.utils.OshiUtils).getLocalIp()}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>System Time</th>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
parent && parent.referenceSearch__dlg && parent.referenceSearch__dlg.resize()
|
||||
}
|
||||
$(document).ready(function () {
|
||||
$('.J_select').click(function () {
|
||||
$('.J_select').on('click', () => {
|
||||
const ss = RbListPage._RbList.getSelectedIds()
|
||||
if (ss.length > 0 && parent && parent.referenceSearch__call) parent.referenceSearch__call(ss)
|
||||
})
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package com.rebuild.core;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ServerStatusTest {
|
||||
|
||||
@Test
|
||||
void oshi() {
|
||||
double[] vmMemory = ServerStatus.getJvmMemoryUsed();
|
||||
System.out.println(vmMemory[0] + ", " + vmMemory[1]);
|
||||
|
||||
System.out.println(ServerStatus.getSystemLoad());
|
||||
System.out.println(ServerStatus.getLocalIp());
|
||||
}
|
||||
}
|
|
@ -11,8 +11,8 @@ import cn.devezhao.persist4j.engine.ID;
|
|||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.rebuild.TestSupport;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.ServerStatus;
|
||||
import com.rebuild.core.privileges.bizz.Department;
|
||||
import com.rebuild.utils.OshiUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class UserHelperTest extends TestSupport {
|
|||
public void generateAvatar() throws Exception {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
UserHelper.generateAvatar("你好", true);
|
||||
System.out.println(ServerStatus.getJvmMemoryUsed()[1]);
|
||||
System.out.println(OshiUtils.getOsMemoryUsed()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public class FieldWritebackTest extends TestSupport {
|
|||
triggerConfig.setInt("when", TriggerWhen.CREATE.getMaskValue());
|
||||
triggerConfig.setString("actionType", ActionType.FIELDWRITEBACK.name());
|
||||
// 更新自己,新建时将修改时间改为:createdOn+1天
|
||||
String content = "{targetEntity:'$PRIMARY$.Account999', items:[{targetField:'modifiedOn', updateMode:'FORMULA', sourceField:'dateadd(`{createdOn}`, `1D`)' }]}";
|
||||
String content = "{targetEntity:'$PRIMARY$.Account999', items:[{targetField:'modifiedOn', updateMode:'FORMULA', sourceField:'DATEADD(`{createdOn}`, `1D`)' }]}";
|
||||
triggerConfig.setString("actionContent", content);
|
||||
Application.getBean(RobotTriggerConfigService.class).create(triggerConfig);
|
||||
|
||||
|
|
|
@ -27,6 +27,6 @@ public class ProtocolFilterParserTest extends TestSupport {
|
|||
@Test
|
||||
public void parseRef() {
|
||||
System.out.println(new ProtocolFilterParser(null)
|
||||
.parseRef("REFERENCE.TESTALLFIELDS"));
|
||||
.parseRef("REFERENCE.TESTALLFIELDS", null));
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test;
|
|||
public class BlockListTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
void isBlock() {
|
||||
Assertions.assertTrue(BlockList.isBlock("admin"));
|
||||
Assertions.assertFalse(BlockList.isBlock("imnotadmin"));
|
||||
}
|
||||
|
|
17
src/test/java/com/rebuild/utils/MarkdownUtilsTest.java
Normal file
17
src/test/java/com/rebuild/utils/MarkdownUtilsTest.java
Normal file
|
@ -0,0 +1,17 @@
|
|||
package com.rebuild.utils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class MarkdownUtilsTest {
|
||||
|
||||
@Test
|
||||
void render() {
|
||||
System.out.println(MarkdownUtils.render("> content"));
|
||||
System.out.println(MarkdownUtils.render(
|
||||
"上海锐昉科技有限公司 [沪ICP备20020345号-3](https://beian.miit.gov.cn/)" +
|
||||
"<script>alert(1)</script>" +
|
||||
"<iframe url=\"http://baidu.com\"></iframe>"));
|
||||
}
|
||||
}
|
28
src/test/java/com/rebuild/utils/OshiUtilsTest.java
Normal file
28
src/test/java/com/rebuild/utils/OshiUtilsTest.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package com.rebuild.utils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class OshiUtilsTest {
|
||||
|
||||
@Test
|
||||
void getSI() {
|
||||
System.out.println(OshiUtils.getSI());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOsMemoryUsed() {
|
||||
System.out.println(Arrays.toString(OshiUtils.getOsMemoryUsed()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSystemLoad() {
|
||||
System.out.println(OshiUtils.getSystemLoad());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocalIp() {
|
||||
System.out.println(OshiUtils.getLocalIp());
|
||||
}
|
||||
}
|
|
@ -7,18 +7,13 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.utils;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class Tests {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
System.out.println(SystemUtil.getHostInfo().getAddress());
|
||||
System.out.println(SystemUtil.getRuntimeInfo());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue