feat:zlb接口

This commit is contained in:
Xiang
2026-05-07 16:38:59 +08:00
parent a1d42a4b27
commit 582beb13db
9 changed files with 454 additions and 39 deletions

View File

@@ -0,0 +1,29 @@
package com.xiang.common.pojo;
/**
* @Author: xiang
* @Date: 2026-05-07 15:57
*/
import lombok.Data;
/**
* 轨迹点类
*/
@Data
public class TrackPoint {
public int x, y, t;
public String type;
public TrackPoint(int x, int y, int t, String type) {
this.x = x;
this.y = y;
this.t = t;
this.type = type;
}
@Override
public String toString() {
return String.format("{\"x\":%d,\"y\":%d,\"t\":%d,\"type\":\"%s\"}", x, y, t, type);
}
}

View File

@@ -1,5 +1,6 @@
package com.xiang.common.pojo.code; package com.xiang.common.pojo.code;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -25,5 +26,6 @@ public class YcCodeRequest {
/** /**
* 模板图片 base64 * 模板图片 base64
*/ */
@JSONField(name = "label_image")
private String labelImage; private String labelImage;
} }

View File

@@ -0,0 +1,26 @@
package com.xiang.common.pojo.jntyzx.zlb;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: xiang
* @Date: 2026-05-07 15:44
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ZlbCaptcha {
private String type;
private String backgroundImage;
private String templateImage;
private String backgroundImageTag;
private String templateImageTag;
private Integer backgroundImageWidth;
private Integer backgroundImageHeight;
private Integer templateImageWidth;
private Integer templateImageHeight;
private String data;
}

View File

@@ -0,0 +1,17 @@
package com.xiang.common.pojo.jntyzx.zlb;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: xiang
* @Date: 2026-05-07 15:44
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ZlbCaptchaResp {
private String id;
private ZlbCaptcha captcha;
}

View File

@@ -10,7 +10,6 @@ import com.xiang.common.pojo.code.YcCodeRequest;
import com.xiang.common.utils.HttpService; import com.xiang.common.utils.HttpService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays; import java.util.Arrays;
@@ -52,6 +51,7 @@ public class CodeServiceImpl implements ICodeService {
ycCodeRequest.setImage(image); ycCodeRequest.setImage(image);
ycCodeRequest.setToken("9LQ1ATKVEeO8Arhq-HavXzpHvkzdZz_r7ydmqlYhp9c"); ycCodeRequest.setToken("9LQ1ATKVEeO8Arhq-HavXzpHvkzdZz_r7ydmqlYhp9c");
ycCodeRequest.setLabelImage(templateImage); ycCodeRequest.setLabelImage(templateImage);
ycCodeRequest.setType(YcCodeTypeEnum.YC_310700.getType());
String resp = HttpService.doPost(YUN_CODE_API_URL, header, JSON.toJSONString(ycCodeRequest)); String resp = HttpService.doPost(YUN_CODE_API_URL, header, JSON.toJSONString(ycCodeRequest));
YcCodeBaseResponse<YcCodeDataResp> response = JSON.parseObject(resp, new TypeReference<YcCodeBaseResponse<YcCodeDataResp>>() { YcCodeBaseResponse<YcCodeDataResp> response = JSON.parseObject(resp, new TypeReference<YcCodeBaseResponse<YcCodeDataResp>>() {

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,198 @@
package com.xiang.common.utils;
import com.xiang.common.pojo.TrackPoint;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 鼠标轨迹生成工具类 - 开箱即用
*
* 使用方法:
* 1. 直接调用 TrajectoryUtil.generate(起点, 终点, 时长, 点数)
* 2. 或使用预设的购物流程 TrajectoryUtil.shoppingFlow()
*/
public class TrajectoryUtil {
private static final Random random = new Random();
// ==================== 核心方法 ====================
/**
* 生成两点之间的移动轨迹
* @param fromX 起点X
* @param fromY 起点Y
* @param toX 终点X
* @param toY 终点Y
* @param duration 移动时长(毫秒)
* @param pointCount 生成点数
* @return 轨迹点列表
*/
public static List<TrackPoint> generateMove(int fromX, int fromY, int toX, int toY, int start ,int duration, int pointCount) {
List<TrackPoint> points = new ArrayList<>();
// 随机控制点(让路径弯曲)
int cx = (fromX + toX) / 2 + (random.nextInt(100) - 50);
int cy = (fromY + toY) / 2 + (random.nextInt(100) - 50);
for (int i = 0; i <= pointCount; i++) {
double t = (double) i / pointCount;
// 缓动:开始慢 -> 中间快 -> 结束慢
t = easeInOutCubic(t);
// 贝塞尔曲线
int x = (int)((1-t)*(1-t)*fromX + 2*(1-t)*t*cx + t*t*toX);
int y = (int)((1-t)*(1-t)*fromY + 2*(1-t)*t*cy + t*t*toY);
// 随机抖动
x += random.nextInt(5) - 2;
y += random.nextInt(5) - 2;
int time = start + (int)(t * duration);
points.add(new TrackPoint(x, y, time, "move"));
}
// 确保终点精确
points.get(points.size()-1).x = toX;
points.get(points.size()-1).y = toY;
return points;
}
/**
* 生成点击动作
* @param x 点击X坐标
* @param y 点击Y坐标
* @param startTime 开始时间(毫秒)
* @return 轨迹点列表
*/
public static List<TrackPoint> generateClick(int x, int y, int startTime) {
List<TrackPoint> points = new ArrayList<>();
int currentTime = startTime;
// 思考时间
currentTime += 100 + random.nextInt(400);
points.add(new TrackPoint(x, y, currentTime, "hover"));
// 微调对准
for (int i = 0; i < 3; i++) {
int offsetX = x + (random.nextInt(6) - 3);
int offsetY = y + (random.nextInt(6) - 3);
currentTime += 30 + random.nextInt(50);
points.add(new TrackPoint(offsetX, offsetY, currentTime, "move"));
}
// 点击
currentTime += 50 + random.nextInt(100);
points.add(new TrackPoint(x, y, currentTime, "click"));
// 点击后停留
currentTime += 80 + random.nextInt(150);
points.add(new TrackPoint(x, y, currentTime, "hover"));
return points;
}
/**
* 生成随机取点(只返回一个点位)
* @param fromX 起点X
* @param fromY 起点Y
* @param toX 终点X
* @param toY 终点Y
* @return 随机点位
*/
public static Point getRandomPoint(int fromX, int fromY, int toX, int toY) {
double ratio = random.nextDouble();
int x = fromX + (int)((toX - fromX) * ratio);
int y = fromY + (int)((toY - fromY) * ratio);
// 添加随机偏移
if (random.nextBoolean()) {
x += random.nextInt(30) - 15;
y += random.nextInt(30) - 15;
}
return new Point(x, y);
}
/**
* 生成多个随机点
* @param fromX 起点X
* @param fromY 起点Y
* @param toX 终点X
* @param toY 终点Y
* @param count 生成数量
* @return 随机点位列表
*/
public static List<Point> getRandomPoints(int fromX, int fromY, int toX, int toY, int count) {
List<Point> points = new ArrayList<>();
for (int i = 0; i < count; i++) {
points.add(getRandomPoint(fromX, fromY, toX, toY));
}
return points;
}
// ==================== 工具方法 ====================
/**
* 缓动函数
*/
private static double easeInOutCubic(double t) {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
/**
* 输出为JSON字符串
*/
public static String toJson(List<TrackPoint> points) {
StringBuilder sb = new StringBuilder("[\n");
for (int i = 0; i < points.size(); i++) {
sb.append(" ").append(points.get(i).toString());
if (i < points.size() - 1) sb.append(",");
sb.append("\n");
}
sb.append("]");
return sb.toString();
}
/**
* 输出为紧凑JSON无换行
*/
public static String toJsonCompact(List<TrackPoint> points) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < points.size(); i++) {
sb.append(points.get(i).toString());
if (i < points.size() - 1) sb.append(",");
}
sb.append("]");
return sb.toString();
}
/**
* 打印轨迹信息
*/
public static void printInfo(List<TrackPoint> points) {
if (points.isEmpty()) {
System.out.println("轨迹为空");
return;
}
int clickCount = 0;
int moveCount = 0;
for (TrackPoint p : points) {
if ("click".equals(p.type)) clickCount++;
if ("move".equals(p.type)) moveCount++;
}
System.out.println("=== 轨迹信息 ===");
System.out.println("总点数: " + points.size());
System.out.println("移动点: " + moveCount);
System.out.println("点击点: " + clickCount);
System.out.println("总时长: " + points.get(points.size()-1).t + "ms");
System.out.println("起始位置: (" + points.get(0).x + "," + points.get(0).y + ")");
System.out.println("结束位置: (" + points.get(points.size()-1).x + "," + points.get(points.size()-1).y + ")");
}
}

View File

@@ -10,7 +10,6 @@ import com.xiang.service.module.jntyzx.zlb.service.ZlbService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date; import java.util.Date;
@Component @Component
@@ -44,9 +43,9 @@ public class ZlbSiteTask extends BaseScheduleTaskTemplate {
TaskResult taskResult = new TaskResult(); TaskResult taskResult = new TaskResult();
//获取当前时间的后一天 //获取当前时间的后一天
Date date1 = DateUtils.addDate(new Date(), 2); Date date1 = DateUtils.addDate(new Date(), 0);
Date date2 = DateUtils.addDate(new Date(), 3); Date date2 = DateUtils.addDate(new Date(), 1);
Date date3 = DateUtils.addDate(new Date(), 4); Date date3 = DateUtils.addDate(new Date(), 2);
String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD); String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD);
String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD); String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD);
String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD); String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD);

View File

@@ -1,6 +1,5 @@
package com.xiang.service.module.jntyzx.zlb.service; package com.xiang.service.module.jntyzx.zlb.service;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
@@ -11,21 +10,39 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.JntyzxDingTalkFactory;
import com.xiang.common.pojo.jntyzx.zlb.*; import com.xiang.common.pojo.TrackPoint;
import com.xiang.common.pojo.jntyzx.zlb.ZlbCaptchaResp;
import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderInfo;
import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderJson;
import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderWqInfo;
import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderWqJson;
import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo;
import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteRequest;
import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo;
import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo;
import com.xiang.common.service.ICodeService; import com.xiang.common.service.ICodeService;
import com.xiang.common.utils.AESECBUtils; import com.xiang.common.utils.AESECBUtils;
import com.xiang.common.utils.Base64ImageScaler;
import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.DateUtils;
import com.xiang.common.utils.OkHttpUtil; import com.xiang.common.utils.OkHttpUtil;
import com.xiang.common.utils.TrajectoryUtil;
import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -35,6 +52,7 @@ import java.util.stream.Collectors;
public class ZlbServiceImpl implements ZlbService { public class ZlbServiceImpl implements ZlbService {
public static volatile boolean running = true;
@Resource @Resource
private ZlbSiteInfoService zlbSiteInfoService; private ZlbSiteInfoService zlbSiteInfoService;
@Resource @Resource
@@ -48,7 +66,6 @@ public class ZlbServiceImpl implements ZlbService {
@Autowired @Autowired
private ICodeService codeService; private ICodeService codeService;
@Override @Override
public void queryZLbSiteInfo(String ymdDate, Integer type) throws Exception { public void queryZLbSiteInfo(String ymdDate, Integer type) throws Exception {
log.info("开始查询场地信息"); log.info("开始查询场地信息");
@@ -361,23 +378,38 @@ public class ZlbServiceImpl implements ZlbService {
@Override @Override
public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException { public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException {
LocalDateTime startTime = LocalDateTime.now();
//获取图片验证码 //获取图片验证码
String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}");
JSONObject jsonObject = JSON.parseObject(s); try {
String id = (String) jsonObject.get("id"); Thread.sleep(2000);
String captcha = JSON.toJSONString(jsonObject.get("captcha")); } catch (InterruptedException e) {
String backgroundImage = JSON.toJSONString(JSON.parseObject(captcha).get("backgroundImage")); e.printStackTrace();
String templateImage = JSON.toJSONString(JSON.parseObject(captcha).get("templateImage")); }
ZlbCaptchaResp zlbCaptchaResp = JSON.parseObject(s, ZlbCaptchaResp.class);
if (Objects.isNull(zlbCaptchaResp)) {
return null;
}
if (Objects.isNull(zlbCaptchaResp.getCaptcha())) {
return null;
}
String id = zlbCaptchaResp.getId();
String backgroundImage = zlbCaptchaResp.getCaptcha().getBackgroundImage();
String templateImage = zlbCaptchaResp.getCaptcha().getTemplateImage();
backgroundImage = Base64ImageScaler.scaleToHalf(backgroundImage);
List<String> trackList = codeService.codeResolve(backgroundImage, templateImage); List<String> trackList = codeService.codeResolve(backgroundImage, templateImage);
ZlbOrderInfo orderInfo = new ZlbOrderInfo(); ZlbOrderInfo orderInfo = new ZlbOrderInfo();
orderInfo.setId(id); orderInfo.setId(id);
//获取验证码轨迹 //获取验证码轨迹
List<ZlbOrderInfo.ZlbData.TrackList> trackListList = convert(trackList); List<ZlbOrderInfo.ZlbData.TrackList> trackListList = convert(trackList);
ZlbOrderInfo.ZlbData data = new ZlbOrderInfo.ZlbData(); ZlbOrderInfo.ZlbData data = new ZlbOrderInfo.ZlbData();
// data.setBgImageWidth(); data.setBgImageWidth(zlbCaptchaResp.getCaptcha().getBackgroundImageWidth() / 2);
// data.setBgImageHeight(); data.setBgImageHeight(zlbCaptchaResp.getCaptcha().getBackgroundImageHeight() / 2);
// data.setStartTime(); data.setStartTime(startTime.toString());
// data.setStopTime(); data.setStopTime(LocalDateTime.now().toString());
data.setTrackList(trackListList); data.setTrackList(trackListList);
orderInfo.setData(data); orderInfo.setData(data);
@@ -388,27 +420,44 @@ public class ZlbServiceImpl implements ZlbService {
} }
private List<ZlbOrderInfo.ZlbData.TrackList> convert(List<String> trackList) { private List<ZlbOrderInfo.ZlbData.TrackList> convert(List<String> trackList) {
int t = 0;
List<ZlbOrderInfo.ZlbData.TrackList> result = Lists.newArrayList(); List<ZlbOrderInfo.ZlbData.TrackList> result = Lists.newArrayList();
for (String track : trackList) { for (int i = 0; i < trackList.size(); i++) {
String[] split = track.split(","); String currentTrack = trackList.get(i);
String x = split[0]; String nextTrack = null;
String y = split[1]; if (i != trackList.size() - 1) {
ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList(); nextTrack = trackList.get(i + 1);
data.setX(Integer.valueOf(x));
data.setY(Integer.valueOf(y));
data.setT(1);
data.setType("click");
result.add(data);
ZlbOrderInfo.ZlbData.TrackList move = new ZlbOrderInfo.ZlbData.TrackList();
move.setX(Integer.valueOf(x));
move.setY(Integer.valueOf(y));
move.setT(1);
move.setType("move");
} }
t += 100;
Integer currentX = Integer.parseInt(currentTrack.split(",")[0]);
Integer currentY = Integer.parseInt(currentTrack.split(",")[1]);
List<TrackPoint> trackPoints = TrajectoryUtil.generateClick(currentX, currentY, t);
for (TrackPoint trackPoint : trackPoints) {
if (StringUtils.equals(trackPoint.getType(), "click")) {
ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList();
data.setX(trackPoint.getX());
data.setY(trackPoint.getY());
data.setT(trackPoint.getT());
data.setType(trackPoint.getType());
result.add(data);
}
}
t += 500;
if (StringUtils.isNotBlank(nextTrack)) {
Integer nextX = Integer.parseInt(nextTrack.split(",")[0]);
Integer nextY = Integer.parseInt(nextTrack.split(",")[1]);
List<TrackPoint> nextTrackPoints = TrajectoryUtil.generateMove(currentX, currentY, nextX, nextY, t, 1200, 10);
TrackPoint trackPoint = nextTrackPoints.get(i + 1);
ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList();
data.setX(trackPoint.getX());
data.setY(trackPoint.getY());
data.setT(trackPoint.getT());
data.setType(trackPoint.getType());
result.add(data);
}
}
log.info("生成的轨迹点结果:{}", JSON.toJSONString(result));
return result; return result;
} }
@@ -431,8 +480,6 @@ public class ZlbServiceImpl implements ZlbService {
redisTemplate.expire(redisKey, 1234, TimeUnit.SECONDS); redisTemplate.expire(redisKey, 1234, TimeUnit.SECONDS);
} }
public static volatile boolean running = true;
@Override @Override
public void jianlou(String name, String day, long time) throws Exception { public void jianlou(String name, String day, long time) throws Exception {
jntyzxDingTalkFactory.sendMsg(name + "自定义捡漏开始捡漏时间:" + day + " 捡漏人:" + name + " 捡漏间隔:" + time + "ms"); jntyzxDingTalkFactory.sendMsg(name + "自定义捡漏开始捡漏时间:" + day + " 捡漏人:" + name + " 捡漏间隔:" + time + "ms");