diff --git a/.vscode/settings.json b/.vscode/settings.json index 217592067..f7140c092 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,8 @@ "html" ], "prettier.eslintIntegration": true, - "workbench.editor.enablePreview": false + "workbench.editor.enablePreview": false, + "java.configuration.updateBuildConfiguration": "disabled" } // node and eslint(-g) // Plugins: Beautify and ESLint \ No newline at end of file diff --git a/src/main/java/com/rebuild/server/helper/SMSender.java b/src/main/java/com/rebuild/server/helper/SMSender.java index 182741ab9..1b4b11bfc 100644 --- a/src/main/java/com/rebuild/server/helper/SMSender.java +++ b/src/main/java/com/rebuild/server/helper/SMSender.java @@ -195,6 +195,8 @@ public class SMSender { } /** + * 短信服务可用 + * * @return */ public static boolean availableSMS() { @@ -202,6 +204,8 @@ public class SMSender { } /** + * 邮件服务可用 + * * @return */ public static boolean availableMail() { diff --git a/src/main/java/com/rebuild/server/helper/VCode.java b/src/main/java/com/rebuild/server/helper/VCode.java index 316c8f10f..ca993dd13 100644 --- a/src/main/java/com/rebuild/server/helper/VCode.java +++ b/src/main/java/com/rebuild/server/helper/VCode.java @@ -24,7 +24,7 @@ import com.rebuild.server.helper.cache.CommonCache; import org.apache.commons.lang.math.RandomUtils; /** - * 验证码 + * 验证码助手 * * @author devezhao * @since 11/05/2018 @@ -40,12 +40,14 @@ public class VCode { } /** + * 生成验证码 + * * @param key * @param level complexity 1<2<3 * @return */ public static String generate(String key, int level) { - String vcode = null; + String vcode; if (level == 3) { vcode = CodecUtils.randomCode(20); } else if (level == 2) { @@ -54,7 +56,8 @@ public class VCode { vcode = RandomUtils.nextInt(999999999) + "888888"; vcode = vcode.substring(0, 6); } - + + // 缓存 10 分钟 Application.getCommonCache().put("VCode-" + key, vcode, CommonCache.TS_HOUR / 6); return vcode; } @@ -63,12 +66,15 @@ public class VCode { * @param key * @param vcode * @return + * @see #verfiy(String, String, boolean) */ public static boolean verfiy(String key, String vcode) { return verfiy(key, vcode, false); } /** + * 验证是否有效 + * * @param key * @param vcode * @param keepAlive @@ -76,7 +82,11 @@ public class VCode { * @see #clean(String) */ public static boolean verfiy(String key, String vcode, boolean keepAlive) { - String ckey = "VCode-" + key; + if (Application.devMode() && "rebuild".equalsIgnoreCase(vcode)) { + return true; + } + + final String ckey = "VCode-" + key; String exists = Application.getCommonCache().get(ckey); if (exists == null) { return false; @@ -92,6 +102,8 @@ public class VCode { } /** + * 清除验证码 + * * @param key * @return */ diff --git a/src/main/java/com/rebuild/server/helper/language/LanguageBundle.java b/src/main/java/com/rebuild/server/helper/language/LanguageBundle.java index c4944e60c..bc20cb5ee 100644 --- a/src/main/java/com/rebuild/server/helper/language/LanguageBundle.java +++ b/src/main/java/com/rebuild/server/helper/language/LanguageBundle.java @@ -111,6 +111,17 @@ public class LanguageBundle implements JSONable { return lang; } + /** + * @param key + * @param args + * @return + * @see String#format(String, Object...) + */ + public String formatLang(String key, Object... args) { + String lang = lang(key); + return String.format(lang, args); + } + /** * @param key * @return diff --git a/src/main/java/com/rebuild/server/helper/language/Languages.java b/src/main/java/com/rebuild/server/helper/language/Languages.java index 038b78a12..37c35da58 100644 --- a/src/main/java/com/rebuild/server/helper/language/Languages.java +++ b/src/main/java/com/rebuild/server/helper/language/Languages.java @@ -138,8 +138,27 @@ public class Languages { * @param key * @param insideKeys * @return + * @see #currentBundle() */ public static String lang(String key, String...insideKeys) { - return instance.getCurrentBundle().lang(key, insideKeys); + return currentBundle().lang(key, insideKeys); + } + + /** + * 当前用户语言 + * + * @return + */ + public static LanguageBundle currentBundle() { + return instance.getCurrentBundle(); + } + + /** + * 默认语言 + * + * @return + */ + public static LanguageBundle defaultBundle() { + return instance.getDefaultBundle(); } } diff --git a/src/main/java/com/rebuild/server/service/bizz/UserService.java b/src/main/java/com/rebuild/server/service/bizz/UserService.java index ae88d5eaa..5ede1b3f0 100644 --- a/src/main/java/com/rebuild/server/service/bizz/UserService.java +++ b/src/main/java/com/rebuild/server/service/bizz/UserService.java @@ -28,6 +28,7 @@ import com.rebuild.server.helper.BlackList; import com.rebuild.server.helper.ConfigurableItem; import com.rebuild.server.helper.SMSender; import com.rebuild.server.helper.SysConfiguration; +import com.rebuild.server.helper.language.Languages; import com.rebuild.server.helper.task.TaskExecutors; import com.rebuild.server.metadata.EntityHelper; import com.rebuild.server.service.DataSpecificationException; @@ -61,13 +62,25 @@ public class UserService extends SystemEntityService { @Override public Record create(Record record) { - final String passwd = record.getString("password"); - saveBefore(record); - Record r = super.create(record); - Application.getUserStore().refreshUser(record.getPrimary()); - notifyNewUser(r, passwd); - return r; + return create(record, true); } + + /** + * @param record + * @param notifyUser + * @return + */ + private Record create(Record record, boolean notifyUser) { + final String passwd = record.getString("password"); + saveBefore(record); + record = super.create(record); + Application.getUserStore().refreshUser(record.getPrimary()); + + if (notifyUser) { + notifyNewUser(record, passwd); + } + return record; + } @Override public Record update(Record record) { @@ -99,7 +112,7 @@ public class UserService extends SystemEntityService { } if (record.hasValue("email") && Application.getUserStore().exists(record.getString("email"))) { - throw new DataSpecificationException("邮箱重复"); + throw new DataSpecificationException(Languages.lang("Repeated", "Email")); } if (record.getPrimary() == null && !record.hasValue("fullName")) { @@ -136,7 +149,7 @@ public class UserService extends SystemEntityService { */ public void checkPassword(String password) throws DataSpecificationException { if (password.length() < 6) { - throw new DataSpecificationException("密码不能小于6位"); + throw new DataSpecificationException(Languages.lang("PasswordLevel1Tip")); } int policy = SysConfiguration.getInt(ConfigurableItem.PasswordPolicy); @@ -161,14 +174,13 @@ public class UserService extends SystemEntityService { } if (policy >= 2 && (countUpper == 0 || countLower == 0 || countDigit == 0)) { - throw new DataSpecificationException("密码必须包含数字和大小写字母"); + throw new DataSpecificationException(Languages.lang("PasswordLevel2Tip")); } if (policy >= 3 && (countSpecial == 0 || password.length() < 8)) { - throw new DataSpecificationException("密码不能小于8位,且必须包含特殊字符"); + throw new DataSpecificationException(Languages.lang("PasswordLevel3Tip")); } } - private static final String MSG_NEWUSER = "

系统管理员已经为你开通了 %s 账号!以下为你的登录信息,请妥善保管。

登录账号 %s
登录密码 %s
登录地址 %s

首次登陆,建议你立即修改登陆密码。修改方式:登陆后点击右上角头像 - 个人设置 - 安全设置 - 更改密码

"; /** * @param newUser * @param passwd @@ -179,39 +191,18 @@ public class UserService extends SystemEntityService { return false; } - String subject = "你的 " + SysConfiguration.get(ConfigurableItem.AppName) + " 账号已就绪"; - String content = String.format(MSG_NEWUSER, - SysConfiguration.get(ConfigurableItem.AppName), - newUser.getString("loginName"), passwd, - SysConfiguration.getHomeUrl(), - SysConfiguration.getHomeUrl()); + String appName = SysConfiguration.get(ConfigurableItem.AppName); + String homeUrl = SysConfiguration.getHomeUrl(); + + String subject = Languages.defaultBundle().formatLang("YourAccountReady", + appName); + String content = Languages.defaultBundle().formatLang("NewUserAddedNotify", + appName, newUser.getString("loginName"), passwd, homeUrl, homeUrl); SMSender.sendMail(newUser.getString("email"), subject, content); return true; } - /** - * 改变部门 - * - * @param user - * @param deptNew - * @see #updateEnableUser(ID, ID, ID, Boolean) - */ - public void updateDepartment(ID user, ID deptNew) { - updateEnableUser(user, deptNew, null, null); - } - - /** - * 改变角色 - * - * @param user - * @param roleNew - * @see #updateEnableUser(ID, ID, ID, Boolean) - */ - public void updateRole(ID user, ID roleNew) { - updateEnableUser(user, null, roleNew, null); - } - /** * 入参值为 null 表示不做修改 * @@ -252,8 +243,7 @@ public class UserService extends SystemEntityService { // 改变记录的所属部门 if (deptOld != null) { - TaskExecutors.submit( - new ChangeOwningDeptTask(user, deptNew), Application.getCurrentUser()); + TaskExecutors.submit(new ChangeOwningDeptTask(user, deptNew), Application.getCurrentUser()); } } @@ -262,20 +252,15 @@ public class UserService extends SystemEntityService { * * @param record */ - public void txSignUp(Record record) { - if (!SysConfiguration.getBool(ConfigurableItem.OpenSignUp)) { - throw new DataSpecificationException("管理员未开放公开注册"); - } - - record = this.create(record); - + public ID txSignUp(Record record) { + record = this.create(record, false); + ID newUserId = record.getPrimary(); - String content = String.format( - "用户 @%s 提交了注册申请。请验证用户有效性后为其启用并指定部门和角色,以便用户登录使用。如果这是一个无效的注册申请请忽略。" - + "[点击此处激活](%s/admin/bizuser/users#!/View/User/%s)", - newUserId, AppUtils.getContextPath(), newUserId); - - Message message = MessageBuilder.createMessage(ADMIN_USER, content); + String viewUrl = AppUtils.getContextPath() + "/app/list-and-view?id=" + newUserId; + String content = Languages.defaultBundle().formatLang("NewUserSignupNotify", newUserId, viewUrl); + + Message message = MessageBuilder.createMessage(ADMIN_USER, content, newUserId); Application.getNotifications().send(message); + return newUserId; } } diff --git a/src/main/java/com/rebuild/server/service/bizz/privileges/UserStore.java b/src/main/java/com/rebuild/server/service/bizz/privileges/UserStore.java index 29a716288..efc7ca989 100644 --- a/src/main/java/com/rebuild/server/service/bizz/privileges/UserStore.java +++ b/src/main/java/com/rebuild/server/service/bizz/privileges/UserStore.java @@ -194,7 +194,7 @@ public class UserStore { } return top.toArray(new Department[0]); } - + /** * @param roleId * @return @@ -246,19 +246,21 @@ public class UserStore { .unique(); final User newUser = new User( userId, (String) o[1], (String) o[2], (String) o[3], (String) o[4], (Boolean) o[5]); + final ID deptId = (ID) o[6]; + final ID roleId = (ID) o[7]; final User oldUser = exists(userId) ? getUser(userId) : null; if (oldUser != null) { Role role = oldUser.getOwningRole(); if (role != null) { role.removeMember(oldUser); - role.addMember(newUser); - } + } + Department dept = oldUser.getOwningDept(); if (dept != null) { dept.removeMember(oldUser); - dept.addMember(newUser); } + for (Team team : oldUser.getOwningTeams().toArray(new Team[0])) { team.removeMember(oldUser); team.addMember(newUser); @@ -268,13 +270,13 @@ public class UserStore { if (oldUser.getEmail() != null) { USERs_MAIL2ID.remove(normalIdentifier(oldUser.getEmail())); } - } else { - if (o[6] != null) { - getDepartment((ID) o[6]).addMember(newUser); - } - if (o[7] != null) { - getRole((ID) o[7]).addMember(newUser); - } + } + + if (deptId != null) { + getDepartment(deptId).addMember(newUser); + } + if (roleId != null) { + getRole(roleId).addMember(newUser); } store(newUser); @@ -530,7 +532,7 @@ public class UserStore { } } - // 组织关系 + // 组织部门关系 for (Map.Entry> e : parentTemp.entrySet()) { BusinessUnit parent = getDepartment(e.getKey()); for (ID child : e.getValue()) { diff --git a/src/main/java/com/rebuild/server/service/notification/MessageBuilder.java b/src/main/java/com/rebuild/server/service/notification/MessageBuilder.java index 8e5a17d21..3fd0d340b 100644 --- a/src/main/java/com/rebuild/server/service/notification/MessageBuilder.java +++ b/src/main/java/com/rebuild/server/service/notification/MessageBuilder.java @@ -116,7 +116,7 @@ public class MessageBuilder { Matcher atMatcher = AT_PATTERN.matcher(message); while (atMatcher.find()) { String at = atMatcher.group(); - String atLabel = parseAtId(at.substring(1)); + String atLabel = parseAtsId(at.substring(1)); if (atLabel != null && !atLabel.equals(at)) { message = message.replace(at, atLabel); } @@ -132,7 +132,7 @@ public class MessageBuilder { * @param atid * @return */ - static String parseAtId(String atid) { + protected static String parseAtsId(String atid) { if (!ID.isId(atid)) { return atid; } diff --git a/src/main/java/com/rebuild/web/admin/bizz/UserControll.java b/src/main/java/com/rebuild/web/admin/bizz/UserControll.java index 6a8447799..3c8743d43 100644 --- a/src/main/java/com/rebuild/web/admin/bizz/UserControll.java +++ b/src/main/java/com/rebuild/web/admin/bizz/UserControll.java @@ -68,6 +68,11 @@ public class UserControll extends BaseEntityControll { @RequestMapping("check-user-status") public void checkUserStatus(HttpServletRequest request, HttpServletResponse response) throws IOException { ID id = getIdParameterNotNull(request, "id"); + if (!Application.getUserStore().exists(id)) { + writeFailure(response); + return; + } + User checkedUser = Application.getUserStore().getUser(id); Map ret = new HashMap<>(); diff --git a/src/main/java/com/rebuild/web/base/general/GeneralDataListControll.java b/src/main/java/com/rebuild/web/base/general/GeneralDataListControll.java index 5c4057b9d..5c20a4717 100644 --- a/src/main/java/com/rebuild/web/base/general/GeneralDataListControll.java +++ b/src/main/java/com/rebuild/web/base/general/GeneralDataListControll.java @@ -118,7 +118,9 @@ public class GeneralDataListControll extends BaseEntityControll { url = MessageFormat.format("{0}/list#!/View/{0}/{1}", entity.getName(), id); } else if (entity.getEntityCode() == EntityHelper.Feeds) { url = "../feeds/home#s=" + id; - } + } else if (entity.getEntityCode() == EntityHelper.User) { + url = MessageFormat.format("../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), id); + } } if (url != null) { diff --git a/src/main/java/com/rebuild/web/user/signin/SignUpControll.java b/src/main/java/com/rebuild/web/user/signin/SignUpControll.java index 69a7a27e3..9da0f1c98 100644 --- a/src/main/java/com/rebuild/web/user/signin/SignUpControll.java +++ b/src/main/java/com/rebuild/web/user/signin/SignUpControll.java @@ -29,6 +29,7 @@ import com.rebuild.server.helper.ConfigurableItem; import com.rebuild.server.helper.SMSender; import com.rebuild.server.helper.SysConfiguration; import com.rebuild.server.helper.VCode; +import com.rebuild.server.helper.language.Languages; import com.rebuild.server.metadata.EntityHelper; import com.rebuild.server.service.DataSpecificationException; import com.rebuild.server.service.bizz.UserService; @@ -47,7 +48,7 @@ import java.io.IOException; import static com.rebuild.server.helper.language.Languages.lang; /** - * 注册 + * 用户自助注册 * * @author devezhao * @since 11/01/2018 @@ -57,7 +58,7 @@ import static com.rebuild.server.helper.language.Languages.lang; public class SignUpControll extends BasePageControll { @RequestMapping("signup") - public ModelAndView pageSignup(HttpServletRequest request, HttpServletResponse response) throws IOException { + public ModelAndView pageSignup(HttpServletResponse response) throws IOException { if (!SysConfiguration.getBool(ConfigurableItem.OpenSignUp)) { response.sendError(400, lang("SignupNotOpenTip")); return null; @@ -119,7 +120,8 @@ public class SignUpControll extends BasePageControll { Application.getBean(UserService.class).txSignUp(userNew); String homeUrl = SysConfiguration.getHomeUrl(); - String content = String.format(lang("SignupPending"), fullName, loginName, passwd, homeUrl, homeUrl); + String content = Languages.currentBundle().formatLang("SignupPending", + fullName, loginName, passwd, homeUrl, homeUrl); SMSender.sendMail(email, lang("AdminReviewSignup"), content); writeSuccess(response); } catch (DataSpecificationException ex) { diff --git a/src/main/resources/locales/language_en-US.json b/src/main/resources/locales/language_en-US.json index 019c741ca..d0a2f2ba9 100644 --- a/src/main/resources/locales/language_en-US.json +++ b/src/main/resources/locales/language_en-US.json @@ -26,7 +26,7 @@ "ReturnHome": "Go Home", "ReportIssue": "Report an issue", "SignupNotOpenTip": "Administrator has not opened public signup", - "UnsetEmailTip": "If you forgot or did not configure your email, please contact the administrator to reset your password", + "UnsetEmailTip": "If you forgot or unfilled email, please contact your administrator to reset your password", "InputEmailPls": "Please enter email", "InputNewPasswordPls": "Enter new password", "PasswordNotMatch": "The new password not match.", diff --git a/src/main/resources/locales/language_zh-CN.json b/src/main/resources/locales/language_zh-CN.json index cee31bec4..fee1d678a 100644 --- a/src/main/resources/locales/language_zh-CN.json +++ b/src/main/resources/locales/language_zh-CN.json @@ -26,7 +26,7 @@ "ReturnHome": "返回首页", "ReportIssue": "报告此问题", "SignupNotOpenTip": "管理员未开放公开注册", - "UnsetEmailTip": "如果你忘记或未配置邮箱,请联系管理员重置密码", + "UnsetEmailTip": "如果你忘记或未填写邮箱,请联系管理员重置密码", "InputEmailPls": "请输入邮箱", "InputNewPasswordPls": "请输入新密码", "PasswordNotMatch": "两次输入的新密码不一致", @@ -62,5 +62,12 @@ "Retry": "重试", "ErrorUnknow": "未知错误,请稍后重试", "Error404": "访问的地址/资源不存在", - "Error403": "权限不足,访问被阻止" + "Error403": "权限不足,访问被阻止", + "NewUserAddedNotify": "

系统管理员已经为你开通了 %s 账号!以下为你的登录信息,请妥善保管。

登录账号 %s
登录密码 %s
登录地址 %s

首次登陆,建议你立即修改登陆密码。修改方式:登陆后点击右上角头像 - 个人设置 - 安全设置 - 更改密码

", + "YourAccountReady": "你的 %s 账号已就绪", + "NewUserSignupNotify": "用户 @%s 提交了注册申请。请验证用户有效性后为其启用并指定部门和角色,以便用户登录使用。如果这是一个无效申请请忽略。[点击此处激活](%s)", + "PasswordLevel1Tip": "密码不能小于6位", + "PasswordLevel2Tip": "密码必须包含数字和大小写字母", + "PasswordLevel3Tip": "密码不能小于8位,且必须包含特殊字符", + "Repeated": "{0}重复" } \ No newline at end of file diff --git a/src/main/webapp/admin/bizuser/dept-list.jsp b/src/main/webapp/admin/bizuser/dept-list.jsp index 80a9b0995..75531e631 100644 --- a/src/main/webapp/admin/bizuser/dept-list.jsp +++ b/src/main/webapp/admin/bizuser/dept-list.jsp @@ -82,7 +82,11 @@ window.__PageConfig = { - + diff --git a/src/main/webapp/admin/bizuser/user-list.jsp b/src/main/webapp/admin/bizuser/user-list.jsp index ceda2289e..b9c340524 100644 --- a/src/main/webapp/admin/bizuser/user-list.jsp +++ b/src/main/webapp/admin/bizuser/user-list.jsp @@ -89,15 +89,15 @@ window.__PageConfig = {