mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-02-22 05:14:38 +08:00
Merge pull request #362 from getrebuild/wxwork-dingtalk
Wxwork dingtalk
This commit is contained in:
commit
7778575250
49 changed files with 705 additions and 194 deletions
BIN
.deps/taobao-sdk-java-auto_1479188381469-20210716.jar
Normal file
BIN
.deps/taobao-sdk-java-auto_1479188381469-20210716.jar
Normal file
Binary file not shown.
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 276e88e1327caf8f6dcc9b08c16ba7b67010e396
|
||||
Subproject commit 1536f8a9f00c61b7868bb16b898b980a61218c5f
|
41
pom.xml
41
pom.xml
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<version>2.5.2</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
|
@ -42,7 +42,7 @@
|
|||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.11.0</version>
|
||||
<version>1.12.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
|
@ -223,10 +223,11 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- Use SPEC -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<version>5.3.3</version>
|
||||
<version>5.3.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -282,17 +283,17 @@
|
|||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.2</version>
|
||||
<version>1.2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.23</version>
|
||||
<version>8.0.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
<artifactId>ehcache</artifactId>
|
||||
<version>2.10.6</version>
|
||||
<version>2.10.9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
|
@ -302,7 +303,7 @@
|
|||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.4.0</version>
|
||||
<version>7.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.whvcse</groupId>
|
||||
|
@ -317,12 +318,12 @@
|
|||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.13.1</version>
|
||||
<version>1.14.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hankcs</groupId>
|
||||
<artifactId>hanlp</artifactId>
|
||||
<version>portable-1.7.8</version>
|
||||
<version>portable-1.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
|
@ -342,7 +343,7 @@
|
|||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>2.2.8</version>
|
||||
<version>2.2.10</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>ehcache</artifactId>
|
||||
|
@ -388,17 +389,17 @@
|
|||
<dependency>
|
||||
<groupId>com.googlecode.aviator</groupId>
|
||||
<artifactId>aviator</artifactId>
|
||||
<version>5.2.3</version>
|
||||
<version>5.2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>0.4.13</version>
|
||||
<version>0.4.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>es.moki.ratelimitj</groupId>
|
||||
<artifactId>ratelimitj-inmemory</artifactId>
|
||||
<version>0.6.0</version>
|
||||
<version>0.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
|
@ -408,7 +409,7 @@
|
|||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.15.0</version>
|
||||
<version>3.16.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
@ -418,7 +419,17 @@
|
|||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<version>5.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Local .deps -->
|
||||
<dependency>
|
||||
<groupId>local</groupId>
|
||||
<artifactId>dingtalk-sdk</artifactId>
|
||||
<version>20210719</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${basedir}/.deps/taobao-sdk-java-auto_1479188381469-20210716.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -40,7 +40,7 @@ public class AuthTokenManager {
|
|||
public static String generateToken(ID user, int expires) {
|
||||
String token = String.format("%s,%d,%s,v1",
|
||||
user, System.currentTimeMillis(), CodecUtils.randomCode(10));
|
||||
token = CodecUtils.base64UrlEncode(token);
|
||||
token = CodecUtils.base64UrlEncode(token); // 64bit
|
||||
Application.getCommonsCache().putx(TOKEN_PREFIX + token, user, expires);
|
||||
return token;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public class LoginToken extends BaseApi {
|
|||
*
|
||||
* @param user
|
||||
* @param password
|
||||
* @return
|
||||
* @return 返回 null 表示成功
|
||||
*/
|
||||
public static String checkUser(String user, String password) {
|
||||
if (!Application.getUserStore().existsUser(user)) {
|
||||
|
|
|
@ -155,9 +155,11 @@ public class BootEnvironmentPostProcessor implements EnvironmentPostProcessor, I
|
|||
*/
|
||||
public static String getProperty(String name, String defaultValue) {
|
||||
String value = null;
|
||||
if (ConfigurationItem.DataDirectory.name().equalsIgnoreCase(name)) {
|
||||
if (ConfigurationItem.DataDirectory.name().equalsIgnoreCase(name)
|
||||
|| ConfigurationItem.MobileUrl.name().equalsIgnoreCase(name)) {
|
||||
value = StringUtils.defaultIfBlank(
|
||||
System.getProperty("DataDirectory"), System.getProperty(V2_PREFIX + "DataDirectory"));
|
||||
System.getProperty(name), System.getProperty(V2_PREFIX + name));
|
||||
|
||||
} else if (ENV_HOLD != null) {
|
||||
if (!(name.startsWith(V2_PREFIX) || name.contains("."))) {
|
||||
name = V2_PREFIX + name;
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.rebuild.core.privileges.UserService;
|
|||
import com.rebuild.core.service.DataSpecificationException;
|
||||
import com.rebuild.core.service.general.QuickCodeReindexTask;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -29,6 +30,7 @@ import org.springframework.stereotype.Service;
|
|||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/04/10
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ClassificationService extends BaseConfigurationService implements AdminGuard {
|
||||
|
||||
|
@ -87,7 +89,7 @@ public class ClassificationService extends BaseConfigurationService implements A
|
|||
} finally {
|
||||
long cost = System.currentTimeMillis() - start;
|
||||
if (cost > 2000 || Application.devMode()) {
|
||||
LOG.info("Reindex FullName [ " + itemId + " ] in " + cost + " ms");
|
||||
log.info("Reindex FullName [ {} ] in {} ms", itemId, cost);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -177,6 +177,7 @@ public class EntityHelper {
|
|||
public static final int RoleMember = 5;
|
||||
public static final int Team = 6;
|
||||
public static final int TeamMember = 7;
|
||||
public static final int ExternalUser = 8;
|
||||
|
||||
// 配置
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.rebuild.core.metadata.MetadataHelper;
|
|||
import com.rebuild.core.privileges.AdminGuard;
|
||||
import com.rebuild.core.service.BaseService;
|
||||
import com.rebuild.core.service.ServiceSpec;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +26,7 @@ import org.springframework.stereotype.Service;
|
|||
* @author zhaofang123@gmail.com
|
||||
* @since 08/03/2018
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MetaEntityService extends BaseService implements AdminGuard {
|
||||
|
||||
|
@ -76,7 +78,8 @@ public class MetaEntityService extends BaseService implements AdminGuard {
|
|||
}
|
||||
|
||||
if (usedArray.length > 0) {
|
||||
LOG.warn("deleted configuration of entity [ " + delEntity.getName() + " ] in [ " + conf + " ] : " + usedArray.length);
|
||||
log.warn("Deleted configuration of entity [ {} ] in [ {} ] : {}",
|
||||
delEntity.getName(), conf, usedArray.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.rebuild.core.metadata.EntityHelper;
|
|||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.AdminGuard;
|
||||
import com.rebuild.core.service.BaseService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
|
@ -27,6 +28,7 @@ import org.springframework.stereotype.Service;
|
|||
* @author zhaofang123@gmail.com
|
||||
* @since 08/03/2018
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class MetaFieldService extends BaseService implements AdminGuard {
|
||||
|
||||
|
@ -70,7 +72,8 @@ public class MetaFieldService extends BaseService implements AdminGuard {
|
|||
}
|
||||
|
||||
if (usedArray.length > 0) {
|
||||
LOG.warn("deleted configuration of field [ " + field.getOwnEntity().getName() + "." + field.getName() + " ] in [ " + who + " ] : " + usedArray.length);
|
||||
log.warn("Deleted configuration of field [ {}.{} ] in [ {} ] : {}",
|
||||
field.getOwnEntity().getName(), field.getName(), who, usedArray.length);
|
||||
|
||||
if ("PickList".equals(who)) {
|
||||
PickListManager.instance.clean(field);
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.rebuild.core.support.task.TaskExecutors;
|
|||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.BlockList;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -46,6 +47,7 @@ import java.util.Map;
|
|||
* @author zhaofang123@gmail.com
|
||||
* @since 07/25/2018
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserService extends BaseServiceImpl {
|
||||
|
||||
|
@ -155,7 +157,7 @@ public class UserService extends BaseServiceImpl {
|
|||
try {
|
||||
UserHelper.generateAvatar(record.getString("fullName"), true);
|
||||
} catch (Exception ex) {
|
||||
LOG.error(null, ex);
|
||||
log.error(null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +383,7 @@ public class UserService extends BaseServiceImpl {
|
|||
newUserId);
|
||||
content += String.format("[%s](%s)", Language.L("点击开始激活"), viewUrl);
|
||||
|
||||
Message message = MessageBuilder.createMessage(ADMIN_USER, content, newUserId);
|
||||
Message message = MessageBuilder.createMessage(ADMIN_USER, content, Message.TYPE_DEFAULT, newUserId);
|
||||
Application.getNotifications().send(message);
|
||||
|
||||
return newUserId;
|
||||
|
|
|
@ -11,8 +11,6 @@ import cn.devezhao.persist4j.PersistManagerFactory;
|
|||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 基础服务类
|
||||
|
@ -22,8 +20,6 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public abstract class BaseService implements ServiceSpec {
|
||||
|
||||
protected final Logger LOG = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final PersistManagerFactory aPMFactory;
|
||||
|
||||
protected BaseService(PersistManagerFactory aPMFactory) {
|
||||
|
|
|
@ -30,11 +30,10 @@ public class MessageBuilder {
|
|||
/**
|
||||
* @param toUser
|
||||
* @param message
|
||||
* @param recordId
|
||||
* @return
|
||||
*/
|
||||
public static Message createMessage(ID toUser, String message, ID recordId) {
|
||||
return new Message(null, toUser, message, recordId, Message.TYPE_DEFAULT);
|
||||
public static Message createMessage(ID toUser, String message) {
|
||||
return new Message(null, toUser, message, null, Message.TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,22 +49,22 @@ public class MessageBuilder {
|
|||
/**
|
||||
* @param toUser
|
||||
* @param message
|
||||
* @param type
|
||||
* @param recordId
|
||||
* @param relatedRecord
|
||||
* @return
|
||||
*/
|
||||
public static Message createMessage(ID toUser, String message, int type, ID recordId) {
|
||||
return new Message(null, toUser, message, recordId, type);
|
||||
public static Message createApproval(ID toUser, String message, ID relatedRecord) {
|
||||
return new Message(null, toUser, message, relatedRecord, Message.TYPE_APPROVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toUser
|
||||
* @param message
|
||||
* @param recordId
|
||||
* @param type
|
||||
* @param relatedRecord
|
||||
* @return
|
||||
*/
|
||||
public static Message createApproval(ID toUser, String message, ID recordId) {
|
||||
return new Message(null, toUser, message, recordId, Message.TYPE_APPROVAL);
|
||||
public static Message createMessage(ID toUser, String message, int type, ID relatedRecord) {
|
||||
return new Message(null, toUser, message, relatedRecord, type);
|
||||
}
|
||||
|
||||
// --
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.service.notification;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
|
||||
/**
|
||||
* 消息分发
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2021/7/20
|
||||
*/
|
||||
public interface MessageDistributor {
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param messageId
|
||||
* @return
|
||||
*/
|
||||
boolean send(Message message, ID messageId);
|
||||
}
|
|
@ -8,12 +8,15 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.notification;
|
||||
|
||||
import cn.devezhao.commons.ObjectUtils;
|
||||
import cn.devezhao.commons.ThreadPool;
|
||||
import cn.devezhao.persist4j.PersistManagerFactory;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.UserContextHolder;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.service.BaseService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
|
@ -22,6 +25,7 @@ import org.springframework.stereotype.Service;
|
|||
* @author devezhao
|
||||
* @since 10/17/2018
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class NotificationService extends BaseService {
|
||||
|
||||
|
@ -66,6 +70,8 @@ public class NotificationService extends BaseService {
|
|||
}
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
|
@ -82,10 +88,27 @@ public class NotificationService extends BaseService {
|
|||
if (message.getRelatedRecord() != null) {
|
||||
record.setID("relatedRecord", message.getRelatedRecord());
|
||||
}
|
||||
this.create(record);
|
||||
|
||||
record = this.create(record);
|
||||
|
||||
// 分发消息
|
||||
final ID messageId = record.getPrimary();
|
||||
ThreadPool.exec(() -> {
|
||||
String[] mdNames = Application.getContext().getBeanNamesForType(MessageDistributor.class);
|
||||
for (String md : mdNames) {
|
||||
try {
|
||||
boolean s = ((MessageDistributor) Application.getContext().getBean(md)).send(message, messageId);
|
||||
log.info("Distribute message : {} >> {}", messageId, s);
|
||||
} catch (Exception ex) {
|
||||
log.error("Distribute message failed : {}", messageId, ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读消息数
|
||||
*
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
|
@ -104,4 +127,15 @@ public class NotificationService extends BaseService {
|
|||
Application.getCommonsCache().putx(ckey, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设为已读
|
||||
*
|
||||
* @param messageId
|
||||
*/
|
||||
public void makeRead(ID messageId) {
|
||||
Record record = EntityHelper.forUpdate(messageId, UserContextHolder.getUser());
|
||||
record.setBoolean("unread", false);
|
||||
this.update(record);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.rebuild.core.service.trigger.ActionContext;
|
|||
import com.rebuild.core.service.trigger.ActionType;
|
||||
import com.rebuild.core.service.trigger.TriggerAction;
|
||||
import com.rebuild.core.support.general.ContentWithFieldVars;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.integration.SMSender;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
@ -85,7 +86,8 @@ public class SendNotification implements TriggerAction {
|
|||
String message = content.getString("content");
|
||||
message = ContentWithFieldVars.replaceWithRecord(message, context.getSourceRecord());
|
||||
|
||||
String emailSubject = StringUtils.defaultIfBlank(content.getString("title"), "你有一条新通知");
|
||||
String emailSubject = content.getString("title");
|
||||
if (StringUtils.isBlank(emailSubject)) emailSubject = Language.L("你有一条新通知");
|
||||
|
||||
for (ID user : toUsers) {
|
||||
if (type == TYPE_MAIL) {
|
||||
|
@ -101,7 +103,7 @@ public class SendNotification implements TriggerAction {
|
|||
}
|
||||
|
||||
} else { // TYPE_NOTIFICATION
|
||||
Message m = MessageBuilder.createMessage(user, message, context.getSourceRecord());
|
||||
Message m = MessageBuilder.createMessage(user, message, Message.TYPE_DEFAULT, context.getSourceRecord());
|
||||
Application.getNotifications().send(m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ import com.rebuild.core.support.i18n.LanguageBundle;
|
|||
*/
|
||||
public enum ConfigurationItem {
|
||||
|
||||
// 仅命令行指定
|
||||
DataDirectory, // 数据目录
|
||||
MobileUrl, // 移动端地址
|
||||
|
||||
// 系统指定
|
||||
SN, DBVer, AppBuild,
|
||||
// 数据目录(命令行指定)
|
||||
DataDirectory,
|
||||
// 缓存服务(安装/配置文件指定)
|
||||
CacheHost, CachePort, CacheUser, CachePassword,
|
||||
|
||||
|
@ -89,6 +91,12 @@ public enum ConfigurationItem {
|
|||
// 登录密码过期时间
|
||||
PasswordExpiredDays(0),
|
||||
|
||||
// DingTalk
|
||||
DingtalkAgentid, DingtalkAppkey, DingtalkAppsecret, DingtalkCorpid,
|
||||
DingtalkPushAeskey, DingtalkPushToken,
|
||||
// WxWork
|
||||
WxworkCorpid, WxworkAgentid, WxworkSecret
|
||||
|
||||
;
|
||||
|
||||
private Object defaultVal;
|
||||
|
|
|
@ -36,7 +36,7 @@ public class DataMasking {
|
|||
int starLen = textLen - len3 * 2;
|
||||
return text.substring(0, len3)
|
||||
+ StringUtils.repeat("*", Math.min(starLen, 20))
|
||||
+ text.substring(len3 * 2);
|
||||
+ text.substring(textLen - len3 * 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.core.support;
|
||||
|
||||
import com.rebuild.core.BootEnvironmentPostProcessor;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
@ -145,17 +146,33 @@ public class RebuildConfiguration extends KVStorage {
|
|||
*/
|
||||
public static String getHomeUrl(String path) {
|
||||
String homeUrl = get(ConfigurationItem.HomeURL);
|
||||
if (!homeUrl.endsWith("/")) {
|
||||
homeUrl += "/";
|
||||
if (path != null) homeUrl = joinPath(homeUrl, path);
|
||||
else if (!homeUrl.endsWith("/")) homeUrl += "/";
|
||||
return homeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绝对 URL H5
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @see #getHomeUrl(String)
|
||||
*/
|
||||
public static String getMobileUrl(String path) {
|
||||
String mobileUrl = BootEnvironmentPostProcessor.getProperty(ConfigurationItem.MobileUrl.name());
|
||||
if (mobileUrl != null) {
|
||||
return path == null ? mobileUrl : joinPath(mobileUrl, path);
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return homeUrl + path;
|
||||
}
|
||||
return homeUrl;
|
||||
mobileUrl = "/h5app/";
|
||||
if (path != null) mobileUrl = joinPath(mobileUrl, path);
|
||||
return getHomeUrl(mobileUrl);
|
||||
}
|
||||
|
||||
static String joinPath(String path1, String path2) {
|
||||
if (path1.endsWith("/")) path1 = path1.substring(0, path1.length() - 1);
|
||||
if (path2.startsWith("/")) path2 = path2.substring(1);
|
||||
return path1 + "/" + path2;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,6 +220,15 @@ public class RebuildConfiguration extends KVStorage {
|
|||
return s == null ? (Integer) name.getDefaultValue() : NumberUtils.toInt(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static long getLong(ConfigurationItem name) {
|
||||
String s = get(name);
|
||||
return s == null ? (Long) name.getDefaultValue() : NumberUtils.toLong(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
|
|
|
@ -11,6 +11,8 @@ import cn.devezhao.commons.ThrowableUtils;
|
|||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.commons.web.WebUtils;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import com.rebuild.api.user.AuthTokenManager;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.BootApplication;
|
||||
|
@ -222,4 +224,15 @@ public class AppUtils {
|
|||
String ua = request.getHeader("user-agent");
|
||||
return ua != null && ua.contains("Trident/") && ua.contains("rv:11.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否移动端
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMobile(HttpServletRequest request) {
|
||||
UserAgent ua = UserAgentUtil.parse(request.getHeader("user-agent"));
|
||||
return ua != null && ua.isMobile();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,11 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.*;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
@ -95,35 +93,47 @@ public class HttpUtils {
|
|||
* POST
|
||||
*
|
||||
* @param url
|
||||
* @param formData
|
||||
* @param reqData
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String post(String url, Map<String, Object> formData) throws IOException {
|
||||
return post(url, formData, null);
|
||||
public static String post(String url, Object reqData) throws IOException {
|
||||
return post(url, reqData, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST with Headers
|
||||
*
|
||||
* @param url
|
||||
* @param formData
|
||||
* @param reqData
|
||||
* @param headers
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String post(String url, Map<String, Object> formData, Map<String, String> headers) throws IOException {
|
||||
FormBody.Builder formBuilder = new FormBody.Builder();
|
||||
if (formData != null && !formData.isEmpty()) {
|
||||
for (Map.Entry<String, Object> e : formData.entrySet()) {
|
||||
public static String post(String url, Object reqData, Map<String, String> headers) throws IOException {
|
||||
RequestBody requestBody;
|
||||
|
||||
// JSON
|
||||
if (reqData instanceof JSON) {
|
||||
requestBody = RequestBody.create(((JSON) reqData).toJSONString(), MediaType.parse("application/json"));
|
||||
}
|
||||
// Map
|
||||
else if (reqData instanceof Map) {
|
||||
FormBody.Builder formBuilder = new FormBody.Builder();
|
||||
for (Map.Entry<?, ?> e : ((Map<?, ?>) reqData).entrySet()) {
|
||||
Object v = e.getValue();
|
||||
formBuilder.add(e.getKey(), v == null ? StringUtils.EMPTY : v.toString());
|
||||
formBuilder.add(e.getKey().toString(), v == null ? StringUtils.EMPTY : v.toString());
|
||||
}
|
||||
requestBody = formBuilder.build();
|
||||
}
|
||||
// Text
|
||||
else {
|
||||
requestBody = RequestBody.create(reqData.toString(), MediaType.parse("text/plain"));
|
||||
}
|
||||
|
||||
Request.Builder builder = new Request.Builder().url(url);
|
||||
Request request = useHeaders(builder, headers)
|
||||
.post(formBuilder.build())
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
long ms = System.currentTimeMillis();
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.rebuild.core.support.i18n.Language;
|
|||
import com.rebuild.core.support.integration.QiniuCloud;
|
||||
import com.rebuild.core.support.integration.SMSender;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import com.rebuild.web.BaseController;
|
||||
import com.rebuild.web.RebuildWebConfigurer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -275,4 +276,69 @@ public class ConfigurationController extends BaseController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DingTalk
|
||||
|
||||
@GetMapping("integration/dingtalk")
|
||||
public ModelAndView pageIntegrationDingtalk() {
|
||||
RbAssert.isCommercial(
|
||||
Language.L("免费版不支持钉钉集成 [(查看详情)](https://getrebuild.com/docs/rbv-features)"));
|
||||
|
||||
ModelAndView mv = createModelAndView("/admin/integration/dingtalk");
|
||||
for (ConfigurationItem item : ConfigurationItem.values()) {
|
||||
String name = item.name();
|
||||
if (name.startsWith("Dingtalk")) {
|
||||
String value = RebuildConfiguration.get(item);
|
||||
|
||||
if (value != null && item == ConfigurationItem.DingtalkAppsecret) {
|
||||
value = DataMasking.masking(value);
|
||||
}
|
||||
mv.getModel().put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
String homeUrl = RebuildConfiguration.getHomeUrl("/user/dingtalk");
|
||||
mv.getModel().put("_DingtalkHomeUrl", homeUrl);
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
@PostMapping("integration/dingtalk")
|
||||
public RespBody postIntegrationDingtalk(@RequestBody JSONObject data) {
|
||||
setValues(data);
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
// WxWork
|
||||
|
||||
@GetMapping("integration/wxwork")
|
||||
public ModelAndView pageIntegrationWxwork() {
|
||||
RbAssert.isCommercial(
|
||||
Language.L("免费版不支持企业微信集成 [(查看详情)](https://getrebuild.com/docs/rbv-features)"));
|
||||
|
||||
ModelAndView mv = createModelAndView("/admin/integration/wxwork");
|
||||
for (ConfigurationItem item : ConfigurationItem.values()) {
|
||||
String name = item.name();
|
||||
if (name.startsWith("Wxwork")) {
|
||||
String value = RebuildConfiguration.get(item);
|
||||
|
||||
if (value != null && item == ConfigurationItem.WxworkSecret) {
|
||||
value = DataMasking.masking(value);
|
||||
}
|
||||
mv.getModel().put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
String homeUrl = RebuildConfiguration.getHomeUrl("/user/wxwork");
|
||||
mv.getModel().put("_WxworkHomeUrl", homeUrl);
|
||||
mv.getModel().put("_WxworkAuthCallUrl", homeUrl.split("//")[1].split("/")[0]);
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
@PostMapping("integration/wxwork")
|
||||
public RespBody postIntegrationWxwork(@RequestBody JSONObject data) {
|
||||
setValues(data);
|
||||
return RespBody.ok();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,12 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.web.commons;
|
||||
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import com.rebuild.core.support.License;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
@ -27,8 +31,12 @@ import java.io.IOException;
|
|||
public class CommonPageView extends BaseController {
|
||||
|
||||
@GetMapping("/")
|
||||
public void index(HttpServletResponse response) throws IOException {
|
||||
response.sendRedirect("user/login");
|
||||
public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
if (AppUtils.isMobile(request) && License.isRbvAttached()) {
|
||||
response.sendRedirect(RebuildConfiguration.getMobileUrl("/"));
|
||||
} else {
|
||||
response.sendRedirect("user/login");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/*.txt")
|
||||
|
@ -39,7 +47,7 @@ public class CommonPageView extends BaseController {
|
|||
String content = CommonsUtils.getStringOfRes("web/" + url);
|
||||
|
||||
if (content == null) {
|
||||
response.sendError(404);
|
||||
response.sendError(HttpStatus.NOT_FOUND.value());
|
||||
} else {
|
||||
ServletUtils.setContentType(response, ServletUtils.CT_PLAIN);
|
||||
ServletUtils.write(response, content);
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.web.commons;
|
|||
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.web.BaseController;
|
||||
import com.rebuild.web.WebConstants;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
@ -24,7 +25,9 @@ public class RbvMissingController extends BaseController {
|
|||
|
||||
@GetMapping({"/h5app/**"})
|
||||
public ModelAndView pageH5app() {
|
||||
return ErrorPageView.createErrorPage(
|
||||
ModelAndView mv = ErrorPageView.createErrorPage(
|
||||
Language.L("免费版不支持手机访问功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)"));
|
||||
mv.getModelMap().put(WebConstants.$BUNDLE, Language.getCurrentBundle());
|
||||
return mv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ import java.text.MessageFormat;
|
|||
@Controller
|
||||
public class ListAndViewRedirection extends BaseController {
|
||||
|
||||
@GetMapping("/app/list-and-view")
|
||||
// H5: "/app/redirect"
|
||||
@GetMapping({ "/app/list-and-view", "/app/redirect" })
|
||||
public void redirect(@IdParam ID anyId, HttpServletResponse response) throws IOException {
|
||||
String url = null;
|
||||
|
||||
|
|
|
@ -7,16 +7,15 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.web.notification;
|
||||
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.service.approval.ApprovalState;
|
||||
import com.rebuild.core.service.notification.MessageBuilder;
|
||||
import com.rebuild.core.support.i18n.I18nUtils;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
@ -25,6 +24,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
|
@ -60,8 +60,9 @@ public class NotificationController extends BaseController {
|
|||
|
||||
if ("ALL".equalsIgnoreCase(ids)) {
|
||||
Object[][] unreads = Application.createQueryNoFilter(
|
||||
"select messageId from Notification where toUser = ?")
|
||||
"select messageId from Notification where toUser = ? and unread = ?")
|
||||
.setParameter(1, user)
|
||||
.setParameter(2, true)
|
||||
.array();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -71,13 +72,9 @@ public class NotificationController extends BaseController {
|
|||
ids = sb.toString();
|
||||
}
|
||||
|
||||
for (String id : ids.split(",")) {
|
||||
if (!ID.isId(id)) continue;
|
||||
|
||||
Record record = EntityHelper.forUpdate(ID.valueOf(id), user);
|
||||
record.setBoolean("unread", false);
|
||||
Application.getNotifications().update(record);
|
||||
}
|
||||
Arrays.stream(ids.split(","))
|
||||
.filter(ID::isId)
|
||||
.forEach(id -> Application.getNotifications().makeRead(ID.valueOf(id)));
|
||||
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
@ -141,19 +138,20 @@ public class NotificationController extends BaseController {
|
|||
.setParameter(1, approvalStep)
|
||||
.unique();
|
||||
if (stepState == null) {
|
||||
m[3] = new Object[]{0};
|
||||
m[3] = new Object[] { 0 };
|
||||
} else {
|
||||
boolean canceled = (Boolean) stepState[0];
|
||||
ApprovalState state = (ApprovalState) ApprovalState.valueOf((Integer) stepState[1]);
|
||||
if (state == ApprovalState.DRAFT) {
|
||||
m[3] = canceled ? new Object[]{2, "已处理"} : new Object[]{1, "待处理"};
|
||||
boolean canceled = (Boolean) stepState[0];
|
||||
m[3] = canceled
|
||||
? new Object[] { 2, Language.L("已处理") }
|
||||
: new Object[] { 1, Language.L("待处理") };
|
||||
} else if (state == ApprovalState.APPROVED) {
|
||||
m[3] = new Object[]{10, "已同意"};
|
||||
m[3] = new Object[] { 10, Language.L("已同意") };
|
||||
} else if (state == ApprovalState.REJECTED) {
|
||||
m[3] = new Object[]{11, "已驳回"};
|
||||
m[3] = new Object[] { 11, Language.L("已驳回") };
|
||||
}
|
||||
}
|
||||
|
||||
array[i] = m;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,10 @@ import com.rebuild.api.RespBody;
|
|||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.privileges.bizz.User;
|
||||
import com.rebuild.core.service.DataSpecificationException;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.VerfiyCode;
|
||||
import com.rebuild.core.support.i18n.I18nUtils;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
|
@ -41,7 +44,25 @@ public class UserSettings extends EntityController {
|
|||
@GetMapping("/user")
|
||||
public ModelAndView pageUser(HttpServletRequest request) {
|
||||
ModelAndView mv = createModelAndView("/settings/user-settings");
|
||||
mv.getModelMap().put("user", Application.getUserStore().getUser(getRequestUser(request)));
|
||||
|
||||
User user = Application.getUserStore().getUser(getRequestUser(request));
|
||||
mv.getModelMap().put("user", user);
|
||||
|
||||
if (RebuildConfiguration.get(ConfigurationItem.DingtalkCorpid) != null) {
|
||||
Object[] dingtalkUser = Application.createQueryNoFilter(
|
||||
"select appUser from ExternalUser where bindUser = ? and appType = 1")
|
||||
.setParameter(1, user.getId())
|
||||
.unique();
|
||||
if (dingtalkUser != null) mv.getModelMap().put("dingtalkUser", dingtalkUser[0]);
|
||||
}
|
||||
if (RebuildConfiguration.get(ConfigurationItem.WxworkCorpid) != null) {
|
||||
Object[] wxworkUser = Application.createQueryNoFilter(
|
||||
"select appUser from ExternalUser where bindUser = ? and appType = 2")
|
||||
.setParameter(1, user.getId())
|
||||
.unique();
|
||||
if (wxworkUser != null) mv.getModelMap().put("wxworkUser", wxworkUser[0]);
|
||||
}
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
|
@ -146,4 +167,19 @@ public class UserSettings extends EntityController {
|
|||
}
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/cancel-external-user")
|
||||
public RespBody cancelExternalUser(HttpServletRequest request) {
|
||||
int appType = getIntParameter(request, "type", 0);
|
||||
Object[] externalUser = Application.createQueryNoFilter(
|
||||
"select userId from ExternalUser where bindUser = ? and appType = ?")
|
||||
.setParameter(1, getRequestUser(request))
|
||||
.setParameter(2, appType)
|
||||
.unique();
|
||||
if (externalUser != null) {
|
||||
Application.getCommonsService().delete((ID) externalUser[0]);
|
||||
}
|
||||
|
||||
return RespBody.ok();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,17 +62,14 @@ import java.util.Map;
|
|||
public class LoginController extends BaseController {
|
||||
|
||||
public static final String CK_AUTOLOGIN = "rb.alt";
|
||||
|
||||
private static final String SK_NEED_VCODE = "needLoginVCode";
|
||||
|
||||
public static final String SK_USER_THEME = "currentUseTheme";
|
||||
|
||||
private static final String DEFAULT_HOME = "../dashboard/home";
|
||||
private static final String SK_NEED_VCODE = "needLoginVCode";
|
||||
|
||||
@GetMapping("login")
|
||||
public ModelAndView checkLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
final String homeUrl = "../dashboard/home";
|
||||
if (AppUtils.getRequestUser(request) != null) {
|
||||
response.sendRedirect(DEFAULT_HOME);
|
||||
response.sendRedirect(homeUrl);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -83,7 +80,7 @@ public class LoginController extends BaseController {
|
|||
if (tokenUser != null) {
|
||||
loginSuccessed(request, response, tokenUser, false);
|
||||
|
||||
String nexturl = StringUtils.defaultIfBlank(request.getParameter("nexturl"), DEFAULT_HOME);
|
||||
String nexturl = getParameter(request, "nexturl", homeUrl);
|
||||
response.sendRedirect(CodecUtils.urlDecode(nexturl));
|
||||
return null;
|
||||
} else {
|
||||
|
@ -116,7 +113,7 @@ public class LoginController extends BaseController {
|
|||
if (altUser != null && Application.getUserStore().existsUser(altUser)) {
|
||||
loginSuccessed(request, response, altUser, true);
|
||||
|
||||
String nexturl = StringUtils.defaultIfBlank(request.getParameter("nexturl"), DEFAULT_HOME);
|
||||
String nexturl = getParameter(request, "nexturl", homeUrl);
|
||||
response.sendRedirect(CodecUtils.urlDecode(nexturl));
|
||||
return null;
|
||||
} else {
|
||||
|
@ -136,9 +133,10 @@ public class LoginController extends BaseController {
|
|||
ServletUtils.setSessionAttribute(request, SK_NEED_VCODE, true);
|
||||
}
|
||||
|
||||
// H5 QR
|
||||
String mobileQrUrl = RebuildConfiguration.getHomeUrl("/h5app/");
|
||||
mobileQrUrl = AppUtils.getContextPath() + "/commons/barcode/render-qr?t=" + CodecUtils.urlEncode(mobileQrUrl);
|
||||
// H5
|
||||
String mobileUrl = RebuildConfiguration.getMobileUrl("/");
|
||||
String mobileQrUrl = AppUtils.getContextPath() + "/commons/barcode/render-qr?t=" + CodecUtils.urlEncode(mobileUrl);
|
||||
mv.getModel().put("mobileUrl", mobileUrl);
|
||||
mv.getModel().put("mobileQrUrl", mobileQrUrl);
|
||||
|
||||
mv.getModelMap().put("UsersMsg", CheckDangers.getUsersDanger());
|
||||
|
@ -190,35 +188,22 @@ public class LoginController extends BaseController {
|
|||
return RespBody.ok(resMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param user
|
||||
* @param state
|
||||
* @return
|
||||
*/
|
||||
private int getLoginRetryTimes(String user, int state) {
|
||||
String key = "LoginRetry-" + user;
|
||||
final String ckey = "LoginRetry-" + user;
|
||||
if (state == -1) {
|
||||
Application.getCommonsCache().evict(key);
|
||||
Application.getCommonsCache().evict(ckey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Integer retry = (Integer) Application.getCommonsCache().getx(key);
|
||||
Integer retry = (Integer) Application.getCommonsCache().getx(ckey);
|
||||
retry = retry == null ? 0 : retry;
|
||||
if (state == 1) {
|
||||
retry += 1;
|
||||
Application.getCommonsCache().putx(key, retry, CommonsCache.TS_HOUR);
|
||||
Application.getCommonsCache().putx(ckey, retry, CommonsCache.TS_HOUR);
|
||||
}
|
||||
return retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param user
|
||||
* @param autoLogin
|
||||
*/
|
||||
private void loginSuccessed(HttpServletRequest request, HttpServletResponse response, ID user, boolean autoLogin) {
|
||||
// 自动登录
|
||||
if (autoLogin) {
|
||||
|
@ -237,16 +222,8 @@ public class LoginController extends BaseController {
|
|||
Application.getSessionStore().storeLoginSuccessed(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建登陆日志
|
||||
*
|
||||
* @param request
|
||||
* @param user
|
||||
*/
|
||||
protected void createLoginLog(HttpServletRequest request, ID user) {
|
||||
String ipAddr = ServletUtils.getRemoteAddr(request);
|
||||
private void createLoginLog(HttpServletRequest request, ID user) {
|
||||
String UA = request.getHeader("user-agent");
|
||||
|
||||
try {
|
||||
UserAgent uas = UserAgentUtil.parse(UA);
|
||||
UA = String.format("%s-%s (%s)",
|
||||
|
@ -260,7 +237,7 @@ public class LoginController extends BaseController {
|
|||
|
||||
Record record = EntityHelper.forNew(EntityHelper.LoginLog, UserService.SYSTEM_USER);
|
||||
record.setID("user", user);
|
||||
record.setString("ipAddr", ipAddr);
|
||||
record.setString("ipAddr", ServletUtils.getRemoteAddr(request));
|
||||
record.setString("userAgent", UA.toUpperCase());
|
||||
record.setDate("loginTime", CalendarUtils.now());
|
||||
Application.getCommonsService().create(record);
|
||||
|
|
|
@ -1260,7 +1260,7 @@
|
|||
"短信签名":"短信签名",
|
||||
"短信账户未配置或配置错误":"短信账户未配置或配置错误",
|
||||
"确定":"确定",
|
||||
"确定要禁用此用户吗?":"确定要禁用此用户吗?",
|
||||
"确认要禁用此用户吗?":"确认要禁用此用户吗?",
|
||||
"确认删除当前记录吗?":"确认删除当前记录吗?",
|
||||
"确认删除此仪表盘?":"确认删除此仪表盘?",
|
||||
"确认删除此任务?":"确认删除此任务?",
|
||||
|
|
|
@ -75,6 +75,14 @@
|
|||
<index field-list="teamId,userId" type="unique"/>
|
||||
</entity>
|
||||
|
||||
<entity name="ExternalUser" type-code="008" description="外部用户" queryable="false" parent="false">
|
||||
<field name="userId" type="primary"/>
|
||||
<field name="appUser" type="string" max-length="100" nullable="false" updatable="false"/>
|
||||
<field name="appType" type="small-int" nullable="false" updatable="false" description="1=DingTalk,2=WxWork"/>
|
||||
<field name="bindUser" type="reference" ref-entity="User" cascade="delete" nullable="false" updatable="false"/>
|
||||
<index field-list="appType,appUser" type="unique"/>
|
||||
</entity>
|
||||
|
||||
<entity name="MetaEntity" type-code="010" description="实体" name-field="entityName" queryable="false">
|
||||
<field name="entityId" type="primary"/>
|
||||
<field name="typeCode" type="small-int" nullable="false" updatable="false"/>
|
||||
|
|
|
@ -112,6 +112,16 @@ create table if not exists `team_member` (
|
|||
unique index UIX0_team_member (`TEAM_ID`, `USER_ID`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- ************ Entity [ExternalUser] DDL ************
|
||||
create table if not exists `external_user` (
|
||||
`USER_ID` char(20) not null,
|
||||
`APP_USER` varchar(100) not null,
|
||||
`APP_TYPE` smallint(6) not null comment '1=DingTalk,2=WxWork',
|
||||
`BIND_USER` char(20) not null,
|
||||
primary key (`USER_ID`),
|
||||
unique index UIX0_external_user (`APP_TYPE`, `APP_USER`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- ************ Entity [MetaEntity] DDL ************
|
||||
create table if not exists `meta_entity` (
|
||||
`ENTITY_ID` char(20) not null,
|
||||
|
@ -790,4 +800,4 @@ insert into `project_plan_config` (`CONFIG_ID`, `PROJECT_ID`, `PLAN_NAME`, `SEQ`
|
|||
|
||||
-- DB Version (see `db-upgrade.sql`)
|
||||
insert into `system_config` (`CONFIG_ID`, `ITEM`, `VALUE`)
|
||||
values ('021-9000000000000001', 'DBVer', 36);
|
||||
values ('021-9000000000000001', 'DBVer', 37);
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
-- Database upgrade scripts for rebuild 1.x and 2.x
|
||||
-- Each upgraded starts with `-- #VERSION`
|
||||
|
||||
-- #37 (v2.5)
|
||||
-- ************ Entity [ExternalUser] DDL ************
|
||||
create table if not exists `external_user` (
|
||||
`USER_ID` char(20) not null,
|
||||
`APP_USER` varchar(100) not null,
|
||||
`APP_TYPE` smallint(6) not null comment '1=DingTalk,2=WxWork',
|
||||
`BIND_USER` char(20) not null,
|
||||
primary key (`USER_ID`),
|
||||
unique index UIX0_external_user (`APP_TYPE`, `APP_USER`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- #36 (v2.4)
|
||||
-- ************ Entity [FrontjsCode] DDL ************
|
||||
create table if not exists `frontjs_code` (
|
||||
|
|
|
@ -8,18 +8,28 @@
|
|||
<ul class="sidebar-elements">
|
||||
<li class="divider">[[${bundle.L('系统')}]]</li>
|
||||
<li th:class="${active == 'systems'} ? 'active'">
|
||||
<a th:href="@{/admin/systems}"><i class="icon zmdi zmdi-settings"></i><span>[[${bundle.L('通用配置')}]]</span></a>
|
||||
<a th:href="@{/admin/systems}"><i class="icon zmdi zmdi-settings"></i>[[${bundle.L('通用配置')}]]</a>
|
||||
</li>
|
||||
<li class="parent">
|
||||
<a><i class="icon zmdi zmdi-puzzle-piece"></i><span>[[${bundle.L('服务集成')}]]</span></a>
|
||||
<a><i class="icon zmdi zmdi-puzzle-piece"></i>[[${bundle.L('服务集成')}]]</a>
|
||||
<ul class="sub-menu">
|
||||
<li class="title">[[${bundle.L('服务集成')}]]</li>
|
||||
<li class="nav-items">
|
||||
<div class="rb-scroller">
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li th:class="${active == 'integration-storage'} ? 'active'"><a th:href="@{/admin/integration/storage}">[[${bundle.L('云存储')}]]</a></li>
|
||||
<li th:class="${active == 'integration-submail'} ? 'active'"><a th:href="@{/admin/integration/submail}">[[${bundle.L('邮件&短信')}]]</a></li>
|
||||
<li th:class="${active == 'integration-storage'} ? 'active'">
|
||||
<a th:href="@{/admin/integration/storage}">[[${bundle.L('云存储')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'integration-submail'} ? 'active'">
|
||||
<a th:href="@{/admin/integration/submail}">[[${bundle.L('邮件&短信')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'integration-dingtalk'} ? 'active'">
|
||||
<a th:href="@{/admin/integration/dingtalk}">[[${bundle.L('钉钉')}]] <sup class="rbv"></sup></a>
|
||||
</li>
|
||||
<li th:class="${active == 'integration-wxwork'} ? 'active'">
|
||||
<a th:href="@{/admin/integration/wxwork}">[[${bundle.L('企业微信')}]] <sup class="rbv"></sup></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,59 +37,59 @@
|
|||
</ul>
|
||||
</li>
|
||||
<li th:class="${active == 'apis-manager'} ? 'active'">
|
||||
<a th:href="@{/admin/apis-manager}"><i class="icon zmdi zmdi-key"></i><span>[[${bundle.L('API 秘钥')}]]</span></a>
|
||||
<a th:href="@{/admin/apis-manager}"><i class="icon zmdi zmdi-key"></i>[[${bundle.L('API 秘钥')}]]</a>
|
||||
</li>
|
||||
<li class="divider">[[${bundle.L('业务实体')}]]</li>
|
||||
<li th:class="${active == 'entities'} ? 'active'">
|
||||
<a th:href="@{/admin/entities}"><i class="icon zmdi zmdi-widgets"></i><span>[[${bundle.L('实体管理')}]]</span></a>
|
||||
<a th:href="@{/admin/entities}"><i class="icon zmdi zmdi-widgets"></i>[[${bundle.L('实体管理')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'classifications'} ? 'active'">
|
||||
<a th:href="@{/admin/metadata/classifications}"><i class="icon zmdi zmdi-layers"></i><span>[[${bundle.L('分类数据')}]]</span></a>
|
||||
<a th:href="@{/admin/metadata/classifications}"><i class="icon zmdi zmdi-layers"></i>[[${bundle.L('分类数据')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'robot-approval'} ? 'active'">
|
||||
<a th:href="@{/admin/robot/approvals}"><i class="icon zmdi zmdi-assignment-check"></i><span>[[${bundle.L('审批流程')}]]</span></a>
|
||||
<a th:href="@{/admin/robot/approvals}"><i class="icon zmdi zmdi-assignment-check"></i>[[${bundle.L('审批流程')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'transforms'} ? 'active'">
|
||||
<a th:href="@{/admin/transforms}"><i class="icon zmdi zmdi-transform"></i><span>[[${bundle.L('记录转换映射')}]]</span></a>
|
||||
<a th:href="@{/admin/transforms}"><i class="icon zmdi zmdi-transform"></i>[[${bundle.L('记录转换映射')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'robot-trigger'} ? 'active'">
|
||||
<a th:href="@{/admin/robot/triggers}"><i class="icon zmdi zmdi-rotate-cw"></i><span>[[${bundle.L('触发器')}]]</span></a>
|
||||
<a th:href="@{/admin/robot/triggers}"><i class="icon zmdi zmdi-rotate-cw"></i>[[${bundle.L('触发器')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'extforms'} ? 'active'">
|
||||
<a th:href="@{/admin/extforms}"><i class="icon zmdi zmdi-collection-text"></i><span>[[${bundle.L('外部表单')}]]</span> <sup class="rbv"></sup></a>
|
||||
<a th:href="@{/admin/extforms}"><i class="icon zmdi zmdi-collection-text"></i>[[${bundle.L('外部表单')}]] <sup class="rbv"></sup> </a>
|
||||
</li>
|
||||
<li th:class="${active == 'data-imports'} ? 'active'">
|
||||
<a th:href="@{/admin/data/data-imports}"><i class="icon zmdi zmdi-cloud-upload"></i><span>[[${bundle.L('数据导入')}]]</span></a>
|
||||
<a th:href="@{/admin/data/data-imports}"><i class="icon zmdi zmdi-cloud-upload"></i>[[${bundle.L('数据导入')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'report-templates'} ? 'active'">
|
||||
<a th:href="@{/admin/data/report-templates}"><i class="icon zmdi zmdi-map"></i><span>[[${bundle.L('报表模板')}]]</span></a>
|
||||
<a th:href="@{/admin/data/report-templates}"><i class="icon zmdi zmdi-map"></i>[[${bundle.L('报表模板')}]]</a>
|
||||
</li>
|
||||
<li class="divider">[[${bundle.L('高级功能')}]]</li>
|
||||
<li th:class="${active == 'projects'} ? 'active'">
|
||||
<a th:href="@{/admin/projects}"><i class="icon zmdi zmdi-shape"></i><span>[[${bundle.L('项目')}]]</span></a>
|
||||
<a th:href="@{/admin/projects}"><i class="icon zmdi zmdi-shape"></i>[[${bundle.L('项目')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'frontjs-code'} ? 'active'">
|
||||
<a th:href="@{/admin/frontjs-code}"><i class="icon zmdi zmdi-code"></i><span>FrontJS</span> <sup class="rbv"></sup></a>
|
||||
<a th:href="@{/admin/frontjs-code}"><i class="icon zmdi zmdi-code"></i>FrontJS <sup class="rbv"></sup></a>
|
||||
</li>
|
||||
<li class="divider">[[${bundle.L('用户')}]]</li>
|
||||
<li th:class="${active == 'users'} ? 'active'">
|
||||
<a th:href="@{/admin/bizuser/users}"><i class="icon zmdi zmdi-accounts"></i><span>[[${bundle.L('部门用户')}]]</span></a>
|
||||
<a th:href="@{/admin/bizuser/users}"><i class="icon zmdi zmdi-accounts"></i>[[${bundle.L('部门用户')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'role-privileges'} ? 'active'">
|
||||
<a th:href="@{/admin/bizuser/role-privileges}"><i class="icon zmdi zmdi-lock"></i><span>[[${bundle.L('角色权限')}]]</span></a>
|
||||
<a th:href="@{/admin/bizuser/role-privileges}"><i class="icon zmdi zmdi-lock"></i>[[${bundle.L('角色权限')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'teams'} ? 'active'">
|
||||
<a th:href="@{/admin/bizuser/teams}"><i class="icon zmdi zmdi-case"></i><span>[[${bundle.L('团队')}]]</span></a>
|
||||
<a th:href="@{/admin/bizuser/teams}"><i class="icon zmdi zmdi-case"></i>[[${bundle.L('团队')}]]</a>
|
||||
</li>
|
||||
<li class="divider">[[${bundle.L('审计')}]]</li>
|
||||
<li th:class="${active == 'login-logs'} ? 'active'">
|
||||
<a th:href="@{/admin/audit/login-logs}"><i class="icon zmdi zmdi-pin-account"></i><span>[[${bundle.L('登录日志')}]]</span></a>
|
||||
<a th:href="@{/admin/audit/login-logs}"><i class="icon zmdi zmdi-pin-account"></i>[[${bundle.L('登录日志')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'revision-history'} ? 'active'">
|
||||
<a th:href="@{/admin/audit/revision-history}"><i class="icon zmdi zmdi-wrap-text"></i><span>[[${bundle.L('变更历史')}]]</span></a>
|
||||
<a th:href="@{/admin/audit/revision-history}"><i class="icon zmdi zmdi-wrap-text"></i>[[${bundle.L('变更历史')}]]</a>
|
||||
</li>
|
||||
<li th:class="${active == 'recycle-bin'} ? 'active'">
|
||||
<a th:href="@{/admin/audit/recycle-bin}"><i class="icon zmdi zmdi-delete fs-16"></i><span>[[${bundle.L('回收站')}]]</span></a>
|
||||
<a th:href="@{/admin/audit/recycle-bin}"><i class="icon zmdi zmdi-delete fs-16"></i>[[${bundle.L('回收站')}]]</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<style type="text/css">
|
||||
.rb-navbar-header .rb-toggle-left-sidebar,
|
||||
.rb-icons-nav {
|
||||
display: none !important
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
67
src/main/resources/web/admin/integration/dingtalk.html
Normal file
67
src/main/resources/web/admin/integration/dingtalk.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:replace="~{/_include/header}" />
|
||||
<meta name="page-help" content="https://getrebuild.com/docs/admin/integration-dingtalk" />
|
||||
<title>[[${bundle.L('钉钉')}]]</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="rb-wrapper rb-fixed-sidebar rb-collapsible-sidebar rb-collapsible-sidebar-hide-logo rb-color-header" th:classappend="${sideCollapsedClazz}">
|
||||
<th:block th:replace="~{/_include/nav-top}" />
|
||||
<th:block th:replace="~{/_include/nav-left-admin(active='integration-dingtalk')}" />
|
||||
<div class="rb-content">
|
||||
<div class="main-content container-fluid syscfg">
|
||||
<div class="row">
|
||||
<div class="col-lg-9 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header pb-1">
|
||||
[[${bundle.L('钉钉')}]]
|
||||
<a href="#modfiy" class="float-right"><i class="icon zmdi zmdi-edit"></i> [[${bundle.L('修改')}]]</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>[[${bundle.L('接口凭证')}]]</h5>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">AgentId</td>
|
||||
<td data-id="DingtalkAgentid" th:data-value="${DingtalkAgentid ?:''}">[[${DingtalkAgentid ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AppKey</td>
|
||||
<td data-id="DingtalkAppkey" th:data-value="${DingtalkAppkey ?:''}">[[${DingtalkAppkey ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AppSecret</td>
|
||||
<td data-id="DingtalkAppsecret" th:data-value="${DingtalkAppsecret ?:''}">[[${DingtalkAppsecret ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CorpId</td>
|
||||
<td data-id="DingtalkCorpid" th:data-value="${DingtalkCorpid ?:''}">[[${DingtalkCorpid ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h5>[[${bundle.L('钉钉侧配置')}]]</h5>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">[[${bundle.L('应用首页地址')}]]</td>
|
||||
<td>[[${_DingtalkHomeUrl}]]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="edit-footer">
|
||||
<button class="btn btn-link">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-12"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:replace="~{/_include/footer}" />
|
||||
<script th:src="@{/assets/js/admin/syscfg.js}" type="text/babel"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -24,22 +24,30 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">[[${bundle.L('访问域名')}]]</td>
|
||||
<td data-id="StorageURL" th:data-value="${storageAccount == null ? '' : storageAccount[3]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[3]}]]</td>
|
||||
<td data-id="StorageURL" th:data-value="${storageAccount == null ? '' : storageAccount[3]}">
|
||||
[[${storageAccount == null ? bundle.L('未设置') : storageAccount[3]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('存储空间')}]]
|
||||
<p>[[${bundle.L('存储空间变更需你自行迁移原有数据')}]]</p>
|
||||
</td>
|
||||
<td data-id="StorageBucket" th:data-value="${storageAccount == null ? '' : storageAccount[2]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[2]}]]</td>
|
||||
<td data-id="StorageBucket" th:data-value="${storageAccount == null ? '' : storageAccount[2]}">
|
||||
[[${storageAccount == null ? bundle.L('未设置') : storageAccount[2]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('秘钥 AK')}]]</td>
|
||||
<td data-id="StorageApiKey" th:data-value="${storageAccount == null ? '' : storageAccount[0]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[0]}]]</td>
|
||||
<td data-id="StorageApiKey" th:data-value="${storageAccount == null ? '' : storageAccount[0]}">
|
||||
[[${storageAccount == null ? bundle.L('未设置') : storageAccount[0]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('秘钥 SK')}]]</td>
|
||||
<td data-id="StorageApiSecret" th:data-value="${storageAccount == null ? '' : storageAccount[1]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[1]}]]</td>
|
||||
<td data-id="StorageApiSecret" th:data-value="${storageAccount == null ? '' : storageAccount[1]}">
|
||||
[[${storageAccount == null ? bundle.L('未设置') : storageAccount[1]}]]
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -54,25 +54,33 @@
|
|||
<span class="smtp-hide">APPID</span>
|
||||
<span class="smtp-show">[[${bundle.L('SMTP 用户名')}]]</span>
|
||||
</td>
|
||||
<td data-id="MailUser" th:data-value="${mailAccount == null ? '' : mailAccount[0]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[0]}]]</td>
|
||||
<td data-id="MailUser" th:data-value="${mailAccount == null ? '' : mailAccount[0]}">
|
||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[0]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="smtp-hide">APPKEY</span>
|
||||
<span class="smtp-show">[[${bundle.L('SMTP 密码')}]]</span>
|
||||
</td>
|
||||
<td data-id="MailPassword" th:data-value="${mailAccount == null ? '' : mailAccount[1]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[1]}]]</td>
|
||||
<td data-id="MailPassword" th:data-value="${mailAccount == null ? '' : mailAccount[1]}">
|
||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[1]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('发件人地址')}]]
|
||||
<p class="smtp-hide">[[${bundle.L('地址域名需与 SUBMAIL 中配置的域名匹配')}]]</p>
|
||||
</td>
|
||||
<td data-id="MailAddr" th:data-value="${mailAccount == null ? '' : mailAccount[2]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[2]}]]</td>
|
||||
<td data-id="MailAddr" th:data-value="${mailAccount == null ? '' : mailAccount[2]}">
|
||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[2]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('发件人名称')}]]</td>
|
||||
<td data-id="MailName" th:data-value="${mailAccount == null ? '' : mailAccount[3]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[3]}]]</td>
|
||||
<td data-id="MailName" th:data-value="${mailAccount == null ? '' : mailAccount[3]}">
|
||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[3]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="show-on-edit">
|
||||
<td></td>
|
||||
|
@ -93,15 +101,21 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">APPID</td>
|
||||
<td data-id="SmsUser" th:data-value="${smsAccount == null ? '' : smsAccount[0]}">[[${smsAccount == null ? bundle.L('未设置') : smsAccount[0]}]]</td>
|
||||
<td data-id="SmsUser" th:data-value="${smsAccount == null ? '' : smsAccount[0]}">
|
||||
[[${smsAccount == null ? bundle.L('未设置') : smsAccount[0]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>APPKEY</td>
|
||||
<td data-id="SmsPassword" th:data-value="${smsAccount == null ? '' : smsAccount[1]}">[[${smsAccount == null ? bundle.L('未设置') : smsAccount[1]}]]</td>
|
||||
<td data-id="SmsPassword" th:data-value="${smsAccount == null ? '' : smsAccount[1]}">
|
||||
[[${smsAccount == null ? bundle.L('未设置') : smsAccount[1]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('短信签名')}]]</td>
|
||||
<td data-id="SmsSign" th:data-value="${smsAccount == null ? '' : smsAccount[2]}">[[${smsAccount == null ? bundle.L('未设置') : smsAccount[2]}]]</td>
|
||||
<td data-id="SmsSign" th:data-value="${smsAccount == null ? '' : smsAccount[2]}">
|
||||
[[${smsAccount == null ? bundle.L('未设置') : smsAccount[2]}]]
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="show-on-edit">
|
||||
<td></td>
|
||||
|
|
67
src/main/resources/web/admin/integration/wxwork.html
Normal file
67
src/main/resources/web/admin/integration/wxwork.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<th:block th:replace="~{/_include/header}" />
|
||||
<meta name="page-help" content="https://getrebuild.com/docs/admin/integration-wxwork" />
|
||||
<title>[[${bundle.L('企业微信')}]]</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="rb-wrapper rb-fixed-sidebar rb-collapsible-sidebar rb-collapsible-sidebar-hide-logo rb-color-header" th:classappend="${sideCollapsedClazz}">
|
||||
<th:block th:replace="~{/_include/nav-top}" />
|
||||
<th:block th:replace="~{/_include/nav-left-admin(active='integration-wxwork')}" />
|
||||
<div class="rb-content">
|
||||
<div class="main-content container-fluid syscfg">
|
||||
<div class="row">
|
||||
<div class="col-lg-9 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header pb-1">
|
||||
[[${bundle.L('企业微信')}]]
|
||||
<a href="#modfiy" class="float-right"><i class="icon zmdi zmdi-edit"></i> [[${bundle.L('修改')}]]</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5>[[${bundle.L('接口凭证')}]]</h5>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">AgentId</td>
|
||||
<td data-id="WxworkAgentid" th:data-value="${WxworkAgentid ?:''}">[[${WxworkAgentid ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Secret</td>
|
||||
<td data-id="WxworkSecret" th:data-value="${WxworkSecret ?:''}">[[${WxworkSecret ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('企业 ID')}]]</td>
|
||||
<td data-id="WxworkCorpid" th:data-value="${WxworkCorpid ?:''}">[[${WxworkCorpid ?:bundle.L('未设置')}]]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h5>[[${bundle.L('企业微信侧配置')}]]</h5>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">[[${bundle.L('应用主页')}]]</td>
|
||||
<td>[[${_WxworkHomeUrl}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('可信域名')}]]</td>
|
||||
<td>[[${_WxworkAuthCallUrl}]]</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="edit-footer">
|
||||
<button class="btn btn-link">[[${bundle.L('取消')}]]</button>
|
||||
<button class="btn btn-primary">[[${bundle.L('保存')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-12"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:replace="~{/_include/footer}" />
|
||||
<script th:src="@{/assets/js/admin/syscfg.js}" type="text/babel"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -11542,7 +11542,7 @@ canvas {
|
|||
.rb-offcanvas-menu .rb-top-header .rb-navbar-header {
|
||||
display: block;
|
||||
width: auto;
|
||||
background-color: #4285f4;
|
||||
/*background-color: #4285f4;*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4100,4 +4100,43 @@ body.fullscreen .rb-wrapper > .rb-content {
|
|||
|
||||
.card-header {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
html.external-auth,
|
||||
html.external-auth body {
|
||||
height: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body {
|
||||
max-width: 421px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body .alogo {
|
||||
margin-top: 61px;
|
||||
margin-bottom: 51px;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body .alogo img {
|
||||
display: inline-block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body .alogo .zmdi {
|
||||
font-size: 3rem;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body .card {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
html.external-auth .auth-body.must-center .card {
|
||||
display: none;
|
||||
}
|
||||
|
|
BIN
src/main/resources/web/assets/img/dingtalk256.png
Normal file
BIN
src/main/resources/web/assets/img/dingtalk256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
src/main/resources/web/assets/img/rebuild256.png
Normal file
BIN
src/main/resources/web/assets/img/rebuild256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
BIN
src/main/resources/web/assets/img/wxwork256.png
Normal file
BIN
src/main/resources/web/assets/img/wxwork256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -35,7 +35,7 @@ $(document).ready(function () {
|
|||
})
|
||||
|
||||
$('.J_disable').click(() => {
|
||||
RbAlert.create($L('确定要禁用此用户吗?'), {
|
||||
RbAlert.create($L('确认要禁用此用户吗?'), {
|
||||
confirmText: $L('禁用'),
|
||||
confirm: function () {
|
||||
toggleDisabled(true, this)
|
||||
|
|
|
@ -50,6 +50,22 @@ $(document).ready(function () {
|
|||
})
|
||||
})
|
||||
|
||||
const $unauth = $('.J_unauth-dingtalk, .J_unauth-wxwork').on('click', () => {
|
||||
RbAlert.create($L('确认要取消授权吗?'), {
|
||||
confirm: function () {
|
||||
this.hide()
|
||||
$.post(`/settings/cancel-external-user?type=${$unauth.data('type')}`, (res) => {
|
||||
if (res.error_code === 0) {
|
||||
location.hash = 'secure'
|
||||
location.reload()
|
||||
} else {
|
||||
RbHighbar.create(res.error_msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// load log
|
||||
|
||||
$('a.nav-link[href="#logs"]').click(() => {
|
||||
|
|
|
@ -28,6 +28,11 @@
|
|||
.error-container a[target='_blank']:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.rb-error .error-container {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="rb-splash-screen">
|
||||
|
|
|
@ -99,20 +99,20 @@
|
|||
<div class="col-md-8 col-12">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label text-left">[[${bundle.L('用户名')}]]</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="col-4 col-form-label">[[${bundle.L('用户名')}]]</label>
|
||||
<div class="col-8">
|
||||
<div class="form-control-plaintext" th:text="${user.getName()}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label text-left">[[${bundle.L('所属部门')}]]</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="col-4 col-form-label">[[${bundle.L('所属部门')}]]</label>
|
||||
<div class="col-8">
|
||||
<div class="form-control-plaintext" th:text="${user.getOwningBizUnit().getName()}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label text-left">[[${bundle.L('加入团队')}]]</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="col-4 col-form-label">[[${bundle.L('加入团队')}]]</label>
|
||||
<div class="col-8">
|
||||
<div class="form-control-plaintext split-span">
|
||||
<th:block th:each="team : ${user.getOwningTeams()}">
|
||||
<span th:text="${team.getName()}"></span>
|
||||
|
@ -122,14 +122,20 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label text-left">[[${bundle.L('姓名')}]]</label>
|
||||
<div class="col-sm-8">
|
||||
<input class="form-control form-control-sm" type="text" id="fullName" th:value="${user.getFullName()}" th:data-o="${user.getFullName()}" />
|
||||
<label class="col-4 col-form-label">[[${bundle.L('姓名')}]]</label>
|
||||
<div class="col-8">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="text"
|
||||
id="fullName"
|
||||
th:value="${user.getFullName()}"
|
||||
th:data-o="${user.getFullName()}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label text-left">[[${bundle.L('工作电话')}]]</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="col-4 col-form-label">[[${bundle.L('工作电话')}]]</label>
|
||||
<div class="col-8">
|
||||
<input
|
||||
class="form-control form-control-sm"
|
||||
type="text"
|
||||
|
@ -141,7 +147,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row border-none mt-3">
|
||||
<div class="col-sm-8 offset-sm-4">
|
||||
<div class="col-8 offset-sm-4">
|
||||
<button class="btn btn-primary J_save" type="button">[[${bundle.L('确定')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -152,23 +158,41 @@
|
|||
<div class="tab-pane" id="secure">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label text-left">[[${bundle.L('修改邮箱')}]]</label>
|
||||
<div class="col-sm-7 pl-0">
|
||||
<label class="col-2 col-form-label">[[${bundle.L('修改邮箱')}]]</label>
|
||||
<div class="col-7 pl-0">
|
||||
<div class="form-control-plaintext text-muted J_email-account" th:text="${user.getEmail() ?: bundle.L('邮箱未设置')}"></div>
|
||||
</div>
|
||||
<div class="col-sm-3 text-right">
|
||||
<div class="col-3 text-right">
|
||||
<button class="btn btn-primary btn-outline J_email" type="button">[[${bundle.L('修改')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label text-left">[[${bundle.L('修改密码')}]]</label>
|
||||
<div class="col-sm-7 pl-0">
|
||||
<label class="col-2 col-form-label">[[${bundle.L('修改密码')}]]</label>
|
||||
<div class="col-7 pl-0">
|
||||
<div class="form-control-plaintext text-muted">[[${bundle.L('建议 90 天更改一次密码')}]]</div>
|
||||
</div>
|
||||
<div class="col-sm-3 text-right">
|
||||
<div class="col-3 text-right">
|
||||
<button class="btn btn-primary btn-outline J_passwd" type="button">[[${bundle.L('修改')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${dingtalkUser != null}" class="form-group row">
|
||||
<label class="col-2 col-form-label">[[${bundle.L('钉钉授权')}]]</label>
|
||||
<div class="col-7 pl-0">
|
||||
<div class="form-control-plaintext text-muted">[[${dingtalkUser}]]</div>
|
||||
</div>
|
||||
<div class="col-3 text-right">
|
||||
<button class="btn btn-danger btn-outline J_unauth-dingtalk" type="button" data-type="1">[[${bundle.L('取消授权')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
<div th:if="${wxworkUser != null}" class="form-group row">
|
||||
<label class="col-2 col-form-label">[[${bundle.L('企业微信授权')}]]</label>
|
||||
<div class="col-7 pl-0">
|
||||
<div class="form-control-plaintext text-muted">[[${wxworkUser}]]</div>
|
||||
</div>
|
||||
<div class="col-3 text-right">
|
||||
<button class="btn btn-danger btn-outline J_unauth-wxwork" type="button" data-type="2">[[${bundle.L('取消授权')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="logs">
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
z-index: 1;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.o-platform a > i.zmdi {
|
||||
.o-platform a > .icon {
|
||||
background-color: #fc9a00;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
|
@ -50,7 +50,7 @@
|
|||
height: 25px;
|
||||
line-height: 26px;
|
||||
font-size: 1.2rem;
|
||||
padding-left: 9px;
|
||||
text-align: center;
|
||||
}
|
||||
.o-platform a > span {
|
||||
color: #666;
|
||||
|
@ -62,6 +62,10 @@
|
|||
width: 160px;
|
||||
padding: 6px;
|
||||
}
|
||||
.o-platform:hover .dropdown-menu {
|
||||
display: block;
|
||||
margin-top: 0; /* remove the gap so it doesn't close */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="rb-splash-screen">
|
||||
|
@ -112,7 +116,7 @@
|
|||
<div class="row mb-2">
|
||||
<div class="col" th:if="${mobileQrUrl != null}">
|
||||
<div class="btn-group dropup o-platform">
|
||||
<a class="hover-opacity dropdown-toggle" data-toggle="dropdown">
|
||||
<a class="hover-opacity dropdown-toggle" data-toggle="dropdown" th:href="${mobileUrl}">
|
||||
<i class="icon zmdi zmdi-smartphone-iphone"></i> <span class="up-1">[[${bundle.L('手机版')}]]</span>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
|
@ -154,10 +158,11 @@
|
|||
return
|
||||
}
|
||||
|
||||
$('.o-platform .dropdown-toggle').on('mouseenter', function () {
|
||||
$(this).trigger('click')
|
||||
// $(this).dropdown('show')
|
||||
})
|
||||
if ($.browser.mobile) {
|
||||
$(`<div class="bg-info"><i class="icon zmdi zmdi-smartphone-iphone"></i><p>${$L('点击切换到手机版访问')}</p></div>`)
|
||||
.appendTo('.announcement-wrapper')
|
||||
.on('click', () => (location.href = $('.o-platform .dropdown-toggle').attr('href')))
|
||||
}
|
||||
|
||||
$('.vcode-row img').on('click', function () {
|
||||
$(this).attr('src', 'captcha?' + $random())
|
||||
|
|
|
@ -18,14 +18,14 @@ import org.junit.jupiter.api.Test;
|
|||
public class NotificationServiceTest extends TestSupport {
|
||||
|
||||
@Test
|
||||
public void testSend() {
|
||||
Message msg = MessageBuilder.createMessage(SIMPLE_USER, "发一条消息", null);
|
||||
void testSend() {
|
||||
Message msg = MessageBuilder.createMessage(SIMPLE_USER, "发一条消息");
|
||||
Application.getNotifications().send(msg);
|
||||
System.out.println("Notification Sent");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnread() {
|
||||
void testGetUnread() {
|
||||
Application.getNotifications().getUnreadMessage(SIMPLE_USER);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue