diff --git a/plugin/src/main/java/team/kitemc/verifymc/command/VmcCommandExecutor.java b/plugin/src/main/java/team/kitemc/verifymc/command/VmcCommandExecutor.java index 218d1c77..250d1ccd 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/command/VmcCommandExecutor.java +++ b/plugin/src/main/java/team/kitemc/verifymc/command/VmcCommandExecutor.java @@ -120,7 +120,7 @@ private void handleApprove(CommandSender sender, String[] args) { String email = (String) user.get("email"); if (email != null && !email.isEmpty()) { ctx.getMailService().sendReviewResult(email, storedTarget, true, - "", ctx.getConfigManager().getLanguage()); + "", resolveUserLanguage(user)); } } @@ -151,7 +151,7 @@ private void handleReject(CommandSender sender, String[] args) { String email = (String) user.get("email"); if (email != null && !email.isEmpty()) { ctx.getMailService().sendReviewResult(email, storedTarget, false, - reason, ctx.getConfigManager().getLanguage()); + reason, resolveUserLanguage(user)); } } @@ -182,6 +182,14 @@ private void handleDelete(CommandSender sender, String[] args) { } } + private String resolveUserLanguage(java.util.Map user) { + String lang = user.get("language") == null ? null : String.valueOf(user.get("language")).trim(); + if (lang == null || lang.isEmpty()) { + lang = ctx.getConfigManager().getLanguage(); + } + return lang; + } + private void handleBan(CommandSender sender, String[] args) { if (!sender.hasPermission("verifymc.admin")) { sender.sendMessage("§6[VerifyMC] §cNo permission."); diff --git a/plugin/src/main/java/team/kitemc/verifymc/db/FileUserDao.java b/plugin/src/main/java/team/kitemc/verifymc/db/FileUserDao.java index 8a2b1173..87ea2b35 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/db/FileUserDao.java +++ b/plugin/src/main/java/team/kitemc/verifymc/db/FileUserDao.java @@ -384,6 +384,23 @@ public boolean updateUserEmail(String username, String email) { return true; } + @Override + public boolean setUserLanguage(String username, String language) { + debugLog("setUserLanguage called: username=" + username + ", language=" + language); + String key = keyForUsername(username); + Map user = users.get(key); + + if (user == null) { + debugLog("User not found: " + username); + return false; + } + + user.put("language", language); + saveLater(); + debugLog("User language updated: " + user.get("username")); + return true; + } + @Override public Map getUserByUsername(String username) { debugLog("Getting user by username: " + username); diff --git a/plugin/src/main/java/team/kitemc/verifymc/db/MysqlUserDao.java b/plugin/src/main/java/team/kitemc/verifymc/db/MysqlUserDao.java index ba786b7e..cb0a4618 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/db/MysqlUserDao.java +++ b/plugin/src/main/java/team/kitemc/verifymc/db/MysqlUserDao.java @@ -78,6 +78,7 @@ private void initDatabase() throws SQLException { "password VARCHAR(255)," + "regTime BIGINT," + "discord_id VARCHAR(64)," + + "language VARCHAR(8) NULL," + "questionnaire_score INT NULL," + "questionnaire_passed BOOLEAN NULL," + "questionnaire_review_summary TEXT NULL," + @@ -103,6 +104,12 @@ private void initDatabase() throws SQLException { stmt.executeUpdate("ALTER TABLE users ADD COLUMN discord_id VARCHAR(64)"); } + try { + stmt.executeQuery("SELECT language FROM users LIMIT 1"); + } catch (SQLException e) { + stmt.executeUpdate("ALTER TABLE users ADD COLUMN language VARCHAR(8) NULL"); + } + try { stmt.executeQuery("SELECT questionnaire_score FROM users LIMIT 1"); } catch (SQLException e) { @@ -278,6 +285,21 @@ public boolean updateUserEmail(String username, String email) { } } + @Override + public boolean setUserLanguage(String username, String language) { + String sql = "UPDATE users SET language=? WHERE username=?"; + try (PreparedStatement ps = getConnection().prepareStatement(sql)) { + ps.setString(1, language); + ps.setString(2, username); + int rows = ps.executeUpdate(); + debugLog("User language updated: " + username); + return rows > 0; + } catch (SQLException e) { + debugLog("Error updating user language: " + e.getMessage()); + return false; + } + } + @Override public List> getAllUsers() { List> result = new ArrayList<>(); @@ -314,6 +336,7 @@ private Map mapUserFromResultSet(ResultSet rs) throws SQLExcepti user.put("password", rs.getString("password")); user.put("regTime", rs.getLong("regTime")); user.put("discordId", rs.getString("discord_id")); + user.put("language", rs.getString("language")); user.put("questionnaireScore", rs.getObject("questionnaire_score")); user.put("questionnairePassed", rs.getObject("questionnaire_passed")); user.put("questionnaireReviewSummary", rs.getString("questionnaire_review_summary")); diff --git a/plugin/src/main/java/team/kitemc/verifymc/db/UserDao.java b/plugin/src/main/java/team/kitemc/verifymc/db/UserDao.java index 8fa2d866..1abd0278 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/db/UserDao.java +++ b/plugin/src/main/java/team/kitemc/verifymc/db/UserDao.java @@ -26,6 +26,12 @@ boolean registerUser(String username, String email, String status, String passwo boolean updateUserEmail(String username, String email); + /** + * Persists the interface language the user registered with, + * so later emails (e.g. review notifications) can be localized per user. + */ + boolean setUserLanguage(String username, String language); + List> getAllUsers(); List> getUsersWithPagination(int page, int pageSize); diff --git a/plugin/src/main/java/team/kitemc/verifymc/mail/MailService.java b/plugin/src/main/java/team/kitemc/verifymc/mail/MailService.java index 3d37045e..3c7693dd 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/mail/MailService.java +++ b/plugin/src/main/java/team/kitemc/verifymc/mail/MailService.java @@ -82,10 +82,12 @@ public boolean sendCode(String to, String subject, String code, String language) File emailDir = new File(plugin.getDataFolder(), "email"); File templateFile = new File(emailDir, "verify_code_" + lang + ".html"); String content; + String serverName = plugin.getConfig().getString("web_server_prefix", "VerifyMC"); if (templateFile.exists()) { debugLog("Using custom template: " + templateFile.getAbsolutePath()); content = new String(Files.readAllBytes(templateFile.toPath()), StandardCharsets.UTF_8); - content = content.replace("{code}", escapeHtml(code)); + content = content.replace("{code}", escapeHtml(code)) + .replace("{server_name}", escapeHtml(serverName)); } else { debugLog("Using default template"); content = getDefaultVerifyCodeTemplate(lang, escapeHtml(code)); @@ -225,9 +227,10 @@ public boolean sendReviewResultNotification(String email, String username, boole try { String lang = (language != null && !language.isEmpty()) ? language : plugin.getConfig().getString("language", "zh"); String templateName = approved ? "review_approved_" + lang + ".html" : "review_rejected_" + lang + ".html"; - String subject = approved ? - ("zh".equals(lang) ? "[VerifyMC] 您的白名单申请已通过" : "[VerifyMC] Your whitelist application has been approved") : - ("zh".equals(lang) ? "[VerifyMC] 您的白名单申请被拒绝" : "[VerifyMC] Your whitelist application has been rejected"); + String serverPrefix = plugin.getConfig().getString("web_server_prefix", "VerifyMC"); + String subject = approved ? + ("zh".equals(lang) ? serverPrefix + " 白名单申请已通过" : serverPrefix + " Whitelist Application Approved") : + ("zh".equals(lang) ? serverPrefix + " 白名单申请未通过" : serverPrefix + " Whitelist Application Rejected"); File emailDir = new File(plugin.getDataFolder(), "email"); File templateFile = new File(emailDir, templateName); diff --git a/plugin/src/main/java/team/kitemc/verifymc/web/RegistrationProcessingHandler.java b/plugin/src/main/java/team/kitemc/verifymc/web/RegistrationProcessingHandler.java index 05cdf6af..ff1a4d2d 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/web/RegistrationProcessingHandler.java +++ b/plugin/src/main/java/team/kitemc/verifymc/web/RegistrationProcessingHandler.java @@ -294,6 +294,9 @@ private JSONObject executeRegistration(RegistrationRequest request, Questionnair ok = userDao.registerUser(request.normalizedUsername(), request.email(), status, questionnaireScore, questionnairePassedValue, questionnaireReviewSummary, questionnaireScoredAt); } + if (ok && request.language() != null && !request.language().isEmpty()) { + userDao.setUserLanguage(request.normalizedUsername(), request.language()); + } RegistrationApplicationService.RegistrationDecision decision = registrationApplicationService.resolveDecision(ok, manualReviewRequired, questionnairePassed, registerAutoApprove, scoringServiceUnavailable); diff --git a/plugin/src/main/java/team/kitemc/verifymc/web/handler/AdminUserHandler.java b/plugin/src/main/java/team/kitemc/verifymc/web/handler/AdminUserHandler.java index 9ad0936d..f83e935b 100644 --- a/plugin/src/main/java/team/kitemc/verifymc/web/handler/AdminUserHandler.java +++ b/plugin/src/main/java/team/kitemc/verifymc/web/handler/AdminUserHandler.java @@ -316,10 +316,18 @@ private void sendReviewResult(String username, boolean approved, String reason) String email = (String) user.get("email"); if (email != null && !email.isEmpty()) { ctx.getMailService().sendReviewResult(email, username, approved, reason, - ctx.getConfigManager().getLanguage()); + resolveUserLanguage(user)); } } + private String resolveUserLanguage(Map user) { + String lang = user.get("language") == null ? null : String.valueOf(user.get("language")).trim(); + if (lang == null || lang.isEmpty()) { + lang = ctx.getConfigManager().getLanguage(); + } + return lang; + } + private void addAudit(String action, String operator, String target, String detail) { ctx.getAuditDao().addAudit(new AuditRecord(action, operator, target, detail, System.currentTimeMillis())); }