feat: CraftBank Folia 1.21.8 经济与银行核心插件初始实现

实现完整的现金/钱包、银行卡、支票、活期储蓄与定期存款系统,
实现并以 Highest 优先级注册 Vault Economy 接口,接入 PlaceholderAPI。
全程遵循 Folia 调度模型(AsyncScheduler / 实体区域线程),
数据缓存线程安全,支票兑现与定期领取做防刷处理。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Purpur Build
2026-06-29 18:16:06 +08:00
parent 6a5ee9906b
commit eddc706c9a
36 changed files with 3775 additions and 0 deletions
@@ -0,0 +1,160 @@
package com.craftbank.commands;
import com.craftbank.CraftBank;
import com.craftbank.economy.EconomyManager;
import com.craftbank.util.Amounts;
import com.craftbank.util.Msg;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* /bankadmin &lt;reload|give|take|set|setinterest&gt; —— 管理员指令。
*/
public final class BankAdminCommand implements TabExecutor {
private final CraftBank plugin;
private final Msg msg;
public BankAdminCommand(CraftBank plugin) {
this.plugin = plugin;
this.msg = new Msg(plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission("craftbank.admin")) {
msg.key(sender, "no-permission", "&c你没有权限执行该操作。");
return true;
}
if (args.length == 0) {
sendHelp(sender);
return true;
}
switch (args[0].toLowerCase()) {
case "reload" -> {
plugin.reloadConfig();
plugin.getEconomyManager().reload();
msg.key(sender, "reload-success", "&a配置文件已重载。");
}
case "give" -> modify(sender, args, ModifyType.GIVE);
case "take" -> modify(sender, args, ModifyType.TAKE);
case "set" -> modify(sender, args, ModifyType.SET);
case "setinterest" -> setInterest(sender, args);
default -> sendHelp(sender);
}
return true;
}
private enum ModifyType {
GIVE, TAKE, SET
}
private void modify(CommandSender sender, String[] args, ModifyType type) {
if (args.length < 3) {
msg.raw(sender, "&c用法: /bankadmin " + type.name().toLowerCase() + " <玩家> <金额>");
return;
}
double amount = Amounts.parse(args[2]);
if (amount < 0 || (type != ModifyType.SET && amount <= 0)) {
msg.key(sender, "invalid-amount", "&c请输入一个有效的正数金额。");
return;
}
String targetName = args[1];
EconomyManager econ = plugin.getEconomyManager();
plugin.runAsync(() -> {
@SuppressWarnings("deprecation")
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
UUID uuid = target.getUniqueId();
if (uuid == null || (!target.hasPlayedBefore() && !target.isOnline())) {
msg.key(sender, "player-not-found", "&c找不到目标玩家。");
return;
}
econ.loadBlocking(uuid, target.getName(), true);
boolean ok;
switch (type) {
case GIVE -> ok = econ.deposit(uuid, amount);
case TAKE -> ok = econ.withdraw(uuid, amount);
case SET -> ok = econ.set(uuid, amount);
default -> ok = false;
}
if (!ok) {
msg.raw(sender, "&c操作失败 (可能是余额不足)。");
return;
}
msg.raw(sender, "&a已" + label(type) + " &f" + targetName + " &a现金 " + econ.format(amount)
+ " &7(当前: " + econ.format(econ.getCash(uuid)) + ")");
});
}
private String label(ModifyType type) {
return switch (type) {
case GIVE -> "给予";
case TAKE -> "扣除";
case SET -> "设置";
};
}
private void setInterest(CommandSender sender, String[] args) {
if (args.length < 3) {
msg.raw(sender, "&c用法: /bankadmin setinterest <savings|term_7d|term_15d|term_30d> <利率>");
return;
}
String key = args[1].toLowerCase();
List<String> valid = List.of("savings", "term_7d", "term_15d", "term_30d");
if (!valid.contains(key)) {
msg.raw(sender, "&c利率类型无效, 可选: " + String.join(", ", valid));
return;
}
double rate;
try {
rate = Double.parseDouble(args[2]);
} catch (NumberFormatException ex) {
msg.raw(sender, "&c请输入一个有效的利率 (例如 0.005)。");
return;
}
if (!Double.isFinite(rate) || rate < 0) {
msg.raw(sender, "&c利率必须为非负数。");
return;
}
plugin.getConfig().set("Interest_Rates." + key, rate);
plugin.saveConfig();
msg.raw(sender, "&a已将 &f" + key + " &a的日利率设置为 &e" + rate + " &7(" + (rate * 100) + "%)");
}
private void sendHelp(CommandSender sender) {
msg.raw(sender, "&b&l工艺银行管理 &7指令帮助:");
sender.sendMessage(com.craftbank.util.Text.color("&7/bankadmin reload"));
sender.sendMessage(com.craftbank.util.Text.color("&7/bankadmin give <玩家> <金额>"));
sender.sendMessage(com.craftbank.util.Text.color("&7/bankadmin take <玩家> <金额>"));
sender.sendMessage(com.craftbank.util.Text.color("&7/bankadmin set <玩家> <金额>"));
sender.sendMessage(com.craftbank.util.Text.color("&7/bankadmin setinterest <类型> <利率>"));
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!sender.hasPermission("craftbank.admin")) {
return List.of();
}
if (args.length == 1) {
List<String> base = new ArrayList<>(List.of("reload", "give", "take", "set", "setinterest"));
base.removeIf(s -> !s.startsWith(args[0].toLowerCase()));
return base;
}
if (args.length == 2 && args[0].equalsIgnoreCase("setinterest")) {
return new ArrayList<>(List.of("savings", "term_7d", "term_15d", "term_30d"));
}
if (args.length == 2 && List.of("give", "take", "set").contains(args[0].toLowerCase())) {
return null; // 在线玩家名
}
return List.of();
}
}