mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Merge branch 'master' into develop
This commit is contained in:
commit
f80ac11390
|
@ -40,6 +40,8 @@ module.exports = {
|
|||
React: true,
|
||||
ReactDOM: true,
|
||||
PropTypes: true,
|
||||
RBCOLORS: true,
|
||||
RBEMOJIS: true,
|
||||
rb: true,
|
||||
$setTimeout: true,
|
||||
$random: true,
|
||||
|
|
18
README.md
18
README.md
|
@ -1,15 +1,15 @@
|
|||
[![Codacy](https://api.codacy.com/project/badge/Grade/599a0a3e46f84e6bbc29e8fbe4632860)](https://www.codacy.com/app/getrebuild/rebuild)
|
||||
[![codecov](https://codecov.io/gh/getrebuild/rebuild/branch/master/graph/badge.svg)](https://codecov.io/gh/getrebuild/rebuild)
|
||||
[![Package](https://github.com/getrebuild/rebuild/actions/workflows/maven-publish.yml/badge.svg)](https://github.com/getrebuild/rebuild/actions/workflows/maven-publish.yml)
|
||||
[![Package](https://github.com/getrebuild/rebuild/actions/workflows/maven-publish.yml/badge.svg)](https://github.com/getrebuild?tab=packages&repo_name=rebuild)
|
||||
[![Build Status](https://travis-ci.com/getrebuild/rebuild.svg?branch=master)](https://travis-ci.com/getrebuild/rebuild)
|
||||
[![License GPLv3](https://img.shields.io/github/license/getrebuild/rebuild.svg)](LICENSE)
|
||||
[![License COMMERCIAL](https://img.shields.io/badge/license-COMMERCIAL-orange.svg)](COMMERCIAL)
|
||||
[![License GPLv3](https://img.shields.io/github/license/getrebuild/rebuild.svg)](https://getrebuild.com/legal/service-terms)
|
||||
[![License 商业授权](https://img.shields.io/badge/license-%E5%95%86%E4%B8%9A%E6%8E%88%E6%9D%83-red.svg)](https://getrebuild.com/legal/service-terms)
|
||||
|
||||
## 项目特色
|
||||
|
||||
**_REBUILD 通过创新的业务流程引擎为你快速搭建各类企业管理系统,全图形化配置无需了解技术。_**
|
||||
**_REBUILD 通过创新的业务流程引擎帮助你快速搭建各类企业管理系统,全图形化配置无需了解技术。_**
|
||||
|
||||
REBUILD 侧重于业务需求实现,而非基础技术框架或项目启动模板,通过 REBUILD 可以真正实现零代码快速搭建,无需编程、无需编译代码,甚至无需了解技术。
|
||||
REBUILD 侧重于业务需求实现,而非基础技术框架或项目启动模板,通过 REBUILD 可以真正实现零代码快速搭建!无需编程、无需编译代码,甚至无需了解任何技术。
|
||||
|
||||
更多详情介绍 [https://getrebuild.com/](https://getrebuild.com/)
|
||||
|
||||
|
@ -39,7 +39,7 @@ REBUILD 侧重于业务需求实现,而非基础技术框架或项目启动模
|
|||
|
||||
## 使用
|
||||
|
||||
开始使用 REBUILD 非常简单,不需要搭建复杂的运行环境,零依赖快速部署,超简单!
|
||||
开始使用 REBUILD 非常简单,不需要配置复杂的运行环境,零依赖快速部署,超简单!
|
||||
|
||||
#### 1. 使用已发布版本
|
||||
|
||||
|
@ -80,7 +80,7 @@ REBUILD 对于开发环境的要求非常简单,由于使用 Java 开发,因
|
|||
|
||||
- JDK 1.8+(兼容 OpenJDK)
|
||||
- MySQL 5.6+
|
||||
- Redis 3.2+(非必须,默认使用内建的 Ehcache 缓存)
|
||||
- Redis 3.2+(非必须,默认使用内置的 Ehcache 缓存)
|
||||
- Tomcat 8.0+(非必须,默认使用 SpringBooot 内置 Tomcat)
|
||||
- Apache Maven 3.3+
|
||||
- IDEA 或 Eclipse (for JEE)
|
||||
|
@ -93,6 +93,6 @@ REBUILD 使用 GPL-3.0 开源许可和商业授权双重授权协议,使用将
|
|||
|
||||
REBUILD uses the GPL-3.0 open source license and commercial license dual license agreement. Use will be deemed your voluntary commitment to accept all terms of the [Agreement](https://getrebuild.com/legal/service-terms).
|
||||
|
||||
## 购买商业版
|
||||
## 购买商业授权
|
||||
|
||||
从 2.0 版本开始,REBUILD 将推出商业版增值功能计划。如果 REBUILD 对贵公司业务有帮助,请考虑 [购买商业授权](https://getrebuild.com/#pricing-plans) 支持 REBUILD 可持续发展。除了增值功能以外,还可以得到更专业的技术支持服务。非常感谢!
|
||||
从 2.0 版本开始,REBUILD 将推出 [增值功能](https://getrebuild.com/docs/rbv-features) 计划。如果 REBUILD 对贵公司业务有帮助,请考虑 [购买商业授权](https://getrebuild.com/#pricing-plans) 以支持 REBUILD 可持续发展。除了永久享有全部最新功能以外,还可以得到更专业的技术支持服务。非常感谢!
|
||||
|
|
47
pom.xml
47
pom.xml
|
@ -5,12 +5,12 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.11</version>
|
||||
<version>2.6.13</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
<artifactId>rebuild</artifactId>
|
||||
<version>3.1.0-beta1</version>
|
||||
<version>3.2.0-dev</version>
|
||||
<name>rebuild</name>
|
||||
<description>Building your business-systems freely!</description>
|
||||
<!-- UNCOMMENT USE TOMCAT -->
|
||||
|
@ -295,15 +295,10 @@
|
|||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
<version>2.13.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.12</version>
|
||||
<version>1.2.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
|
@ -317,12 +312,12 @@
|
|||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>4.2.3</version>
|
||||
<version>4.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.11.0</version>
|
||||
<version>7.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
|
@ -332,7 +327,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.2.2</version>
|
||||
<version>6.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
|
@ -372,7 +367,7 @@
|
|||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.1.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>ehcache</artifactId>
|
||||
|
@ -403,12 +398,12 @@
|
|||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>5.3.2</version>
|
||||
<version>5.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>0.4.17</version>
|
||||
<version>0.4.18</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>es.moki.ratelimitj</groupId>
|
||||
|
@ -423,7 +418,7 @@
|
|||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.17.6</version>
|
||||
<version>3.17.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
@ -454,10 +449,10 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>5.8.6</version>
|
||||
<version>5.8.9</version>
|
||||
</dependency>
|
||||
|
||||
<!-- CVEs fix -->
|
||||
<!-- fix: CVEs -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
|
@ -466,8 +461,22 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.21</version>
|
||||
<version>1.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
<version>2.14.0-rc3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.14.0-rc3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.33</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -69,10 +69,8 @@ public class ApiContext {
|
|||
* @return
|
||||
*/
|
||||
public ID getBindUser() {
|
||||
if (bindUser == null) {
|
||||
return UserService.SYSTEM_USER;
|
||||
}
|
||||
return bindUser;
|
||||
if (bindUser == null) return UserService.SYSTEM_USER;
|
||||
else return bindUser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,8 +117,9 @@ public class ApiContext {
|
|||
* @return
|
||||
*/
|
||||
public ID getParameterAsId(String name) {
|
||||
String value = getParameterMap().get(name);
|
||||
return ID.isId(value) ? ID.valueOf(value) : null;
|
||||
String value = getParameterNotBlank(name);
|
||||
if (ID.isId(value)) return ID.valueOf(value);
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADPARAMS, "Parameter [" + name + "] is invalid");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,10 +129,8 @@ public class ApiContext {
|
|||
*/
|
||||
public int getParameterAsInt(String name, int defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
if (NumberUtils.isNumber(value)) {
|
||||
return NumberUtils.toInt(value);
|
||||
}
|
||||
return defaultValue;
|
||||
if (NumberUtils.isNumber(value)) return NumberUtils.toInt(value);
|
||||
else return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,10 +140,8 @@ public class ApiContext {
|
|||
*/
|
||||
public long getParameterAsLong(String name, long defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
if (NumberUtils.isNumber(value)) {
|
||||
return NumberUtils.toLong(value);
|
||||
}
|
||||
return defaultValue;
|
||||
if (NumberUtils.isNumber(value)) return NumberUtils.toLong(value);
|
||||
else return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,9 +151,7 @@ public class ApiContext {
|
|||
*/
|
||||
public boolean getParameterAsBool(String name, boolean defaultValue) {
|
||||
String value = getParameterMap().get(name);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return BooleanUtils.toBoolean(value);
|
||||
if (StringUtils.isBlank(value)) return defaultValue;
|
||||
else return BooleanUtils.toBoolean(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import com.rebuild.core.configuration.RebuildApiManager;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.DataSpecificationException;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.task.TaskExecutors;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.RateLimiters;
|
||||
|
@ -165,6 +167,9 @@ public class ApiGateway extends Controller implements Initialization {
|
|||
|
||||
// 明文签名
|
||||
if (timestamp == null && signType == null) {
|
||||
if (RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced)) {
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADAUTH, "Invalid [timestamp] or [sign_type]");
|
||||
}
|
||||
if (!apiConfig.getString("appSecret").equals(sign)) {
|
||||
throw new ApiInvokeException(ApiInvokeException.ERR_BADAUTH, "Invalid [sign] : " + sign);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ public class AuthTokenManager {
|
|||
*/
|
||||
public static final String TYPE_ONCE_TOKEN = "RBOT";
|
||||
|
||||
private static final int ACCESSTOKEN_EXPIRES = CommonsCache.TS_HOUR * 12;
|
||||
// 3d
|
||||
private static final int ACCESSTOKEN_EXPIRES = CommonsCache.TS_HOUR * 24 * 3;
|
||||
|
||||
private static final String TOKEN_PREFIX = "TOKEN:";
|
||||
|
||||
|
@ -107,7 +108,7 @@ public class AuthTokenManager {
|
|||
* @return
|
||||
*/
|
||||
public static ID verifyToken(String token) {
|
||||
return verifyToken(token, Boolean.FALSE);
|
||||
return verifyToken(token, Boolean.FALSE, Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,9 +116,10 @@ public class AuthTokenManager {
|
|||
*
|
||||
* @param token
|
||||
* @param verifyAfterDestroy
|
||||
* @param verifyAfterRefresh
|
||||
* @return
|
||||
*/
|
||||
public static ID verifyToken(String token, boolean verifyAfterDestroy) {
|
||||
public static ID verifyToken(String token, boolean verifyAfterDestroy, boolean verifyAfterRefresh) {
|
||||
Assert.notNull(token, "[token] cannot be null");
|
||||
String desc = Application.getCommonsCache().get(TOKEN_PREFIX + token);
|
||||
if (desc == null) return null;
|
||||
|
@ -130,6 +132,11 @@ public class AuthTokenManager {
|
|||
if (verifyAfterDestroy) {
|
||||
log.debug("Destroy token ({}) : {}", descs[0], token);
|
||||
Application.getCommonsCache().evict(TOKEN_PREFIX + token);
|
||||
verifyAfterRefresh = false;
|
||||
}
|
||||
|
||||
if (verifyAfterRefresh && TYPE_ACCESS_TOKEN.equals(descs[0])) {
|
||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, ACCESSTOKEN_EXPIRES);
|
||||
}
|
||||
|
||||
return ID.valueOf(descs[1]);
|
||||
|
@ -140,7 +147,10 @@ public class AuthTokenManager {
|
|||
*
|
||||
* @param token
|
||||
* @return
|
||||
* @see #verifyToken(String, boolean, boolean)
|
||||
* @deprecated Use {@link #verifyToken(String, boolean, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static ID refreshAccessToken(String token) {
|
||||
Assert.notNull(token, "[token] cannot be null");
|
||||
String desc = Application.getCommonsCache().get(TOKEN_PREFIX + token);
|
||||
|
|
|
@ -30,8 +30,8 @@ public class LoginToken extends BaseApi {
|
|||
|
||||
@Override
|
||||
public JSON execute(ApiContext context) throws ApiInvokeException {
|
||||
String user = context.getParameterNotBlank("user");
|
||||
String password = context.getParameterNotBlank("password");
|
||||
final String user = context.getParameterNotBlank("user");
|
||||
final String password = context.getParameterNotBlank("password");
|
||||
|
||||
if (RateLimiters.RRL_LOGIN.overLimitWhenIncremented("user:" + user)) {
|
||||
return formatFailure(Language.L("请求过于频繁,请稍后重试"), ApiInvokeException.ERR_FREQUENCY);
|
||||
|
|
|
@ -67,11 +67,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Version
|
||||
*/
|
||||
public static final String VER = "3.1.0-beta1";
|
||||
public static final String VER = "3.2.0-dev";
|
||||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
*/
|
||||
public static final int BUILD = 3010001;
|
||||
public static final int BUILD = 3020000;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.jsoup.nodes.Document;
|
|||
import org.jsoup.nodes.Element;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
|
@ -180,25 +182,28 @@ public class NavBuilder extends NavManager {
|
|||
*/
|
||||
private JSONArray buildAvailableProjects(ID user) {
|
||||
ConfigBean[] projects = ProjectManager.instance.getAvailable(user);
|
||||
// 排序 a-z
|
||||
Arrays.sort(projects, Comparator.comparing(o -> o.getString("projectName")));
|
||||
|
||||
JSONArray navsOfProjects = new JSONArray();
|
||||
JSONArray navsOfProjects2 = new JSONArray();
|
||||
JSONArray navsOfProjectsArchived = new JSONArray();
|
||||
for (ConfigBean e : projects) {
|
||||
JSONObject item = JSONUtils.toJSONObject(
|
||||
NAV_ITEM_PROPS,
|
||||
new Object[] { e.getString("iconName"), e.getString("projectName"), NAV_PROJECT, e.getID("id") });
|
||||
if (e.getInteger("status") == ProjectManager.STATUS_ARCHIVED) {
|
||||
navsOfProjects2.add(item);
|
||||
navsOfProjectsArchived.add(item);
|
||||
} else {
|
||||
navsOfProjects.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (!navsOfProjects2.isEmpty()) {
|
||||
// 归档的
|
||||
if (!navsOfProjectsArchived.isEmpty()) {
|
||||
navsOfProjects.add(JSONUtils.toJSONObject(
|
||||
NAV_ITEM_PROPS,
|
||||
new String[] { null, Language.L("已归档"), NAV_DIVIDER, "ARCHIVED" }));
|
||||
navsOfProjects.addAll(navsOfProjects2);
|
||||
navsOfProjects.addAll(navsOfProjectsArchived);
|
||||
}
|
||||
|
||||
// 管理员显示新建项目入口
|
||||
|
@ -312,7 +317,10 @@ public class NavBuilder extends NavManager {
|
|||
navUrl = AppUtils.getContextPath("/app/" + navUrl + "/list");
|
||||
}
|
||||
|
||||
String navIcon = StringUtils.defaultIfBlank(item.getString("icon"), "texture");
|
||||
String iconClazz = StringUtils.defaultIfBlank(item.getString("icon"), "texture");
|
||||
if (iconClazz.startsWith("mdi-")) iconClazz = "mdi " + iconClazz;
|
||||
else iconClazz = "zmdi zmdi-" + iconClazz;
|
||||
|
||||
String navText = item.getString("text");
|
||||
|
||||
JSONArray subNavs = null;
|
||||
|
@ -331,12 +339,12 @@ public class NavBuilder extends NavManager {
|
|||
if (BooleanUtils.toBoolean(item.getString("open"))) parentClass += " open";
|
||||
|
||||
navItemHtml = String.format(
|
||||
"<li class=\"%s\" data-entity=\"%s\"><a href=\"%s\" target=\"%s\"><i class=\"icon zmdi zmdi-%s\"></i><span>%s</span></a>",
|
||||
"<li class=\"%s\" data-entity=\"%s\"><a href=\"%s\" target=\"%s\"><i class=\"icon %s\"></i><span>%s</span></a>",
|
||||
navName + (subNavs == null ? StringUtils.EMPTY : parentClass),
|
||||
navEntity == null ? StringUtils.EMPTY : navEntity,
|
||||
subNavs == null ? navUrl : "###",
|
||||
isOutUrl ? "_blank" : "_self",
|
||||
navIcon,
|
||||
iconClazz,
|
||||
navText);
|
||||
}
|
||||
StringBuilder navHtml = new StringBuilder(navItemHtml);
|
||||
|
|
|
@ -15,6 +15,9 @@ import com.rebuild.core.configuration.ConfigManager;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 分类数据
|
||||
|
@ -22,6 +25,7 @@ import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
|||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/03/28
|
||||
*/
|
||||
@Slf4j
|
||||
public class ClassificationManager implements ConfigManager {
|
||||
|
||||
public static final ClassificationManager instance = new ClassificationManager();
|
||||
|
@ -38,8 +42,8 @@ public class ClassificationManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public String getName(ID itemId) {
|
||||
String[] ns = getItemNames(itemId);
|
||||
return ns == null ? null : ns[0];
|
||||
Item item = getItem(itemId);
|
||||
return item == null ? null : item.Name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,31 +53,32 @@ public class ClassificationManager implements ConfigManager {
|
|||
* @return
|
||||
*/
|
||||
public String getFullName(ID itemId) {
|
||||
String[] ns = getItemNames(itemId);
|
||||
return ns == null ? null : ns[1];
|
||||
Item item = getItem(itemId);
|
||||
return item == null ? null : item.FullName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemId
|
||||
* @return [名称, 全名称]
|
||||
* @return
|
||||
*/
|
||||
private String[] getItemNames(ID itemId) {
|
||||
final String ckey = "ClassificationNAME-" + itemId;
|
||||
String[] cached = (String[]) Application.getCommonsCache().getx(ckey);
|
||||
if (cached != null) {
|
||||
return cached[0].equals(DELETED_ITEM) ? null : cached;
|
||||
private Item getItem(ID itemId) {
|
||||
final String ckey = "ClassificationITEM31-" + itemId;
|
||||
Item ditem = (Item) Application.getCommonsCache().getx(ckey);
|
||||
if (ditem != null) {
|
||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||
}
|
||||
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select name,fullName from ClassificationData where itemId = ?")
|
||||
"select name,fullName,code from ClassificationData where itemId = ?")
|
||||
.setParameter(1, itemId)
|
||||
.unique();
|
||||
if (o != null) cached = new String[]{(String) o[0], (String) o[1]};
|
||||
// 可能已删除
|
||||
if (cached == null) cached = new String[]{DELETED_ITEM, DELETED_ITEM};
|
||||
if (o != null) ditem = new Item((String) o[0], (String) o[1], (String) o[2]);
|
||||
|
||||
Application.getCommonsCache().putx(ckey, cached);
|
||||
return cached[0].equals(DELETED_ITEM) ? null : cached;
|
||||
// 可能已删除
|
||||
if (ditem == null) ditem = new Item(DELETED_ITEM, null, null);
|
||||
|
||||
Application.getCommonsCache().putx(ckey, ditem);
|
||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,21 +90,23 @@ public class ClassificationManager implements ConfigManager {
|
|||
*/
|
||||
public ID findItemByName(String name, Field field) {
|
||||
ID dataId = getUseClassification(field, false);
|
||||
if (dataId == null) {
|
||||
return null;
|
||||
}
|
||||
if (dataId == null) return null;
|
||||
|
||||
int openLevel = getOpenLevel(field);
|
||||
|
||||
// 后匹配
|
||||
String ql = String.format(
|
||||
"select itemId from ClassificationData where dataId = '%s' and fullName like '%%%s'", dataId, name);
|
||||
Object[][] hasMany = Application.createQueryNoFilter(ql).array();
|
||||
if (hasMany.length == 0) {
|
||||
"select itemId from ClassificationData where dataId = ? and level = ? and fullName like '%%%s'", name);
|
||||
Object[][] found = Application.createQueryNoFilter(ql)
|
||||
.setParameter(1, dataId)
|
||||
.setParameter(2, openLevel)
|
||||
.array();
|
||||
|
||||
if (found.length == 0) {
|
||||
return null;
|
||||
} else if (hasMany.length == 1) {
|
||||
return (ID) hasMany[0][0];
|
||||
} else {
|
||||
// TODO 多个匹配
|
||||
return (ID) hasMany[0][0];
|
||||
// TODO 找到多个匹配的优选
|
||||
return (ID) found[0][0];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,12 +118,11 @@ public class ClassificationManager implements ConfigManager {
|
|||
*/
|
||||
public int getOpenLevel(Field field) {
|
||||
ID dataId = getUseClassification(field, false);
|
||||
if (dataId == null) {
|
||||
return BAD_CLASSIFICATION;
|
||||
}
|
||||
if (dataId == null) return BAD_CLASSIFICATION;
|
||||
|
||||
String ckey = "ClassificationLEVEL-" + dataId;
|
||||
final String ckey = "ClassificationLEVEL-" + dataId;
|
||||
Integer cLevel = (Integer) Application.getCommonsCache().getx(ckey);
|
||||
|
||||
if (cLevel == null) {
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select openLevel from Classification where dataId = ?")
|
||||
|
@ -141,29 +147,40 @@ public class ClassificationManager implements ConfigManager {
|
|||
* 获取指定字段所使用的分类
|
||||
*
|
||||
* @param field
|
||||
* @param verfiy
|
||||
* @param checkBad
|
||||
* @return
|
||||
*/
|
||||
public ID getUseClassification(Field field, boolean verfiy) {
|
||||
public ID getUseClassification(Field field, boolean checkBad) {
|
||||
String classUse = EasyMetaFactory.valueOf(field).getExtraAttr(EasyFieldConfigProps.CLASSIFICATION_USE);
|
||||
ID dataId = ID.isId(classUse) ? ID.valueOf(classUse) : null;
|
||||
if (dataId == null) {
|
||||
return null;
|
||||
}
|
||||
if (dataId == null) return null;
|
||||
|
||||
if (verfiy && getOpenLevel(field) == BAD_CLASSIFICATION) {
|
||||
return null;
|
||||
}
|
||||
return dataId;
|
||||
if (checkBad && getOpenLevel(field) == BAD_CLASSIFICATION) return null;
|
||||
else return dataId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean(Object cid) {
|
||||
ID id2 = (ID) cid;
|
||||
if (id2.getEntityCode() == EntityHelper.ClassificationData) {
|
||||
Application.getCommonsCache().evict("ClassificationNAME-" + cid);
|
||||
Application.getCommonsCache().evict("ClassificationITEM31-" + cid);
|
||||
} else if (id2.getEntityCode() == EntityHelper.Classification) {
|
||||
Application.getCommonsCache().evict("ClassificationLEVEL-" + cid);
|
||||
}
|
||||
}
|
||||
|
||||
// Bean
|
||||
public static class Item implements Serializable {
|
||||
private static final long serialVersionUID = -1903227875771376652L;
|
||||
|
||||
Item(String name, String fullName, String code) {
|
||||
this.Name = name;
|
||||
this.FullName = fullName;
|
||||
this.Code = code;
|
||||
}
|
||||
|
||||
final public String Name;
|
||||
final public String FullName;
|
||||
final public String Code;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import cn.devezhao.persist4j.engine.ID;
|
|||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.cache.CacheTemplate;
|
||||
import com.rebuild.core.configuration.ConfigBean;
|
||||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
|
@ -42,60 +41,58 @@ public class DataListCategory {
|
|||
* @return
|
||||
*/
|
||||
public static JSON datas(Entity entity, ID user) {
|
||||
final Field classField = getFieldOfCategory(entity);
|
||||
if (classField == null) return null;
|
||||
final Field categoryField = getFieldOfCategory(entity);
|
||||
if (categoryField == null) return null;
|
||||
|
||||
final String ckey = String.format("DLC1.%s.%s", entity.getName(), classField.getName());
|
||||
JSON c = (JSON) Application.getCommonsCache().getx(ckey);
|
||||
if (c != null) return c;
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(categoryField);
|
||||
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(classField);
|
||||
|
||||
List<Object[]> list = new ArrayList<>();
|
||||
List<Object[]> clist = new ArrayList<>();
|
||||
|
||||
if (dt == DisplayType.MULTISELECT || dt == DisplayType.PICKLIST) {
|
||||
ConfigBean[] entries = MultiSelectManager.instance.getPickListRaw(classField, true);
|
||||
ConfigBean[] entries = MultiSelectManager.instance.getPickListRaw(categoryField, true);
|
||||
for (ConfigBean e : entries) {
|
||||
Object id = e.getID("id");
|
||||
if (dt == DisplayType.MULTISELECT) id = e.getLong("mask");
|
||||
|
||||
list.add(new Object[] { e.getString("text"), id });
|
||||
clist.add(new Object[] { e.getString("text"), id });
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// TODO 考虑支持更多分组字段类型,例如日期(但要考虑日期格式)
|
||||
|
||||
String sql;
|
||||
if (dt == DisplayType.N2NREFERENCE) {
|
||||
sql = MessageFormat.format(
|
||||
"select referenceId from NreferenceItem where belongEntity = ''{0}'' and belongField = ''{1}'' group by referenceId",
|
||||
entity.getName(), classField.getName());
|
||||
"select distinct referenceId from NreferenceItem where belongEntity = ''{0}'' and belongField = ''{1}''",
|
||||
entity.getName(), categoryField.getName());
|
||||
} else {
|
||||
sql = MessageFormat.format(
|
||||
"select {0} from {1} where {0} is not null group by {0}", classField.getName(), entity.getName());
|
||||
"select distinct {0} from {1} where {0} is not null", categoryField.getName(), entity.getName());
|
||||
}
|
||||
|
||||
|
||||
Query query = user == null
|
||||
? Application.createQueryNoFilter(sql) : Application.getQueryFactory().createQuery(sql, user);
|
||||
? Application.createQueryNoFilter(sql)
|
||||
: Application.getQueryFactory().createQuery(sql, user);
|
||||
Object[][] array = query.array();
|
||||
|
||||
for (Object[] o : array) {
|
||||
Object id = o[0];
|
||||
Object label = FieldValueHelper.getLabelNotry((ID) id);
|
||||
list.add(new Object[] { label, id });
|
||||
clist.add(new Object[] { label, id });
|
||||
}
|
||||
|
||||
list.sort(Comparator.comparing(o -> o[0].toString()));
|
||||
// TODO 分类数据 code 排序
|
||||
clist.sort(Comparator.comparing(o -> o[0].toString()));
|
||||
}
|
||||
|
||||
JSONArray res = new JSONArray();
|
||||
for (Object[] o : list) {
|
||||
for (Object[] o : clist) {
|
||||
res.add(JSONUtils.toJSONObject(
|
||||
new String[] { "label", "id", "count" },
|
||||
new Object[] { o[0], o[1], 0 } ));
|
||||
}
|
||||
|
||||
// FIXME 1min 缓存
|
||||
Application.getCommonsCache().putx(ckey, res, CacheTemplate.TS_HOUR / 60); // 1min
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.rebuild.core.metadata.MetadataHelper;
|
|||
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.core.privileges.bizz.Department;
|
||||
import com.rebuild.core.privileges.bizz.User;
|
||||
|
@ -54,8 +55,10 @@ public class FormsBuilder extends FormsManager {
|
|||
|
||||
// 分割线
|
||||
public static final String DIVIDER_LINE = "$DIVIDER$";
|
||||
|
||||
// 引用主记录
|
||||
public static final String DV_MAINID = "$MAINID$";
|
||||
|
||||
// 引用记录
|
||||
public static final String DV_REFERENCE_PREFIX = "&";
|
||||
|
||||
|
@ -193,7 +196,12 @@ public class FormsBuilder extends FormsManager {
|
|||
if (hasMainEntity != null) {
|
||||
model.set("mainMeta", EasyMetaFactory.toJSON(hasMainEntity));
|
||||
} else if (entityMeta.getDetailEntity() != null) {
|
||||
model.set("detailMeta", EasyMetaFactory.toJSON(entityMeta.getDetailEntity()));
|
||||
// v3.1
|
||||
if (!entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.NOT_COEDITING)) {
|
||||
model.set("detailMeta", EasyMetaFactory.toJSON(entityMeta.getDetailEntity()));
|
||||
model.set("detailsNotEmpty",
|
||||
entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.DETAILS_NOTEMPTY));
|
||||
}
|
||||
}
|
||||
|
||||
if (recordData != null && recordData.hasValue(EntityHelper.ModifiedOn)) {
|
||||
|
@ -266,7 +274,8 @@ public class FormsBuilder extends FormsManager {
|
|||
final Date now = CalendarUtils.now();
|
||||
|
||||
// 新建
|
||||
final boolean isNew = recordData == null || recordData.getPrimary() == null || EntityHelper.isUnsavedId(recordData.getPrimary());
|
||||
final boolean isNew = recordData == null || recordData.getPrimary() == null
|
||||
|| EntityHelper.isUnsavedId(recordData.getPrimary());
|
||||
|
||||
// Check and clean
|
||||
for (Iterator<Object> iter = elements.iterator(); iter.hasNext(); ) {
|
||||
|
@ -368,10 +377,11 @@ public class FormsBuilder extends FormsManager {
|
|||
}
|
||||
|
||||
// 父级级联
|
||||
ID parentValue = dt == DisplayType.REFERENCE && recordData.getPrimary() != null
|
||||
? getCascadingFieldParentValue(easyField, recordData.getPrimary()) : null;
|
||||
if (parentValue != null) {
|
||||
el.put("_cascadingFieldParentValue", parentValue);
|
||||
if ((dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE) && recordData.getPrimary() != null) {
|
||||
ID parentValue = getCascadingFieldParentValue(easyField, recordData.getPrimary(), false);
|
||||
if (parentValue != null) {
|
||||
el.put("_cascadingFieldParentValue", parentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 新建记录
|
||||
|
@ -433,9 +443,10 @@ public class FormsBuilder extends FormsManager {
|
|||
}
|
||||
|
||||
// v3.1 父级级联
|
||||
if (entity.getMainEntity() != null && dt == DisplayType.REFERENCE) {
|
||||
if (entity.getMainEntity() != null && (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) {
|
||||
ID mainid = FormsBuilderContextHolder.getMainIdOfDetail(false);
|
||||
ID parentValue = getCascadingFieldParentValue(easyField, mainid);
|
||||
ID parentValue = EntityHelper.isUnsavedId(mainid) ? null
|
||||
: getCascadingFieldParentValue(easyField, mainid, true);
|
||||
if (parentValue != null) {
|
||||
el.put("_cascadingFieldParentValue", parentValue);
|
||||
}
|
||||
|
@ -549,7 +560,8 @@ public class FormsBuilder extends FormsManager {
|
|||
inFormFields.add(((JSONObject) o).getString("field"));
|
||||
}
|
||||
|
||||
// 保持在初始值中(TODO 更多保持字段)
|
||||
// 保持在初始值中
|
||||
// TODO 更多保持字段
|
||||
Set<String> initialValKeeps = new HashSet<>();
|
||||
|
||||
Map<String, Object> initialValReady = new HashMap<>();
|
||||
|
@ -573,8 +585,10 @@ public class FormsBuilder extends FormsManager {
|
|||
// 主实体字段
|
||||
else if (field.equals(DV_MAINID)) {
|
||||
Field dtmField = MetadataHelper.getDetailToMainField(entity);
|
||||
Object mixValue = inFormFields.contains(dtmField.getName()) ? getReferenceMixValue(value)
|
||||
: (DV_MAINID.equals(value) ? EntityHelper.UNSAVED_ID : value);
|
||||
Object mixValue = inFormFields.contains(dtmField.getName())
|
||||
? getReferenceMixValue(value)
|
||||
: (isNewMainId(value) ? EntityHelper.UNSAVED_ID : value);
|
||||
|
||||
if (mixValue != null) {
|
||||
initialValReady.put(dtmField.getName(), mixValue);
|
||||
initialValKeeps.add(dtmField.getName());
|
||||
|
@ -619,7 +633,7 @@ public class FormsBuilder extends FormsManager {
|
|||
* @return returns [ID, LABEL]
|
||||
*/
|
||||
private JSON getReferenceMixValue(String idValue) {
|
||||
if (DV_MAINID.equals(idValue)) {
|
||||
if (isNewMainId(idValue)) {
|
||||
return FieldValueHelper.wrapMixValue(EntityHelper.UNSAVED_ID, Language.L("新的"));
|
||||
} else if (!ID.isId(idValue)) {
|
||||
return null;
|
||||
|
@ -639,28 +653,36 @@ public class FormsBuilder extends FormsManager {
|
|||
*
|
||||
* @param field
|
||||
* @param record
|
||||
* @param recordIsMain
|
||||
* @return
|
||||
*/
|
||||
private ID getCascadingFieldParentValue(EasyField field, ID record) {
|
||||
private ID getCascadingFieldParentValue(EasyField field, ID record, boolean recordIsMain) {
|
||||
String pf = field.getExtraAttr("_cascadingFieldParent");
|
||||
if (pf == null) return null;
|
||||
|
||||
String[] pfs = pf.split(MetadataHelper.SPLITER_RE);
|
||||
String fieldParent = pfs[0];
|
||||
// 明细级联主实体
|
||||
if (pfs[0].contains(".")) {
|
||||
|
||||
// 明细字段使用主实体字段
|
||||
// format: MAINENTITY.FIELD
|
||||
boolean useMainField = pfs[0].contains(".");
|
||||
|
||||
if (recordIsMain) {
|
||||
if (useMainField) {
|
||||
fieldParent = pfs[0].split("\\.")[1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (useMainField) {
|
||||
Field dtf = MetadataHelper.getDetailToMainField(field.getRawMeta().getOwnEntity());
|
||||
fieldParent = dtf.getName() + "." + pfs[0].split("\\.")[1];
|
||||
|
||||
// 明细新建时 record 传入的是主记录
|
||||
int fieldCode = field.getRawMeta().getOwnEntity().getEntityCode();
|
||||
int recordCode = record.getEntityCode();
|
||||
if (fieldCode != recordCode) {
|
||||
fieldParent = pfs[0].split("\\.")[1];
|
||||
}
|
||||
}
|
||||
|
||||
Object[] o = Application.getQueryFactory().uniqueNoFilter(record, fieldParent);
|
||||
return o == null ? null : (ID) o[0];
|
||||
}
|
||||
|
||||
private boolean isNewMainId(Object id) {
|
||||
return DV_MAINID.equals(id) || EntityHelper.isUnsavedId(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ public class EntityHelper {
|
|||
*
|
||||
* @param entityCode
|
||||
* @return
|
||||
* @see #isUnsavedId(ID)
|
||||
* @see #isUnsavedId(Object)
|
||||
*/
|
||||
public static ID newUnsavedId(int entityCode) {
|
||||
if (entityCode == 0) return UNSAVED_ID;
|
||||
|
@ -174,11 +174,12 @@ public class EntityHelper {
|
|||
/**
|
||||
* @param id
|
||||
* @return
|
||||
* @see #newUnsavedId(int)
|
||||
*/
|
||||
public static boolean isUnsavedId(ID id) {
|
||||
return UNSAVED_ID.equals(id) || id.toLiteral().endsWith(UNSAVED_ID_SUFFIX);
|
||||
public static boolean isUnsavedId(Object id) {
|
||||
return ID.isId(id) && (UNSAVED_ID.equals(id) || id.toString().endsWith(UNSAVED_ID_SUFFIX));
|
||||
}
|
||||
|
||||
|
||||
// 公共字段/保留字段
|
||||
|
||||
public static final String CreatedOn = "createdOn";
|
||||
|
@ -196,6 +197,7 @@ public class EntityHelper {
|
|||
public static final String ApprovalState = "approvalState";
|
||||
public static final String ApprovalStepNode = "approvalStepNode";
|
||||
public static final String ApprovalLastUser = "approvalLastUser";
|
||||
public static final String ApprovalLastRemark = "approvalLastRemark";
|
||||
|
||||
// 用户
|
||||
|
||||
|
|
|
@ -256,7 +256,8 @@ public class MetadataHelper {
|
|||
return EntityHelper.ApprovalId.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.ApprovalState.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.ApprovalStepNode.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.ApprovalLastUser.equalsIgnoreCase(fieldName);
|
||||
|| EntityHelper.ApprovalLastUser.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.ApprovalLastRemark.equalsIgnoreCase(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -158,7 +158,7 @@ public abstract class EasyField extends BaseEasyMeta<Field> {
|
|||
// abstract T checkoutValue(Object rawValue);
|
||||
|
||||
/**
|
||||
* 信息脱敏
|
||||
* 启用信息脱敏
|
||||
*
|
||||
* @return
|
||||
* @see com.rebuild.core.support.general.FieldValueHelper#desensitized(EasyField, Object)
|
||||
|
|
|
@ -180,7 +180,8 @@ public class DynamicMetadataFactory extends ConfigurationMetadataFactory {
|
|||
extraAttrs.put("displayType", dt.name());
|
||||
|
||||
String cascadingField = extraAttrs.getString(EasyFieldConfigProps.REFERENCE_CASCADINGFIELD);
|
||||
if (StringUtils.isNotBlank(cascadingField) && dt == DisplayType.REFERENCE) {
|
||||
if (StringUtils.isNotBlank(cascadingField)
|
||||
&& (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) {
|
||||
extraAttrs.put("_cascadingFieldParent", cascadingField);
|
||||
String[] fs = cascadingField.split(SPLITER_RE);
|
||||
cascadingFieldsChild.add(entityName + SPLITER + fs[0] + SPLITER + fieldName + SPLITER + fs[1]);
|
||||
|
@ -191,6 +192,7 @@ public class DynamicMetadataFactory extends ConfigurationMetadataFactory {
|
|||
|
||||
// 处理父级级联的父子级关系
|
||||
// 多个子级可能使用同一个父级字段,这里仅保留一个子级,对使用效果无影响(已布局的情况下)
|
||||
// v3.1 有影响:因为如果是明细>主实体会涉及主实体变化清空明细,此处优先使用明细的子级(只能一个)
|
||||
for (String child : cascadingFieldsChild) {
|
||||
String[] fs = child.split(SPLITER_RE);
|
||||
Element fieldElement;
|
||||
|
@ -212,8 +214,12 @@ public class DynamicMetadataFactory extends ConfigurationMetadataFactory {
|
|||
}
|
||||
|
||||
JSONObject extraAttrs = JSON.parseObject(fieldElement.valueOf("@extra-attrs"));
|
||||
extraAttrs.put("_cascadingFieldChild", fs[2] + SPLITER + fs[3]);
|
||||
fieldElement.addAttribute("extra-attrs", extraAttrs.toJSONString());
|
||||
if (extraAttrs.containsKey("_cascadingFieldChild") && extraAttrs.getString("_cascadingFieldChild").contains(".")) {
|
||||
// 优先明细>主实体
|
||||
} else {
|
||||
extraAttrs.put("_cascadingFieldChild", fs[2] + SPLITER + fs[3]);
|
||||
fieldElement.addAttribute("extra-attrs", extraAttrs.toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) XmlHelper.dump(rootElement);
|
||||
|
|
|
@ -21,6 +21,14 @@ public class EasyEntityConfigProps {
|
|||
* 实体分类
|
||||
*/
|
||||
public static final String TAGS = "tags";
|
||||
/**
|
||||
* 关闭共同编辑
|
||||
*/
|
||||
public static final String NOT_COEDITING = "notCoEditing";
|
||||
/**
|
||||
* 明细不允许为空
|
||||
*/
|
||||
public static final String DETAILS_NOTEMPTY = "detailsNotEmpty";
|
||||
|
||||
/**
|
||||
* 隐藏常用查询面板
|
||||
|
|
|
@ -70,6 +70,11 @@ public class EasyFieldConfigProps {
|
|||
*/
|
||||
public static final String IMAGE_UPLOADNUMBER = FILE_UPLOADNUMBER;
|
||||
|
||||
/**
|
||||
* 图片获取方式(仅H5)
|
||||
*/
|
||||
public static final String IMAGE_CAPTURE = "imageCapture";
|
||||
|
||||
/**
|
||||
* 自动编号规则
|
||||
*/
|
||||
|
|
|
@ -118,7 +118,7 @@ public class Entity2Schema extends Field2Schema {
|
|||
}
|
||||
record.setString("nameField", nameFiled);
|
||||
record = Application.getCommonsService().create(record);
|
||||
recordedMetaId.add(record.getPrimary());
|
||||
recordedMetaIds.add(record.getPrimary());
|
||||
|
||||
Entity tempEntity = new UnsafeEntity(entityName, physicalName, entityLabel, typeCode, nameFiled);
|
||||
try {
|
||||
|
@ -151,15 +151,17 @@ public class Entity2Schema extends Field2Schema {
|
|||
createBuiltinField(tempEntity, EntityHelper.OwningUser, Language.L("所属用户"), DisplayType.REFERENCE, null, "User", null);
|
||||
createBuiltinField(tempEntity, EntityHelper.OwningDept, Language.L("所属部门"), DisplayType.REFERENCE, null, "Department", null);
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
log.error(null, ex);
|
||||
Application.getCommonsService().delete(recordedMetaId.toArray(new ID[0]));
|
||||
log.error("Error on create fields", ex);
|
||||
Application.getCommonsService().delete(recordedMetaIds.toArray(new ID[0]));
|
||||
|
||||
throw new MetadataModificationException(Language.L("无法同步元数据到数据库 : %s", ex.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
boolean schemaReady = schema2Database(tempEntity);
|
||||
if (!schemaReady) {
|
||||
Application.getCommonsService().delete(recordedMetaId.toArray(new ID[0]));
|
||||
Application.getCommonsService().delete(recordedMetaIds.toArray(new ID[0]));
|
||||
throw new MetadataModificationException(Language.L("无法同步元数据到数据库"));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import cn.devezhao.persist4j.Record;
|
|||
import cn.devezhao.persist4j.dialect.Dialect;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.CascadeModel;
|
||||
import cn.devezhao.persist4j.metadata.impl.AnyEntity;
|
||||
import cn.devezhao.persist4j.metadata.impl.FieldImpl;
|
||||
import cn.devezhao.persist4j.util.StringHelper;
|
||||
import cn.devezhao.persist4j.util.support.Table;
|
||||
|
@ -33,6 +34,7 @@ import com.rebuild.core.support.SetUser;
|
|||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.setup.Installer;
|
||||
import com.rebuild.utils.BlockList;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.CharSet;
|
||||
|
@ -57,7 +59,7 @@ public class Field2Schema extends SetUser {
|
|||
// 小数位真实长度
|
||||
private static final int DECIMAL_SCALE = 8;
|
||||
|
||||
final protected Set<ID> recordedMetaId = new HashSet<>();
|
||||
final protected Set<ID> recordedMetaIds = new HashSet<>();
|
||||
|
||||
public Field2Schema() {
|
||||
super();
|
||||
|
@ -84,6 +86,20 @@ public class Field2Schema extends SetUser {
|
|||
* @return
|
||||
*/
|
||||
public String createField(Entity entity, String fieldLabel, DisplayType type, String comments, String refEntity, JSON extConfig) {
|
||||
return createField(entity, fieldLabel, null, type, comments, refEntity, extConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
* @param fieldLabel
|
||||
* @param fieldName
|
||||
* @param type
|
||||
* @param comments
|
||||
* @param refEntity
|
||||
* @param extConfig
|
||||
* @return
|
||||
*/
|
||||
public String createField(Entity entity, String fieldLabel, String fieldName, DisplayType type, String comments, String refEntity, JSON extConfig) {
|
||||
if (!License.isCommercial()) {
|
||||
if (entity.getFields().length >= 50) {
|
||||
throw new NeedRbvException("字段数量超出免费版限制");
|
||||
|
@ -91,7 +107,7 @@ public class Field2Schema extends SetUser {
|
|||
|
||||
if (type == DisplayType.LOCATION || type == DisplayType.SIGN) {
|
||||
Object[] limit = Application.createQueryNoFilter(
|
||||
"select count(fieldId) from MetaField where displayType = ?")
|
||||
"select count(fieldId) from MetaField where displayType = ?")
|
||||
.setParameter(1, type.name())
|
||||
.unique();
|
||||
if (ObjectUtils.toInt(limit[0]) >= 1) {
|
||||
|
@ -100,7 +116,8 @@ public class Field2Schema extends SetUser {
|
|||
}
|
||||
}
|
||||
|
||||
String fieldName = toPinyinName(fieldLabel);
|
||||
if (StringUtils.length(fieldName) < 4) fieldName = toPinyinName(fieldLabel);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (entity.containsField(fieldName) || MetadataHelper.isCommonsField(fieldName)) {
|
||||
fieldName += RandomUtils.nextInt(0, 9);
|
||||
|
@ -117,7 +134,7 @@ public class Field2Schema extends SetUser {
|
|||
|
||||
boolean schemaReady = schema2Database(entity, new Field[]{field}, uniqueKeyFields);
|
||||
if (!schemaReady) {
|
||||
Application.getCommonsService().delete(recordedMetaId.toArray(new ID[0]));
|
||||
Application.getCommonsService().delete(recordedMetaIds.toArray(new ID[0]));
|
||||
throw new MetadataModificationException(Language.L("无法同步元数据到数据库"));
|
||||
}
|
||||
|
||||
|
@ -301,6 +318,8 @@ public class Field2Schema extends SetUser {
|
|||
refEntity = "PickList";
|
||||
} else if (dt == DisplayType.CLASSIFICATION) {
|
||||
refEntity = "ClassificationData";
|
||||
} else if (dt == DisplayType.ANYREFERENCE) {
|
||||
refEntity = AnyEntity.FLAG;
|
||||
}
|
||||
|
||||
if (extConfig != null) {
|
||||
|
@ -311,7 +330,7 @@ public class Field2Schema extends SetUser {
|
|||
// 忽略验证实体是否存在
|
||||
// 在导入实体时需要,需自行保证引用实体有效性,否则系统会出错
|
||||
if (!DynamicMetadataContextHolder.isSkipRefentityCheck(false)) {
|
||||
if (!MetadataHelper.containsEntity(refEntity)) {
|
||||
if (!(MetadataHelper.containsEntity(refEntity) || AnyEntity.FLAG.equals(refEntity))) {
|
||||
throw new MetadataModificationException(Language.L("无效引用实体 : %s", refEntity));
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +355,7 @@ public class Field2Schema extends SetUser {
|
|||
}
|
||||
|
||||
recordOfField = Application.getCommonsService().create(recordOfField);
|
||||
recordedMetaId.add(recordOfField.getPrimary());
|
||||
recordedMetaIds.add(recordOfField.getPrimary());
|
||||
|
||||
// 以下会改变一些属性,因为并不想他们保存在元数据中
|
||||
|
||||
|
@ -369,16 +388,13 @@ public class Field2Schema extends SetUser {
|
|||
*/
|
||||
protected String toPinyinName(final String text) {
|
||||
String identifier = text;
|
||||
if (text.length() < 4) {
|
||||
identifier = "rb" + text + RandomUtils.nextInt(1000, 9999);
|
||||
}
|
||||
|
||||
// 全英文直接返回
|
||||
if (identifier.matches("[a-zA-Z0-9]+")) {
|
||||
if (identifier.length() >= 4 && identifier.matches("[a-zA-Z0-9]+")) {
|
||||
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0)) || BlockList.isBlock(identifier)) {
|
||||
identifier = "rb" + identifier;
|
||||
}
|
||||
return identifier;
|
||||
return CommonsUtils.maxstr(identifier, 40);
|
||||
}
|
||||
|
||||
identifier = HanLP.convertToPinyinString(identifier, "", false);
|
||||
|
@ -387,14 +403,13 @@ public class Field2Schema extends SetUser {
|
|||
identifier = "rb" + RandomUtils.nextInt(1000, 9999);
|
||||
}
|
||||
|
||||
char start = identifier.charAt(0);
|
||||
if (!CharSet.ASCII_ALPHA.contains(start)) {
|
||||
if (!CharSet.ASCII_ALPHA.contains(identifier.charAt(0))) {
|
||||
identifier = "rb" + identifier;
|
||||
}
|
||||
|
||||
identifier = identifier.toLowerCase();
|
||||
if (identifier.length() > 42) {
|
||||
identifier = identifier.substring(0, 42);
|
||||
if (identifier.length() > 40) {
|
||||
identifier = identifier.substring(0, 40);
|
||||
} else if (identifier.length() < 4) {
|
||||
identifier += RandomUtils.nextInt(1000, 9999);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,10 @@ public class ApprovalFields2Schema extends Field2Schema {
|
|||
public boolean createFields(Entity approvalEntity) throws MetadataModificationException {
|
||||
if (MetadataHelper.hasApprovalField(approvalEntity)) {
|
||||
if (!approvalEntity.containsField(EntityHelper.ApprovalLastUser)) {
|
||||
return createApporvalLastUser(approvalEntity);
|
||||
return schema2DatabaseInternal(approvalEntity, buildApporvalLastUser(approvalEntity));
|
||||
}
|
||||
if (!approvalEntity.containsField(EntityHelper.ApprovalLastRemark)) {
|
||||
return schema2DatabaseInternal(approvalEntity, buildApporvalLastRemark(approvalEntity));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -57,36 +60,35 @@ public class ApprovalFields2Schema extends Field2Schema {
|
|||
DisplayType.STATE, true, false, false, true, true, null, null, null, null, ApprovalState.DRAFT.getState());
|
||||
Field apporvalStepId = createUnsafeField(approvalEntity, EntityHelper.ApprovalStepNode, Language.L("审批步骤"),
|
||||
DisplayType.TEXT, true, false, false, true, false, null, null, null, null, null);
|
||||
|
||||
Field apporvalLastUser = buildApporvalLastUser(approvalEntity);
|
||||
Field apporvalLastRemark = buildApporvalLastRemark(approvalEntity);
|
||||
|
||||
boolean schemaReady = schema2Database(approvalEntity,
|
||||
new Field[] { apporvalId, apporvalState, apporvalStepId, apporvalLastUser });
|
||||
|
||||
if (!schemaReady) {
|
||||
Application.getCommonsService().delete(recordedMetaId.toArray(new ID[0]));
|
||||
throw new MetadataModificationException(Language.L("无法同步元数据到数据库"));
|
||||
}
|
||||
|
||||
MetadataHelper.getMetadataFactory().refresh();
|
||||
schema2DatabaseInternal(approvalEntity, apporvalId, apporvalState, apporvalStepId, apporvalLastUser, apporvalLastRemark);
|
||||
return true;
|
||||
}
|
||||
|
||||
// v2.7 最后审批人
|
||||
private boolean createApporvalLastUser(Entity approvalEntity) {
|
||||
Field apporvalLastUser = buildApporvalLastUser(approvalEntity);
|
||||
boolean schemaReady = schema2Database(approvalEntity, new Field[] { apporvalLastUser });
|
||||
private Field buildApporvalLastUser(Entity approvalEntity) {
|
||||
return createUnsafeField(approvalEntity, EntityHelper.ApprovalLastUser, Language.L("最后审批人"),
|
||||
DisplayType.REFERENCE, true, false, false, true, true, null, "User", CascadeModel.Ignore, null, null);
|
||||
}
|
||||
|
||||
// v3.1 最后审批批注
|
||||
private Field buildApporvalLastRemark(Entity approvalEntity) {
|
||||
return createUnsafeField(approvalEntity, EntityHelper.ApprovalLastRemark, Language.L("最后审批批注"),
|
||||
DisplayType.NTEXT, true, false, false, true, true, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private boolean schema2DatabaseInternal(Entity approvalEntity, Field... fields) {
|
||||
boolean schemaReady = schema2Database(approvalEntity, fields);
|
||||
|
||||
if (!schemaReady) {
|
||||
Application.getCommonsService().delete(recordedMetaId.toArray(new ID[0]));
|
||||
Application.getCommonsService().delete(recordedMetaIds.toArray(new ID[0]));
|
||||
throw new MetadataModificationException(Language.L("无法同步元数据到数据库"));
|
||||
}
|
||||
|
||||
MetadataHelper.getMetadataFactory().refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
private Field buildApporvalLastUser(Entity approvalEntity) {
|
||||
return createUnsafeField(approvalEntity, EntityHelper.ApprovalLastUser, Language.L("最后审批人"),
|
||||
DisplayType.REFERENCE, true, false, false, true, true, null, "User", CascadeModel.Ignore, null, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,9 +99,9 @@ public class ApprovalProcessor extends SetUser {
|
|||
recordOfMain.setID(EntityHelper.ApprovalId, this.approval);
|
||||
recordOfMain.setInt(EntityHelper.ApprovalState, ApprovalState.PROCESSING.getState());
|
||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNodes.getApprovalNode().getNodeId());
|
||||
if (recordOfMain.getEntity().containsField(EntityHelper.ApprovalLastUser)) {
|
||||
recordOfMain.setNull(EntityHelper.ApprovalLastUser);
|
||||
}
|
||||
// Clear on submit
|
||||
ApprovalStepService.setApprovalLastX(recordOfMain, null, null);
|
||||
|
||||
Application.getBean(ApprovalStepService.class).txSubmit(recordOfMain, ccs, nextApprovers);
|
||||
|
||||
// 非主事物
|
||||
|
|
|
@ -172,8 +172,8 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
final String currentNode = (String) stepObject[2];
|
||||
final ID approver = UserContextHolder.getUser();
|
||||
|
||||
String entityLabel = EasyMetaFactory.getLabel(MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||
String remark = stepRecord.getString("remark");
|
||||
final String entityLabel = EasyMetaFactory.getLabel(MetadataHelper.getEntity(recordId.getEntityCode()));
|
||||
final String remark = stepRecord.getString("remark");
|
||||
|
||||
// 抄送人
|
||||
if (cc != null && !cc.isEmpty()) {
|
||||
|
@ -186,17 +186,15 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
}
|
||||
}
|
||||
|
||||
// 更新主记录
|
||||
final Record recordOfMain = EntityHelper.forUpdate(recordId, approver, false);
|
||||
setApprovalLastX(recordOfMain, approver, remark);
|
||||
|
||||
// 拒绝了直接返回
|
||||
if (state == ApprovalState.REJECTED || state == ApprovalState.BACKED) {
|
||||
// 拒绝了,同一节点的其他审批人全部作废
|
||||
cancelAliveSteps(recordId, approvalId, currentNode, stepRecordId, true);
|
||||
|
||||
// 更新主记录
|
||||
Record recordOfMain = EntityHelper.forUpdate(recordId, UserService.SYSTEM_USER, false);
|
||||
if (recordOfMain.getEntity().containsField(EntityHelper.ApprovalLastUser)) {
|
||||
recordOfMain.setID(EntityHelper.ApprovalLastUser, approver);
|
||||
}
|
||||
|
||||
// 退回
|
||||
if (state == ApprovalState.BACKED) {
|
||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNode);
|
||||
|
@ -223,11 +221,11 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
|
||||
String approvalMsg = Language.L("有一条 %s 记录请你审批", entityLabel);
|
||||
|
||||
// 或签。一人通过其他作废
|
||||
// 或签:一人通过其他作废
|
||||
if (FlowNode.SIGN_OR.equals(signMode)) {
|
||||
cancelAliveSteps(recordId, approvalId, currentNode, stepRecordId, true);
|
||||
}
|
||||
// 会签。检查是否都签了
|
||||
// 会签:检查是否都签了
|
||||
else {
|
||||
Object[][] currentNodeApprovers = Application.createQueryNoFilter(
|
||||
"select state,isWaiting,stepId from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and isCanceled = 'F'")
|
||||
|
@ -262,17 +260,15 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
|
||||
// 最终状态(审批通过)
|
||||
if (goNextNode && (nextApprovers == null || nextNode == null)) {
|
||||
super.update(recordOfMain);
|
||||
|
||||
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, approver);
|
||||
return;
|
||||
}
|
||||
|
||||
// 进入下一步
|
||||
if (goNextNode) {
|
||||
Record recordOfMain = EntityHelper.forUpdate(recordId, UserService.SYSTEM_USER, false);
|
||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNode);
|
||||
if (recordOfMain.getEntity().containsField(EntityHelper.ApprovalLastUser)) {
|
||||
recordOfMain.setID(EntityHelper.ApprovalLastUser, approver);
|
||||
}
|
||||
super.update(recordOfMain);
|
||||
}
|
||||
|
||||
|
@ -436,7 +432,7 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
public boolean txAutoApproved(ID recordId, ID useApprover, ID useApproval) {
|
||||
final ApprovalState currentState = ApprovalHelper.getApprovalState(recordId);
|
||||
if (currentState == ApprovalState.PROCESSING || currentState == ApprovalState.APPROVED) {
|
||||
log.warn("Invalid state {} for auto approval : {}", currentState, recordId);
|
||||
log.warn("Invalid state {} for auto approve : {}", currentState, recordId);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -458,6 +454,7 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
Record recordOfMain = EntityHelper.forUpdate(recordId, useApprover, false);
|
||||
recordOfMain.setID(EntityHelper.ApprovalId, useApproval);
|
||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, FlowNode.NODE_AUTOAPPROVAL);
|
||||
setApprovalLastX(recordOfMain, useApprover, Language.L("自动审批"));
|
||||
super.update(recordOfMain);
|
||||
|
||||
Application.getEntityService(recordId.getEntityCode()).approve(recordId, ApprovalState.APPROVED, useApprover);
|
||||
|
@ -491,9 +488,7 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
recordOfMain.setID(EntityHelper.ApprovalId, useApproval);
|
||||
recordOfMain.setInt(EntityHelper.ApprovalState, ApprovalState.PROCESSING.getState());
|
||||
recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNodes.getApprovalNode().getNodeId());
|
||||
if (recordOfMain.getEntity().containsField(EntityHelper.ApprovalLastUser)) {
|
||||
recordOfMain.setNull(EntityHelper.ApprovalLastUser);
|
||||
}
|
||||
setApprovalLastX(recordOfMain, null, null);
|
||||
|
||||
Set<ID> ccList = nextNodes.getCcUsers(useApprover, recordId, null);
|
||||
Set<ID> ccs4share = nextNodes.getCcUsers4Share(useApprover, recordId, null);
|
||||
|
@ -622,4 +617,23 @@ public class ApprovalStepService extends InternalPersistService {
|
|||
OperatingContext.create(approvalUser, BizzPermission.UPDATE, null, approvalRecord));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param record
|
||||
* @param approver
|
||||
* @param remark
|
||||
*/
|
||||
protected static void setApprovalLastX(Record record, ID approver, String remark) {
|
||||
Entity entity = record.getEntity();
|
||||
|
||||
if (entity.containsField(EntityHelper.ApprovalLastUser)) {
|
||||
if (approver == null) record.setNull(EntityHelper.ApprovalLastUser);
|
||||
else record.setID(EntityHelper.ApprovalLastUser, approver);
|
||||
}
|
||||
|
||||
if (entity.containsField(EntityHelper.ApprovalLastRemark)) {
|
||||
if (remark == null) record.setNull(EntityHelper.ApprovalLastRemark);
|
||||
else record.setString(EntityHelper.ApprovalLastRemark, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.rebuild.core.service.dashboard.charts.ChartsFactory;
|
|||
import com.rebuild.core.service.dashboard.charts.builtin.BuiltinChart;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
@ -135,8 +136,8 @@ public class ChartManager implements ConfigManager {
|
|||
public void richingCharts(JSONArray charts, ID user) {
|
||||
for (Iterator<Object> iter = charts.iterator(); iter.hasNext(); ) {
|
||||
JSONObject ch = (JSONObject) iter.next();
|
||||
ID chartid = ID.valueOf(ch.getString("chart"));
|
||||
ConfigBean e = getChart(chartid);
|
||||
ID chartId = ID.valueOf(ch.getString("chart"));
|
||||
ConfigBean e = getChart(chartId);
|
||||
if (e == null) {
|
||||
iter.remove();
|
||||
continue;
|
||||
|
@ -145,6 +146,11 @@ public class ChartManager implements ConfigManager {
|
|||
ch.put("title", e.getString("title"));
|
||||
ch.put("type", e.getString("type"));
|
||||
|
||||
try {
|
||||
String c = ((JSONObject) e.getJSON("config")).getJSONObject("option").getString("useColor");
|
||||
if (StringUtils.isNotBlank(c)) ch.put("color", c);
|
||||
} catch (Exception ignore) {}
|
||||
|
||||
if (user != null) {
|
||||
ID createdBy = e.getID("createdBy");
|
||||
ch.put("isManageable", UserHelper.isSelf(user, createdBy));
|
||||
|
|
|
@ -113,12 +113,13 @@ public interface EntityService extends ServiceSpec {
|
|||
List<Record> getAndCheckRepeated(Record checkRecord, int limit);
|
||||
|
||||
/**
|
||||
* 审批(此处只是为了方便子类复写)
|
||||
* 审批
|
||||
*
|
||||
* @param record
|
||||
* @param state 只接受通过或撤销
|
||||
* @param approvalUser 审批人
|
||||
* @see com.rebuild.core.service.approval.ApprovalStepService
|
||||
* @see com.rebuild.core.service.approval.ApprovalProcessor
|
||||
*/
|
||||
void approve(ID record, ApprovalState state, ID approvalUser);
|
||||
}
|
||||
|
|
|
@ -696,16 +696,11 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
if (approvalUser == null) {
|
||||
approvalUser = UserService.SYSTEM_USER;
|
||||
log.warn("Use system user for approval");
|
||||
log.warn("Use system user for approve");
|
||||
}
|
||||
|
||||
Record approvalRecord = EntityHelper.forUpdate(recordId, approvalUser, false);
|
||||
approvalRecord.setInt(EntityHelper.ApprovalState, state.getState());
|
||||
if (state == ApprovalState.APPROVED
|
||||
&& MetadataHelper.getEntity(recordId.getEntityCode()).containsField(EntityHelper.ApprovalLastUser)) {
|
||||
approvalRecord.setID(EntityHelper.ApprovalLastUser, approvalUser);
|
||||
}
|
||||
|
||||
delegateService.update(approvalRecord);
|
||||
|
||||
// 触发器
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.service.NoRecordFoundException;
|
||||
import com.rebuild.core.service.query.QueryHelper;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
@ -23,6 +24,7 @@ import java.util.*;
|
|||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/04/25
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class RecentlyUsedHelper {
|
||||
|
||||
// 最大缓存数量
|
||||
|
@ -37,7 +39,20 @@ public class RecentlyUsedHelper {
|
|||
* @return
|
||||
*/
|
||||
public static ID[] gets(ID user, String entity, String type) {
|
||||
return gets(user, entity, type, 10);
|
||||
return gets(user, entity, type, 10, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近使用(最多10个)
|
||||
*
|
||||
* @param user
|
||||
* @param entity
|
||||
* @param type
|
||||
* @param checkFilter
|
||||
* @return
|
||||
*/
|
||||
public static ID[] gets(ID user, String entity, String type, String checkFilter) {
|
||||
return gets(user, entity, type, 10, checkFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,29 +62,34 @@ public class RecentlyUsedHelper {
|
|||
* @param entity
|
||||
* @param type
|
||||
* @param limit
|
||||
* @param checkFilter
|
||||
* @return
|
||||
*/
|
||||
public static ID[] gets(ID user, String entity, String type, int limit) {
|
||||
final String key = formatKey(user, entity, type);
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedList<ID> cached = (LinkedList<ID>) Application.getCommonsCache().getx(key);
|
||||
if (cached == null || cached.isEmpty()) {
|
||||
return ID.EMPTY_ID_ARRAY;
|
||||
}
|
||||
protected static ID[] gets(ID user, String entity, String type, int limit, String checkFilter) {
|
||||
final String ckey = formatKey(user, entity, type);
|
||||
LinkedList<ID> cached = (LinkedList<ID>) Application.getCommonsCache().getx(ckey);
|
||||
if (cached == null || cached.isEmpty()) return ID.EMPTY_ID_ARRAY;
|
||||
|
||||
Set<ID> missed = new HashSet<>();
|
||||
List<ID> data = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < limit && i < cached.size(); i++) {
|
||||
final ID raw = cached.get(i);
|
||||
if (!(raw.getEntityCode() == EntityHelper.ClassificationData
|
||||
|| Application.getPrivilegesManager().allowRead(user, raw))) {
|
||||
continue;
|
||||
|
||||
boolean allowRead = raw.getEntityCode() == EntityHelper.ClassificationData
|
||||
|| Application.getPrivilegesManager().allowRead(user, raw);
|
||||
if (!allowRead) continue;
|
||||
|
||||
// 是否符合条件
|
||||
if (checkFilter != null) {
|
||||
if (!QueryHelper.isMatchFilter(raw, checkFilter)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
String label = FieldValueHelper.getLabel(raw);
|
||||
ID clone = ID.valueOf(raw.toLiteral());
|
||||
clone.setLabel(label);
|
||||
clone.setLabel(FieldValueHelper.getLabel(raw));
|
||||
data.add(clone);
|
||||
} catch (NoRecordFoundException ex) {
|
||||
missed.add(raw);
|
||||
|
@ -78,13 +98,14 @@ public class RecentlyUsedHelper {
|
|||
|
||||
if (!missed.isEmpty()) {
|
||||
cached.removeAll(missed);
|
||||
Application.getCommonsCache().putx(key, cached);
|
||||
Application.getCommonsCache().putx(ckey, cached);
|
||||
}
|
||||
|
||||
return data.toArray(new ID[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加搜索缓存
|
||||
* 添加最近使用
|
||||
*
|
||||
* @param user
|
||||
* @param id
|
||||
|
@ -92,7 +113,6 @@ public class RecentlyUsedHelper {
|
|||
*/
|
||||
public static void add(ID user, ID id, String type) {
|
||||
final String key = formatKey(user, MetadataHelper.getEntityName(id), type);
|
||||
@SuppressWarnings("unchecked")
|
||||
LinkedList<ID> cached = (LinkedList<ID>) Application.getCommonsCache().getx(key);
|
||||
if (cached == null) {
|
||||
cached = new LinkedList<>();
|
||||
|
@ -121,6 +141,6 @@ public class RecentlyUsedHelper {
|
|||
}
|
||||
|
||||
private static String formatKey(ID user, String entity, String type) {
|
||||
return String.format("RS.%s-%s-%s", user, entity, StringUtils.defaultIfBlank(type, StringUtils.EMPTY));
|
||||
return String.format("RS31.%s-%s-%s", user, entity, StringUtils.defaultIfBlank(type, StringUtils.EMPTY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ public class RecordTransfomer extends SetUser {
|
|||
sourceDetailEntity = sourceEntity.getDetailEntity();
|
||||
Field sourceRefField;
|
||||
|
||||
// v2.10 1 > 2(主+明细)
|
||||
// v2.10 1条记录 > 2条记录(主+明细)
|
||||
if (sourceDetailEntity == null) {
|
||||
sourceDetailEntity = sourceEntity;
|
||||
sourceRefField = sourceDetailEntity.getPrimaryField();
|
||||
|
@ -120,7 +120,7 @@ public class RecordTransfomer extends SetUser {
|
|||
}
|
||||
|
||||
String sql = String.format(
|
||||
"select %s from %s where %s = '%s'",
|
||||
"select %s from %s where %s = '%s' order by autoId asc",
|
||||
sourceDetailEntity.getPrimaryField().getName(), sourceDetailEntity.getName(), sourceRefField.getName(), sourceRecordId);
|
||||
sourceDetails = Application.createQueryNoFilter(sql).array();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ 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.query.compiler.QueryCompiler;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
@ -26,7 +27,6 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.bizz.Department;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.general.ContentWithFieldVars;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -69,6 +69,9 @@ public class AdvFilterParser extends SetUser {
|
|||
// 快速查询
|
||||
private static final String MODE_QUICK = "QUICK";
|
||||
|
||||
// 名称字段 &
|
||||
private static final String NAME_FIELD_PREFIX = "" + QueryCompiler.NAME_FIELD_PREFIX;
|
||||
|
||||
final private JSONObject filterExpr;
|
||||
final private Entity rootEntity;
|
||||
// v3.1 条件值使用记录作为变量
|
||||
|
@ -107,9 +110,7 @@ public class AdvFilterParser extends SetUser {
|
|||
* @return
|
||||
*/
|
||||
public String toSqlWhere() {
|
||||
if (filterExpr == null || filterExpr.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (filterExpr == null || filterExpr.isEmpty()) return null;
|
||||
|
||||
this.includeFields = new HashSet<>();
|
||||
|
||||
|
@ -136,7 +137,7 @@ public class AdvFilterParser extends SetUser {
|
|||
index = incrIndex++;
|
||||
}
|
||||
|
||||
String itemSql = parseItem(item, values);
|
||||
String itemSql = parseItem(item, values, rootEntity);
|
||||
if (itemSql != null) {
|
||||
indexItemSqls.put(index, itemSql.trim());
|
||||
this.includeFields.add(item.getString("field"));
|
||||
|
@ -207,35 +208,36 @@ public class AdvFilterParser extends SetUser {
|
|||
*
|
||||
* @param item
|
||||
* @param values
|
||||
* @param specRootEntity
|
||||
* @return
|
||||
*/
|
||||
private String parseItem(JSONObject item, JSONObject values) {
|
||||
private String parseItem(JSONObject item, JSONObject values, Entity specRootEntity) {
|
||||
String field = item.getString("field");
|
||||
if (field.startsWith("&")) field = field.replace("&", "&"); // fix: _$unthy
|
||||
if (field.startsWith("&")) field = field.replace("&", NAME_FIELD_PREFIX); // fix: _$unthy
|
||||
|
||||
boolean hasNameFlag = field.startsWith("&");
|
||||
if (hasNameFlag) {
|
||||
field = field.substring(1);
|
||||
}
|
||||
final boolean hasNameFlag = field.startsWith(NAME_FIELD_PREFIX);
|
||||
if (hasNameFlag) field = field.substring(1);
|
||||
|
||||
Field fieldMeta = VF_ACU.equals(field) ? rootEntity.getField(EntityHelper.ApprovalLastUser)
|
||||
: MetadataHelper.getLastJoinField(rootEntity, field);
|
||||
Field fieldMeta = VF_ACU.equals(field)
|
||||
? specRootEntity.getField(EntityHelper.ApprovalLastUser)
|
||||
: MetadataHelper.getLastJoinField(specRootEntity, field);
|
||||
if (fieldMeta == null) {
|
||||
log.warn("Invalid field : {} in {}", field, rootEntity.getName());
|
||||
log.warn("Invalid field : {} in {}", field, specRootEntity.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(fieldMeta);
|
||||
|
||||
if (dt == DisplayType.CLASSIFICATION
|
||||
|| (dt == DisplayType.PICKLIST && hasNameFlag) /* 快速查询 */) {
|
||||
field = "&" + field;
|
||||
field = NAME_FIELD_PREFIX + field;
|
||||
} else if (hasNameFlag) {
|
||||
if (!(dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) {
|
||||
log.warn("Non reference-field '{}' in '{}'", field, rootEntity.getName());
|
||||
log.warn("Non reference-field : {} in {}", field, specRootEntity.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 转化为名称字段
|
||||
// 转为名称字段
|
||||
if (dt == DisplayType.REFERENCE) {
|
||||
fieldMeta = fieldMeta.getReferenceEntity().getNameField();
|
||||
dt = EasyMetaFactory.getDisplayType(fieldMeta);
|
||||
|
@ -247,20 +249,31 @@ public class AdvFilterParser extends SetUser {
|
|||
String value = useValueOfVarRecord(item.getString("value"));
|
||||
String valueEnd = null;
|
||||
|
||||
// FIXME N2N 特殊处理,仅支持 LK NLK EQ NEQ
|
||||
// exists ( in (...) )
|
||||
if (hasNameFlag && dt == DisplayType.N2NREFERENCE) {
|
||||
Entity refEntity = fieldMeta.getReferenceEntity();
|
||||
String inWhere = String.format("select %s from %s where %s %s %s",
|
||||
refEntity.getPrimaryField().getName(),
|
||||
refEntity.getName(),
|
||||
refEntity.getNameField().getName(),
|
||||
ParseHelper.convetOperation(op),
|
||||
quoteValue('%' + value + '%', FieldType.STRING));
|
||||
if (dt == DisplayType.N2NREFERENCE) {
|
||||
String inWhere = null;
|
||||
if (hasNameFlag) {
|
||||
Entity refEntity = fieldMeta.getReferenceEntity();
|
||||
Field nameField = refEntity.getNameField();
|
||||
|
||||
return String.format(
|
||||
"exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId in (%s))",
|
||||
rootEntity.getPrimaryField().getName(), fieldMeta.getName(), inWhere);
|
||||
JSONObject innerItem = (JSONObject) JSONUtils.clone(item);
|
||||
innerItem.put("field", nameField.getName());
|
||||
String innerWhereSql = parseItem(innerItem, null, refEntity);
|
||||
|
||||
inWhere = String.format("select %s from %s where %s",
|
||||
refEntity.getPrimaryField().getName(), refEntity.getName(), innerWhereSql);
|
||||
|
||||
}
|
||||
// 查询 ID,仅支持 IN
|
||||
else if (ParseHelper.IN.equals(op) && ID.isId(value)) {
|
||||
inWhere = quoteValue(value, FieldType.STRING);
|
||||
}
|
||||
|
||||
if (inWhere != null) {
|
||||
return String.format(
|
||||
"exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId in (%s))",
|
||||
specRootEntity.getPrimaryField().getName(), fieldMeta.getName(), inWhere);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据字段类型转换 `op`
|
||||
|
@ -474,7 +487,7 @@ public class AdvFilterParser extends SetUser {
|
|||
if (VF_ACU.equals(field)) {
|
||||
return String.format(
|
||||
"(exists (select recordId from RobotApprovalStep where ^%s = recordId and state = 1 and %s) and approvalState = 2)",
|
||||
rootEntity.getPrimaryField().getName(), sb.toString().replace(VF_ACU, "approver"));
|
||||
specRootEntity.getPrimaryField().getName(), sb.toString().replace(VF_ACU, "approver"));
|
||||
} else {
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -649,17 +662,17 @@ public class AdvFilterParser extends SetUser {
|
|||
return null;
|
||||
}
|
||||
|
||||
// {{xxx}}
|
||||
private static final String VAR_PATT = "\\{" + ContentWithFieldVars.PATT_VAR.pattern() + "}";
|
||||
// {@FIELD}
|
||||
private static final String VAR_PATT = "\\{@([\\w.]+)}";
|
||||
|
||||
private String useValueOfVarRecord(String value) {
|
||||
if (varRecord == null || StringUtils.isBlank(value)) return value;
|
||||
if (!value.matches(VAR_PATT)) return value;
|
||||
|
||||
String fieldName = value.substring(2, value.length() - 2);
|
||||
String fieldName = value.substring(2, value.length() - 1);
|
||||
Field field = MetadataHelper.getLastJoinField(rootEntity, fieldName);
|
||||
if (field == null) {
|
||||
log.warn("Invalid field : {} in {}", value, rootEntity.getName());
|
||||
log.warn("Invalid var-field : {} in {}", value, rootEntity.getName());
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -264,11 +264,17 @@ public class ParseHelper {
|
|||
}
|
||||
}
|
||||
|
||||
// QuickCode
|
||||
// if: QuickCode
|
||||
if (entity.containsField(EntityHelper.QuickCode)) {
|
||||
usesFields.add(EntityHelper.QuickCode);
|
||||
}
|
||||
|
||||
// if: User
|
||||
if (entity.getEntityCode() == EntityHelper.User) {
|
||||
usesFields.add("loginName");
|
||||
usesFields.add("email");
|
||||
}
|
||||
|
||||
if (usesFields.isEmpty()) {
|
||||
log.warn("No fields of search found : " + usesFields);
|
||||
}
|
||||
|
|
|
@ -39,12 +39,11 @@ public class QueryHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* 指定记录是否符合过滤条件
|
||||
*
|
||||
* @param recordId
|
||||
* @param advFilter
|
||||
* @param useVarRecord
|
||||
* @return
|
||||
* @see #isMatchFilter(ID, String)
|
||||
*/
|
||||
public static boolean isMatchAdvFilter(ID recordId, JSONObject advFilter, boolean useVarRecord) {
|
||||
if (!ParseHelper.validAdvFilter(advFilter)) return true;
|
||||
|
@ -52,15 +51,26 @@ public class QueryHelper {
|
|||
String filterSql = useVarRecord ? new AdvFilterParser(advFilter, recordId).toSqlWhere()
|
||||
: new AdvFilterParser(advFilter).toSqlWhere();
|
||||
|
||||
if (filterSql != null) {
|
||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
String sql = MessageFormat.format(
|
||||
"select {0} from {1} where {0} = ? and {2}",
|
||||
entity.getPrimaryField().getName(), entity.getName(), filterSql);
|
||||
Object[] m = Application.createQueryNoFilter(sql).setParameter(1, recordId).unique();
|
||||
return m != null;
|
||||
}
|
||||
return true;
|
||||
return isMatchFilter(recordId, filterSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定记录是否符合过滤条件
|
||||
*
|
||||
* @param recordId
|
||||
* @param filterSql
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatchFilter(ID recordId, String filterSql) {
|
||||
if (StringUtils.isBlank(filterSql)) return true;
|
||||
|
||||
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
String sql = MessageFormat.format(
|
||||
"select {0} from {1} where {0} = ? and {2}",
|
||||
entity.getPrimaryField().getName(), entity.getName(), filterSql);
|
||||
|
||||
Object[] m = Application.createQueryNoFilter(sql).setParameter(1, recordId).unique();
|
||||
return m != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,8 +129,9 @@ public class QueryHelper {
|
|||
*/
|
||||
public static List<ID> detailIdsNoFilter(ID mainId) {
|
||||
Entity detailEntity = MetadataHelper.getEntity(mainId.getEntityCode()).getDetailEntity();
|
||||
String sql = String.format("select %s from %s where %s = ?",
|
||||
detailEntity.getPrimaryField().getName(), detailEntity.getName(),
|
||||
String sql = String.format("select %s from %s where %s = ? order by autoId asc",
|
||||
detailEntity.getPrimaryField().getName(),
|
||||
detailEntity.getName(),
|
||||
MetadataHelper.getDetailToMainField(detailEntity).getName());
|
||||
|
||||
Query query = Application.createQueryNoFilter(sql).setParameter(1, mainId);
|
||||
|
|
|
@ -131,33 +131,40 @@ public class RobotTriggerObserver extends OperatingObserver {
|
|||
if (o != null) log.warn("Force clean last trigger-chain : {}", o);
|
||||
|
||||
} else {
|
||||
// 是否自己触发自己,避免无限执行
|
||||
boolean isOriginRecord = primaryId.equals(triggerSource.getOriginRecord());
|
||||
|
||||
String lastKey = triggerSource.getLastSourceKey();
|
||||
// // FIXME 20220811 此处的判断可能不需要,因为有 `trigger-chain`
|
||||
//
|
||||
// // 是否自己触发自己,避免无限执行
|
||||
// boolean isOriginRecord = primaryId.equals(triggerSource.getOriginRecord());
|
||||
//
|
||||
// String lastKey = triggerSource.getLastSourceKey();
|
||||
// triggerSource.addNext(context, when);
|
||||
// String currentKey = triggerSource.getLastSourceKey();
|
||||
//
|
||||
// if (isOriginRecord && lastKey.equals(currentKey)) {
|
||||
// if (!triggerSource.isSkipOnce()) {
|
||||
// log.warn("Self trigger, ignore : {} < {}", currentKey, lastKey);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// v3.1-b5
|
||||
triggerSource.addNext(context, when);
|
||||
String currentKey = triggerSource.getLastSourceKey();
|
||||
|
||||
if (isOriginRecord && lastKey.equals(currentKey)) {
|
||||
if (!triggerSource.isSkipOnce()) {
|
||||
log.warn("Self trigger, ignore : {} < {}", currentKey, lastKey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME 20220811 此处的判断可能不需要,因为有 `trigger-chain`
|
||||
}
|
||||
|
||||
final String sourceId = triggerSource.getSourceId();
|
||||
try {
|
||||
for (TriggerAction action : beExecuted) {
|
||||
String w = String.format("Trigger.%s [ %s ] executing on record (%s) : %s",
|
||||
sourceId, action.getType(), when.name(), primaryId);
|
||||
final int t = triggerSource.incrTriggerTimes();
|
||||
final String w = String.format("Trigger.%s.%d [ %s ] executing on record (%s) : %s",
|
||||
sourceId, t, action, when, primaryId);
|
||||
log.info(w);
|
||||
|
||||
try {
|
||||
Object res = action.execute(context);
|
||||
System.out.println("[dev] " + w + " > " + (res == null ? "N" : res));
|
||||
|
||||
boolean hasAffected = res instanceof TriggerResult && ((TriggerResult) res).hasAffected();
|
||||
System.out.println("[dev] " + w + " > " + (res == null ? "N" : res) + (hasAffected ? " < REALLY AFFECTED" : ""));
|
||||
|
||||
if (res instanceof TriggerResult) {
|
||||
if (originTriggerSource) {
|
||||
|
|
|
@ -37,10 +37,14 @@ public class TriggerResult implements JSONAware {
|
|||
this.affected = affected;
|
||||
}
|
||||
|
||||
public void setChain(TriggerSource chain) {
|
||||
protected void setChain(TriggerSource chain) {
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
public boolean hasAffected() {
|
||||
return affected != null && !affected.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJSONString() {
|
||||
JSONObject res = JSONUtils.toJSONObject("level", level);
|
||||
|
|
|
@ -32,6 +32,9 @@ public class TriggerSource {
|
|||
|
||||
private boolean skipOnce = false;
|
||||
|
||||
// 触发次数
|
||||
private int triggerTimes = 0;
|
||||
|
||||
protected TriggerSource(OperatingContext origin, TriggerWhen originAction) {
|
||||
this.id = TSNO.incrementAndGet() + "-";
|
||||
addNext(origin, originAction);
|
||||
|
@ -76,6 +79,10 @@ public class TriggerSource {
|
|||
return skipOnceHold;
|
||||
}
|
||||
|
||||
public int incrTriggerTimes() {
|
||||
return ++triggerTimes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
|
@ -9,8 +9,8 @@ package com.rebuild.core.service.trigger.aviator;
|
|||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.hutool.core.date.DateBetween;
|
||||
import cn.hutool.core.date.DateUnit;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorLong;
|
||||
import com.googlecode.aviator.runtime.type.AviatorNil;
|
||||
|
@ -84,12 +84,13 @@ public class DateDiffFunction extends AbstractFunction {
|
|||
} else {
|
||||
|
||||
long res = 0;
|
||||
DateBetween between = DateBetween.create($date1, $date2, Boolean.FALSE);
|
||||
|
||||
if (AviatorDate.DU_YEAR.equalsIgnoreCase($du)) res = DateUtil.betweenYear($date1, $date2, true);
|
||||
else if (AviatorDate.DU_MONTH.equalsIgnoreCase($du)) res = DateUtil.betweenMonth($date1, $date2, true);
|
||||
else if (AviatorDate.DU_DAY.equalsIgnoreCase($du)) res = DateUtil.betweenDay($date1, $date2, true);
|
||||
else if (AviatorDate.DU_HOUR.equalsIgnoreCase($du)) res = DateUtil.between($date1, $date2, DateUnit.HOUR, false);
|
||||
else if (AviatorDate.DU_MINUTE.equalsIgnoreCase($du)) res = DateUtil.between($date1, $date2, DateUnit.MINUTE, false);
|
||||
if (AviatorDate.DU_YEAR.equalsIgnoreCase($du)) res = between.betweenYear(Boolean.TRUE);
|
||||
else if (AviatorDate.DU_MONTH.equalsIgnoreCase($du)) res = between.betweenMonth(Boolean.TRUE);
|
||||
else if (AviatorDate.DU_DAY.equalsIgnoreCase($du)) res = between.between(DateUnit.DAY);
|
||||
else if (AviatorDate.DU_HOUR.equalsIgnoreCase($du)) res = between.between(DateUnit.HOUR);
|
||||
else if (AviatorDate.DU_MINUTE.equalsIgnoreCase($du)) res = between.between(DateUnit.MINUTE);
|
||||
|
||||
return AviatorLong.valueOf(res);
|
||||
}
|
||||
|
|
|
@ -151,6 +151,11 @@ public class FieldAggregation extends TriggerAction {
|
|||
dataFilterSql = new AdvFilterParser(dataFilter).toSqlWhere();
|
||||
}
|
||||
|
||||
String filterSql = followSourceWhere;
|
||||
if (dataFilterSql != null) {
|
||||
filterSql = String.format("( %s ) and ( %s )", followSourceWhere, dataFilterSql);
|
||||
}
|
||||
|
||||
// 构建目标记录数据
|
||||
Record targetRecord = EntityHelper.forUpdate(targetRecordId, UserService.SYSTEM_USER, false);
|
||||
|
||||
|
@ -162,11 +167,6 @@ public class FieldAggregation extends TriggerAction {
|
|||
continue;
|
||||
}
|
||||
|
||||
String filterSql = followSourceWhere;
|
||||
if (dataFilterSql != null) {
|
||||
filterSql = String.format("( %s ) and ( %s )", followSourceWhere, dataFilterSql);
|
||||
}
|
||||
|
||||
Object evalValue = new AggregationEvaluator(item, sourceEntity, filterSql).eval();
|
||||
if (evalValue == null) continue;
|
||||
|
||||
|
@ -227,10 +227,11 @@ public class FieldAggregation extends TriggerAction {
|
|||
String fillbackField = ((JSONObject) actionContext.getActionContent()).getString("fillbackField");
|
||||
if (fillbackField != null && MetadataHelper.checkAndWarnField(sourceEntity, fillbackField)) {
|
||||
String sql = String.format("select %s from %s where %s",
|
||||
sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), dataFilterSql);
|
||||
sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), filterSql);
|
||||
Object[][] fillbacks = Application.createQueryNoFilter(sql).array();
|
||||
for (Object[] fb : fillbacks) {
|
||||
Record fbRecord = EntityHelper.forUpdate((ID) fb[0], UserService.SYSTEM_USER, false);
|
||||
|
||||
for (Object[] to : fillbacks) {
|
||||
Record fbRecord = EntityHelper.forUpdate((ID) to[0], UserService.SYSTEM_USER, false);
|
||||
fbRecord.setID(fillbackField, targetRecordId);
|
||||
|
||||
// FIXME 回填仅更新,无业务规则
|
||||
|
|
|
@ -102,6 +102,8 @@ public class FieldWriteback extends FieldAggregation {
|
|||
final boolean forceUpdate = ((JSONObject) actionContext.getActionContent()).getBooleanValue("forceUpdate");
|
||||
|
||||
List<ID> affected = new ArrayList<>();
|
||||
boolean targetSame = false;
|
||||
|
||||
for (ID targetRecordId : targetRecordIds) {
|
||||
if (operatingContext.getAction() == BizzPermission.DELETE
|
||||
&& targetRecordId.equals(operatingContext.getAnyRecord().getPrimary())) {
|
||||
|
@ -116,6 +118,7 @@ public class FieldWriteback extends FieldAggregation {
|
|||
// 相等则不更新
|
||||
if (isCurrentSame(targetRecord)) {
|
||||
log.info("Ignore execution because the record are same : {}", targetRecordId);
|
||||
targetSame = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -145,7 +148,8 @@ public class FieldWriteback extends FieldAggregation {
|
|||
}
|
||||
}
|
||||
|
||||
return TriggerResult.success(affected);
|
||||
if (targetSame && affected.isEmpty()) return TriggerResult.targetSame();
|
||||
else return TriggerResult.success(affected);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,9 +43,9 @@ public class CommonsLock {
|
|||
* @return
|
||||
*/
|
||||
public static Object[] getLockedUserFormat(ID source) {
|
||||
ID l = getLockedUser(source);
|
||||
if (l == null) return null;
|
||||
return new Object[] { l, UserHelper.getName(l) };
|
||||
ID u = getLockedUser(source);
|
||||
if (u == null) return null;
|
||||
return new Object[] { u, UserHelper.getName(u) };
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,16 +78,15 @@ public class CommonsLock {
|
|||
|
||||
if (lockedUser.equals(unlockUser)) {
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select lockId from CommonsLock where source = ?")
|
||||
"select lockId from CommonsLock where source = ?")
|
||||
.setParameter(1, source)
|
||||
.unique();
|
||||
if (o != null) {
|
||||
Application.getCommonsService().delete((ID) o[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.support;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.ThreadPool;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.support.task.TaskExecutors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
|
@ -67,12 +67,6 @@ public class CommonsLog {
|
|||
clog.setDate("logTime", CalendarUtils.now());
|
||||
if (content != null) clog.setString("logContent", content);
|
||||
|
||||
ThreadPool.exec(() -> {
|
||||
try {
|
||||
Application.getCommonsService().create(clog);
|
||||
} catch (Throwable ex) {
|
||||
log.error("Cannot create common-log: {}", clog, ex);
|
||||
}
|
||||
});
|
||||
TaskExecutors.queue(() -> Application.getCommonsService().create(clog, false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ public final class License {
|
|||
private static JSONObject siteApi(String api, int t, String domain) {
|
||||
if (t > 0) {
|
||||
JSONObject c = MCACHED.get(api, t);
|
||||
if (c != null) return c;
|
||||
if (c != null) return c.clone();
|
||||
}
|
||||
|
||||
Map<String, String> hs = new HashMap<>();
|
||||
|
@ -140,7 +140,8 @@ public final class License {
|
|||
} else {
|
||||
MCACHED.put(api, o);
|
||||
}
|
||||
return o;
|
||||
return o.clone();
|
||||
|
||||
} else {
|
||||
log.error("Bad result format : {}", result);
|
||||
}
|
||||
|
|
|
@ -260,6 +260,9 @@ public class RebuildConfiguration extends KVStorage {
|
|||
* @return
|
||||
*/
|
||||
public static void set(ConfigurationItem name, Object value) {
|
||||
if (ConfigurationItem.DataDirectory == name || ConfigurationItem.RedisDatabase == name) {
|
||||
throw new SecurityException("Attack configuration detected : " + name + "=" + value);
|
||||
}
|
||||
setValue(name.name(), value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import com.rebuild.core.metadata.easymeta.EasyField;
|
|||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.bizz.ZeroEntry;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
|
@ -80,6 +82,9 @@ public class DataListWrapper {
|
|||
|
||||
if (user != null) {
|
||||
this.useDesensitized = !Application.getPrivilegesManager().allow(user, ZeroEntry.AllowNoDesensitized);
|
||||
if (!this.useDesensitized) {
|
||||
this.useDesensitized = UserHelper.isAdmin(user) && RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,9 @@ import com.rebuild.core.privileges.bizz.ZeroEntry;
|
|||
import com.rebuild.core.service.NoRecordFoundException;
|
||||
import com.rebuild.core.service.approval.ApprovalState;
|
||||
import com.rebuild.core.service.approval.ApprovalStepService;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.DataDesensitized;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -94,8 +96,10 @@ public class FieldValueHelper {
|
|||
* @see EasyField#wrapValue(Object)
|
||||
*/
|
||||
public static Object wrapFieldValue(Object value, EasyField field) {
|
||||
final DisplayType dt = field.getDisplayType();
|
||||
|
||||
if (value != null && !field.isQueryable() &&
|
||||
(field.getDisplayType() == DisplayType.TEXT || field.getDisplayType() == DisplayType.NTEXT)) {
|
||||
(dt == DisplayType.TEXT || dt == DisplayType.NTEXT)) {
|
||||
return DataDesensitized.SECURE_TEXT;
|
||||
}
|
||||
|
||||
|
@ -111,7 +115,7 @@ public class FieldValueHelper {
|
|||
}
|
||||
|
||||
// 非 ID 数组表示记录主键
|
||||
if (field.getDisplayType() == DisplayType.N2NREFERENCE && value instanceof ID) {
|
||||
if (dt == DisplayType.N2NREFERENCE && value instanceof ID) {
|
||||
value = N2NReferenceSupport.items(field.getRawMeta(), (ID) value);
|
||||
}
|
||||
|
||||
|
@ -275,8 +279,14 @@ public class FieldValueHelper {
|
|||
return false;
|
||||
}
|
||||
|
||||
return field.isDesensitized()
|
||||
&& !Application.getPrivilegesManager().allow(user, ZeroEntry.AllowNoDesensitized);
|
||||
if (field.isDesensitized()) {
|
||||
if (UserHelper.isAdmin(user) && RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced)) {
|
||||
return true;
|
||||
} else {
|
||||
return !Application.getPrivilegesManager().allow(user, ZeroEntry.AllowNoDesensitized);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,15 +46,20 @@ import java.util.*;
|
|||
public class ProtocolFilterParser {
|
||||
|
||||
// 协议
|
||||
|
||||
// via:xxx:[field]
|
||||
public static final String P_VIA = "via";
|
||||
// ref:xxx:[id]
|
||||
public static final String P_REF = "ref";
|
||||
// category:entity:value
|
||||
public static final String P_CATEGORY = "category";
|
||||
// related:field:id
|
||||
public static final String P_RELATED = "related";
|
||||
|
||||
final private String protocolExpr;
|
||||
|
||||
/**
|
||||
* @param protocolExpr via:xxx:[field] ref:xxx:[id] category:entity:value related:field:id
|
||||
* @param protocolExpr
|
||||
*/
|
||||
public ProtocolFilterParser(String protocolExpr) {
|
||||
this.protocolExpr = protocolExpr;
|
||||
|
@ -142,9 +147,14 @@ public class ProtocolFilterParser {
|
|||
|
||||
List<String> sqls = new ArrayList<>();
|
||||
|
||||
JSONObject advFilter = getFieldDataFilter(field);
|
||||
if (advFilter != null) sqls.add(new AdvFilterParser(advFilter).toSqlWhere());
|
||||
// 字段附加过滤条件
|
||||
JSONObject fieldFilter = getFieldDataFilter(field);
|
||||
if (ParseHelper.validAdvFilter(fieldFilter)) {
|
||||
String s = new AdvFilterParser(fieldFilter).toSqlWhere();
|
||||
if (StringUtils.isNotBlank(s)) sqls.add(s);
|
||||
}
|
||||
|
||||
// 父级级联字段
|
||||
if (hasFieldCascadingField(field) && ID.isId(cascadingValue)) {
|
||||
String cascadingFieldParent = field.getExtraAttrs().getString("_cascadingFieldParent");
|
||||
String cascadingFieldChild = field.getExtraAttrs().getString("_cascadingFieldChild");
|
||||
|
@ -153,15 +163,16 @@ public class ProtocolFilterParser {
|
|||
String[] fs = cascadingFieldParent.split(MetadataHelper.SPLITER_RE);
|
||||
sqls.add(String.format("%s = '%s'", fs[1], cascadingValue));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(cascadingFieldChild)) {
|
||||
String[] fs = cascadingFieldChild.split(MetadataHelper.SPLITER_RE);
|
||||
Entity refEntity = entity.getField(fs[0]).getReferenceEntity();
|
||||
|
||||
String sql = String.format("exists (select %s from %s where ^%s = %s and %s = '%s')",
|
||||
String s = 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);
|
||||
sqls.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +185,7 @@ public class ProtocolFilterParser {
|
|||
* @param value
|
||||
* @return
|
||||
* @see #P_CATEGORY
|
||||
* @see AdvFilterParser#parseItem(JSONObject, JSONObject)
|
||||
* @see DataListCategory
|
||||
*/
|
||||
protected String parseCategory(String entity, String value) {
|
||||
Entity rootEntity = MetadataHelper.getEntity(entity);
|
||||
|
|
|
@ -184,21 +184,21 @@ public class QueryParser {
|
|||
wheres.add(defaultFilter);
|
||||
}
|
||||
|
||||
// appends ProtocolFilter
|
||||
// append: ProtocolFilter
|
||||
String protocolFilter = queryExpr.getString("protocolFilter");
|
||||
if (StringUtils.isNotBlank(protocolFilter)) {
|
||||
String where = new ProtocolFilterParser(protocolFilter).toSqlWhere();
|
||||
if (StringUtils.isNotBlank(where)) wheres.add(where);
|
||||
}
|
||||
|
||||
// appends AdvFilter
|
||||
// append: AdvFilter
|
||||
String advFilter = queryExpr.getString("advFilter");
|
||||
if (ID.isId(advFilter)) {
|
||||
String where = parseAdvFilter(ID.valueOf(advFilter));
|
||||
if (StringUtils.isNotBlank(where)) wheres.add(where);
|
||||
}
|
||||
|
||||
// appends QuickQuery
|
||||
// append: QuickQuery
|
||||
JSONObject quickFilter = queryExpr.getJSONObject("filter");
|
||||
if (quickFilter != null) {
|
||||
String where = new AdvFilterParser(quickFilter, entity).toSqlWhere();
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.core.support.integration;
|
|||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.qiniu.common.QiniuException;
|
||||
|
@ -252,29 +253,21 @@ public class QiniuCloud {
|
|||
* @see #parseFileName(String)
|
||||
*/
|
||||
public static String formatFileKey(String fileName, boolean keepName) {
|
||||
if (!keepName) {
|
||||
String[] fileNameSplit = fileName.split("\\.");
|
||||
fileName = CommonsUtils.randomHex(true);
|
||||
if (fileNameSplit.length > 1 && StringUtils.isNotBlank(fileNameSplit[fileNameSplit.length - 1])) {
|
||||
fileName += "." + fileNameSplit[fileNameSplit.length - 1];
|
||||
}
|
||||
|
||||
} else {
|
||||
if (keepName) {
|
||||
while (fileName.contains("__")) {
|
||||
fileName = fileName.replace("__", "_");
|
||||
}
|
||||
if (fileName.contains("+")) {
|
||||
fileName = fileName.replace("+", "");
|
||||
}
|
||||
if (fileName.contains("#")) {
|
||||
fileName = fileName.replace("#", "");
|
||||
}
|
||||
if (fileName.contains("?")) {
|
||||
fileName = fileName.replace("?", "");
|
||||
}
|
||||
// 去除特殊符号
|
||||
fileName = fileName.replaceAll("[&+#?%=/\\s]", "");
|
||||
|
||||
if (fileName.length() > 41) {
|
||||
fileName = fileName.substring(0, 20) + "-" + fileName.substring(fileName.length() - 20);
|
||||
}
|
||||
|
||||
} else {
|
||||
String fileExt = FileUtil.getSuffix(fileName);
|
||||
fileName = CommonsUtils.randomHex(true);
|
||||
if (StringUtils.isNotBlank(fileExt)) fileName += "." + fileExt;
|
||||
}
|
||||
|
||||
String datetime = CalendarUtils.getDateFormat("yyyyMMddHHmmssSSS").format(CalendarUtils.now());
|
||||
|
|
|
@ -60,7 +60,7 @@ public class SMSender {
|
|||
try {
|
||||
sendMail(to, subject, content);
|
||||
} catch (Exception ex) {
|
||||
log.error("Mail failed to send : " + to + " < " + subject, ex);
|
||||
log.error("Mail failed to send : {} < {}", to, subject, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -97,11 +97,18 @@ public class SMSender {
|
|||
|
||||
Objects.requireNonNull(mailbody.selectFirst(".rb-title")).text(subject);
|
||||
Objects.requireNonNull(mailbody.selectFirst(".rb-content")).html(content);
|
||||
|
||||
// 处理变量
|
||||
String htmlContent = mailbody.html();
|
||||
// 处理公共变量
|
||||
htmlContent = htmlContent.replace("%TO%", to);
|
||||
htmlContent = htmlContent.replace("%TIME%", CalendarUtils.getUTCDateTimeFormat().format(CalendarUtils.now()));
|
||||
htmlContent = htmlContent.replace("%APPNAME%", RebuildConfiguration.get(ConfigurationItem.AppName));
|
||||
htmlContent = htmlContent.replace("%APPURL%", RebuildConfiguration.getHomeUrl());
|
||||
htmlContent = htmlContent.replace("%APPLOGO%", RebuildConfiguration.getHomeUrl("commons/theme/use-logo"));
|
||||
if (License.isCommercial()) {
|
||||
htmlContent = htmlContent.replace("%APPNAME%", RebuildConfiguration.get(ConfigurationItem.AppName));
|
||||
} else {
|
||||
htmlContent = htmlContent.replace("%APPNAME%", "REBUILD");
|
||||
}
|
||||
|
||||
String pageFooter = RebuildConfiguration.get(ConfigurationItem.PageFooter);
|
||||
if (StringUtils.isNotBlank(pageFooter)) {
|
||||
|
@ -124,7 +131,7 @@ public class SMSender {
|
|||
return emailId;
|
||||
|
||||
} catch (EmailException ex) {
|
||||
log.error("SMTP failed to send : " + to + " > " + subject, ex);
|
||||
log.error("SMTP failed to send : {} > {}", to, subject, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +157,7 @@ public class SMSender {
|
|||
String r = OkHttpUtils.post("https://api-v4.mysubmail.com/mail/send.json", params);
|
||||
rJson = JSON.parseObject(r);
|
||||
} catch (Exception ex) {
|
||||
log.error("Submail failed to send : " + to + " > " + subject, ex);
|
||||
log.error("Submail failed to send : {} > {}", to, subject, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -161,7 +168,7 @@ public class SMSender {
|
|||
return sendId;
|
||||
|
||||
} else {
|
||||
log.error("Mail failed to send : " + to + " > " + subject + "\nError : " + rJson);
|
||||
log.error("Mail failed to send : {} > {}\nError : {}", to, subject, rJson);
|
||||
createLog(to, logContent, TYPE_EMAIL, null, rJson.getString("msg"));
|
||||
return null;
|
||||
}
|
||||
|
@ -203,21 +210,13 @@ public class SMSender {
|
|||
* @return
|
||||
*/
|
||||
protected static Element getMailTemplate() {
|
||||
if (MT_CACHE != null && !Application.devMode()) return MT_CACHE.clone();
|
||||
if (MT_CACHE != null) return MT_CACHE.clone();
|
||||
|
||||
String content = CommonsUtils.getStringOfRes("i18n/email.zh_CN.html");
|
||||
Assert.notNull(content, "Cannot load template of email");
|
||||
|
||||
// 生硬替换
|
||||
if (Application.isReady() && License.getCommercialType() > 10) {
|
||||
content = content.replace("REBUILD", RebuildConfiguration.get(ConfigurationItem.AppName));
|
||||
content = content.replace("https://getrebuild.com/img/logo.png", RebuildConfiguration.getHomeUrl("commons/theme/use-logo"));
|
||||
content = content.replace("https://getrebuild.com/", RebuildConfiguration.getHomeUrl());
|
||||
}
|
||||
|
||||
Document html = Jsoup.parse(content);
|
||||
MT_CACHE = html.body();
|
||||
|
||||
MT_CACHE = html.body();
|
||||
return MT_CACHE.clone();
|
||||
}
|
||||
|
||||
|
@ -230,7 +229,7 @@ public class SMSender {
|
|||
try {
|
||||
sendSMS(to, content);
|
||||
} catch (Exception ex) {
|
||||
log.error("SMS failed to send : " + to, ex);
|
||||
log.error("SMS failed to send : {}", to, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -275,7 +274,7 @@ public class SMSender {
|
|||
String r = OkHttpUtils.post("https://api-v4.mysubmail.com/sms/send.json", params);
|
||||
rJson = JSON.parseObject(r);
|
||||
} catch (Exception ex) {
|
||||
log.error("Subsms failed to send : " + to + " > " + content, ex);
|
||||
log.error("Subsms failed to send : {} > {}", to, content, ex);
|
||||
return null;
|
||||
} finally {
|
||||
HeavyStopWatcher.clean();
|
||||
|
@ -287,12 +286,13 @@ public class SMSender {
|
|||
return sendId;
|
||||
|
||||
} else {
|
||||
log.error("SMS failed to send : " + to + " > " + content + "\nError : " + rJson);
|
||||
log.error("SMS failed to send : {} > {}\nError : {}", to, content, rJson);
|
||||
createLog(to, content, TYPE_SMS, null, rJson.getString("msg"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// @see com.rebuild.core.support.CommonsLog
|
||||
private static void createLog(String to, String content, int type, String sentid, String error) {
|
||||
if (!Application.isReady()) return;
|
||||
|
||||
|
@ -307,6 +307,7 @@ public class SMSender {
|
|||
slog.setString("sendResult",
|
||||
CommonsUtils.maxstr("ERR:" + StringUtils.defaultIfBlank(error, "Unknow"), 200));
|
||||
}
|
||||
|
||||
Application.getCommonsService().create(slog);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.apache.commons.lang.StringUtils;
|
|||
import org.springframework.util.Assert;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.args.FlushMode;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
|
@ -518,7 +519,8 @@ public class Installer implements InstallState {
|
|||
public static void clearAllCache() {
|
||||
if (isUseRedis()) {
|
||||
try (Jedis jedis = Application.getCommonsCache().getJedisPool().getResource()) {
|
||||
jedis.flushAll();
|
||||
// Delete all the keys of the currently selected DB
|
||||
jedis.flushDB(FlushMode.SYNC);
|
||||
}
|
||||
} else {
|
||||
Application.getCommonsCache().getEhcacheCache().clear();
|
||||
|
|
|
@ -105,13 +105,10 @@ public class AppUtils {
|
|||
* @param refreshToken 是否需要刷新 Token 有效期
|
||||
* @return null or UserID
|
||||
*/
|
||||
public static ID getRequestUserViaToken(HttpServletRequest request, boolean refreshToken) {
|
||||
protected static ID getRequestUserViaToken(HttpServletRequest request, boolean refreshToken) {
|
||||
String authToken = request.getHeader(HF_AUTHTOKEN);
|
||||
ID user = authToken == null ? null : AuthTokenManager.verifyToken(authToken);
|
||||
if (user != null && refreshToken) {
|
||||
AuthTokenManager.refreshAccessToken(authToken);
|
||||
}
|
||||
return user;
|
||||
return authToken == null
|
||||
? null : AuthTokenManager.verifyToken(authToken, Boolean.FALSE, refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,6 +44,20 @@ public class ImageView2 {
|
|||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param img
|
||||
* @return
|
||||
*/
|
||||
public File thumbQuietly(File img) {
|
||||
try {
|
||||
File thumb = thumb(img);
|
||||
return thumb != null && thumb.exists() ? thumb : img;
|
||||
} catch (Exception ex) {
|
||||
log.warn("Image thumb failed : {}", img, ex);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param img
|
||||
* @return
|
||||
|
|
|
@ -45,41 +45,42 @@ public class LocationUtils {
|
|||
* @return
|
||||
*/
|
||||
public static JSON getLocation(String ip, boolean useCache) {
|
||||
ip = ip.split(",")[0];
|
||||
|
||||
if (PRIVATE_IP.matcher(ip).find()) {
|
||||
return JSONUtils.toJSONObject(new String[] { "ip", "country"}, new String[] { ip, "R" });
|
||||
}
|
||||
|
||||
final String ckey = "IPLocation31" + ip;
|
||||
|
||||
JSONObject result;
|
||||
if (useCache && Application.isReady()) {
|
||||
result = (JSONObject) Application.getCommonsCache().getx("IPLocation2" + ip);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
result = (JSONObject) Application.getCommonsCache().getx(ckey);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
result = new JSONObject();
|
||||
result.put("ip", ip);
|
||||
result.put("country", "N");
|
||||
|
||||
JSONObject fetchTry;
|
||||
JSONObject fetchTry = getJSON(String.format("http://ip-api.com/json/%s", ip));
|
||||
if (fetchTry != null) {
|
||||
String message = fetchTry.getString("message");
|
||||
if (fetchTry.getString("countryCode") != null) {
|
||||
result.put("country", fetchTry.getString("countryCode"));
|
||||
result.put("region", fetchTry.getString("regionName"));
|
||||
result.put("city", fetchTry.getString("city"));
|
||||
} else if (message != null && (message.contains("private") || message.contains("reserved"))) {
|
||||
result.put("country", "R");
|
||||
}
|
||||
|
||||
// // #1
|
||||
// fetchTry = getJSON(String.format("https://ip.taobao.com/outGetIpInfo?ip=%s&accessKey=alibaba-inc", ip));
|
||||
// if (fetchTry != null && fetchTry.getIntValue("code") == 0) {
|
||||
// fetchTry = fetchTry.getJSONObject("data");
|
||||
// String c = fetchTry.getString("country");
|
||||
// if ("local".equalsIgnoreCase(fetchTry.getString("isp_id")) || "xx".equalsIgnoreCase(c)) {
|
||||
// result.put("country", "R");
|
||||
// } else {
|
||||
// result.put("country", "xx".equalsIgnoreCase(c) ? "" : c);
|
||||
// c = fetchTry.getString("region");
|
||||
// result.put("region", "xx".equalsIgnoreCase(c) ? "" : c);
|
||||
// c = fetchTry.getString("city");
|
||||
// result.put("city", "xx".equalsIgnoreCase(c) ? "" : c);
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
if (Application.isReady()) {
|
||||
Application.getCommonsCache().putx(ckey, result, CommonsCache.TS_DAY * 90);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// #2
|
||||
// try
|
||||
fetchTry = getJSON(String.format("https://ipapi.co/%s/json/", ip));
|
||||
if (fetchTry != null) {
|
||||
if (fetchTry.getString("country") != null) {
|
||||
|
@ -89,30 +90,10 @@ public class LocationUtils {
|
|||
} else if (fetchTry.getBooleanValue("reserved")) {
|
||||
result.put("country", "R");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// #3
|
||||
fetchTry = getJSON(String.format("http://ip-api.com/json/%s", ip));
|
||||
if (fetchTry != null) {
|
||||
String message = fetchTry.getString("message");
|
||||
if (fetchTry.getString("countryCode") != null) {
|
||||
result.put("country", fetchTry.getString("countryCode"));
|
||||
result.put("region", fetchTry.getString("regionName"));
|
||||
result.put("city", fetchTry.getString("city"));
|
||||
return result;
|
||||
} else if (message != null && (message.contains("private") || message.contains("reserved"))) {
|
||||
result.put("country", "R");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.getString("country") == null) {
|
||||
result.put("country", "N");
|
||||
}
|
||||
|
||||
if (Application.isReady()) {
|
||||
Application.getCommonsCache().putx("IPLocation2" + ip, result, CommonsCache.TS_DAY * 90);
|
||||
Application.getCommonsCache().putx(ckey, result, CommonsCache.TS_DAY * 90);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -43,4 +43,12 @@ public class RbAssert {
|
|||
throw new DefinedException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression
|
||||
* @see #is(boolean, String)
|
||||
*/
|
||||
public static void checkAllow(boolean expression) {
|
||||
is(expression, "Not Allow");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,11 +211,12 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver
|
|||
*/
|
||||
protected static String getRequestUrls(HttpServletRequest request) {
|
||||
String reqUrl = request.getRequestURL().toString();
|
||||
if (StringUtils.isNotBlank(request.getQueryString())) reqUrl += "?" + request.getQueryString();
|
||||
String refUrl = ServletUtils.getReferer(request);
|
||||
|
||||
if (refUrl == null) return reqUrl;
|
||||
else if (reqUrl.endsWith("/error")) return refUrl;
|
||||
else return reqUrl + " with " + refUrl;
|
||||
else return reqUrl + " via " + refUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,11 +86,11 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
request.setAttribute(WebConstants.LOCALE, requestEntry.getLocale());
|
||||
request.setAttribute(WebConstants.$BUNDLE, Application.getLanguage().getBundle(requestEntry.getLocale()));
|
||||
|
||||
final String requestUri = requestEntry.getRequestUri();
|
||||
final String requestUrl = requestEntry.getRequestUrl();
|
||||
|
||||
// 服务暂不可用
|
||||
if (!Application.isReady()) {
|
||||
final boolean isError = requestUri.endsWith("/error") || requestUri.contains("/error/");
|
||||
final boolean isError = requestUrl.endsWith("/error") || requestUrl.contains("/error/");
|
||||
|
||||
// 已安装
|
||||
if (checkInstalled()) {
|
||||
|
@ -104,7 +104,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
}
|
||||
}
|
||||
// 未安装
|
||||
else if (!(requestUri.contains("/setup/") || requestUri.contains("/commons/theme/") || isError)) {
|
||||
else if (!(requestUrl.contains("/setup/") || requestUrl.contains("/commons/theme/") || isError)) {
|
||||
sendRedirect(response, "/setup/install", null);
|
||||
return false;
|
||||
} else {
|
||||
|
@ -120,9 +120,9 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
if (requestUser != null) {
|
||||
|
||||
// 管理中心二次验证
|
||||
if (requestUri.contains("/admin/") && !AppUtils.isAdminVerified(request)) {
|
||||
if (requestUrl.contains("/admin/") && !AppUtils.isAdminVerified(request)) {
|
||||
if (isHtmlRequest(request)) {
|
||||
sendRedirect(response, "/user/admin-verify", requestUri);
|
||||
sendRedirect(response, "/user/admin-verify", requestEntry.getRequestUri());
|
||||
} else {
|
||||
response.sendError(HttpStatus.FORBIDDEN.value());
|
||||
}
|
||||
|
@ -133,8 +133,6 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|
||||
// User
|
||||
request.setAttribute(WebConstants.$USER, Application.getUserStore().getUser(requestUser));
|
||||
request.setAttribute(ZeroEntry.AllowCustomNav.name(),
|
||||
Application.getPrivilegesManager().allow(requestUser, ZeroEntry.AllowCustomNav));
|
||||
|
||||
if (isHtmlRequest(request)) {
|
||||
// Last active
|
||||
|
@ -144,25 +142,28 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
String sidebarCollapsed = ServletUtils.readCookie(request, "rb.sidebarCollapsed");
|
||||
String sideCollapsedClazz = BooleanUtils.toBoolean(sidebarCollapsed) ? "rb-collapsible-sidebar-collapsed" : "";
|
||||
// Aside collapsed
|
||||
if (!(requestUri.contains("/admin/") || requestUri.contains("/setup/"))) {
|
||||
if (!(requestUrl.contains("/admin/") || requestUrl.contains("/setup/"))) {
|
||||
String asideCollapsed = ServletUtils.readCookie(request, "rb.asideCollapsed");
|
||||
if (BooleanUtils.toBoolean(asideCollapsed)) sideCollapsedClazz += " rb-aside-collapsed";
|
||||
}
|
||||
request.setAttribute("sideCollapsedClazz", sideCollapsedClazz);
|
||||
|
||||
request.setAttribute(ZeroEntry.AllowCustomNav.name(),
|
||||
Application.getPrivilegesManager().allow(requestUser, ZeroEntry.AllowCustomNav));
|
||||
}
|
||||
|
||||
// 非增强安全超管可访问
|
||||
if (RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced)) skipCheckSafeUse = false;
|
||||
else skipCheckSafeUse = UserHelper.isSuperAdmin(requestUser);
|
||||
|
||||
} else if (!isIgnoreAuth(requestUri)) {
|
||||
} else if (!isIgnoreAuth(requestUrl)) {
|
||||
// 独立验证逻辑
|
||||
if (requestUri.contains("/filex/")) return true;
|
||||
if (requestUrl.contains("/filex/")) return true;
|
||||
|
||||
log.warn("Unauthorized access {}", RebuildWebConfigurer.getRequestUrls(request));
|
||||
|
||||
if (isHtmlRequest(request)) {
|
||||
sendRedirect(response, "/user/login", requestUri);
|
||||
sendRedirect(response, "/user/login", requestEntry.getRequestUri());
|
||||
} else {
|
||||
response.sendError(HttpStatus.UNAUTHORIZED.value());
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
skipCheckSafeUse = true;
|
||||
}
|
||||
|
||||
if (!skipCheckSafeUse) checkSafeUse(ipAddr, requestUri);
|
||||
if (!skipCheckSafeUse) checkSafeUse(ipAddr, requestEntry.getRequestUri());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -258,7 +259,8 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|| requestUri.startsWith("/commons/barcode/render")
|
||||
|| requestUri.startsWith("/commons/theme/")
|
||||
|| requestUri.startsWith("/account/user-avatar/")
|
||||
|| requestUri.startsWith("/rbmob/env");
|
||||
|| requestUri.startsWith("/rbmob/env")
|
||||
|| requestUri.endsWith("/logout");
|
||||
}
|
||||
|
||||
private boolean isHtmlRequest(HttpServletRequest request) {
|
||||
|
@ -313,6 +315,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
private static class RequestEntry {
|
||||
final long requestTime;
|
||||
final String requestUri;
|
||||
final String requestUrl;
|
||||
final ID requestUser;
|
||||
final String locale;
|
||||
|
||||
|
@ -320,6 +323,7 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
this.requestTime = System.currentTimeMillis();
|
||||
this.requestUri = request.getRequestURI()
|
||||
+ (request.getQueryString() != null ? ("?" + request.getQueryString()) : "");
|
||||
this.requestUrl = request.getRequestURI();
|
||||
this.requestUser = AppUtils.getRequestUser(request, true);
|
||||
this.locale = locale;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class AdminCLI2 {
|
|||
* @return
|
||||
*/
|
||||
public String exec() {
|
||||
if (this.commands.length == 0) return "Bad command";
|
||||
if (this.commands.length == 0) return "WRAN: Bad command";
|
||||
|
||||
String result = null;
|
||||
switch (commands[0]) {
|
||||
|
@ -92,7 +92,7 @@ public class AdminCLI2 {
|
|||
}
|
||||
}
|
||||
|
||||
return StringUtils.defaultIfBlank(result, "Unknown command : " + commands[0]);
|
||||
return StringUtils.defaultIfBlank(result, "WRAN: Unknown command : `" + commands[0] + "`");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +101,7 @@ public class AdminCLI2 {
|
|||
* @return
|
||||
*/
|
||||
protected String execCache() {
|
||||
if (commands.length < 2) return "Bad arguments";
|
||||
if (commands.length < 2) return "WRAN: Bad arguments";
|
||||
|
||||
String result = SUCCESS;
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class AdminCLI2 {
|
|||
if ("clean".equals(name)) {
|
||||
Installer.clearAllCache();
|
||||
} else {
|
||||
result = "Bad arguments";
|
||||
result = "WRAN: Bad arguments";
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -122,7 +122,7 @@ public class AdminCLI2 {
|
|||
* @see ConfigurationItem
|
||||
*/
|
||||
protected String execSyscfg() {
|
||||
if (commands.length < 2) return "Bad arguments";
|
||||
if (commands.length < 2) return "WRAN: Bad arguments";
|
||||
|
||||
String name = commands[1];
|
||||
try {
|
||||
|
@ -142,18 +142,19 @@ public class AdminCLI2 {
|
|||
}
|
||||
|
||||
ConfigurationItem item = ConfigurationItem.valueOf(name);
|
||||
// Get
|
||||
// Getter
|
||||
if (commands.length == 2) {
|
||||
return RebuildConfiguration.get(item);
|
||||
}
|
||||
|
||||
// Set
|
||||
String value = commands[2];
|
||||
RebuildConfiguration.set(item, value);
|
||||
return "OK";
|
||||
// Setter
|
||||
else {
|
||||
String value = commands[2];
|
||||
RebuildConfiguration.set(item, value);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return "Bad arguments [1] : " + name;
|
||||
return "WRAN: Bad arguments [1] : " + name;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,10 +183,10 @@ public class AdminCLI2 {
|
|||
result.add("Backup datafile : " + backup);
|
||||
}
|
||||
|
||||
return result.isEmpty() ? "Nothing backup" : StringUtils.join(result, "\n");
|
||||
return result.isEmpty() ? "WRAN: Nothing to backup" : StringUtils.join(result, "\n");
|
||||
|
||||
} catch (Exception ex) {
|
||||
return "Exec failed : " + ex.getLocalizedMessage();
|
||||
return "WRAN: Exec failed `" + ex.getLocalizedMessage() + "`";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +196,7 @@ public class AdminCLI2 {
|
|||
* @return
|
||||
*/
|
||||
protected String execAes() {
|
||||
if (commands.length < 2) return "Bad arguments";
|
||||
if (commands.length < 2) return "WRAN: Bad arguments";
|
||||
|
||||
String value = commands.length > 2 ? commands[2] : commands[1];
|
||||
if ("decrypt".equalsIgnoreCase(commands[1])) {
|
||||
|
|
|
@ -79,6 +79,7 @@ public class MetaEntityController extends BaseController {
|
|||
|
||||
mv.getModel().put("nameField", metaEntity.getNameField().getName());
|
||||
|
||||
mv.getModel().put("currentEntity", metaEntity.getName());
|
||||
if (metaEntity.getMainEntity() != null) {
|
||||
mv.getModel().put("mainEntity", metaEntity.getMainEntity().getName());
|
||||
mv.getModel().put("detailEntity", metaEntity.getName());
|
||||
|
|
|
@ -159,6 +159,7 @@ public class MetaFieldController extends BaseController {
|
|||
|
||||
String entityName = reqJson.getString("entity");
|
||||
String label = reqJson.getString("label");
|
||||
String name = reqJson.getString("name");
|
||||
String type = reqJson.getString("type");
|
||||
String comments = reqJson.getString("comments");
|
||||
String refEntity = reqJson.getString("refEntity");
|
||||
|
@ -182,7 +183,7 @@ public class MetaFieldController extends BaseController {
|
|||
}
|
||||
|
||||
try {
|
||||
String fieldName = new Field2Schema().createField(entity, label, dt, comments, refEntity, extConfig);
|
||||
String fieldName = new Field2Schema().createField(entity, label, name, dt, comments, refEntity, extConfig);
|
||||
return RespBody.ok(fieldName);
|
||||
|
||||
} catch (Exception ex) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.support.setup.InstallState;
|
|||
import com.rebuild.core.support.setup.Installer;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import com.rebuild.web.BaseController;
|
||||
import com.rebuild.web.user.signup.LoginController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -34,7 +35,6 @@ import redis.clients.jedis.JedisPool;
|
|||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
|
@ -51,12 +51,8 @@ import java.sql.SQLException;
|
|||
public class InstallController extends BaseController implements InstallState {
|
||||
|
||||
@GetMapping("install")
|
||||
public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
if (Application.isReady() && !Application.devMode()) {
|
||||
response.sendError(404);
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModelAndView index(HttpServletRequest request) throws IOException {
|
||||
RbAssert.checkAllow(checkInstalled());
|
||||
ModelAndView mv = createModelAndView("/admin/setup/install");
|
||||
mv.getModel().put("Version", Application.VER);
|
||||
|
||||
|
@ -68,6 +64,7 @@ public class InstallController extends BaseController implements InstallState {
|
|||
|
||||
@PostMapping("test-connection")
|
||||
public RespBody testConnection(HttpServletRequest request) {
|
||||
RbAssert.checkAllow(checkInstalled());
|
||||
JSONObject dbProps = (JSONObject) ServletUtils.getRequestJson(request);
|
||||
JSONObject props = JSONUtils.toJSONObject("databaseProps", dbProps);
|
||||
|
||||
|
@ -108,6 +105,7 @@ public class InstallController extends BaseController implements InstallState {
|
|||
|
||||
@PostMapping("test-cache")
|
||||
public RespBody testCache(HttpServletRequest request) {
|
||||
RbAssert.checkAllow(checkInstalled());
|
||||
JSONObject cacheProps = (JSONObject) ServletUtils.getRequestJson(request);
|
||||
|
||||
JedisPool pool = new JedisPool(new JedisPoolConfig(),
|
||||
|
@ -133,6 +131,7 @@ public class InstallController extends BaseController implements InstallState {
|
|||
|
||||
@PostMapping("install-rebuild")
|
||||
public RespBody installExec(HttpServletRequest request) {
|
||||
RbAssert.checkAllow(checkInstalled());
|
||||
JSONObject installProps = (JSONObject) ServletUtils.getRequestJson(request);
|
||||
|
||||
try {
|
||||
|
|
|
@ -12,7 +12,6 @@ import cn.devezhao.commons.web.ServletUtils;
|
|||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.api.user.AuthTokenManager;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.integration.QiniuCloud;
|
||||
|
@ -69,8 +68,11 @@ public class FileDownloader extends BaseController {
|
|||
String imageView2 = request.getQueryString();
|
||||
if (imageView2 != null && imageView2.contains("imageView2/")) {
|
||||
imageView2 = "imageView2/" + imageView2.split("imageView2/")[1].split("&")[0];
|
||||
// svg does support
|
||||
if (filePath.toLowerCase().endsWith(".svg")) imageView2 = null;
|
||||
|
||||
// svg/webp does not support
|
||||
if (filePath.toLowerCase().endsWith(".svg") || filePath.toLowerCase().endsWith(".webp")) {
|
||||
imageView2 = null;
|
||||
}
|
||||
} else {
|
||||
imageView2 = null;
|
||||
}
|
||||
|
@ -85,7 +87,7 @@ public class FileDownloader extends BaseController {
|
|||
response.setContentType(mimeType);
|
||||
}
|
||||
|
||||
ImageView2 iv2 = imageView2 == null ? null : new ImageView2(imageView2);
|
||||
final ImageView2 iv2 = imageView2 == null ? null : new ImageView2(imageView2);
|
||||
|
||||
// 使用原图
|
||||
if (iv2 == null || iv2.getWidth() <= 0 || iv2.getWidth() >= ImageView2.ORIGIN_WIDTH) {
|
||||
|
@ -101,7 +103,7 @@ public class FileDownloader extends BaseController {
|
|||
return;
|
||||
}
|
||||
|
||||
writeLocalFile(iv2.thumb(img), response);
|
||||
writeLocalFile(iv2.thumbQuietly(img), response);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -214,7 +216,7 @@ public class FileDownloader extends BaseController {
|
|||
if (filepath.contains("../")
|
||||
|| filepath.startsWith("_log/") || filepath.contains("/_log/")
|
||||
|| filepath.startsWith("_backups/") || filepath.contains("/_backups/")) {
|
||||
throw new RebuildException("Attack path detected : " + filepath);
|
||||
throw new SecurityException("Attack path detected : " + filepath);
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
|
|
@ -62,13 +62,15 @@ public class WidgetController extends BaseController implements ShareTo {
|
|||
|
||||
@GetMapping("widget-charts")
|
||||
public RespBody gets(@PathVariable String entity, HttpServletRequest request) {
|
||||
ConfigBean config = DataListManager.instance.getWidgetCharts(getRequestUser(request), entity);
|
||||
ConfigBean config = DataListManager.instance.getWidgetCharts(
|
||||
getRequestUser(request), entity);
|
||||
return RespBody.ok(config == null ? null : config.toJSON());
|
||||
}
|
||||
|
||||
@GetMapping("widget-category-data")
|
||||
public RespBody getCategoryData(@PathVariable String entity) {
|
||||
JSON data = DataListCategory.datas(MetadataHelper.getEntity(entity), null);
|
||||
public RespBody getCategoryData(@PathVariable String entity, HttpServletRequest request) {
|
||||
JSON data = DataListCategory.datas(
|
||||
MetadataHelper.getEntity(entity), getRequestUser(request));
|
||||
return RespBody.ok(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ public class GeneralModelController extends EntityController {
|
|||
FormsBuilder.instance.setFormInitialValue(metaEntity, model, (JSONObject) initialVal);
|
||||
}
|
||||
|
||||
// v3.1 明细导入
|
||||
// v3.1 明细导入配置
|
||||
if (metaEntity.getDetailEntity() != null) {
|
||||
List<ConfigBean> imports = TransformManager.instance.getDetailImports(metaEntity.getDetailEntity().getName());
|
||||
if (!imports.isEmpty()) {
|
||||
|
@ -202,7 +202,7 @@ public class GeneralModelController extends EntityController {
|
|||
final ID user = getRequestUser(request);
|
||||
final Entity metaEntity = MetadataHelper.getEntity(entity);
|
||||
|
||||
// 转换预览模式
|
||||
// 记录转换预览模式
|
||||
final String previewid = request.getParameter("previewid");
|
||||
if (StringUtils.isNotBlank(previewid)) {
|
||||
return new TransformerPreview(previewid, user).buildForm(true);
|
||||
|
|
|
@ -39,7 +39,7 @@ public class PicklistDataController extends BaseController {
|
|||
|
||||
// for PickList/MultiSelect/State
|
||||
@GetMapping({"picklist", "field-options"})
|
||||
public JSON fetchPicklist(HttpServletRequest request) {
|
||||
public JSON fetchOptions(HttpServletRequest request) {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String field = getParameterNotNull(request, "field");
|
||||
|
||||
|
@ -61,8 +61,8 @@ public class PicklistDataController extends BaseController {
|
|||
// for Classification
|
||||
@RequestMapping("classification")
|
||||
public RespBody fetchClassification(HttpServletRequest request) {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String field = getParameterNotNull(request, "field");
|
||||
final String entity = getParameterNotNull(request, "entity");
|
||||
final String field = getParameterNotNull(request, "field");
|
||||
|
||||
Field fieldMeta = getRealField(entity, field);
|
||||
ID useClassification = ClassificationManager.instance.getUseClassification(fieldMeta, true);
|
||||
|
@ -72,13 +72,10 @@ public class PicklistDataController extends BaseController {
|
|||
|
||||
ID parent = getIdParameter(request, "parent");
|
||||
String sql = "select itemId,name from ClassificationData where dataId = ? and isHide = 'F' and ";
|
||||
if (parent != null) {
|
||||
sql += "parent = '" + parent + "'";
|
||||
} else {
|
||||
sql += "parent is null";
|
||||
}
|
||||
sql += " order by code, name";
|
||||
Object[][] data = Application.createQueryNoFilter(sql)
|
||||
if (parent != null) sql += "parent = '" + parent + "'";
|
||||
else sql += "parent is null";
|
||||
|
||||
Object[][] data = Application.createQueryNoFilter(sql + " order by code,name")
|
||||
.setParameter(1, useClassification)
|
||||
.setLimit(500) // 最多显示
|
||||
.array();
|
||||
|
|
|
@ -66,6 +66,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
Field referenceField = entity.getField(field);
|
||||
if (!(referenceField.getType() == FieldType.REFERENCE || referenceField.getType() == FieldType.REFERENCE_LIST)) {
|
||||
log.warn("Unsupportted type of field : {}", referenceField);
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
|
@ -73,8 +74,11 @@ public class ReferenceSearchController extends EntityController {
|
|||
Entity searchEntity = referenceField.getReferenceEntity();
|
||||
|
||||
// 引用字段数据过滤
|
||||
String cascadingValue = getParameter(request, "cascadingValue", StringUtils.EMPTY);
|
||||
if (cascadingValue.contains(",")) cascadingValue = cascadingValue.split(",")[0]; // N2N
|
||||
|
||||
String protocolFilter = new ProtocolFilterParser(null)
|
||||
.parseRef(field + "." + entity.getName(), request.getParameter("cascadingValue"));
|
||||
.parseRef(field + "." + entity.getName(), cascadingValue);
|
||||
|
||||
String q = getParameter(request, "q");
|
||||
|
||||
|
@ -84,23 +88,13 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
// 为空则加载最近使用的
|
||||
if (StringUtils.isBlank(q) && !forceSearchs) {
|
||||
ID[] recently = null;
|
||||
ID[] used = RecentlyUsedHelper.gets(
|
||||
user, searchEntity.getName(), getParameter(request, "type"), protocolFilter);
|
||||
|
||||
// 启用数据过滤后最近搜索将不可用
|
||||
if (protocolFilter != null) {
|
||||
if (forceResults) recently = new ID[0];
|
||||
else return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
if (recently == null) {
|
||||
String type = getParameter(request, "type");
|
||||
recently = RecentlyUsedHelper.gets(user, searchEntity.getName(), type);
|
||||
}
|
||||
|
||||
if (recently == null || recently.length == 0) {
|
||||
if (used.length == 0) {
|
||||
if (!forceResults) return JSONUtils.EMPTY_ARRAY;
|
||||
} else {
|
||||
return RecentlyUsedSearchController.formatSelect2(recently, Language.L("最近使用"));
|
||||
return RecentlyUsedSearchController.formatSelect2(used, Language.L("最近使用"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +105,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
// 搜索指定实体的指定字段
|
||||
@GetMapping("search")
|
||||
public JSON search(@EntityParam Entity searchEntity, HttpServletRequest request) {
|
||||
public JSON commonSearch(@EntityParam Entity searchEntity, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
|
||||
// 强制搜索 H5
|
||||
|
@ -136,6 +130,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
searchEntity, getParameter(request, "quickFields"), q, null, pageSize);
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
private JSON buildResultSearch(Entity searchEntity, String quickFields, String q, String appendWhere, int maxResults) {
|
||||
String searchWhere = "(1=1)";
|
||||
|
||||
|
@ -161,6 +156,9 @@ public class ReferenceSearchController extends EntityController {
|
|||
}
|
||||
|
||||
// 搜索分类字段
|
||||
/**
|
||||
* @see PicklistDataController#fetchClassification(HttpServletRequest)
|
||||
*/
|
||||
@GetMapping("classification")
|
||||
public JSON searchClassification(@EntityParam Entity entity, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
|
@ -168,19 +166,19 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
Field fieldMeta = entity.getField(field);
|
||||
ID useClassification = ClassificationManager.instance.getUseClassification(fieldMeta, false);
|
||||
if (useClassification == null) {
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
if (useClassification == null) return JSONUtils.EMPTY_ARRAY;
|
||||
|
||||
String q = getParameter(request, "q");
|
||||
|
||||
// 为空则加载最近使用的
|
||||
if (StringUtils.isBlank(q)) {
|
||||
String type = "d" + useClassification + ":" + ClassificationManager.instance.getOpenLevel(fieldMeta);
|
||||
ID[] recently = RecentlyUsedHelper.gets(user, "ClassificationData", type);
|
||||
if (recently.length == 0) {
|
||||
ID[] used = RecentlyUsedHelper.gets(user, "ClassificationData", type);
|
||||
|
||||
if (used.length == 0) {
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
} else {
|
||||
return RecentlyUsedSearchController.formatSelect2(recently, null);
|
||||
return RecentlyUsedSearchController.formatSelect2(used, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,6 +194,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
return (JSON) JSON.toJSON(result);
|
||||
}
|
||||
|
||||
// 查询结果
|
||||
private List<Object> resultSearch(String sqlWhere, Entity entity, int maxResults) {
|
||||
Field nameField = entity.getNameField();
|
||||
|
||||
|
@ -265,6 +264,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 引用字段搜索页面
|
||||
* @see com.rebuild.web.general.GeneralListController#pageList(String, HttpServletRequest, HttpServletResponse)
|
||||
*/
|
||||
@GetMapping("reference-search")
|
||||
|
@ -294,9 +294,9 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
if (ProtocolFilterParser.getFieldDataFilter(field) != null
|
||||
|| ProtocolFilterParser.hasFieldCascadingField(field)) {
|
||||
String protocolExpr = String.format("ref:%s:%s",
|
||||
String protocolExpr = String.format("%s:%s:%s", ProtocolFilterParser.P_REF,
|
||||
getParameterNotNull(request, "field"),
|
||||
StringUtils.defaultString(getParameter(request, "cascadingValue"), ""));
|
||||
getParameter(request, "cascadingValue", StringUtils.EMPTY));
|
||||
mv.getModel().put("referenceFilter", protocolExpr);
|
||||
} else {
|
||||
mv.getModel().put("referenceFilter", StringUtils.EMPTY);
|
||||
|
|
|
@ -101,6 +101,12 @@ public class RelatedListController extends BaseController {
|
|||
|
||||
Map<String, Integer> countMap = new HashMap<>();
|
||||
for (String related : relateds) {
|
||||
// 附件特殊处理
|
||||
if (related.startsWith("Attachment.")
|
||||
&& !Application.getPrivilegesManager().allowRead(user, mainid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String sql = buildBaseSql(mainid, related, null, true, user);
|
||||
|
||||
// 任务是获取了全部的相关记录,因此总数可能与实际显示的条目数量不一致
|
||||
|
|
|
@ -8,11 +8,18 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.web.user;
|
||||
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.support.License;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* UCenter
|
||||
*
|
||||
|
@ -21,10 +28,13 @@ import org.springframework.web.bind.annotation.*;
|
|||
*/
|
||||
@RestController
|
||||
@RequestMapping("/settings/ucenter")
|
||||
public class UCenterController {
|
||||
public class UCenterController extends BaseController {
|
||||
|
||||
@PostMapping("/bind")
|
||||
public RespBody bindCloudAccount(@RequestBody JSONObject body) {
|
||||
public RespBody bindCloudAccount(@RequestBody JSONObject body, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
RbAssert.isAllow(UserHelper.isSuperAdmin(user), Language.L("仅超级管理员可操作"));
|
||||
|
||||
String account = body.getString("cloudAccount");
|
||||
String passwd = body.getString("cloudPasswd");
|
||||
|
||||
|
@ -42,9 +52,9 @@ public class UCenterController {
|
|||
}
|
||||
|
||||
@GetMapping("/bind-query")
|
||||
public RespBody bindQuery() {
|
||||
public RespBody bindQuery(HttpServletRequest request) {
|
||||
JSONObject res = License.siteApi("api/ucenter/bind-query");
|
||||
String bindAccount = res.getString("bindAccount");
|
||||
return RespBody.ok(bindAccount);
|
||||
res.put("canBind", UserHelper.isSuperAdmin(getRequestUser(request)));
|
||||
return RespBody.ok(res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,16 +107,7 @@ public class LoginAction extends BaseController {
|
|||
String authToken = AuthTokenManager.generateAccessToken(user);
|
||||
resMap.put("authToken", authToken);
|
||||
|
||||
// FIXME 暂不启用 lauthToken 前端有问题
|
||||
// // 2FA
|
||||
// int faMode = RebuildConfiguration.getInt(ConfigurationItem.Login2FAMode);
|
||||
// if (faMode <= 0) {
|
||||
// String lauthToken = user + "," + System.currentTimeMillis() + ",h5";
|
||||
// resMap.put("lauthToken", AES.encrypt(lauthToken));
|
||||
// }
|
||||
|
||||
request.getSession().invalidate();
|
||||
|
||||
return resMap;
|
||||
}
|
||||
|
||||
|
@ -149,14 +140,14 @@ public class LoginAction extends BaseController {
|
|||
|
||||
String ipAddr = StringUtils.defaultString(ServletUtils.getRemoteAddr(request), "127.0.0.1");
|
||||
|
||||
final Record record = EntityHelper.forNew(EntityHelper.LoginLog, UserService.SYSTEM_USER);
|
||||
record.setID("user", user);
|
||||
record.setString("ipAddr", ipAddr);
|
||||
record.setString("userAgent", uaClear);
|
||||
record.setDate("loginTime", CalendarUtils.now());
|
||||
final Record llog = EntityHelper.forNew(EntityHelper.LoginLog, UserService.SYSTEM_USER);
|
||||
llog.setID("user", user);
|
||||
llog.setString("ipAddr", ipAddr);
|
||||
llog.setString("userAgent", uaClear);
|
||||
llog.setDate("loginTime", CalendarUtils.now());
|
||||
|
||||
TaskExecutors.queue(() -> {
|
||||
Application.getCommonsService().create(record);
|
||||
Application.getCommonsService().create(llog);
|
||||
|
||||
User u = Application.getUserStore().getUser(user);
|
||||
String uid = StringUtils.defaultString(u.getEmail(), u.getName());
|
||||
|
|
|
@ -67,7 +67,7 @@ public class LoginController extends LoginAction {
|
|||
// Token 登录
|
||||
final String useToken = getParameter(request, "token");
|
||||
if (StringUtils.isNotBlank(useToken)) {
|
||||
ID tokenUser = AuthTokenManager.verifyToken(useToken, true);
|
||||
ID tokenUser = AuthTokenManager.verifyToken(useToken, true, false);
|
||||
if (tokenUser != null) {
|
||||
loginSuccessed(request, response, tokenUser, false);
|
||||
|
||||
|
@ -187,28 +187,28 @@ public class LoginController extends LoginAction {
|
|||
ServletUtils.setSessionAttribute(request, UserAvatar.SK_DAVATAR, System.currentTimeMillis());
|
||||
|
||||
final User loginUser = Application.getUserStore().getUser(user);
|
||||
final boolean isMobile = AppUtils.isRbMobile(request);
|
||||
final boolean isRbMobile = AppUtils.isRbMobile(request);
|
||||
|
||||
Map<String, Object> resMap = new HashMap<>();
|
||||
|
||||
// 2FA
|
||||
int faMode = RebuildConfiguration.getInt(ConfigurationItem.Login2FAMode);
|
||||
if (faMode > 0
|
||||
&& (!UserHelper.isSuperAdmin(loginUser.getId()) || RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced))) {
|
||||
boolean faModeSkip = UserHelper.isSuperAdmin(loginUser.getId()) && !RebuildConfiguration.getBool(ConfigurationItem.SecurityEnhanced);
|
||||
if (faMode > 0 && !faModeSkip) {
|
||||
resMap.put("login2FaMode", faMode);
|
||||
|
||||
String userToken = CodecUtils.randomCode(40);
|
||||
Application.getCommonsCache().putx(PREFIX_2FA + userToken, loginUser.getId(), 15 * 60); // 15m
|
||||
resMap.put("login2FaUserToken", userToken);
|
||||
|
||||
if (isMobile) {
|
||||
if (isRbMobile) {
|
||||
request.getSession().invalidate();
|
||||
}
|
||||
|
||||
return RespBody.ok(resMap);
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
if (isRbMobile) {
|
||||
resMap = loginSuccessedH5(request, response, loginUser.getId());
|
||||
} else {
|
||||
Integer ed = loginSuccessed(
|
||||
|
|
|
@ -1,31 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>MAIL TEMPLATE</title>
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>EMAIL TEMPLATE</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="background-color:#f5f5f5;margin:0;font-size:0.9rem;padding:20px;color:#333;font-family:Roboto,Helvetica,'Microsoft YaHei','宋体',sans-serif;line-height:1.5">
|
||||
<div>
|
||||
<a href="https://getrebuild.com/" target="_blank">
|
||||
<img src="https://getrebuild.com/img/logo.png" alt="REBUILD" style="width:134px;display:inline-block;font-size:0"/>
|
||||
<body>
|
||||
<div style="background-color: #f5f5f5; margin: 0; font-size: 0.9rem; padding: 20px; color: #333; font-family: Roboto, Helvetica, 'Microsoft YaHei', '宋体', sans-serif; line-height: 1.5">
|
||||
<div>
|
||||
<a href="%APPURL%" target="_blank">
|
||||
<img src="%APPLOGO%" alt="%APPNAME%" style="width: 134px; display: inline-block; font-size: 0" />
|
||||
</a>
|
||||
</div>
|
||||
<div style="border:1px solid #eee;padding:30px 20px;background-color:#fff;border-radius:3px;border-top:3px solid #4285f4;max-width:720px;margin:10px 0">
|
||||
<h2 class="rb-title" style="font-size:1.3rem;margin:0;font-weight:400">%TITLE%</h2>
|
||||
<div class="rb-content" style="margin-top:10px">%CONTETN%</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="rb-footer" style="margin:0;color:#888;font-size:12px;max-width:760px">
|
||||
本消息由 REBUILD 系统自动发送,请不要直接回复。
|
||||
<br>
|
||||
<em>This email was sent to %TO% at %TIME%</em>
|
||||
<br>
|
||||
%PAGE_FOOTER%
|
||||
</div>
|
||||
<div style="border: 1px solid #eee; padding: 30px 20px; background-color: #fff; border-radius: 3px; border-top: 3px solid #4285f4; max-width: 720px; margin: 10px 0">
|
||||
<h2 class="rb-title" style="font-size: 1.3rem; margin: 0; font-weight: 400">%TITLE%</h2>
|
||||
<div class="rb-content" style="margin-top: 10px">%CONTETN%</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="rb-footer" style="margin: 0; color: #888; font-size: 12px; max-width: 760px">
|
||||
本消息由 %APPNAME% 系统自动发送,请不要直接回复。
|
||||
<br />
|
||||
<em>This email was sent to %TO% at %TIME%</em>
|
||||
<br />
|
||||
%PAGE_FOOTER%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2360,5 +2360,14 @@
|
|||
"绑定 REBUILD 云账号":"绑定 REBUILD 云账号",
|
||||
"当前已绑定云账号":"当前已绑定云账号",
|
||||
"是否保留已有明细记录?":"是否保留已有明细记录?",
|
||||
"Office 文件":"Office 文件"
|
||||
"Office 文件":"Office 文件",
|
||||
"修改字段":"修改字段",
|
||||
"请填写新值":"请填写新值",
|
||||
"最后审批批注":"最后审批批注",
|
||||
"修改方式":"修改方式",
|
||||
"仅允许拍照上传":"仅允许拍照上传",
|
||||
"批量添加字段":"批量添加字段",
|
||||
"支持 H5":"支持 H5",
|
||||
"手机登录":"手机登录",
|
||||
"批量添加":"批量添加"
|
||||
}
|
||||
|
|
|
@ -184,12 +184,12 @@
|
|||
<field name="name" type="string" max-length="100" nullable="false"/>
|
||||
<field name="fullName" type="string" max-length="191" nullable="false" description="(包括父级名称, 用点号分割)"/>
|
||||
<field name="parent" type="reference" ref-entity="ClassificationData" cascade="delete"/>
|
||||
<field name="code" type="string" max-length="50"/>
|
||||
<field name="code" type="string" max-length="50" default-value="0"/>
|
||||
<field name="level" type="small-int" default-value="0" updatable="false"/>
|
||||
<field name="isHide" type="bool" default-value="F"/>
|
||||
<field name="quickCode" type="string" max-length="50" queryable="false"/>
|
||||
<index field-list="dataId,parent"/>
|
||||
<index field-list="dataId,fullName,quickCode"/>
|
||||
<index field-list="dataId,parent,code"/>
|
||||
<index field-list="dataId,fullName,quickCode,code"/>
|
||||
</entity>
|
||||
|
||||
<entity name="ShareAccess" type-code="020" description="记录共享" queryable="false">
|
||||
|
|
|
@ -273,7 +273,7 @@ create table if not exists `classification_data` (
|
|||
`NAME` varchar(100) not null,
|
||||
`FULL_NAME` varchar(191) not null comment '(包括父级名称, 用点号分割)',
|
||||
`PARENT` char(20),
|
||||
`CODE` varchar(50),
|
||||
`CODE` varchar(50) default '0',
|
||||
`LEVEL` smallint(6) default '0',
|
||||
`IS_HIDE` char(1) default 'F',
|
||||
`QUICK_CODE` varchar(50),
|
||||
|
@ -282,8 +282,8 @@ create table if not exists `classification_data` (
|
|||
`CREATED_BY` char(20) not null comment '创建人',
|
||||
`CREATED_ON` timestamp not null default current_timestamp comment '创建时间',
|
||||
primary key (`ITEM_ID`),
|
||||
index IX0_classification_data (`DATA_ID`, `PARENT`),
|
||||
index IX1_classification_data (`DATA_ID`, `FULL_NAME`, `QUICK_CODE`)
|
||||
index IX0_classification_data (`DATA_ID`, `PARENT`, `CODE`),
|
||||
index IX1_classification_data (`DATA_ID`, `FULL_NAME`, `QUICK_CODE`, `CODE`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- ************ Entity [ShareAccess] DDL ************
|
||||
|
@ -830,7 +830,10 @@ insert into `layout_config` (`CONFIG_ID`, `BELONG_ENTITY`, `CONFIG`, `APPLY_TYPE
|
|||
('013-9000000000000001', 'Department', '[{"field":"name"},{"field":"principalId"},{"field":"parentDept"},{"field":"isDisabled"}]', 'FORM', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000002', 'User', '[{"field":"fullName"},{"field":"jobTitle"},{"field":"workphone"},{"field":"email"},{"field":"loginName"},{"field":"password"},{"field":"$DIVIDER$"},{"field":"deptId"},{"field":"roleId"},{"field":"isDisabled"}]', 'FORM', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000003', 'Role', '[{"field":"name"},{"field":"isDisabled"}]', 'FORM', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000004', 'Team', '[{"field":"name"},{"field":"principalId"},{"field":"isDisabled"}]', 'FORM', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001');
|
||||
('013-9000000000000004', 'Team', '[{"field":"name"},{"field":"principalId"},{"field":"isDisabled"}]', 'FORM', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000005', 'User', '[{"field":"fullName"},{"field":"jobTitle"},{"field":"deptId"},{"field":"workphone"},{"field":"email"},{"field":"loginName"},{"field":"roleId"},{"field":"isDisabled"},{"field":"createdOn"}]', 'DATALIST', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000006', 'Department', '[{"field":"name"},{"field":"principalId"},{"field":"parentDept"},{"field":"isDisabled"},{"field":"createdOn"}]', 'DATALIST', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001'),
|
||||
('013-9000000000000007', 'Team', '[{"field":"name"},{"field":"principalId"},{"field":"isDisabled"},{"field":"createdOn"}]', 'DATALIST', 'ALL', CURRENT_TIMESTAMP, '001-0000000000000001', CURRENT_TIMESTAMP, '001-0000000000000001');
|
||||
|
||||
-- Classifications (No data)
|
||||
insert into `classification` (`DATA_ID`, `NAME`, `DESCRIPTION`, `OPEN_LEVEL`, `IS_DISABLED`, `CREATED_ON`, `CREATED_BY`, `MODIFIED_ON`, `MODIFIED_BY`)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<script th:src="@{/assets/lib/widget/mprogress.min.js}"></script>
|
||||
<script th:src="@{/assets/lib/moment-with-locales.min.js?v=2.27.0}"></script>
|
||||
<script th:src="@{/assets/lib/widget/bootstrap-datetimepicker.min.js?v=2.4.4}"></script>
|
||||
<script th:src="@{/assets/lib/jquery.html5uploader.js}"></script>
|
||||
<script th:src="@{/assets/lib/jquery.html5uploader.js?v=310}"></script>
|
||||
<script th:src="@{/assets/lib/qiniu.min.js?v=3.4.1}"></script>
|
||||
<script th:src="@{/assets/lib/widget/select2.js?v=4.0.13.fix2}"></script>
|
||||
<script th:src="@{/assets/lib/jquery-ui.min.js?v=1.13.1}"></script>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<meta name="rb._uploadMaxSize" th:content="${PortalUploadMaxSize}" />
|
||||
<!--[if lt IE 10]><script>location.href = '[[${baseUrl}]]/error/unsupported-browser'</script><![endif]-->
|
||||
<script th:if="${T(com.rebuild.utils.AppUtils).isIE11(#request)}" th:src="@{/assets/lib/react/polyfill.min.js?v=7.6.0}"></script>
|
||||
<script th:if="${markWatermark == 'true'}" th:src="@{/assets/lib/watermark.js?v=2.3.2.2}"></script>
|
||||
<script th:if="${markWatermark == 'true'}" th:src="@{/assets/lib/watermark.js?v=310}"></script>
|
||||
<style>
|
||||
.logo-img,
|
||||
.rb-top-header .rb-navbar-header .navbar-brand {
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
$.post('/admin/admin-cli/exec', c, (o) => {
|
||||
$c.addClass('ok')
|
||||
if (o && o.data) _print(o.data, true)
|
||||
else if (o && o.error_msg) _print(`ERROR: ${o.error_msg}`, true)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<div class="col-12 col-md-6">
|
||||
<div class="dataTables_filter">
|
||||
<div class="input-group input-search">
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('查询')}" maxlength="40" data-quickFields="user.loginName,user.email,&user,ipAddr" />
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('查询')}" maxlength="40" data-quickFields="&user,ipAddr" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button>
|
||||
</span>
|
||||
|
|
|
@ -112,10 +112,10 @@
|
|||
<div class="icon"><span class="zmdi zmdi-info-outline"></span></div>
|
||||
<div class="message">
|
||||
<a class="close" data-dismiss="alert"><span class="zmdi zmdi-close"></span></a>
|
||||
<p>1</p>
|
||||
<p>ALERT</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-right" style="margin-top: -1px">
|
||||
<div class="float-right">
|
||||
<button class="btn btn-secondary btn-space J_new-role" type="button"><i class="icon zmdi zmdi-plus"></i> [[${bundle.L('新建角色')}]]</button>
|
||||
<button class="btn btn-primary btn-space J_save mr-0" type="button" disabled="disabled">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
|
@ -165,6 +165,7 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p th:if="${Entities.size() == 0}" class="text-muted mt-5">[[${bundle.L('暂无可用业务实体')}]]</p>
|
||||
<div class="legend-wrap">
|
||||
<div class="legend">
|
||||
[[${bundle.L('图例')}]]
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<div class="col-12 col-lg-5">
|
||||
<div class="dataTables_filter">
|
||||
<div class="input-group input-search">
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" data-quickFields="loginName,fullName,email,quickCode" />
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button>
|
||||
</span>
|
||||
|
|
|
@ -74,16 +74,13 @@
|
|||
<dt class="col-12 col-lg-4">[[${bundle.L('上次登录')}]]</dt>
|
||||
<dd class="col-12 col-lg-8 J_loginOn"><span>[[${bundle.L('无')}]]</span></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="alert alert-info alert-icon alert-icon-border alert-sm hide J_roles mt-5">
|
||||
<div class="icon"><span class="zmdi zmdi-lock"></span></div>
|
||||
<div class="message">
|
||||
<span class="text-muted">[[${bundle.L('附加角色')}]]</span>
|
||||
<p class="column-n2n"></p>
|
||||
</div>
|
||||
<div class="form-line admin-show">
|
||||
<fieldset><legend></legend></fieldset>
|
||||
</div>
|
||||
<dl class="row admin-show">
|
||||
<dt class="col-12 col-lg-4">[[${bundle.L('附加角色')}]]</dt>
|
||||
<dd class="col-12 col-lg-8 J_roleAppends"><span>[[${bundle.L('无')}]]</span></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<div class="float-left">
|
||||
<div class="page-head-title">
|
||||
[[${bundle.L('报表模板')}]]
|
||||
<i class="support-plat mdi mdi-monitor" th:title="${bundle.L('支持 PC')}" style="margin-top: 9px"></i>
|
||||
<i class="support-plat2 mdi mdi-monitor mt-2" th:title="${bundle.L('支持 PC')}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-right pt-1">
|
||||
|
|
|
@ -78,7 +78,9 @@
|
|||
<div class="main-content container-fluid pt-1">
|
||||
<div th:if="${useListMode}" class="card">
|
||||
<div class="card-header">
|
||||
[[${bundle.L('列表模式')}]] <sup class="rbv"></sup> <i class="support-plat mdi mdi-monitor" th:title="${bundle.L('支持 PC')}" style="margin-top: 2px"></i>
|
||||
[[${bundle.L('列表模式')}]]
|
||||
<sup class="rbv"></sup>
|
||||
<i class="support-plat2 mdi mdi-monitor mt-1" th:title="${bundle.L('支持 PC')}"></i>
|
||||
</div>
|
||||
<div class="card-body mode-select">
|
||||
<div class="row">
|
||||
|
@ -125,12 +127,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="bosskey-show">
|
||||
<div class="bosskey-show hide">
|
||||
<div class="row ph">
|
||||
<div class="col-3 p-0">
|
||||
<div class="col-2 p-0">
|
||||
<div class="block"></div>
|
||||
</div>
|
||||
<div class="col-9 p-0">
|
||||
<div class="col-10 p-0">
|
||||
<div class="block">
|
||||
<div class="block-item2" style="margin-left: 3%"><i class="mdi mdi-image"></i></div>
|
||||
<div class="block-item2"><i class="mdi mdi-image"></i></div>
|
||||
|
|
|
@ -74,6 +74,26 @@
|
|||
<p class="form-text mb-0">[[${bundle.L('用于列表、表单引用字段等处的快速查询')}]]</p>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:if="${currentEntity == mainEntity}">
|
||||
<div class="form-group row bosskey-show">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">Not Co-Editing</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="notCoEditing" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row bosskey-show">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">Details Not Allow Empty</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="detailsNotEmpty" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
<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">
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${fieldType == 'REFERENCE'}" class="form-group row">
|
||||
<div th:if="${fieldType == 'REFERENCE' or fieldType == 'N2NREFERENCE'}" 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">
|
||||
|
@ -161,6 +161,18 @@
|
|||
<div class="form-text" th:utext="${bundle.L('如有多个类型请使用逗号分开')}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${fieldType == 'IMAGE'}" class="form-group row J_for-IMAGE bosskey-show">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">
|
||||
<i class="support-plat2 mdi mdi-cellphone-check" style="margin-left: -18px" th:title="${bundle.L('支持 H5')}"></i>
|
||||
[[${bundle.L('仅允许拍照上传')}]]
|
||||
</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="imageCapture" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${fieldType == 'PICKLIST' or fieldType == 'MULTISELECT'}" class="form-group row J_for-PICKLIST J_for-MULTISELECT">
|
||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('选项列表')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
|
@ -219,7 +231,8 @@
|
|||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('是否允许负数')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="notNegative" /><span class="custom-control-label"> [[${bundle.L('不允许')}]]</span>
|
||||
<input class="custom-control-input" type="checkbox" id="notNegative" />
|
||||
<span class="custom-control-label"> [[${bundle.L('不允许')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -234,10 +247,12 @@
|
|||
<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 pt-1">
|
||||
<label class="custom-control custom-control-sm custom-radio custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="radio" name="barcodeType" value="QRCODE" checked /><span class="custom-control-label"> [[${bundle.L('二维码')}]]</span>
|
||||
<input class="custom-control-input" type="radio" name="barcodeType" value="QRCODE" checked />
|
||||
<span class="custom-control-label"> [[${bundle.L('二维码')}]]</span>
|
||||
</label>
|
||||
<label class="custom-control custom-control-sm custom-radio custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="radio" name="barcodeType" value="BARCODE" /><span class="custom-control-label"> [[${bundle.L('条形码')}]] (CODE128)</span>
|
||||
<input class="custom-control-input" type="radio" name="barcodeType" value="BARCODE" />
|
||||
<span class="custom-control-label"> [[${bundle.L('条形码')}]] (CODE128)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -287,7 +302,8 @@
|
|||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('使用富文本编辑器')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="useMdedit" /><span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
<input class="custom-control-input" type="checkbox" id="useMdedit" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -296,7 +312,8 @@
|
|||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('自动定位')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="locationAutoLocation" /><span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
<input class="custom-control-input" type="checkbox" id="locationAutoLocation" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -304,7 +321,8 @@
|
|||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right pt-1">[[${bundle.L('视图页直接显示地图')}]]</label>
|
||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
|
||||
<input class="custom-control-input" type="checkbox" id="locationMapOnView" /><span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
<input class="custom-control-input" type="checkbox" id="locationMapOnView" />
|
||||
<span class="custom-control-label"> [[${bundle.L('是')}]]</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<div class="col-sm-6">
|
||||
<div class="dataTables_oper">
|
||||
<button class="btn btn-primary btn-space J_new-field" type="button"><i class="icon zmdi zmdi-plus"></i> [[${bundle.L('添加')}]]</button>
|
||||
<button class="btn btn-primary btn-space J_new2-field bosskey-show" type="button"><i class="icon zmdi zmdi-plus"></i> [[${bundle.L('批量添加')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -98,7 +99,7 @@
|
|||
$(document).ready(() => {
|
||||
loadFields()
|
||||
|
||||
$('.input-search .btn').on('click', () => __renderList())
|
||||
$('.input-search .btn').on('click', () => renderList())
|
||||
$('.input-search .form-control').keydown((e) => {
|
||||
if (e.which === 13) $('.input-search .btn').trigger('click')
|
||||
})
|
||||
|
@ -106,19 +107,23 @@
|
|||
if (wpc.isSuperAdmin) RbModal.create(`/p/admin/metadata/field-new?entity=${wpc.entityName}`, $L('添加字段'))
|
||||
else RbHighbar.error($L('仅超级管理员可添加字段'))
|
||||
})
|
||||
$('.J_new2-field').on('click', () => {
|
||||
if (wpc.isSuperAdmin) RbModal.create(`/p/admin/metadata/field-new2?entity=${wpc.entityName}`, $L('批量添加字段'), { width: 1064 })
|
||||
else RbHighbar.error($L('仅超级管理员可添加字段'))
|
||||
})
|
||||
})
|
||||
|
||||
let fields_data = []
|
||||
const loadFields = function () {
|
||||
$.get(`../list-field?entity=${wpc.entityName}&refname=true`, function (res) {
|
||||
fields_data = res.data || []
|
||||
__renderList()
|
||||
renderList()
|
||||
|
||||
$('.tablesort').tablesort()
|
||||
})
|
||||
}
|
||||
|
||||
const __renderList = function () {
|
||||
const renderList = function () {
|
||||
const $tbody = $('#dataList tbody').empty()
|
||||
const q = ($val('.input-search .form-control') || '').toLowerCase()
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</div>
|
||||
|
||||
<div class="dialog-footer">
|
||||
<button class="btn btn-link" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-secondary" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
|
||||
<div class="dialog-footer">
|
||||
<button class="btn btn-link" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-secondary" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -77,8 +77,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('保存')}]]</button>
|
||||
<button class="btn btn-secondary" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:replace="~{/_include/footer}" />
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<span class="custom-control-label">[[${bundle.L('隐藏无记录项')}]]</span>
|
||||
</label>
|
||||
</span>
|
||||
<button class="btn btn-link" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-secondary" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -51,9 +51,6 @@
|
|||
border-radius: 2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.support-plat {
|
||||
margin-top: -1px;
|
||||
}
|
||||
.J_cloudAccount .icon {
|
||||
font-size: 24px;
|
||||
float: left;
|
||||
|
@ -121,14 +118,14 @@
|
|||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('登录页每日一图')}]]
|
||||
<i class="support-plat mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i>
|
||||
<i class="support-plat2 mdi mdi-monitor mt-0" th:title="${bundle.L('支持 PC')}"></i>
|
||||
</td>
|
||||
<td data-id="LiveWallpaper" th:data-value="${LiveWallpaper}">[[${LiveWallpaper ? bundle.L('是') : bundle.L('否')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('自定义登录页图')}]]
|
||||
<i class="support-plat mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i>
|
||||
<i class="support-plat2 mdi mdi-monitor mt-0" th:title="${bundle.L('支持 PC')}"></i>
|
||||
</td>
|
||||
<td class="fs-0 bgimg">
|
||||
<a class="img-thumbnail p-0 border-0" data-id="CustomWallpaper">
|
||||
|
@ -307,7 +304,7 @@
|
|||
</div>
|
||||
<div class="J_has-bind">
|
||||
<p class="mb-1 mt-1 text-bold">
|
||||
<i class="mdi mdi-shield-check icon"></i>
|
||||
<i class="mdi mdi-shield-check icon up-3"></i>
|
||||
[[${bundle.L('当前已绑定云账号')}]] <a href="https://getrebuild.com/ucenter/account" target="_blank" class="text-uppercase"></a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -33,7 +33,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
position: relative;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.chart-box .chart-head .chart-title {
|
||||
|
@ -56,13 +55,18 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
}
|
||||
|
||||
.chart-box .chart-head .chart-oper a {
|
||||
color: #aaa;
|
||||
color: #000;
|
||||
opacity: 0.4;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
margin-left: 10px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.chart-box .chart-head .chart-oper a:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.chart-box .chart-head .chart-oper a .zmdi {
|
||||
font-size: 1.3rem;
|
||||
vertical-align: middle;
|
||||
|
@ -77,10 +81,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
font-size: 1.43rem;
|
||||
}
|
||||
|
||||
.chart-box .chart-head .chart-oper a:hover {
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
.chart-undata {
|
||||
margin: 0;
|
||||
color: #999;
|
||||
|
@ -120,12 +120,12 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
.chart.index > .data-item p {
|
||||
font-size: 1.1rem;
|
||||
color: #888;
|
||||
margin-bottom: 1px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.chart.index > .data-item strong {
|
||||
font-size: 2.8rem;
|
||||
font-size: 3.1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
}
|
||||
|
||||
.chart.index > .data-item a:hover {
|
||||
opacity: 0.8;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.chart.ctable {
|
||||
|
@ -452,3 +452,18 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
.grid-stack-item.fullscreen .ui-resizable-handle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* useColor */
|
||||
|
||||
.grid-stack-item.color .grid-stack-item-content,
|
||||
.grid-stack-item.color .chart.index > .data-item strong {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.grid-stack-item.color .rb-loading:after {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.grid-stack-item.color .rb-spinner svg {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
|
|
@ -327,7 +327,6 @@ form.field-attr label > span {
|
|||
position: absolute;
|
||||
font-size: 18px;
|
||||
left: 5px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.form-preview .dd-handle:hover::before,
|
||||
|
|
|
@ -780,6 +780,7 @@ body.dialog .main-content {
|
|||
|
||||
.dialog-footer .btn {
|
||||
margin-left: 5px;
|
||||
min-width: 98px;
|
||||
}
|
||||
|
||||
.dialog-footer > .float-left > .custom-control {
|
||||
|
@ -912,14 +913,8 @@ select.form-control:not([disabled]) {
|
|||
}
|
||||
|
||||
@media (min-width: 1064px) {
|
||||
.form-layout > .row .form-group.col-sm-3,
|
||||
.form-layout > .row .form-group.col-sm-4 {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.form-layout > .row .form-group.col-sm-3 > .col-form-label,
|
||||
.form-layout > .row .form-group.col-sm-4 > .col-form-label {
|
||||
width: 110px;
|
||||
.form-layout > .row .form-group.col-sm-3 > .col-form-label {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1056,8 @@ select.form-control:not([disabled]) {
|
|||
|
||||
.img-field .img-upload[disabled],
|
||||
.img-field .img-upload[disabled]:hover,
|
||||
.img-field .img-upload[disabled] .zmdi {
|
||||
.img-field .img-upload[disabled] .zmdi,
|
||||
.img-field .img-upload[disabled] .mdi {
|
||||
opacity: 1 !important;
|
||||
color: #dbdbdb !important;
|
||||
cursor: not-allowed !important;
|
||||
|
@ -1076,10 +1072,10 @@ select.form-control:not([disabled]) {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.img-field label.img-upload .zmdi {
|
||||
font-size: 1.6rem;
|
||||
.img-field label.img-upload .zmdi,
|
||||
.img-field label.img-upload .mdi {
|
||||
font-size: 1.8rem;
|
||||
color: #aaa;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.img-field a.img-upload {
|
||||
|
@ -2433,6 +2429,10 @@ th.column-fixed {
|
|||
padding: 30px;
|
||||
}
|
||||
|
||||
.dropdown-menu-advfilter .alert {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.dataTables_wrapper div.dataTables_filter .dropdown-menu-advfilter input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
@ -3537,7 +3537,7 @@ form {
|
|||
}
|
||||
|
||||
#asideFilters .dropdown-item,
|
||||
#asideClass .dropdown-item {
|
||||
#asideCategory .dropdown-item {
|
||||
padding: 8px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 1rem;
|
||||
|
@ -3546,17 +3546,17 @@ form {
|
|||
}
|
||||
|
||||
#asideFilters .dropdown-item:hover,
|
||||
#asideClass .dropdown-item:hover {
|
||||
#asideCategory .dropdown-item:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#asideFilters .dropdown-item:active,
|
||||
#asideClass .dropdown-item:active {
|
||||
#asideCategory .dropdown-item:active {
|
||||
color: #404040;
|
||||
}
|
||||
|
||||
#asideFilters .dropdown-item.active,
|
||||
#asideClass .dropdown-item.active {
|
||||
#asideCategory .dropdown-item.active {
|
||||
background-color: #4285f4;
|
||||
color: #fff;
|
||||
}
|
||||
|
@ -4183,6 +4183,12 @@ h3.modal-title > .rbv {
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mdedit-content a[target]:hover,
|
||||
.editor-preview a[target]:hover {
|
||||
text-decoration: underline;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popover {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
@ -4258,6 +4264,14 @@ html.external-auth .auth-body.must-center .login {
|
|||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
html.external-auth .auth-body .card {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
|
@ -4881,6 +4895,7 @@ pre.unstyle {
|
|||
.rb-left-sidebar .sidebar-elements > li ul {
|
||||
background-color: #f5f5f5;
|
||||
background-color: #eee;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.rb-left-sidebar .sidebar-elements > li > a:hover,
|
||||
|
@ -4921,20 +4936,27 @@ pre.unstyle {
|
|||
background-color: #eee;
|
||||
}
|
||||
|
||||
/* mdi7 */
|
||||
/* v3: mdi7 */
|
||||
|
||||
.mdi {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mdi.text-bold::before {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.zmdi.mdi::before {
|
||||
line-height: unset;
|
||||
}
|
||||
|
||||
.modal-header > .icon.mdi,
|
||||
.header-icon.mdi {
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.over-tab {
|
||||
margin-top: -63px;
|
||||
transition: margin-top 0.4s;
|
||||
}
|
||||
|
||||
/* theme defs v3.1 */
|
||||
/* v3.1: theme defs */
|
||||
|
||||
.rb-left-sidebar .sidebar-elements > li.active > a,
|
||||
.rb-left-sidebar .sidebar-elements > li.active > a > span,
|
||||
|
@ -4946,17 +4968,14 @@ pre.unstyle {
|
|||
border-top-color: var(--rb-theme-color);
|
||||
}
|
||||
|
||||
.support-plat {
|
||||
.support-plat2 {
|
||||
position: absolute;
|
||||
margin-left: 8px;
|
||||
background-color: var(--rb-color);
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
cursor: help;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-size: 15px;
|
||||
margin-top: -1px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.modal-title > .support-plat2 {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
.task-form .hover:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.task-form .user-selector,
|
||||
|
@ -223,6 +223,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
font-size: 1.231rem;
|
||||
background-color: #eee;
|
||||
border-radius: 50%;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.task-tags a.tag-add:hover {
|
||||
|
@ -248,15 +250,15 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
}
|
||||
|
||||
.tags-editor .colors > a {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: inline-block;
|
||||
background-color: #ccc;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
font-size: 1.3rem;
|
||||
padding-top: 6px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.tags-editor .colors > a:hover {
|
||||
|
@ -264,7 +266,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
}
|
||||
|
||||
.tags-editor .colors > a + a {
|
||||
margin-left: 11px;
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.tags-editor .colors > a > i {
|
||||
|
|
|
@ -98,11 +98,14 @@ body {
|
|||
line-height: 1.428571;
|
||||
}
|
||||
|
||||
.rbview-form .type-NTEXT .form-control-plaintext.mdedit-content,
|
||||
.rbview-form .type-NTEXT.col-sm-12 .form-control-plaintext {
|
||||
max-height: 134px;
|
||||
}
|
||||
|
||||
.rbview-form .type-NTEXT .form-control-plaintext.mdedit-content {
|
||||
max-height: 1001px;
|
||||
}
|
||||
|
||||
.nav-tabs > li.nav-item a.nav-link {
|
||||
padding: 11px 10px;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -31,7 +31,7 @@ class DataList extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const _pageIps = []
|
||||
let _pageIps = []
|
||||
const CellRenders_renderSimple = CellRenders.renderSimple
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
|
@ -39,7 +39,7 @@ CellRenders.renderSimple = function (v, s, k) {
|
|||
let comp = CellRenders_renderSimple(v, s, k)
|
||||
if (k.endsWith('.ipAddr')) {
|
||||
if (!_pageIps.contains(v)) _pageIps.push(v)
|
||||
comp = React.cloneElement(comp, { className: `J_ip-${v.replace(/\./g, '-')}` })
|
||||
comp = React.cloneElement(comp, { className: `J_ip-${v.replace(/[^0-9]/g, '')}` })
|
||||
}
|
||||
return comp
|
||||
}
|
||||
|
@ -50,13 +50,14 @@ RbList.renderAfter = function () {
|
|||
if (res.error_code === 0 && res.data.country !== 'N') {
|
||||
let L = res.data.country === 'R' ? $L('局域网') : [res.data.region, res.data.country].join(', ')
|
||||
L = `${ip} (${L})`
|
||||
$(`.J_ip-${ip.replace(/\./g, '-')}`)
|
||||
.attr('title', L)
|
||||
$(`.J_ip-${ip.replace(/[^0-9]/g, '')}`)
|
||||
.find('div')
|
||||
.attr('title', L)
|
||||
.text(L)
|
||||
}
|
||||
})
|
||||
})
|
||||
_pageIps = []
|
||||
}
|
||||
|
||||
// ~ 在线用户
|
||||
|
|
|
@ -221,7 +221,6 @@ class ReporEdit extends ConfigFormDlg {
|
|||
|
||||
if (this.props.id) {
|
||||
post.isDisabled = this.state.isDisabled === true
|
||||
this._save(post)
|
||||
} else {
|
||||
post.belongEntity = this.__select2.val()
|
||||
post.templateFile = this.state.templateFile
|
||||
|
|
|
@ -19,14 +19,18 @@ $(document).ready(() => {
|
|||
})
|
||||
|
||||
// UC
|
||||
UCenter.query((bindAccount) => {
|
||||
UCenter.query((res) => {
|
||||
const bindAccount = res.bindAccount
|
||||
$('.J_cloudAccount').removeClass('hide')
|
||||
if (bindAccount) {
|
||||
$('.J_not-bind').addClass('hide')
|
||||
$('.J_has-bind a').text(bindAccount)
|
||||
} else {
|
||||
$('.J_has-bind').addClass('hide')
|
||||
$('.J_not-bind .btn').on('click', () => UCenter.bind())
|
||||
$('.J_not-bind .btn').on('click', () => {
|
||||
if (res.canBind) UCenter.bind()
|
||||
else RbHighbar.create($L('仅超级管理员可操作'))
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -125,9 +125,9 @@ $(document).ready(function () {
|
|||
|
||||
if (res.data.roleAppends && res.data.roleAppends.length > 0) {
|
||||
$.get(`/commons/search/read-labels?ids=${res.data.roleAppends.join(',')}`, (res) => {
|
||||
const $p = $('.J_roles').removeClass('hide').find('p')
|
||||
const $p = $('.J_roleAppends').empty()
|
||||
for (let k in res.data) {
|
||||
$(`<a class="text-bold">${res.data[k]}</a>`).appendTo($p)
|
||||
$(`<span class="badge badge-light up-2">${res.data[k]}</span>`).appendTo($p)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -85,9 +85,8 @@ $(document).ready(() => {
|
|||
$this.addClass('select')
|
||||
render_option()
|
||||
})
|
||||
$('.chart-option .custom-control').on('click', function () {
|
||||
render_option()
|
||||
})
|
||||
|
||||
$('.chart-option .custom-control').on('click', () => render_option())
|
||||
|
||||
// 保存按钮
|
||||
$('.rb-toggle-left-sidebar')
|
||||
|
@ -377,12 +376,13 @@ const build_config = () => {
|
|||
if (dims.length === 0 && nums.length === 0) return
|
||||
cfg.axis = { dimension: dims, numerical: nums }
|
||||
|
||||
const opts = {}
|
||||
const option = {}
|
||||
$('.chart-option input').each(function () {
|
||||
const name = $(this).data('name')
|
||||
if (name) opts[name] = $val(this)
|
||||
if (name) option[name] = $val(this)
|
||||
})
|
||||
cfg.option = opts
|
||||
if (option.useColor === '#000000') delete option.useColor
|
||||
cfg.option = option
|
||||
|
||||
if (dataFilter) cfg.filter = dataFilter
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -16,7 +16,7 @@ class BaseChart extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const opers = (
|
||||
const opActions = (
|
||||
<div className="chart-oper">
|
||||
{!this.props.builtin && (
|
||||
<a title={$L('查看来源数据')} href={`${rb.baseUrl}/dashboard/view-chart-source?id=${this.props.id}`}>
|
||||
|
@ -46,7 +46,7 @@ class BaseChart extends React.Component {
|
|||
<div className={`chart-box ${this.props.type}`} ref={(c) => (this._$box = c)}>
|
||||
<div className="chart-head">
|
||||
<div className="chart-title text-truncate">{this.state.title}</div>
|
||||
{opers}
|
||||
{opActions}
|
||||
</div>
|
||||
<div ref={(c) => (this._$body = c)} className={`chart-body rb-loading ${!this.state.chartdata && 'rb-loading-active'}`}>
|
||||
{this.state.chartdata || <RbSpinner />}
|
||||
|
@ -79,7 +79,7 @@ class BaseChart extends React.Component {
|
|||
|
||||
resize() {
|
||||
if (this._echarts) {
|
||||
$setTimeout(() => this._echarts.resize(), 400, `resize-chart-${this.state.id || ''}`)
|
||||
$setTimeout(() => this._echarts.resize(), 400, `resize-chart-${this.state.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ class ChartIndex extends BaseChart {
|
|||
|
||||
renderChart(data) {
|
||||
const chartdata = (
|
||||
<div className="chart index" ref={(c) => (this._chart = c)}>
|
||||
<div className="chart index color" ref={(c) => (this._$chart = c)}>
|
||||
<div className="data-item must-center text-truncate w-auto">
|
||||
<p>{data.index.label || this.label}</p>
|
||||
<a href={__PREVIEW ? null : `${rb.baseUrl}/dashboard/view-chart-source?id=${this.props.id}`}>
|
||||
|
@ -154,16 +154,20 @@ class ChartIndex extends BaseChart {
|
|||
}
|
||||
|
||||
resize() {
|
||||
$setTimeout(() => this._resize(), 200, 'resize-chart-index')
|
||||
$setTimeout(() => this._resize(), 200, `resize-chart-${this.props.id}`)
|
||||
}
|
||||
|
||||
_resize() {
|
||||
const ch = $(this._chart).height()
|
||||
const $text = $(this._chart).find('strong')
|
||||
let zoom = $(this._chart).width() / $text.width() / 3
|
||||
if (zoom < 1 || ch < 120) zoom = 1
|
||||
if (zoom > 2 && ch < 200) zoom = 2
|
||||
$text.css('zoom', Math.min(zoom, 3))
|
||||
const ch = $(this._$chart).height()
|
||||
const zoom = ch > 100 ? 1.3 : 1
|
||||
$(this._$chart).find('strong').css('zoom', zoom)
|
||||
|
||||
// const $text = $(this._$chart).find('strong')
|
||||
// zoom = $(this._$chart).width() / $text.width()
|
||||
// console.log(this.props.id, zoom)
|
||||
// if (zoom < 1 || ch < 120) zoom = 1
|
||||
// if (zoom > 2 && ch < 200) zoom = 2
|
||||
// $text.css('zoom', Math.min(zoom, 3))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +222,7 @@ class ChartTable extends BaseChart {
|
|||
if (this._$tb) this._$tb.find('.ctable').css('height', this._$tb.height() - 20)
|
||||
},
|
||||
400,
|
||||
'resize-chart-' + this.state.id
|
||||
`resize-chart-${this.state.id}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +252,7 @@ const ECHART_BASE = {
|
|||
textStyle: {
|
||||
fontFamily: 'Roboto, "Hiragina Sans GB", San Francisco, "Helvetica Neue", Helvetica, Arial, PingFangSC-Light, "WenQuanYi Micro Hei", "Microsoft YaHei UI", "Microsoft YaHei", sans-serif',
|
||||
},
|
||||
color: RBCOLORS,
|
||||
}
|
||||
|
||||
const ECHART_AXIS_LABEL = {
|
||||
|
@ -702,7 +707,7 @@ class ApprovalList extends BaseChart {
|
|||
if (this._$tb) this._$tb.find('.ApprovalList').css('height', this._$tb.height() - 5)
|
||||
},
|
||||
400,
|
||||
'resize-chart-' + this.state.id
|
||||
`resize-chart-${this.state.id}`
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -808,7 +813,7 @@ class FeedsSchedule extends BaseChart {
|
|||
if (this._$tb) this._$tb.find('.FeedsSchedule').css('height', this._$tb.height() - 13)
|
||||
},
|
||||
400,
|
||||
'resize-chart-' + this.state.id
|
||||
`resize-chart-${this.state.id}`
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1113,6 +1118,7 @@ const detectChart = function (cfg, id) {
|
|||
// isManageable = 图表可编辑
|
||||
// editable = 仪表盘可编辑
|
||||
const props = { config: cfg, id: id, title: cfg.title, type: cfg.type, isManageable: cfg.isManageable, editable: cfg.editable }
|
||||
|
||||
if (cfg.type === 'INDEX') {
|
||||
return <ChartIndex {...props} />
|
||||
} else if (cfg.type === 'TABLE') {
|
||||
|
|
|
@ -55,7 +55,7 @@ $(document).ready(function () {
|
|||
RbHighbar.success($L('仪表盘已删除'))
|
||||
location.hash = ''
|
||||
} else {
|
||||
const high = $('#chart-' + location.hash.substr(1)).addClass('high')
|
||||
const high = $(`#chart-${location.hash.substr(1)}`).addClass('high')
|
||||
if (high.length > 0) {
|
||||
high.on('mouseleave', () => {
|
||||
high.removeClass('high').off('mouseleave')
|
||||
|
@ -228,13 +228,13 @@ const render_dashboard = function (init) {
|
|||
}
|
||||
|
||||
const add_widget = function (item) {
|
||||
const chid = 'chart-' + item.chart
|
||||
if ($('#' + chid).length > 0) return false
|
||||
const chid = `chart-${item.chart}`
|
||||
if ($(`#${chid}`)[0]) return false // exsist
|
||||
|
||||
const chart_add = $('#chart-add')
|
||||
if (chart_add.length > 0) gridstack.removeWidget(chart_add.parent())
|
||||
|
||||
const gsi = `<div class="grid-stack-item"><div id="${chid}" class="grid-stack-item-content"></div></div>`
|
||||
const gsi = `<div class="grid-stack-item ${item.color && 'color'}"><div id="${chid}" class="grid-stack-item-content" ${item.color ? `style="background-color:${item.color}` : ''}"></div></div>`
|
||||
// Use gridstar
|
||||
if (item.size_x || item.size_y) {
|
||||
gridstack.addWidget(gsi, (item.col || 1) - 1, (item.row || 1) - 1, item.size_x || 2, item.size_y || 2, true, 2, 12, 2, 24)
|
||||
|
@ -309,7 +309,9 @@ class DlgAddChart extends RbFormHandler {
|
|||
const $entity = $(this.refs['entity'])
|
||||
$.get('/commons/metadata/entities?detail=true', (res) => {
|
||||
$(res.data).each(function () {
|
||||
$('<option value="' + this.name + '">' + this.label + '</option>').appendTo($entity)
|
||||
if (!$isSysMask(this.label)) {
|
||||
$(`<option value="${this.name}">${this.label}</option>`).appendTo($entity)
|
||||
}
|
||||
})
|
||||
this.__select2 = $entity.select2({
|
||||
allowClear: false,
|
||||
|
@ -454,7 +456,7 @@ class DlgDashAdd extends RbFormHandler {
|
|||
if (this.state.copy === true) _data.__copy = gridstack_serialize
|
||||
|
||||
$.post('/dashboard/dash-new', JSON.stringify(_data), (res) => {
|
||||
if (res.error_code === 0) location.href = '?d=' + res.data.id
|
||||
if (res.error_code === 0) location.href = `?d=${res.data.id}`
|
||||
else RbHighbar.error(res.error_msg)
|
||||
})
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue