玩命加载中🤣🤣🤣

一些 java 模板的笔记


枚举 + Option 的动态鉴权

使用场景:当有一组规则需要进行配置及匹配,通过枚举完成封装,但是在进行判断的时候通过 Option 来干掉 if-else

枚举类

public enum RoleEnum {

    ADMIN("admin", "管理员", ""),

    NORMAL("normal", "普通用户", "dataZt"),

    LEADER("leader", "领导", "userList")

    ;

    private String code;

    private String desc;

    private String homeUrl;

    private static final Map<String, RoleEnum> CODE_MAP = new HashMap<>();

    static {
        for (RoleEnum role : values()) {
            CODE_MAP.put(role.code.toLowerCase(), role);
        }
    }


    RoleEnum(String code, String desc, String homeUrl) {
        this.code = code;
        this.desc = desc;
        this.homeUrl = homeUrl;
    }

    public String getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

    public String getHomeUrl() {
        return homeUrl;
    }

    public static Optional<RoleEnum> fromCode(String code) {
        return Optional.ofNullable(CODE_MAP.get(code.toLowerCase()));
    }
}

调用者

RoleEnum enmu = RoleEnum.fromCode("admin")
    .orElseThrow(() -> new BizException("该类型不存在"));

注解实现 map > 实体的映射转换

注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ElementK {
    String value();
    boolean required() default true;
}

属性转换工具类

import cn.hutool.core.util.StrUtil;
import com.haigui.pangu.exception.BizException;
import com.haigui.qingting.anno.ElementK;

import java.lang.reflect.Field;
import java.util.Map;

public class ConvertUtil {
    /**
     * 自动转换属性
     *
     * @param elements 元素表
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T convertN(Map<String, String> elements, Class<T> clazz) {
        T instance = null;
        try {
            instance = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            for (Field f : fields) {
                if (f.isAnnotationPresent(ElementK.class)) {
                    ElementK anno = f.getAnnotation(ElementK.class);
                    String key = anno.value();
                    boolean required = anno.required();
                    if (required && StrUtil.isBlank(elements.get(key))) {
                        throw new BizException("要素【" + key + "】缺失");
                    }
                    if (elements.containsKey(key)) {
                        f.setAccessible(true);
                        f.set(instance, elements.get(key));
                    }
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }
}

调用者

// 以业务中的json串为例
public static <T> T checkElement(String json, Class<T> clazz) {
    Map<String, String> elements = JSONUtil.toBean(JSONUtil.parseObj(json).getJSONObject("elements"), Map.class);
    return ConvertUtil.convertN(elements, clazz);
}

Element element = CommonScriptUtil.checkElement(json, Element.class);

实体

public class Element {
    @ElementK("民初案号")
    private String minChuAnHao;

    @ElementK("被告名称")
    private String beiGao;

    @ElementK("案由")
    private String anYou;

    @ElementK("执行标的")
    private String zhiXingBiaoDi;
	// set get toString...
}

autoIT实现文件上传的封装

此处特色,根据文件地址,标题,驱动,以及consumer,可以自定义上传的一些动作

/**
 *
 * @param filePath 文件路径
 * @param title 上传标题,用于日志输出
 * @param driver driver
 * @param driverAction action
 */
public static void uploadByAutoIT(String filePath, String title, ChromeWebDriver driver, Consumer<ChromeWebDriver> driverAction) {
    if (x == null) {
        x = AutoITUtil.getInstance().getAutoItX();
    }
    log.info("开始上传<{}>附件", title);
    // 当前时间
    long tempStartTime = System.currentTimeMillis();
    // 等待时间
    while (!x.winWait("打开", "", 3)) {
        if (System.currentTimeMillis() - tempStartTime > 90000) {
            throw new BizException("文件上传异常");
        }
        driverAction.accept(driver);
    }
    tempStartTime = System.currentTimeMillis();
    //上传文件
    while (x.winWait("打开", "", 3)) {
        if (System.currentTimeMillis() - tempStartTime > 90000) {
            throw new BizException("文件上传异常");
        }
        x.winActivate("打开", "");
        // 输入框
        x.ControlSetText("打开", "", "Edit1", filePath);
        // 点击按钮
        x.controlClick("打开", "", "Button1");
    }
    log.info("选择<{}: {}>附件完成", title, filePath);
}

调用者

CommonScriptUtil.uploadByAutoIT(FILE_MAP.get("申请书").getAbsolutePath(), "申请书", driver, driver -> driver.actionClick(By.xpath("//div[text()='添加文件']/../div")));

文件下载并移动及重命名

/**
 * 下载并移动文件
 * @param keyword 关键字
 * @param operateFile 操作流
 */
public static void downLoadFileAndMove(String keyword, Function<File, String> operateFile) {
    int times = 1;
    List<File> downloadFileList;
    while (true) {
        if (times == 30) {
            throw new BizException("下载失败, 请检查是否被浏览器拦截或者网速太慢");
        }
        ThreadUtil.sleep(2000L);
        downloadFileList = FileUtil.loopFiles(DOWNLOAD_DIR, f -> f.getAbsolutePath().contains(keyword));
        if (downloadFileList.isEmpty()) {
            times++;
        } else if (downloadFileList.size() > 1) {
            throw new BizException("下载的文件疑似存在多个, 请手动确认 >>> " + downloadFileList);
        } else {
            File file = downloadFileList.get(0);
            FileUtil.move(file, new File(operateFile.apply(file)), true);
            ThreadUtil.sleep(2000L);
            break;
        }
    }
}

调用

CommonScriptUtil.downLoadFileAndMove("交纳诉讼费通知书-渤海银行股份有限公司", f -> DOWNLOAD_PATH + "交纳诉讼费通知书-" + anHao +".pdf");

基于selelium的handle切换

/**
 * 切换新窗口
 * @param driver
 * @param xpath
 */
public static void switchNewWindow(ChromeWebDriver driver, String xpath) {
    // 点击-案号链接
    driver.click(By.xpath(xpath));
    driver.sleep(2000L);
    ArrayList<String> newHandles = new ArrayList<>(driver.getWindowHandles());
    log.warn("dee >>> 即将切换新窗口 = {}", driver.getWindowHandle());
    driver.switchWindow(newHandles.get(newHandles.size() - 1));
}

public static void toPageByTitle(ChromeWebDriver driver, String title) {
    Set<String> windowHandles = driver.getWebDriver().getWindowHandles();
    log.info("当前所有的handles: 【{}】, 个数{}", JSONUtil.toJsonStr(windowHandles), windowHandles.size());
    for (String handle : windowHandles) {
        if (driver.getWebDriver().switchTo().window(handle).getTitle().contains(title)) {
            log.warn("dee >>> 当前窗口标题为: 【{}】", driver.getWebDriver().switchTo().window(handle).getTitle());
            driver.getWebDriver().switchTo().window(handle);
            break;
        }
    }
}

public static void closeAndToPageByTitle(ChromeWebDriver driver, String title) {
    driver.sleep(2000L);
    if (driver.getWindowHandles().size() == 1) {
        log.warn("dee >>> 仅剩一个窗口, 阻止关闭并转到首页【天津办公办案平台】(此处逻辑可根据现场具体调整)");
        toPageByTitle(driver, PageEnum.天津办公办案平台.name());
        driver.sleep(1000L);
        return;
        // 如果当前页已经在首页, 则不动
    } else if (driver.getTitle().contains(PageEnum.天津办公办案平台.name())) {
        // 等2s
        driver.sleep(2000L);
        toPageByTitle(driver, title);
    }
    // 关闭当前页
    log.warn("dee >>> 已关闭当前页");
    driver.close();
    // 等2s
    driver.sleep(2000L);
    toPageByTitle(driver, title);
}

debug模式启动chrome, 并通过selenium进行连接

浏览器快捷方式属性配置

"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\Users\haigui\AppData\Local\Google\Chrome\User Data"
import com.haigui.tourmaline.selenium.driver.ChromeWebDriver;
import com.haigui.tourmaline.selenium.util.ThreadDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

public class GridUtil {
    private static final Logger log = LoggerFactory.getLogger(GridUtil.class);

    public static ChromeWebDriver driver;

    public static ChromeWebDriver getChromeDriver(String driverPath, String binary) {
        ThreadDriver.CurrentThreadDriver threadDriver = ThreadDriver.get();
        driver = threadDriver.getChromeWebDriver();
        if (driver != null) {
            return driver;
        }
        ChromeOptions options = new ChromeOptions();
        options.setBinary(new File(binary));
        // 不检查是否为默认浏览器
        options.addArguments("--no-default-browser-check");
        options.addArguments("--no-first-run");
        // "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\Users\haigui\AppData\Local\Google\Chrome\User Data"
        options.setExperimentalOption("debuggerAddress", "127.0.0.1:9222"); // 与浏览器配置保持一致
        // chrome打开设置
        ChromeDriverService service = new ChromeDriverService
            .Builder()
            .usingDriverExecutable(new File(driverPath))
            .build();
        driver = new ChromeWebDriver(new ChromeDriver(service, options));
        threadDriver.setAbstractWebDriver(driver);
        threadDriver.setChromeWebDriver(driver);
        ThreadDriver.set(threadDriver);
        return driver;
    }
}

记一次selenium的js操作

前置说明:网页端有一个按钮点击报错,经排查最终确定原因:网页中将 Lodash 注入并引用赋值给 _,但是点击按钮前有一个 input 操作,input 之后会替换 _ 。因此解决思路是尽量避免替换 _, 或者在 input 前后使用 js 操作改变方法

方案一:避免使用 selenium 的 input 操作

driver.copy("地址...");
driver.click(By.xpath("//input[@id='kw']")); // 点击输入框
driver.paste(); // 这里不确定能不能粘贴成功

原生 selenium

// 定位输入框
WebElement input = driver.findElement(By.cssSelector(inputSelector));

// 点击输入框(聚焦)
input.click();

// 将文本复制到剪贴板(使用 AWT 工具类)
StringSelection stringSelection = new StringSelection(text);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);

// 模拟粘贴操作(Mac 用 Keys.COMMAND,Windows 用 Keys.CONTROL)
Actions actions = new Actions(driver);
actions.keyDown(Keys.CONTROL)
       .sendKeys("v")
       .keyUp(Keys.CONTROL)
       .perform();

// 验证输入内容(可选)
String value = input.getAttribute("value");
if (!value.equals(text)) {
    throw new RuntimeException("粘贴失败,输入框内容不符");
}

方案二:在输入前备份变量

String backupScript =
    "window.__originalLodash = window._;"; // 将 _ 存储到 __originalLodash
((JavascriptExecutor) driver.getWebDriver()).executeScript(backupScript);

// 输入的逻辑。。。
String restoreScript =
    "window._ = window.__originalLodash;";
((JavascriptExecutor) driver.getWebDriver()).executeScript(restoreScript);

// 验证 _.cloneDeep 是否恢复
Boolean isLodashValid = (Boolean) ((JavascriptExecutor) driver.getWebDriver())
    .executeScript("return typeof _.cloneDeep === 'function'");
System.out.println("Lodash 是否有效: " + isLodashValid); // 应为 true

方案三:替换_.cloneDeep 方法

String patchCloneDeep =
    "if (typeof _.cloneDeep === 'undefined') { " +
        "   _.cloneDeep = function(obj) { " +
        "       return JSON.parse(JSON.stringify(obj)); " +
        "   };" +
        "}";
((JavascriptExecutor) driver.getWebDriver()).executeScript(patchCloneDeep);

方案四:绕过输入事件触发

WebElement input = driver.findElement(By.xpath("输入框选择器"));

// 通过 JavaScript 直接设置值(不触发 input 事件)
String script = "arguments[0].value = '" + "输入内容" + "';";
((JavascriptExecutor) driver.getWebDriver()).executeScript(script, input);

// 验证输入框值是否设置成功
System.out.println("输入框值: " + input.getAttribute("value")); // 应输出 "输入内容"

文章作者: 👑Dee👑
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 👑Dee👑 !
  目录