package com.xiang.common.utils; import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderInfo; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** * 浙里办验证码轨迹生成工具。 */ public class ZlbCaptchaTrackUtil { private ZlbCaptchaTrackUtil() { } public static List generateBezierTrackList(List trackList) { List result = new ArrayList<>(); if (trackList == null || trackList.isEmpty()) { return result; } for (int i = 0; i < trackList.size(); i++) { int[] currentPoint = parseTrackCoordinate(trackList.get(i)); int clickTime = result.isEmpty() ? randomBetween(1800, 2200) : result.get(result.size() - 1).getT() + randomBetween(25, 60); result.add(buildTrackPoint(currentPoint[0], currentPoint[1], clickTime, "click")); if (i < trackList.size() - 1) { int[] nextPoint = parseTrackCoordinate(trackList.get(i + 1)); int moveStartTime = result.get(result.size() - 1).getT(); int moveDuration = randomBetween(800, 1200); List movePoints = buildBezierMovePoints(currentPoint[0], currentPoint[1], nextPoint[0], nextPoint[1]); // for (int moveIndex = 0; moveIndex < movePoints.size(); moveIndex++) { // int[] movePoint = movePoints.get(moveIndex); // int moveTime = moveStartTime + (int) Math.round((double) moveDuration * (moveIndex + 1) / (movePoints.size() + 1)); // result.add(buildTrackPoint(movePoint[0], movePoint[1], moveTime, "move")); // } int[] movePoint = movePoints.get(movePoints.size() / 2); int moveTime = moveStartTime + randomBetween(800, 1200); result.add(buildTrackPoint(movePoint[0], movePoint[1], moveTime, "move")); } } return result; } public static List parseCoordinateText(String coordinateText) { if (coordinateText == null || coordinateText.trim().isEmpty()) { return Collections.emptyList(); } String normalized = coordinateText.replace("\n", "|").replace(";", "|"); String[] parts = normalized.split("\\|"); List result = new ArrayList<>(); for (String part : parts) { if (part != null && !part.trim().isEmpty()) { result.add(part.trim()); } } return result; } private static int[] parseTrackCoordinate(String track) { String[] coordinateArr = track.split(","); return new int[]{ Integer.parseInt(coordinateArr[0].trim()), Integer.parseInt(coordinateArr[1].trim()) }; } private static List buildBezierMovePoints(int fromX, int fromY, int toX, int toY) { int movePointCount = randomBetween(3, 5); double dx = toX - fromX; double dy = toY - fromY; double distance = Math.max(1d, Math.hypot(dx, dy)); double normalX = -dy / distance; double normalY = dx / distance; double direction = ThreadLocalRandom.current().nextBoolean() ? 1d : -1d; double offset = Math.min(28d, Math.max(6d, distance * ThreadLocalRandom.current().nextDouble(0.08d, 0.22d))) * direction; double controlX = (fromX + toX) / 2d + normalX * offset; double controlY = (fromY + toY) / 2d + normalY * offset; List movePoints = new ArrayList<>(movePointCount); for (int i = 1; i <= movePointCount; i++) { double progress = easeInOutCubic((double) i / (movePointCount + 1)); int x = (int) Math.round( Math.pow(1 - progress, 2) * fromX + 2 * (1 - progress) * progress * controlX + Math.pow(progress, 2) * toX ); int y = (int) Math.round( Math.pow(1 - progress, 2) * fromY + 2 * (1 - progress) * progress * controlY + Math.pow(progress, 2) * toY ); movePoints.add(new int[]{x, y}); } return movePoints; } private static double easeInOutCubic(double value) { return value < 0.5d ? 4d * value * value * value : 1d - Math.pow(-2d * value + 2d, 3d) / 2d; } private static ZlbOrderInfo.ZlbData.TrackList buildTrackPoint(int x, int y, int t, String type) { ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList(); data.setX(x); data.setY(y); data.setT(t); data.setType(type); return data; } private static int randomBetween(int minInclusive, int maxInclusive) { return ThreadLocalRandom.current().nextInt(minInclusive, maxInclusive + 1); } }