From b8083381b6b45a850f42b759810699eca5bf7d9b Mon Sep 17 00:00:00 2001 From: xiang Date: Sat, 2 May 2026 14:36:21 +0800 Subject: [PATCH 01/32] =?UTF-8?q?feat:ddns=20=E5=8A=A8=E6=80=81=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 ++ .../config/AliyunDnsPropertyConfig.java | 17 +++ .../com/xiang/common/config/RedisConfig.java | 2 - .../common/enums/DingTalkBizTypeEnum.java | 20 ++++ .../common/factory/ScriptDingTalkFactory.java | 19 +++ .../java/com/xiang/common/utils/IpUtils.java | 21 ++++ .../schedule/DomainDynamicAnalysisTask.java | 31 +++++ .../domain/server/DomainController.java | 45 +++++++ .../module/domain/service/IDomainService.java | 15 +++ .../domain/service/IDomainServiceImpl.java | 111 ++++++++++++++++++ src/main/resources/application-local.yml | 33 +++++- 11 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/xiang/common/config/AliyunDnsPropertyConfig.java create mode 100644 src/main/java/com/xiang/common/enums/DingTalkBizTypeEnum.java create mode 100644 src/main/java/com/xiang/common/factory/ScriptDingTalkFactory.java create mode 100644 src/main/java/com/xiang/common/utils/IpUtils.java create mode 100644 src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java create mode 100644 src/main/java/com/xiang/service/module/domain/server/DomainController.java create mode 100644 src/main/java/com/xiang/service/module/domain/service/IDomainService.java create mode 100644 src/main/java/com/xiang/service/module/domain/service/IDomainServiceImpl.java diff --git a/pom.xml b/pom.xml index af9e3d0..a65e6bc 100644 --- a/pom.xml +++ b/pom.xml @@ -145,6 +145,14 @@ alibaba-dingtalk-service-sdk 2.0.0 + + + + com.aliyun + alidns20150109 + 3.4.7 + + \ No newline at end of file diff --git a/src/main/java/com/xiang/common/config/AliyunDnsPropertyConfig.java b/src/main/java/com/xiang/common/config/AliyunDnsPropertyConfig.java new file mode 100644 index 0000000..e092887 --- /dev/null +++ b/src/main/java/com/xiang/common/config/AliyunDnsPropertyConfig.java @@ -0,0 +1,17 @@ +package com.xiang.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@ConfigurationProperties(prefix = "aliyun.dns") +@Data +public class AliyunDnsPropertyConfig { + + private List RR; + + private String rootDomain; +} diff --git a/src/main/java/com/xiang/common/config/RedisConfig.java b/src/main/java/com/xiang/common/config/RedisConfig.java index 60f5a5a..ad5657a 100644 --- a/src/main/java/com/xiang/common/config/RedisConfig.java +++ b/src/main/java/com/xiang/common/config/RedisConfig.java @@ -3,8 +3,6 @@ package com.xiang.common.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; -import com.xiang.common.utils.RedisService; -import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; diff --git a/src/main/java/com/xiang/common/enums/DingTalkBizTypeEnum.java b/src/main/java/com/xiang/common/enums/DingTalkBizTypeEnum.java new file mode 100644 index 0000000..f197b91 --- /dev/null +++ b/src/main/java/com/xiang/common/enums/DingTalkBizTypeEnum.java @@ -0,0 +1,20 @@ +package com.xiang.common.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * @Author: xiang + * @Date: 2026-01-04 16:13 + */ +@Getter +@RequiredArgsConstructor +public enum DingTalkBizTypeEnum implements BaseDingTalkBizType { + + JT("venue", "江南体育中心"), + XB("xb", "股票基金变化通知"), + SCRIPT("script", "脚本运行通知群") + ; + private final String bizName; + private final String desc; +} diff --git a/src/main/java/com/xiang/common/factory/ScriptDingTalkFactory.java b/src/main/java/com/xiang/common/factory/ScriptDingTalkFactory.java new file mode 100644 index 0000000..d97e7a8 --- /dev/null +++ b/src/main/java/com/xiang/common/factory/ScriptDingTalkFactory.java @@ -0,0 +1,19 @@ +package com.xiang.common.factory; + +import com.xiang.common.config.DingTalkRobotProperties; +import com.xiang.common.enums.DingTalkBizTypeEnum; +import com.xiang.common.utils.dingTalk.AbstractDingTalkFactory; +import com.xiang.common.utils.dingTalk.DingTalkSender; +import org.springframework.stereotype.Service; + +@Service +public class ScriptDingTalkFactory extends AbstractDingTalkFactory { + public ScriptDingTalkFactory(DingTalkRobotProperties dingTalkRobotProperties, DingTalkSender dingTalkSender) { + super(dingTalkRobotProperties, dingTalkSender); + } + + @Override + public void sendMsg(String msg) { + getClient(DingTalkBizTypeEnum.SCRIPT).sendDingTalkMsg(msg); + } +} diff --git a/src/main/java/com/xiang/common/utils/IpUtils.java b/src/main/java/com/xiang/common/utils/IpUtils.java new file mode 100644 index 0000000..147ceb5 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/IpUtils.java @@ -0,0 +1,21 @@ +package com.xiang.common.utils; + +import com.google.common.collect.Maps; + +import java.io.IOException; +import java.util.Map; + +/** + * @Author: xiang + * @Date: 2025-06-10 16:50 + */ +public class IpUtils { + + private final static String PUBLIC_IP_URL = "https://api-ipv4.ip.sb/ip"; + + public static String getPublicIp() throws IOException { + Map header = Maps.newHashMap(); + header.put("User-Agent", "Mozilla/5.0"); + return HttpService.doGet(PUBLIC_IP_URL, header, null).trim(); + } +} diff --git a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java new file mode 100644 index 0000000..521e3d7 --- /dev/null +++ b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java @@ -0,0 +1,31 @@ +package com.xiang.service.module.domain.schedule; + +import com.xiang.common.utils.IpUtils; +import com.xiang.service.module.domain.service.IDomainService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.scheduling.annotation.Scheduled; + +@Slf4j +@RequiredArgsConstructor +public class DomainDynamicAnalysisTask { + private final IDomainService IDomainService; + + @Scheduled(cron = "0 0/30 * * * ? ") + public void dynamicDomainSchedule() { + String publicIp = ""; + try { + publicIp = IpUtils.getPublicIp(); + } catch (Exception e) { + log.error("获取公网ip失败,", e); + } + if (StringUtils.isNotBlank(publicIp)) { + try { + IDomainService.dynamicDomainAnalysis(publicIp); + } catch (Exception e) { + log.error("动态解析公网ip失败, ip:{}", publicIp, e); + } + } + } +} diff --git a/src/main/java/com/xiang/service/module/domain/server/DomainController.java b/src/main/java/com/xiang/service/module/domain/server/DomainController.java new file mode 100644 index 0000000..9619039 --- /dev/null +++ b/src/main/java/com/xiang/service/module/domain/server/DomainController.java @@ -0,0 +1,45 @@ +package com.xiang.service.module.domain.server; + +import com.xiang.common.utils.IpUtils; +import com.xiang.service.module.domain.service.IDomainService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/script/domain") +public class DomainController { + + private final IDomainService domainService; + @GetMapping("/ddns") + public void dynamicDomainAnalysis() { + String publicIp = ""; + try { + publicIp = IpUtils.getPublicIp(); + } catch (Exception e) { + log.error("获取公网ip失败,", e); + } + if (StringUtils.isNotBlank(publicIp)) { + try { + domainService.dynamicDomainAnalysis(publicIp); + } catch (Exception e) { + log.error("动态解析公网ip失败, ip:{}", publicIp, e); + } + } + } + + @GetMapping("/ddns4ip") + public void dynamicDomainAnalysis(@RequestParam("ip") String ip, @RequestParam("rr") String rr) { + try { + domainService.dynamicDomainAnalysis(ip, rr); + } catch (Exception e) { + log.error("动态解析公网ip失败, ip:{}", ip, e); + } + } +} diff --git a/src/main/java/com/xiang/service/module/domain/service/IDomainService.java b/src/main/java/com/xiang/service/module/domain/service/IDomainService.java new file mode 100644 index 0000000..2d50c0d --- /dev/null +++ b/src/main/java/com/xiang/service/module/domain/service/IDomainService.java @@ -0,0 +1,15 @@ +package com.xiang.service.module.domain.service; + +/** + * @Author: xiang + * @Date: 2025-06-10 16:48 + */ +public interface IDomainService { + + /** + * 动态域名解析 + * @param publicIp 动态ip + */ + void dynamicDomainAnalysis(String publicIp) throws Exception; + void dynamicDomainAnalysis(String publicIp, String rr) throws Exception; +} diff --git a/src/main/java/com/xiang/service/module/domain/service/IDomainServiceImpl.java b/src/main/java/com/xiang/service/module/domain/service/IDomainServiceImpl.java new file mode 100644 index 0000000..b0fa16d --- /dev/null +++ b/src/main/java/com/xiang/service/module/domain/service/IDomainServiceImpl.java @@ -0,0 +1,111 @@ +package com.xiang.service.module.domain.service; + + +import com.aliyun.alidns20150109.Client; +import com.aliyun.alidns20150109.models.AddDomainRecordRequest; +import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsRequest; +import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsResponse; +import com.aliyun.alidns20150109.models.DescribeSubDomainRecordsResponseBody; +import com.aliyun.alidns20150109.models.UpdateDomainRecordRequest; +import com.aliyun.teaopenapi.models.Config; +import com.xiang.common.config.AliyunDnsPropertyConfig; +import com.xiang.common.factory.ScriptDingTalkFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-06-10 16:48 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class IDomainServiceImpl implements IDomainService { + + private static final String ACCESS_KEY_ID = "LTAI5tDMjaVF8Bbqcpp4dmvP"; + private static final String ACCESS_KEY_SECRET = "nkmnaNjWQy5984C5kjyS0oDmdMKGQd"; + /** + * 根域名 + */ + @Value("${aliyun.dns.rootDomain}") + private String rootDomain; + /** + * 主机记录,例如 home.example.com + */ + private final AliyunDnsPropertyConfig aliyunDnsPropertyConfig; + private static final String TYPE = "A"; + + private final ScriptDingTalkFactory dingTalkService; + @Override + public void dynamicDomainAnalysis(String publicIp) throws Exception { + Client client = createClient(); + + for (String rr : aliyunDnsPropertyConfig.getRR()) { + record(client, publicIp, rr); + } + } + + @Override + public void dynamicDomainAnalysis(String publicIp, String rr) throws Exception { + Client client = createClient(); + record(client, publicIp, rr); + } + + private void record(Client client, String publicIp, String rr) throws Exception{ + // 查询记录 + DescribeSubDomainRecordsRequest query = new DescribeSubDomainRecordsRequest() + .setSubDomain(rr + "." + rootDomain) + .setType(TYPE); + DescribeSubDomainRecordsResponse response = client.describeSubDomainRecords(query); + List records = + response.getBody().getDomainRecords().getRecord(); + + if (records.isEmpty()) { + log.info("未找到记录,添加记录..., ip:{}", publicIp); + addDnsRecord(client, publicIp, rr); + dingTalkService.sendMsg("动态解析公网ip成功,域名:" + rr + "." + rootDomain + ", 新ip:" + publicIp); + } else { + String recordId = records.get(0).getRecordId(); + String currentValue = records.get(0).getValue(); + if (!publicIp.equals(currentValue)) { + log.info("IP变更,更新记录...,ip:{}", publicIp); + updateDnsRecord(client, recordId, publicIp, rr); + dingTalkService.sendMsg("动态解析公网ip成功,域名:" + rr + "." + rootDomain + ", 新ip:" + publicIp); + } else { + log.info("ip未变更,无需修改,ip:{}", publicIp); + } + } + } + + private Client createClient() throws Exception { + Config config = new Config() + .setAccessKeyId(ACCESS_KEY_ID) + .setAccessKeySecret(ACCESS_KEY_SECRET) + .setEndpoint("alidns.cn-hangzhou.aliyuncs.com"); + return new Client(config); + } + + private void updateDnsRecord(Client client, String recordId, String newIp, String rr) throws Exception { + UpdateDomainRecordRequest request = new UpdateDomainRecordRequest() + .setRecordId(recordId) + .setRR(rr) + .setType(TYPE) + .setValue(newIp); + client.updateDomainRecord(request); + log.info("更新成功: ,newIP:{}", newIp); + } + + private void addDnsRecord(Client client, String ip, String rr) throws Exception { + AddDomainRecordRequest request = new AddDomainRecordRequest() + .setDomainName(rootDomain) + .setRR(rr) + .setType(TYPE) + .setValue(ip); + client.addDomainRecord(request); + log.info("添加成功: ip:{}", ip); + } +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 5453b02..dcc9a1b 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -15,11 +15,40 @@ spring: host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com port: 6379 password: Xiang0000 - database: 11 + database: 10 timeout: 3000 lettuce: pool: max-active: 20 max-idle: 10 min-idle: 2 - max-wait: 3000 \ No newline at end of file + max-wait: 3000 + +aliyun: + dns: + rootDomain: xiangtech.xyz + RR: + - local + + +dingtalk: + robot: + properties: + venue: + name: 江南体育中心通知群 + token: 6a218646972c684c75832b0229ea93a234778af537d7469ce96bef290faf530e + secret: SEC9018755ba86d3e5c1ed2fbfa1d6953d84bb2a6c8ebe7ed4e318457bfed5e0465 + users: + - 450841600726084717 + script: + name: 脚本运行通知群 + token: 797be7f32062e31dec1d567f8b490a5649a5366083618e236c7a1263df1f4af3 + secret: SEC9aca642c0c29c9da261462869c464d34623247583d98fc82343a0a4464abbe91 + users: + - 450841600726084717 + xb: + name: 股票基金变化通知群 + token: ad21ead99f0fdc63aa00d6732b7b0888c17590f7612c68297edfcb71844d1437 + secret: SECc09d8aad6635f1a4cbadb7c0ab365523c46299f138438cd885e445e0f5f4d730 + users: + - 450841600726084717 \ No newline at end of file -- 2.49.1 From fb48f447beae4db056e41a6d46e31a48e1f1be4d Mon Sep 17 00:00:00 2001 From: xiang Date: Sat, 2 May 2026 14:39:55 +0800 Subject: [PATCH 02/32] =?UTF-8?q?feat:=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/main/resources/logback-spring.xml diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..3b51ac3 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,106 @@ + + + + + + ${APP_NAME} + + + + + + + + + + + + %green(%contextName) | %cyan(%d{yyyy-MM-dd HH:mm:ss.SSS}) | %highlight(%-5level) | %yellow([%thread{10}]) | %blue(%logger{20}):%line | %magenta([traceId:%X{traceId:-}]) - %msg%n + + + UTF-8 + + + + + + + + ${LOG_HOME}/debug-%d{yyyy-MM-dd}.log + + ${LOG_MAX_HISTORY} + + + + %contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n + UTF-8 + + + + debug + ACCEPT + DENY + + + + + + + + ${LOG_HOME}/info-%d{yyyy-MM-dd}.log + + ${LOG_MAX_HISTORY} + + + + %contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n + UTF-8 + + + + + info + ACCEPT + DENY + + + + + + + + ${LOG_HOME}/error-%d{yyyy-MM-dd}.log + + ${LOG_MAX_HISTORY} + + + + %contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n + UTF-8 + + + + error + ACCEPT + DENY + + + + + + + + + + + + + + + + + + + + + -- 2.49.1 From f36766112f2449863ddee32da6d7c0ff52a03531 Mon Sep 17 00:00:00 2001 From: xiang Date: Sun, 3 May 2026 00:06:07 +0800 Subject: [PATCH 03/32] =?UTF-8?q?feat:=E4=BB=BB=E5=8A=A1=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/enums/ScheduleEnums.java | 17 +++ .../schedule/BaseScheduleTaskTemplate.java | 144 ++++++++++++++++++ .../mapper/ScheduleOpeningConfigDao.java | 11 ++ .../schedule/ScheduleOpeningConfigDO.java | 24 +++ .../common/pojo/schedule/TaskResult.java | 17 +++ .../pojo/schedule/ValidationResult.java | 15 ++ .../IScheduleOpeningConfigService.java | 17 +++ .../ScheduleOpeningConfigServiceImpl.java | 20 +++ .../schedule/DomainDynamicAnalysisTask.java | 36 ++++- .../DomainDynamicAnalysisTaskConfig.java | 23 +++ 10 files changed, 318 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/xiang/common/enums/ScheduleEnums.java create mode 100644 src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java create mode 100644 src/main/java/com/xiang/common/mapper/ScheduleOpeningConfigDao.java create mode 100644 src/main/java/com/xiang/common/pojo/schedule/ScheduleOpeningConfigDO.java create mode 100644 src/main/java/com/xiang/common/pojo/schedule/TaskResult.java create mode 100644 src/main/java/com/xiang/common/pojo/schedule/ValidationResult.java create mode 100644 src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java create mode 100644 src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTaskConfig.java diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java new file mode 100644 index 0000000..7fa8e0e --- /dev/null +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -0,0 +1,17 @@ +package com.xiang.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ScheduleEnums { + + DOMAIN_DYNAMIC_ANALYSIS_TASK(4, "domain", "domainDynamicAnalysisTask"), + + ; + + private final Integer modeleCode; + private final String module; + private final String taskName; +} diff --git a/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java new file mode 100644 index 0000000..1ce762c --- /dev/null +++ b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java @@ -0,0 +1,144 @@ +package com.xiang.common.factory.schedule; + +import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.pojo.schedule.ValidationResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + +public abstract class BaseScheduleTaskTemplate { + + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private final IScheduleOpeningConfigService scheduleOpeningConfigService; + + protected BaseScheduleTaskTemplate(IScheduleOpeningConfigService scheduleOpeningConfigService) { + this.scheduleOpeningConfigService = scheduleOpeningConfigService; + } + + + /** + * 模板方法 - 定义任务执行的标准流程 + * 子类不应覆写此方法 + */ + public final void run() { + String taskName = getTaskName(); + Integer module = getModule(); + LocalDateTime startTime = LocalDateTime.now(); + logger.info("【{}】任务开始执行,开始时间:{}", taskName, startTime.format(formatter)); + + try { + // 1. 参数校验 + logger.info("【{}】开始参数校验...", taskName); + ValidationResult validate = validate(module, taskName); + + if (!validate.isValid()) { + logger.error("【{}】参数校验失败:{}", taskName, validate.getMessage()); + onValidationFailure(validate); + return; + } + logger.info("【{}】参数校验通过", taskName); + + // 2. 执行业务逻辑 + logger.info("【{}】开始执行业务逻辑...", taskName); + TaskResult result = doExecute(validate.getValidatedParams()); + + // 3. 记录成功结果 + recordSuccess(result, startTime); + } catch (Exception e) { + // 4. 记录失败结果 + logger.error("【{}】任务执行异常", taskName, e); + recordFailure(e, startTime); + } + + LocalDateTime endTime = LocalDateTime.now(); + logger.info("【{}】任务结束,结束时间:{}", taskName, endTime.format(formatter)); + } + + /** + * 获取任务名称(子类必须实现) + */ + protected abstract String getTaskName(); + + /** + * 获取模块名称(子类必须实现) + * @return + */ + protected abstract Integer getModule(); + + /** + * 任务校验(子类必须实现) + * @return 校验结果,包含是否通过、错误信息、校验通过的参数对象 + */ + private ValidationResult validate(Integer moduleCode, String taskName) { + ValidationResult validationResult = new ValidationResult(); + boolean flag = checkTaskOpening(moduleCode, taskName); + validationResult.setValidatedParams(null); + validationResult.setValid(flag); + validationResult.setMessage(flag ? "" : "任务未开启"); + return validationResult; + } + + /** + * 校验任务是否开启 + * @param moduleCode 模块id + * @param taskName 任务名称 + * @return + */ + private boolean checkTaskOpening(Integer moduleCode, String taskName) { + ScheduleOpeningConfigDO configsByModule = scheduleOpeningConfigService.getConfigByModule(moduleCode, taskName); + if (Objects.isNull(configsByModule)) { + return false; + } + return Objects.equals(configsByModule.getStatus(), 1); + } + + /** + * 具体的业务逻辑执行(子类必须实现) + * @param validatedParams 校验通过的参数 + * @return 任务执行结果 + */ + protected abstract TaskResult doExecute(Object validatedParams); + + /** + * 记录成功结果 - 子类可选择性覆写 + */ + protected void recordSuccess(TaskResult result, LocalDateTime startTime) { + LocalDateTime endTime = LocalDateTime.now(); + long duration = java.time.Duration.between(startTime, endTime).toMillis(); + + logger.info("【{}】任务执行成功,耗时:{}ms,结果概要:{}", + getTaskName(), duration, result.getSummary()); + + // 可在此扩展:写入数据库、发送通知等 + // 例如:saveTaskLog(getTaskName(), "SUCCESS", duration, result.getDetail()); + } + + /** + * 记录失败结果 - 子类可选择性覆写 + */ + protected void recordFailure(Exception e, LocalDateTime startTime) { + LocalDateTime endTime = LocalDateTime.now(); + long duration = java.time.Duration.between(startTime, endTime).toMillis(); + + logger.error("【{}】任务执行失败,耗时:{}ms,错误:{}", + getTaskName(), duration, e.getMessage()); + + // 可在此扩展:写入数据库、发送告警等 + // 例如:saveTaskLog(getTaskName(), "FAILURE", duration, e.getMessage()); + } + + /** + * 参数校验失败时的处理 - 子类可选择性覆写 + */ + protected void onValidationFailure(ValidationResult result) { + // 默认实现:仅记录日志 + logger.warn("【{}】参数校验失败,跳过任务执行", getTaskName()); + // 可在此扩展:发送通知、写入失败日志等 + } +} diff --git a/src/main/java/com/xiang/common/mapper/ScheduleOpeningConfigDao.java b/src/main/java/com/xiang/common/mapper/ScheduleOpeningConfigDao.java new file mode 100644 index 0000000..a574486 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ScheduleOpeningConfigDao.java @@ -0,0 +1,11 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ScheduleOpeningConfigDao extends BaseMapper { +} diff --git a/src/main/java/com/xiang/common/pojo/schedule/ScheduleOpeningConfigDO.java b/src/main/java/com/xiang/common/pojo/schedule/ScheduleOpeningConfigDO.java new file mode 100644 index 0000000..a025864 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/schedule/ScheduleOpeningConfigDO.java @@ -0,0 +1,24 @@ +package com.xiang.common.pojo.schedule; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("schedule_opening_config") +public class ScheduleOpeningConfigDO { + private Long id; + /** + * 模块id(0:glados 1:芬玩岛 2:江体小程序 3:江体zlb 4:ddns) + */ + private Integer module; + private String beanName; + private Integer status; + private LocalDateTime createTime; + private LocalDateTime updateTime; +} diff --git a/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java b/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java new file mode 100644 index 0000000..e603973 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java @@ -0,0 +1,17 @@ +package com.xiang.common.pojo.schedule; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TaskResult { + /** + * 是否成功 + */ + private boolean success; + private String summary; + private Object detail; +} diff --git a/src/main/java/com/xiang/common/pojo/schedule/ValidationResult.java b/src/main/java/com/xiang/common/pojo/schedule/ValidationResult.java new file mode 100644 index 0000000..d3a919b --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/schedule/ValidationResult.java @@ -0,0 +1,15 @@ +package com.xiang.common.pojo.schedule; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.checkerframework.checker.units.qual.A; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ValidationResult { + private boolean valid; + private String message; + private Object validatedParams; +} diff --git a/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java b/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java new file mode 100644 index 0000000..597bd24 --- /dev/null +++ b/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java @@ -0,0 +1,17 @@ +package com.xiang.common.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; + +import java.util.List; + +public interface IScheduleOpeningConfigService extends IService { + + /** + * 根据模块id和任务名称查询 + * @param moduleCode + * @param taskName + * @return + */ + ScheduleOpeningConfigDO getConfigByModule(Integer moduleCode, String taskName); +} diff --git a/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java b/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java new file mode 100644 index 0000000..69816e3 --- /dev/null +++ b/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java @@ -0,0 +1,20 @@ +package com.xiang.common.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ScheduleOpeningConfigDao; +import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; +import org.springframework.stereotype.Service; + + +@Service +public class ScheduleOpeningConfigServiceImpl extends ServiceImpl implements IScheduleOpeningConfigService { + @Override + public ScheduleOpeningConfigDO getConfigByModule(Integer moduleCode, String taskName) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(ScheduleOpeningConfigDO::getModule, moduleCode); + lqw.eq(ScheduleOpeningConfigDO::getBeanName, taskName); + return baseMapper.selectOne(lqw); + } +} diff --git a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java index 521e3d7..a5116ea 100644 --- a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java +++ b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java @@ -1,20 +1,39 @@ package com.xiang.service.module.domain.schedule; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.utils.IpUtils; import com.xiang.service.module.domain.service.IDomainService; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; @Slf4j -@RequiredArgsConstructor -public class DomainDynamicAnalysisTask { +@Component +public class DomainDynamicAnalysisTask extends BaseScheduleTaskTemplate { private final IDomainService IDomainService; - @Scheduled(cron = "0 0/30 * * * ? ") - public void dynamicDomainSchedule() { + public DomainDynamicAnalysisTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IDomainService IDomainService) { + super(scheduleOpeningConfigService); + this.IDomainService = IDomainService; + } + + @Override + protected Integer getModule() { + return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getModeleCode(); + } + + @Override + protected String getTaskName() { + return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getTaskName(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { String publicIp = ""; + TaskResult taskResult = new TaskResult(); try { publicIp = IpUtils.getPublicIp(); } catch (Exception e) { @@ -27,5 +46,10 @@ public class DomainDynamicAnalysisTask { log.error("动态解析公网ip失败, ip:{}", publicIp, e); } } + taskResult.setSuccess(true); + taskResult.setSummary(""); + taskResult.setDetail(null); + return taskResult; } + } diff --git a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTaskConfig.java b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTaskConfig.java new file mode 100644 index 0000000..4bc12be --- /dev/null +++ b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTaskConfig.java @@ -0,0 +1,23 @@ +package com.xiang.service.module.domain.schedule; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@Component +@Slf4j +@RequiredArgsConstructor +@RestController +public class DomainDynamicAnalysisTaskConfig { + + private final DomainDynamicAnalysisTask domainDynamicAnalysisTask; + + @Scheduled(cron = "0 0/30 * * * ? ") + @GetMapping("/test") + public void dynamicDomainSchedule() { + domainDynamicAnalysisTask.run(); + } +} -- 2.49.1 From 72db541e2202e93746eaef1c2c78a57c70aa8fae Mon Sep 17 00:00:00 2001 From: xiang Date: Sun, 3 May 2026 23:11:50 +0800 Subject: [PATCH 04/32] =?UTF-8?q?feat:glados=E5=92=8Cddns=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/enums/ScheduleEnums.java | 2 +- .../schedule/BaseScheduleTaskTemplate.java | 28 +++- .../xiang/common/mapper/GladosRunLogDao.java | 11 ++ .../xiang/common/mapper/GladosUserDao.java | 17 ++ .../common/mapper/ScheduleRunLogDao.java | 11 ++ .../common/pojo/glados/GladosRunLogDO.java | 47 ++++++ .../common/pojo/glados/GladosUserDO.java | 24 +++ .../pojo/glados/req/GladosCheckInReq.java | 16 ++ .../common/pojo/glados/resp/CheckInResp.java | 20 +++ .../pojo/glados/resp/GLaDOSResponse.java | 21 +++ .../pojo/schedule/ScheduleRunLogDO.java | 22 +++ .../common/pojo/schedule/TaskResult.java | 7 +- .../service/IScheduleRunLogService.java | 7 + .../service/ScheduleRunLogServiceImpl.java | 10 ++ .../schedule/DomainDynamicAnalysisTask.java | 16 +- .../glados/constants/GladosConstants.java | 23 +++ .../glados/manage/GladosRunLogManageImpl.java | 10 ++ .../glados/manage/GladosUserManageImpl.java | 30 ++++ .../glados/manage/IGladosRunLogManage.java | 7 + .../glados/manage/IGladosUserManage.java | 13 ++ .../glados/schedule/GladosCheckInTask.java | 46 ++++++ .../glados/schedule/GladosSchedule.java | 23 +++ .../module/glados/server/GladosServer.java | 27 ++++ .../glados/service/GLaDOSServiceImpl.java | 149 ++++++++++++++++++ .../module/glados/service/IGLaDOSService.java | 23 +++ 25 files changed, 603 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/xiang/common/mapper/GladosRunLogDao.java create mode 100644 src/main/java/com/xiang/common/mapper/GladosUserDao.java create mode 100644 src/main/java/com/xiang/common/mapper/ScheduleRunLogDao.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/GladosRunLogDO.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/GladosUserDO.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/req/GladosCheckInReq.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/resp/CheckInResp.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/resp/GLaDOSResponse.java create mode 100644 src/main/java/com/xiang/common/pojo/schedule/ScheduleRunLogDO.java create mode 100644 src/main/java/com/xiang/common/service/IScheduleRunLogService.java create mode 100644 src/main/java/com/xiang/common/service/ScheduleRunLogServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java create mode 100644 src/main/java/com/xiang/service/module/glados/manage/GladosRunLogManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/glados/manage/GladosUserManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/glados/manage/IGladosRunLogManage.java create mode 100644 src/main/java/com/xiang/service/module/glados/manage/IGladosUserManage.java create mode 100644 src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java create mode 100644 src/main/java/com/xiang/service/module/glados/schedule/GladosSchedule.java create mode 100644 src/main/java/com/xiang/service/module/glados/server/GladosServer.java create mode 100644 src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 7fa8e0e..684c88f 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -8,7 +8,7 @@ import lombok.Getter; public enum ScheduleEnums { DOMAIN_DYNAMIC_ANALYSIS_TASK(4, "domain", "domainDynamicAnalysisTask"), - + GLADOS_CHECK_IN_TASK(0, "glados", "gladosCheckInTask"), ; private final Integer modeleCode; diff --git a/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java index 1ce762c..1d07da0 100644 --- a/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java +++ b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java @@ -1,9 +1,11 @@ package com.xiang.common.factory.schedule; import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; +import com.xiang.common.pojo.schedule.ScheduleRunLogDO; import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.pojo.schedule.ValidationResult; import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,9 +18,11 @@ public abstract class BaseScheduleTaskTemplate { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private final IScheduleOpeningConfigService scheduleOpeningConfigService; + private final IScheduleRunLogService scheduleRunLogService; - protected BaseScheduleTaskTemplate(IScheduleOpeningConfigService scheduleOpeningConfigService) { + protected BaseScheduleTaskTemplate(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService) { this.scheduleOpeningConfigService = scheduleOpeningConfigService; + this.scheduleRunLogService = scheduleRunLogService; } @@ -70,6 +74,11 @@ public abstract class BaseScheduleTaskTemplate { * @return */ protected abstract Integer getModule(); + /** + * 获取模块名称(子类必须实现) + * @return + */ + protected abstract String getModuleName(); /** * 任务校验(子类必须实现) @@ -117,6 +126,14 @@ public abstract class BaseScheduleTaskTemplate { // 可在此扩展:写入数据库、发送通知等 // 例如:saveTaskLog(getTaskName(), "SUCCESS", duration, result.getDetail()); + ScheduleRunLogDO scheduleRunLogDO = new ScheduleRunLogDO(); + scheduleRunLogDO.setModuleName(getModuleName()); + scheduleRunLogDO.setTaskName(getTaskName()); + scheduleRunLogDO.setParams(result.getParams()); + scheduleRunLogDO.setResponse(result.getSummary()); + scheduleRunLogDO.setCreateTime(endTime); + scheduleRunLogDO.setRequestFlag(result.isSuccess() ? 1 : 0); + scheduleRunLogService.save(scheduleRunLogDO); } /** @@ -131,6 +148,15 @@ public abstract class BaseScheduleTaskTemplate { // 可在此扩展:写入数据库、发送告警等 // 例如:saveTaskLog(getTaskName(), "FAILURE", duration, e.getMessage()); + + ScheduleRunLogDO scheduleRunLogDO = new ScheduleRunLogDO(); + scheduleRunLogDO.setModuleName(getModuleName()); + scheduleRunLogDO.setTaskName(getTaskName()); +// scheduleRunLogDO.setParams(); + scheduleRunLogDO.setResponse(e.getMessage()); + scheduleRunLogDO.setCreateTime(endTime); + scheduleRunLogDO.setRequestFlag(0); + scheduleRunLogService.save(scheduleRunLogDO); } /** diff --git a/src/main/java/com/xiang/common/mapper/GladosRunLogDao.java b/src/main/java/com/xiang/common/mapper/GladosRunLogDao.java new file mode 100644 index 0000000..69a7f9a --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/GladosRunLogDao.java @@ -0,0 +1,11 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.glados.GladosRunLogDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface GladosRunLogDao extends BaseMapper { +} diff --git a/src/main/java/com/xiang/common/mapper/GladosUserDao.java b/src/main/java/com/xiang/common/mapper/GladosUserDao.java new file mode 100644 index 0000000..07881d6 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/GladosUserDao.java @@ -0,0 +1,17 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.glados.GladosUserDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + + +/** + * @Author: xiang + * @Date: 2025-05-09 13:49 + */ +@Mapper +@Repository +public interface GladosUserDao extends BaseMapper { + +} diff --git a/src/main/java/com/xiang/common/mapper/ScheduleRunLogDao.java b/src/main/java/com/xiang/common/mapper/ScheduleRunLogDao.java new file mode 100644 index 0000000..8b0b6e6 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ScheduleRunLogDao.java @@ -0,0 +1,11 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.schedule.ScheduleRunLogDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ScheduleRunLogDao extends BaseMapper { +} diff --git a/src/main/java/com/xiang/common/pojo/glados/GladosRunLogDO.java b/src/main/java/com/xiang/common/pojo/glados/GladosRunLogDO.java new file mode 100644 index 0000000..bb4d187 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/GladosRunLogDO.java @@ -0,0 +1,47 @@ +package com.xiang.common.pojo.glados; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2025-05-09 13:56 + */ +@Data +@Builder +public class GladosRunLogDO { + + private static final long serialVersionUID = 1L; + + private Integer id; + + /** + * 用户id + */ + private Integer userId; + /** + * 用户 + */ + private String user; + + /** + * 请求的时间 + */ + private LocalDateTime time; + + /** + * 是否成功(0: 失败 1:成功) + */ + private Integer status; + + + private Integer code; + + /** + * 返回的响应 + */ + private String response; + +} diff --git a/src/main/java/com/xiang/common/pojo/glados/GladosUserDO.java b/src/main/java/com/xiang/common/pojo/glados/GladosUserDO.java new file mode 100644 index 0000000..9684fbf --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/GladosUserDO.java @@ -0,0 +1,24 @@ +package com.xiang.common.pojo.glados; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2025-05-09 13:49 + */ +@Data +@TableName("glados_script_user") +public class GladosUserDO { + + private Integer id; + + private String user; + + private String email; + + private String cookie; + + private Integer status; + +} diff --git a/src/main/java/com/xiang/common/pojo/glados/req/GladosCheckInReq.java b/src/main/java/com/xiang/common/pojo/glados/req/GladosCheckInReq.java new file mode 100644 index 0000000..0e75bff --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/req/GladosCheckInReq.java @@ -0,0 +1,16 @@ +package com.xiang.common.pojo.glados.req; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2025-05-08 15:22 + */ +@Data +public class GladosCheckInReq { + + /** + * cookie + */ + private String cookie; +} diff --git a/src/main/java/com/xiang/common/pojo/glados/resp/CheckInResp.java b/src/main/java/com/xiang/common/pojo/glados/resp/CheckInResp.java new file mode 100644 index 0000000..18aad27 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/resp/CheckInResp.java @@ -0,0 +1,20 @@ +package com.xiang.common.pojo.glados.resp; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2026-01-23 09:21 + */ +@Data +public class CheckInResp { + + private Long id; + private Long userId; + private Long time; + private String asset; + private String business; + private String change; + private String balance; + private String detail; +} diff --git a/src/main/java/com/xiang/common/pojo/glados/resp/GLaDOSResponse.java b/src/main/java/com/xiang/common/pojo/glados/resp/GLaDOSResponse.java new file mode 100644 index 0000000..9fae3eb --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/resp/GLaDOSResponse.java @@ -0,0 +1,21 @@ +package com.xiang.common.pojo.glados.resp; + +import lombok.Data; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-05-08 14:55 + */ +@Data +public class GLaDOSResponse { + + private Long code; + + private Integer points; + + private String message; + + private List list; +} diff --git a/src/main/java/com/xiang/common/pojo/schedule/ScheduleRunLogDO.java b/src/main/java/com/xiang/common/pojo/schedule/ScheduleRunLogDO.java new file mode 100644 index 0000000..f303534 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/schedule/ScheduleRunLogDO.java @@ -0,0 +1,22 @@ +package com.xiang.common.pojo.schedule; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("schedule_run_log") +public class ScheduleRunLogDO { + private Long id; + private String moduleName; + private String taskName; + private String params; + private String response; + private LocalDateTime createTime; + private Integer requestFlag; +} diff --git a/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java b/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java index e603973..cf52a25 100644 --- a/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java +++ b/src/main/java/com/xiang/common/pojo/schedule/TaskResult.java @@ -8,10 +8,15 @@ import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor public class TaskResult { + + private String params; + /** * 是否成功 */ private boolean success; + /** + * 响应结果 + */ private String summary; - private Object detail; } diff --git a/src/main/java/com/xiang/common/service/IScheduleRunLogService.java b/src/main/java/com/xiang/common/service/IScheduleRunLogService.java new file mode 100644 index 0000000..420c001 --- /dev/null +++ b/src/main/java/com/xiang/common/service/IScheduleRunLogService.java @@ -0,0 +1,7 @@ +package com.xiang.common.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.schedule.ScheduleRunLogDO; + +public interface IScheduleRunLogService extends IService { +} diff --git a/src/main/java/com/xiang/common/service/ScheduleRunLogServiceImpl.java b/src/main/java/com/xiang/common/service/ScheduleRunLogServiceImpl.java new file mode 100644 index 0000000..98c31fa --- /dev/null +++ b/src/main/java/com/xiang/common/service/ScheduleRunLogServiceImpl.java @@ -0,0 +1,10 @@ +package com.xiang.common.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ScheduleRunLogDao; +import com.xiang.common.pojo.schedule.ScheduleRunLogDO; +import org.springframework.stereotype.Service; + +@Service +public class ScheduleRunLogServiceImpl extends ServiceImpl implements IScheduleRunLogService { +} diff --git a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java index a5116ea..b41777f 100644 --- a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java +++ b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java @@ -4,6 +4,7 @@ import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; import com.xiang.common.utils.IpUtils; import com.xiang.service.module.domain.service.IDomainService; import lombok.extern.slf4j.Slf4j; @@ -15,8 +16,8 @@ import org.springframework.stereotype.Component; public class DomainDynamicAnalysisTask extends BaseScheduleTaskTemplate { private final IDomainService IDomainService; - public DomainDynamicAnalysisTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IDomainService IDomainService) { - super(scheduleOpeningConfigService); + public DomainDynamicAnalysisTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, IDomainService IDomainService) { + super(scheduleOpeningConfigService, scheduleRunLogService); this.IDomainService = IDomainService; } @@ -25,6 +26,11 @@ public class DomainDynamicAnalysisTask extends BaseScheduleTaskTemplate { return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getModeleCode(); } + @Override + protected String getModuleName() { + return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getModule(); + } + @Override protected String getTaskName() { return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getTaskName(); @@ -44,11 +50,13 @@ public class DomainDynamicAnalysisTask extends BaseScheduleTaskTemplate { IDomainService.dynamicDomainAnalysis(publicIp); } catch (Exception e) { log.error("动态解析公网ip失败, ip:{}", publicIp, e); + taskResult.setSuccess(false); + taskResult.setSummary(e.getMessage()); + return taskResult; } } taskResult.setSuccess(true); - taskResult.setSummary(""); - taskResult.setDetail(null); + taskResult.setSummary("ddns解析成功"); return taskResult; } diff --git a/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java b/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java new file mode 100644 index 0000000..76330c6 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java @@ -0,0 +1,23 @@ +package com.xiang.service.module.glados.constants; + +/** + * @Author: xiang + * @Date: 2025-05-08 15:27 + */ +public class GladosConstants { + + /** + * glados 主域名 + */ + private static final String GLADOS_URL_PREFIX = "https://glados.cloud"; + + /** + * 签到接口 + */ + public static final String GLADOS_CHECK_IN_URL = GLADOS_URL_PREFIX + "/api/user/checkin"; + + /** + * 签到请求体 + */ + public static final String GLADOS_CHECK_IN_BODY = "{\"token\":\"glados.cloud\"}"; +} diff --git a/src/main/java/com/xiang/service/module/glados/manage/GladosRunLogManageImpl.java b/src/main/java/com/xiang/service/module/glados/manage/GladosRunLogManageImpl.java new file mode 100644 index 0000000..86e20a5 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/manage/GladosRunLogManageImpl.java @@ -0,0 +1,10 @@ +package com.xiang.service.module.glados.manage; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.GladosRunLogDao; +import com.xiang.common.pojo.glados.GladosRunLogDO; +import org.springframework.stereotype.Service; + +@Service +public class GladosRunLogManageImpl extends ServiceImpl implements IGladosRunLogManage { +} diff --git a/src/main/java/com/xiang/service/module/glados/manage/GladosUserManageImpl.java b/src/main/java/com/xiang/service/module/glados/manage/GladosUserManageImpl.java new file mode 100644 index 0000000..8d69d6c --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/manage/GladosUserManageImpl.java @@ -0,0 +1,30 @@ +package com.xiang.service.module.glados.manage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.GladosUserDao; +import com.xiang.common.pojo.glados.GladosUserDO; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class GladosUserManageImpl extends ServiceImpl implements IGladosUserManage { + @Override + public List listAllUsers() { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(GladosUserDO::getStatus, 1); + return baseMapper.selectList(lqw); + } + + @Override + public GladosUserDO findByUsername(String username) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(GladosUserDO::getStatus, 1); + lqw.eq(GladosUserDO::getUser, username); + lqw.last("limit 1"); + return baseMapper.selectOne(lqw); + } +} diff --git a/src/main/java/com/xiang/service/module/glados/manage/IGladosRunLogManage.java b/src/main/java/com/xiang/service/module/glados/manage/IGladosRunLogManage.java new file mode 100644 index 0000000..e55f5ee --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/manage/IGladosRunLogManage.java @@ -0,0 +1,7 @@ +package com.xiang.service.module.glados.manage; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.glados.GladosRunLogDO; + +public interface IGladosRunLogManage extends IService { +} diff --git a/src/main/java/com/xiang/service/module/glados/manage/IGladosUserManage.java b/src/main/java/com/xiang/service/module/glados/manage/IGladosUserManage.java new file mode 100644 index 0000000..42c5545 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/manage/IGladosUserManage.java @@ -0,0 +1,13 @@ +package com.xiang.service.module.glados.manage; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.glados.GladosUserDO; + +import java.util.List; + +public interface IGladosUserManage extends IService { + + List listAllUsers(); + + GladosUserDO findByUsername(String username); +} diff --git a/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java b/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java new file mode 100644 index 0000000..60be5e7 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java @@ -0,0 +1,46 @@ +package com.xiang.service.module.glados.schedule; + +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.service.module.glados.service.IGLaDOSService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @Author: xiang + * @Date: 2025-05-08 15:24 + */ +@Component +@Slf4j +public class GladosCheckInTask extends BaseScheduleTaskTemplate { + + private final IGLaDOSService glaDOSService; + + public GladosCheckInTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, IGLaDOSService glaDOSService) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.glaDOSService = glaDOSService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.GLADOS_CHECK_IN_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.GLADOS_CHECK_IN_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.GLADOS_CHECK_IN_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { + return glaDOSService.checkIn(); + } +} diff --git a/src/main/java/com/xiang/service/module/glados/schedule/GladosSchedule.java b/src/main/java/com/xiang/service/module/glados/schedule/GladosSchedule.java new file mode 100644 index 0000000..26755e9 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/schedule/GladosSchedule.java @@ -0,0 +1,23 @@ +package com.xiang.service.module.glados.schedule; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@Component +@Slf4j +@RequiredArgsConstructor +@RestController +public class GladosSchedule { + + private final GladosCheckInTask gladosCheckInJob; + + @Scheduled(cron = "0 0 7 1/1 * ?") + @GetMapping("/test1") + public void checkIn() { + gladosCheckInJob.run(); + } +} diff --git a/src/main/java/com/xiang/service/module/glados/server/GladosServer.java b/src/main/java/com/xiang/service/module/glados/server/GladosServer.java new file mode 100644 index 0000000..b37a400 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/server/GladosServer.java @@ -0,0 +1,27 @@ +package com.xiang.service.module.glados.server; + +import com.xiang.service.module.glados.service.IGLaDOSService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("/script/glados") +@RequiredArgsConstructor +public class GladosServer { + + private final IGLaDOSService gladosService; + + @GetMapping("/checkIn") + public void checkIn(@RequestParam("username") String username) { + try { + gladosService.checkIn(username); + } catch (Exception e) { + log.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java b/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java new file mode 100644 index 0000000..fca9fdd --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java @@ -0,0 +1,149 @@ +package com.xiang.service.module.glados.service; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Maps; +import com.xiang.common.factory.ScriptDingTalkFactory; +import com.xiang.common.pojo.glados.GladosRunLogDO; +import com.xiang.common.pojo.glados.GladosUserDO; +import com.xiang.common.pojo.glados.resp.CheckInResp; +import com.xiang.common.pojo.glados.resp.GLaDOSResponse; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.utils.HttpService; +import com.xiang.service.module.glados.constants.GladosConstants; +import com.xiang.service.module.glados.manage.IGladosRunLogManage; +import com.xiang.service.module.glados.manage.IGladosUserManage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @Author: xiang + * @Date: 2025-05-08 14:38 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class GLaDOSServiceImpl implements IGLaDOSService { + + private final IGladosUserManage gladosUserManage; + + private final ScriptDingTalkFactory dingTalkService; + + + @Override + public TaskResult checkIn() { + TaskResult taskResult = new TaskResult(); + + + List users = gladosUserManage.listAllUsers(); + if (CollectionUtils.isEmpty(users)) { + taskResult.setSuccess(false); + taskResult.setSummary("无可使用的用户"); + return taskResult; + } + StringBuilder sb = new StringBuilder(); + for (GladosUserDO user : users) { + try { + for (int i = 0; i < 3; i++) { + if (checkInV2(user, sb)) { + break; + } + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + log.error("线程暂停10s失败"); + } + } + } catch (Exception e) { + log.error("签到失败,", e); + } + } + if (StringUtils.isNotBlank(sb.toString())) { + taskResult.setSuccess(false); + taskResult.setSummary(sb.toString()); + return taskResult; + } + taskResult.setSuccess(true); + return taskResult; + } + + @Override + public void checkIn(String username) { + GladosUserDO gladosUserDO = gladosUserManage.findByUsername(username); + if (Objects.isNull(gladosUserDO)) { + log.info("[job] Glados Check In user not exists, username:{}", username); + return; + } + checkInV2(gladosUserDO, new StringBuilder()); + } + + public boolean checkInV2(GladosUserDO user, StringBuilder sb) { + Map header = Maps.newHashMap(); + header.put("Cookie", user.getCookie()); + + String response = null; + + try { + response = HttpService.doPost(GladosConstants.GLADOS_CHECK_IN_URL, header, GladosConstants.GLADOS_CHECK_IN_BODY); + } catch (Exception e) { + log.error("http请求异常:{}", user.getEmail()); + return false; + } + if (org.apache.commons.lang3.StringUtils.isBlank(response)) { + sb.append(user.getUser()).append("请求结果为空!"); + return false; + } + + GLaDOSResponse gLaDOSResponse = JSONObject.parseObject(response, new TypeReference>() { + }); + if (Objects.isNull(gLaDOSResponse)) { + sb.append(user.getUser()).append("请求结果为空!"); + return false; + } + + if (0 == gLaDOSResponse.getCode()) { + // 成功请求 + if (Objects.nonNull(gLaDOSResponse.getPoints()) && 0 != gLaDOSResponse.getPoints()) { + // 签到成功 + dingTalkService.sendMsg("[时间:" + LocalDateTime.now() + "] 用户: " + + user.getEmail() + "签到成功,获得积分:" + gLaDOSResponse.getPoints()); + return Boolean.TRUE; + } + } + + if (gLaDOSResponse.getMessage().contains("Today's observation logged")) { + if (!CollectionUtils.isEmpty(gLaDOSResponse.getList())) { + dingTalkService.sendMsg("用户:" + user.getEmail() + "当前已签到。结果:" + gLaDOSResponse.getList().get(0)); + sb.append(user.getUser()).append("当前已经签到!"); + return true; + } + } + + if (-2 == gLaDOSResponse.getCode()) { + log.warn("签到失败,用户:{}, cookie过期:{}", user.getEmail(), gLaDOSResponse.getMessage()); + String message = "[时间:" + LocalDateTime.now() + "] 用户: " + user.getEmail() + ",签到消息: " + + gLaDOSResponse.getMessage() + "手动请求:http://general.xiangtech.xyz:30026/script/glados/checkIn?username=" + user.getUser(); + try { + dingTalkService.sendMsg(message); + sb.append(user.getUser()).append("签到结果异常!结果:").append(gLaDOSResponse.getMessage()); + return true; + } catch (Exception e) { + log.error("发送钉钉消息失败", e); + } + } + + // 请求异常 + dingTalkService.sendMsg("用户:" + user.getEmail() + "请求异常,响应结果:" + gLaDOSResponse.getMessage()); + sb.append(user.getUser()).append("响应结果异常!结果:").append(gLaDOSResponse.getMessage()); + return false; + } +} diff --git a/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java b/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java new file mode 100644 index 0000000..7dbe626 --- /dev/null +++ b/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java @@ -0,0 +1,23 @@ +package com.xiang.service.module.glados.service; + +import com.xiang.common.pojo.glados.GladosUserDO; +import com.xiang.common.pojo.schedule.TaskResult; + +/** + * @Author: xiang + * @Date: 2025-05-08 14:38 + */ +public interface IGLaDOSService { + + + /** + * 签到 + */ + TaskResult checkIn(); + + /** + * 根据用户名签到 + * @param username + */ + void checkIn(String username); +} -- 2.49.1 From d55142782145290a52aa071a1ef71a2c66e4e24f Mon Sep 17 00:00:00 2001 From: xiang Date: Sun, 3 May 2026 23:43:37 +0800 Subject: [PATCH 05/32] =?UTF-8?q?feat:zlb=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 15 + .../xiang/common/enums/DateFormatEnum.java | 38 ++ .../common/factory/JntyzxDingTalkFactory.java | 19 + .../xiang/common/handler/CallbackHandler.java | 6 + .../common/mapper/ZlbJlUserInfoMapper.java | 17 + .../common/mapper/ZlbLoginInfoMapper.java | 17 + .../common/mapper/ZlbOrderInfoMapper.java | 16 + .../common/mapper/ZlbSiteInfoMapper.java | 16 + .../common/mapper/ZlbTokenInfoMapper.java | 16 + .../common/mapper/ZlbUserInfoMapper.java | 16 + .../common/pojo/jntyzx/zlb/ZlbJlUserInfo.java | 55 ++ .../common/pojo/jntyzx/zlb/ZlbLoginInfo.java | 82 +++ .../common/pojo/jntyzx/zlb/ZlbOrderInfo.java | 35 + .../common/pojo/jntyzx/zlb/ZlbOrderJson.java | 56 ++ .../pojo/jntyzx/zlb/ZlbOrderWqInfo.java | 14 + .../pojo/jntyzx/zlb/ZlbOrderWqJson.java | 61 ++ .../common/pojo/jntyzx/zlb/ZlbPayOrder.java | 50 ++ .../common/pojo/jntyzx/zlb/ZlbSiteInfo.java | 139 ++++ .../pojo/jntyzx/zlb/ZlbSiteRequest.java | 18 + .../common/pojo/jntyzx/zlb/ZlbTokenInfo.java | 41 ++ .../common/pojo/jntyzx/zlb/ZlbUserInfo.java | 50 ++ .../com/xiang/common/utils/AESECBUtils.java | 86 +++ .../com/xiang/common/utils/DateUtils.java | 606 ++++++++++++++++++ .../com/xiang/common/utils/OkHttpUtil.java | 169 +++++ .../jntyzx/zlb/constants/ZlbUrlConstants.java | 49 ++ .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 4 + .../module/jntyzx/zlb/service/ZlbService.java | 43 ++ .../jntyzx/zlb/service/ZlbServiceImpl.java | 600 +++++++++++++++++ .../zlb/service/ZlbSiteInfoService.java | 13 + .../zlb/service/ZlbSiteInfoServiceImpl.java | 21 + .../zlb/service/ZlbTokenInfoService.java | 14 + .../zlb/service/ZlbTokenInfoServiceImpl.java | 29 + .../zlb/service/ZlbUserInfoService.java | 8 + .../zlb/service/ZlbUserInfoServiceImpl.java | 16 + 34 files changed, 2435 insertions(+) create mode 100644 src/main/java/com/xiang/common/enums/DateFormatEnum.java create mode 100644 src/main/java/com/xiang/common/factory/JntyzxDingTalkFactory.java create mode 100644 src/main/java/com/xiang/common/handler/CallbackHandler.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbJlUserInfoMapper.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbLoginInfoMapper.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbOrderInfoMapper.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbSiteInfoMapper.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbTokenInfoMapper.java create mode 100644 src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbJlUserInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbLoginInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderJson.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqJson.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbPayOrder.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteRequest.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java create mode 100644 src/main/java/com/xiang/common/utils/AESECBUtils.java create mode 100644 src/main/java/com/xiang/common/utils/DateUtils.java create mode 100644 src/main/java/com/xiang/common/utils/OkHttpUtil.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java diff --git a/pom.xml b/pom.xml index a65e6bc..365a067 100644 --- a/pom.xml +++ b/pom.xml @@ -104,6 +104,11 @@ mapstruct 1.5.5.Final + + cn.hutool + hutool-all + 5.8.9 + @@ -111,6 +116,11 @@ httpclient 4.5.13 + + com.squareup.okhttp3 + okhttp + 4.12.0 + com.google.guava @@ -129,6 +139,11 @@ + + com.alibaba + fastjson + 1.2.83 + com.alibaba.fastjson2 fastjson2 diff --git a/src/main/java/com/xiang/common/enums/DateFormatEnum.java b/src/main/java/com/xiang/common/enums/DateFormatEnum.java new file mode 100644 index 0000000..d157410 --- /dev/null +++ b/src/main/java/com/xiang/common/enums/DateFormatEnum.java @@ -0,0 +1,38 @@ +package com.xiang.common.enums; + +/** + * @author clover + * @Date 2020/9/15 20:11 + */ +public enum DateFormatEnum { + COMMON("ECS_DISK", "yyyy-MM-dd'T'HH:mm:ss'Z'"), + COMMON1("ENUM_FORMAT", "yyyy-MM-dd HH:mm:ss"), + ENUM_FORMAT_OTS("ENUM_FORMAT_OTS", "yyyy-MM-ddHH:mm:ss"), + ASCM_ONE_FORMAT("ASCM_ONE_FORMAT", "yyyy-MM-dd'T'HH:mm'Z'"), + ASCM_TWO_FORMAT("ASCM_TWO_FORMAT", "yyyy-MM-dd'T'HH:mm:ss'Z'"), + ASCM_THREE_FORMAT("ASCM_THREE_FORMAT", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"), + ASCM_FOUR_FORMAT("ASCM_FOUR_FORMAT", "yyyy-MM-dd'T'HH:mm:ss+08:00"), + ASCM_FIVE_FORMAT("ASCM_FIVE_FORMAT", "yyyy-MM-dd'T'HH:mm:ss"), + IRS_FORMAT_YMD("IRS_FORMAT_YMD", "yyyyMMdd"), + IRS_FORMAT_YM("IRS_FORMAT_YM", "yyyyMM"), + ENUM_FORMAT_YMD("ENUM_FORMAT_YMD", "yyyy-MM-dd"), + ENUM_FORMAT_YMD000("ENUM_FORMAT_YMD", "yyyy-MM-dd 00:00:00"), + ENUM_FORMAT_YMD_CSB("ENUM_FORMAT_YMD_CSB", "yyyy-MM-dd/00"); + + private String key; + private String value; + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + DateFormatEnum(String key, String value) { + this.key = key; + this.value = value; + } + +} diff --git a/src/main/java/com/xiang/common/factory/JntyzxDingTalkFactory.java b/src/main/java/com/xiang/common/factory/JntyzxDingTalkFactory.java new file mode 100644 index 0000000..e67c3ac --- /dev/null +++ b/src/main/java/com/xiang/common/factory/JntyzxDingTalkFactory.java @@ -0,0 +1,19 @@ +package com.xiang.common.factory; + +import com.xiang.common.config.DingTalkRobotProperties; +import com.xiang.common.enums.DingTalkBizTypeEnum; +import com.xiang.common.utils.dingTalk.AbstractDingTalkFactory; +import com.xiang.common.utils.dingTalk.DingTalkSender; +import org.springframework.stereotype.Service; + +@Service +public class JntyzxDingTalkFactory extends AbstractDingTalkFactory { + public JntyzxDingTalkFactory(DingTalkRobotProperties dingTalkRobotProperties, DingTalkSender dingTalkSender) { + super(dingTalkRobotProperties, dingTalkSender); + } + + @Override + public void sendMsg(String msg) { + getClient(DingTalkBizTypeEnum.JT).sendDingTalkMsg(msg); + } +} diff --git a/src/main/java/com/xiang/common/handler/CallbackHandler.java b/src/main/java/com/xiang/common/handler/CallbackHandler.java new file mode 100644 index 0000000..ec4b76d --- /dev/null +++ b/src/main/java/com/xiang/common/handler/CallbackHandler.java @@ -0,0 +1,6 @@ +package com.xiang.common.handler; + +public interface CallbackHandler { + void onResponse(String response); + void onFailure(Throwable t); +} diff --git a/src/main/java/com/xiang/common/mapper/ZlbJlUserInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbJlUserInfoMapper.java new file mode 100644 index 0000000..e779a99 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbJlUserInfoMapper.java @@ -0,0 +1,17 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbJlUserInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + + +@Mapper +@Repository +public interface ZlbJlUserInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/mapper/ZlbLoginInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbLoginInfoMapper.java new file mode 100644 index 0000000..144dadd --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbLoginInfoMapper.java @@ -0,0 +1,17 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbLoginInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + + +@Mapper +@Repository +public interface ZlbLoginInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/mapper/ZlbOrderInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbOrderInfoMapper.java new file mode 100644 index 0000000..5c2b242 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbOrderInfoMapper.java @@ -0,0 +1,16 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbPayOrder; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ZlbOrderInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/mapper/ZlbSiteInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbSiteInfoMapper.java new file mode 100644 index 0000000..1e68cb3 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbSiteInfoMapper.java @@ -0,0 +1,16 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ZlbSiteInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/mapper/ZlbTokenInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbTokenInfoMapper.java new file mode 100644 index 0000000..4c185c8 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbTokenInfoMapper.java @@ -0,0 +1,16 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface ZlbTokenInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java new file mode 100644 index 0000000..cc38bae --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java @@ -0,0 +1,16 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface ZlbUserInfoMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbJlUserInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbJlUserInfo.java new file mode 100644 index 0000000..a5316df --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbJlUserInfo.java @@ -0,0 +1,55 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * + * @TableName zlb_jl_user_info + */ +@TableName(value ="zlb_jl_user_info") +@Data +public class ZlbJlUserInfo { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * 星期几 + */ + private String week; + + /** + * 日期 + */ + private String day; + + /** + * token + */ + private String token; + + /** + * secretKey + */ + private String secretKey; + + /** + * 场地信息 + */ + private String placeName; + + /** + * 时间id111 + */ + private String siteTimeName; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbLoginInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbLoginInfo.java new file mode 100644 index 0000000..90d935f --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbLoginInfo.java @@ -0,0 +1,82 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * + * @TableName zlb_login_info + */ +@TableName(value ="zlb_login_info") +@Data +public class ZlbLoginInfo { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * 11 + */ + private String cookie; + + /** + * 22 + */ + private String bizSessionId; + + /** + * 33 + */ + private String xDeviceId; + + /** + * 44 + */ + private String xSignValue; + + /** + * 55 + */ + private String token; + + /** + * 66 + */ + private String gucGsid; + + /** + * 77 + */ + private String xSiteCode; + + /** + * 77 + */ + private String aliyungfTc; + + /** + * 是否失效0-未失效,1-失效 + */ + private Integer isDel; + + /** + * 数据创建时间 + */ + private Date createdDate; + + /** + * 数据修改时间 + */ + private Date updatedDate; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java new file mode 100644 index 0000000..0fa2ce7 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java @@ -0,0 +1,35 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import lombok.Data; + +import java.util.List; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-18 00:52:44 + * @Description:*** + */ +@Data +public class ZlbOrderInfo { + private String id; + private ZlbData data; + + @Data + public static class ZlbData { + private Integer bgImageWidth; + private Integer bgImageHeight; + private String startTime; + private String stopTime; + private List trackList; + + @Data + public static class TrackList { + private Integer x; + private Integer y; + private Integer t; + private String type; + } + } + private String siteOrderDetailsStr; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderJson.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderJson.java new file mode 100644 index 0000000..6b62659 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderJson.java @@ -0,0 +1,56 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-22 19:49:46 + * @Description:*** + */ +@Data +public class ZlbOrderJson { + @JsonProperty("issueIds") + @JSONField(ordinal=1) + private String issueIds; + @JsonProperty("issueAmount") + @JSONField(ordinal=2) + private Integer issueAmount; + @JsonProperty("cardOrderId") + @JSONField(ordinal=3) + private String cardOrderId; + + + @JsonProperty("amount") + @JSONField(ordinal=4) + private Integer amount; + @JsonProperty("belongDate") + @JSONField(ordinal=5) + private String belongDate; + @JsonProperty("dayEffectiveTimes") + @JSONField(ordinal=6) + private String dayEffectiveTimes; + @JsonProperty("dayOverdueTimes") + @JSONField(ordinal=7) + private String dayOverdueTimes; + @JsonProperty("placeName") + @JSONField(ordinal=8) + private String placeName; + @JsonProperty("siteId") + @JSONField(ordinal=9) + private String siteId; + @JsonProperty("siteItemId") + @JSONField(ordinal=10) + private Integer siteItemId; + @JsonProperty("siteRuleId") + @JSONField(ordinal=11) + private String siteRuleId; + @JsonProperty("siteTicketId") + @JSONField(ordinal=12) + private Integer siteTicketId; + @JsonProperty("payChannel") + @JSONField(ordinal=13) + private Integer payChannel; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqInfo.java new file mode 100644 index 0000000..d525bf9 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqInfo.java @@ -0,0 +1,14 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import lombok.Data; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-18 00:52:44 + * @Description:*** + */ +@Data +public class ZlbOrderWqInfo { + private String siteOrderDetailsStr; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqJson.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqJson.java new file mode 100644 index 0000000..570916a --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderWqJson.java @@ -0,0 +1,61 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-22 19:49:46 + * @Description:*** + */ +@Data +public class ZlbOrderWqJson { + @JsonProperty("issueIds") + @JSONField(ordinal=1) + private String issueIds; + @JsonProperty("issueAmount") + @JSONField(ordinal=2) + private Integer issueAmount; + @JsonProperty("cardOrderId") + @JSONField(ordinal=3) + private String cardOrderId; + + + @JsonProperty("amount") + @JSONField(ordinal=4) + private Integer amount; + @JsonProperty("belongDate") + @JSONField(ordinal=5) + private String belongDate; + @JsonProperty("dayEffectiveTimes") + @JSONField(ordinal=6) + private String dayEffectiveTimes; + @JsonProperty("dayOverdueTimes") + @JSONField(ordinal=7) + private String dayOverdueTimes; + @JsonProperty("placeName") + @JSONField(ordinal=8) + private String placeName; + + @JsonProperty("saasTicketId") + @JSONField(ordinal=9) + private String saasTicketId; + + @JsonProperty("siteId") + @JSONField(ordinal=10) + private String siteId; + @JsonProperty("siteItemId") + @JSONField(ordinal=11) + private Integer siteItemId; + @JsonProperty("siteRuleId") + @JSONField(ordinal=12) + private String siteRuleId; + @JsonProperty("siteTicketId") + @JSONField(ordinal=13) + private Integer siteTicketId; + @JsonProperty("payChannel") + @JSONField(ordinal=14) + private Integer payChannel; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbPayOrder.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbPayOrder.java new file mode 100644 index 0000000..bdf54a8 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbPayOrder.java @@ -0,0 +1,50 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * + * @TableName zlb_order_info + */ +@TableName(value ="zlb_order_info") +@Data +public class ZlbPayOrder { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 姓名 + */ + private String name; + + /** + * 日期 + */ + private String day; + + /** + * 场馆 + */ + private String venues; + + /** + * 场地名称 + */ + private String placeName; + + /** + * 时间id111 + */ + private String time; + + /** + * 0-未付款,1-已付款 + */ + private Integer isPay; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteInfo.java new file mode 100644 index 0000000..95e2121 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteInfo.java @@ -0,0 +1,139 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * + * @TableName zlb_site_info + */ +@TableName(value ="zlb_site_info") +@Data +public class ZlbSiteInfo { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 场地Id + */ + private Integer type; + private Integer siteTicketId; + + /** + * 场地名称 + */ + private String siteTicketName; + private String saasTicketId; + + /** + * 金额 + */ + private String siteRuleId; + + /** + * 金额 + */ + private String siteId; + + /** + * 时间顺序id + */ + private String placeName; + + /** + * 票id + */ + private Integer weekType; + + /** + * 票id + */ + private Integer blocId; + + /** + * 票id + */ + private Integer stadiumId; + + /** + * 会员号 + */ + private String stadiumName; + + /** + * 票id + */ + private Integer siteItemId; + + /** + * 订场人信息 + */ + private String spName; + + /** + * 订场人电话 + */ + private String url; + + /** + * 票id + */ + private Integer startCheckMinutes; + + /** + * 票id + */ + private Integer endCheckMinutes; + + /** + * 订场时间 + */ + private String dayEffectiveTimes; + + /** + * 场地票名 + */ + private String dayOverdueTimes; + + /** + * 票id + */ + private Integer amount; + + /** + * 票id + */ + private Integer ticketType; + + /** + * 场地票名 + */ + private String belongDate; + + /** + * 场地票名 + */ + private String isOpen; + + /** + * 票id + */ + private Integer isPreferential; + + /** + * 数据创建时间 + */ + private Date createdDate; + + /** + * 数据修改时间 + */ + private Date updatedDate; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteRequest.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteRequest.java new file mode 100644 index 0000000..391cdbf --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbSiteRequest.java @@ -0,0 +1,18 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import lombok.Data; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-19 23:53:44 + * @Description:*** + */ +@Data +public class ZlbSiteRequest { + + private String dataStr; + private String stadiumId; + private Integer siteItemId; + +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java new file mode 100644 index 0000000..99000df --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java @@ -0,0 +1,41 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * + * @TableName zlb_token_info + */ +@TableName(value ="zlb_token_info") +@Data +public class ZlbTokenInfo { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * tokenId + */ + private String tokenId; + private String zlbUserId; + private String secretKey; + + /** + * 是否失效0-未失效,1-失效 + */ + private Integer isDel; + private Date createdDate; + private Date updatedDate; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java new file mode 100644 index 0000000..59b54c3 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java @@ -0,0 +1,50 @@ +package com.xiang.common.pojo.jntyzx.zlb; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * + * @TableName zlb_user_info + */ +@TableName(value ="zlb_user_info") +@Data +public class ZlbUserInfo { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * 星期几 + */ + private String week; + + /** + * 分配的任务参数 + */ + private String type; + + /** + * 场地信息 + */ + private String placeName; + + /** + * 时间id111 + */ + private String siteTimeName; + + /** + * 是否开抢0-抢,1-不抢 + */ + private Integer isBook; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/utils/AESECBUtils.java b/src/main/java/com/xiang/common/utils/AESECBUtils.java new file mode 100644 index 0000000..1ed916d --- /dev/null +++ b/src/main/java/com/xiang/common/utils/AESECBUtils.java @@ -0,0 +1,86 @@ +package com.xiang.common.utils; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; + +public class AESECBUtils { + // 填充方式(PKCS5Padding) + private static final String ALGORITHM = "AES/ECB/PKCS5Padding"; + + /** + * AES ECB 模式加密 + * + * @param plainText 明文字符串 + * @param key 密钥(16 / 24 / 32 字节) + * @return 十六进制格式的密文 + */ + public static String encrypt(String plainText, String key) throws Exception { + Cipher cipher = Cipher.getInstance(ALGORITHM); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); + cipher.init(Cipher.ENCRYPT_MODE, keySpec); + + byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + return bytesToHex(encrypted).toUpperCase(); + } + + /** + * AES ECB 模式解密 + * + * @param hexCipherText 十六进制格式的密文 + * @param key 密钥(16 / 24 / 32 字节) + * @return 解密后的明文 + */ + public static String decrypt(String hexCipherText, String key) throws Exception { + Cipher cipher = Cipher.getInstance(ALGORITHM); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); + cipher.init(Cipher.DECRYPT_MODE, keySpec); + + byte[] cipherData = hexStringToByteArray(hexCipherText); + byte[] decrypted = cipher.doFinal(cipherData); + return new String(decrypted, StandardCharsets.UTF_8); + } + + // 十六进制字符串转 byte[] + private static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + // byte[] 转十六进制字符串 + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + // 示例主函数 + public static void main(String[] args) { + String key = "1ad2ee1b6e3f4e81"; // 16字节密钥 + // String key = "3f18655f909d495d"; // 16字节密钥 + // String key = "ea08f7da82e44df7"; // 16字节密钥 + // 取前16位数密钥 + //String key = keystr.substring(0,16); + + String longCiphertext = "8A559F1F4A8A782169E6F5BC32217A8CAA263E173FA2C05751CFC49B9018D57779F5B7C47B8C1570142EAB5A781175DE45AD141C3CC8E62380EA6C0036A2D80BF219B326C2302553E43B0F534A51D18D"; + + try { + String decrypted = decrypt(longCiphertext, key); + System.out.println("长密文解密结果: " + decrypted); + decrypted = "{\"stadiumId\":\"49\",\"siteItemId\":1940,\"belongDate\":\"2025-10-10\",\"channelType\":6}"; + // decrypted = " {\"stadiumId\":\"360112\",\"siteItemId\":1148,\"belongDate\":\"2025-08-11\",\"channelType\":6}"; + String reEncrypted = encrypt(decrypted, key); + System.out.println("重新加密结果: " + reEncrypted); + } catch (Exception e) { + System.err.println("长密文解密失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/xiang/common/utils/DateUtils.java b/src/main/java/com/xiang/common/utils/DateUtils.java new file mode 100644 index 0000000..4a2b3df --- /dev/null +++ b/src/main/java/com/xiang/common/utils/DateUtils.java @@ -0,0 +1,606 @@ +package com.xiang.common.utils; + + +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.xiang.common.enums.DateFormatEnum; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * @author clover + * @Date 2020/9/15 09:31 + */ +public class DateUtils { + + + /** + * 构造函数. + */ + public void DateUtil() { + throw new RuntimeException("this is a util class,can not instance!"); + } + + /** + * 添加字段注释. + */ + public static final String ENUM_FORMAT = "yyyy-MM-dd HH:mm:ss"; + /** + * 添加字段注释. + */ + public static final String ASCM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + /** + * 添加字段注释. + */ + public static final String ENUM_FORMAT_YMD = "yyyy-MM-dd"; + public static final String ENUM_FORMAT_YMD_1 = "yyyyMMdd"; + + /** + * 添加字段注释. + */ + public static final String ENUM_FORMAT_YMDS = "yyyy-MM-dd HH:mm:ss.S"; + + /** + * 添加字段注释. + */ + public static final String ENUM_FORMAT_SLASH = "yyyy/MM/dd HH:mm:ss"; + + /** + * 添加字段注释. + */ + public static final String ENUM_FORMAT_YMDS_SLASH = "yyyy/MM/dd HH:mm:ss.S"; + + /** + * 添加字段注释. + */ + public static final String LEVEL_DAY = "day"; // 粒度级别 + + /** + * 添加字段注释. + */ + public static final String LEVEL_HOUR = "hour"; + + /** + * 添加字段注释. + */ + public static final String LEVEL_MINUTE = "minute"; + + /** + * 添加字段注释. + */ + public static final String LEVEL_SECOND = "second"; + + /** + * 日期特殊字符对应. + */ + private static Map mapSign = new HashMap<>(); + + /** + * 使用ThreadLocal保证SimpleDateFormat线程安全. + */ + private static ThreadLocal> threadLocalDateFormat = new ThreadLocal<>(); + + /** + * 初始化DateFormat标志位. + */ + private static void initMapSign() { + if (mapSign.isEmpty()) { + mapSign.put("上午|下午", "a"); + mapSign.put("星期[一二三四五六日天七]", "E"); + mapSign.put("CST", "z"); + mapSign.put("公元[前]?", "G"); + } + } + + public static int getWeekDay() { + Calendar calendar = Calendar.getInstance(); + return calendar.get(Calendar.DAY_OF_WEEK); + } + + public static void main(String[] args) { + Date date = DateUtils.addDate(new Date(), 6); + String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD_1); + System.out.println(day); + System.out.println(getWeekDay(day)); + } + + public static String getWeekDay(String bookTime) {//20250101 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + // 解析输入日期 + LocalDate date = LocalDate.parse(bookTime, formatter); + // 获取星期几 + DayOfWeek dayOfWeek = date.getDayOfWeek(); + // 将星期几转换为中文 + return convertToChinese(dayOfWeek); + } + + public static String getWeekDayTwo(String bookTime) {//20250101 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + // 解析输入日期 + LocalDate date = LocalDate.parse(bookTime, formatter); + // 获取星期几 + DayOfWeek dayOfWeek = date.getDayOfWeek(); + // 将星期几转换为中文 + return convertToChinese(dayOfWeek); + } + + private static String convertToChinese(DayOfWeek dayOfWeek) { + switch (dayOfWeek) { + case MONDAY: + return "星期一"; + case TUESDAY: + return "星期二"; + case WEDNESDAY: + return "星期三"; + case THURSDAY: + return "星期四"; + case FRIDAY: + return "星期五"; + case SATURDAY: + return "星期六"; + case SUNDAY: + return "星期日"; + default: + return "未知"; + } + } + + public static String getBookTime() { + Date date = DateUtils.addDate(new Date(), 1); + return DateUtils.formatYMd(date); + } + + /** + * 获取当前时间的毫秒数 + * + * @return 当前时间 + */ + public static long getTimeInMillis() { + Date now = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(now); + return calendar.getTimeInMillis(); + } + + /** + * 常规日期格式yyyy-MM-dd HH:mm:ss. + * + * @param date date + * @return time + */ + public static String format(Date date) { + return getDateFormat(ENUM_FORMAT).format(date); + } + + public static String format(Long timestamp) { + return getDateFormat(ENUM_FORMAT).format(timestamp); + } + + /** + * 常规日期格式yyyy-MM-dd. + * + * @param date date + * @return time + */ + public static String formatYMd(Date date) { + return getDateFormat(ENUM_FORMAT_YMD).format(date); + } + + /** + * 格式化时间. + * + * @param date date + * @param dateFormat dateFormat + * @return time + */ + public static String format(Date date, String dateFormat) { + if (null == date) { + return null; + } + return getDateFormat(dateFormat).format(date); + } + + /** + * parse时间(yyyy-MM-dd HH:mm:ss). + * + * @param source source + * @return Date + * @throws ParseException ParseException + */ + public static Date parse(String source) throws ParseException { + return getDateFormat(DateFormatEnum.COMMON1.getValue()).parse(source); + } + + /** + * parse时间(yyyy-MM-dd HH:mm:ss). + * + * @param source source + * @return Date + * @throws ParseException ParseException + */ + public static Date parseAscm(String source) throws ParseException { + return getDateFormat(ASCM_FORMAT).parse(source); + } + + /** + * parse时间(yyyy-MM-dd). + * + * @param source source + * @return Date + * @throws ParseException ParseException + */ + public static Date parseYMd(String source) throws ParseException { + return getDateFormat(ENUM_FORMAT_YMD).parse(source); + } + + /** + * 格式化时间. + * + * @param time time + * @param dateFormat dateFormat + * @return Date + * @throws ParseException ParseException + */ + public static Date parse(String time, String dateFormat) throws ParseException { + if (isNullOrEmpty(time) || isNullOrEmpty(dateFormat)) { + return null; + } + return getDateFormat(dateFormat).parse(time); + } + + public static Date parse(String time, String dateFormat, String timeZone) throws ParseException { + if (isNullOrEmpty(time) || isNullOrEmpty(dateFormat)) { + return null; + } + return getDateFormat(dateFormat, timeZone).parse(time); + } + + /** + * 获取指定时间格式的 SimpleDateFormat. + * + * @param pattern 时间格式 + * @return SimpleDateFormat + */ + public static SimpleDateFormat getDateFormat(String pattern) { + Map dateFormatMap = threadLocalDateFormat.get(); + if (dateFormatMap == null) { + dateFormatMap = new HashMap<>(); + } + SimpleDateFormat simpleDateFormat = dateFormatMap.get(pattern); + if (simpleDateFormat == null) { + simpleDateFormat = new SimpleDateFormat(pattern, Locale.getDefault()); + dateFormatMap.put(pattern, simpleDateFormat); + threadLocalDateFormat.set(dateFormatMap); + } + return simpleDateFormat; + } + + private static SimpleDateFormat getDateFormat(String pattern, String timeZone) { + SimpleDateFormat dateFormat = getDateFormat(pattern); + if (StringUtils.isNotBlank(timeZone)) { + dateFormat.setTimeZone(TimeZone.getTimeZone(timeZone)); + } + return dateFormat; + } + + /** + * 自动解析时间格式并parse(时间格式为yyyyMMddHHmmssS,默认24小时制、前包含且必须包含yyyyMMdd). + * + * @param time time + * @return Date + * @throws ParseException ParseException + */ + public static Date parseAuto(String time) throws ParseException { + if (isNullOrEmpty(time) || time.length() < 8) { + return null; + } + initMapSign(); + time = time.trim(); + String formatPattern = ""; + if (time.matches("[\\d]+")) { // 纯数字 + String all = "yyyyMMddHHmmssSSS"; + if (time.length() > all.length()) { // 超长截取 + time = time.substring(0, all.length()); + } + formatPattern = all.substring(0, time.length()); + } else { + char next = 'y'; + String idNext = "yMdHmsS"; + StringBuilder buffer = new StringBuilder(); + for (char var : time.toCharArray()) { + if (String.valueOf(var).matches("[0-9]")) { + buffer.append(next); + } else if ("T".equals(String.valueOf(var))) { + buffer.append("'").append(var).append("'"); + } else { + buffer.append(var); + next = idNext.charAt(Math.min(idNext.indexOf(next) + 1, idNext.length() - 1)); + } + } + formatPattern = buffer.toString(); + } + for (Map.Entry entry : mapSign.entrySet()) { + formatPattern = formatPattern.replaceAll(entry.getKey(), entry.getValue()); + } + return parse(time, formatPattern); + } + + /** + * 是否为空或"". + * + * @param param param + * @return boolean + */ + private static boolean isNullOrEmpty(String param) { + return null == param || "".equals(param.trim()); + } + + /** + * 日期增加num天. + * + * @param date date + * @param num 加减天数 + * @return Date + */ + public static Date addDate(Date date, int num) { + return addDate(date, Calendar.DATE, num); + } + + /** + * 时间增加. + * + * @param date date + * @param calendar 加减级别Calendar + * @param num 加减天数 + * @return Date + */ + public static Date addDate(Date date, int calendar, int num) { + if (null == date || 0 == num) { + return date; + } + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(calendar, num); + return cal.getTime(); + } + + /** + * 保留日期到某一级别(天、时、分、秒...). + * + * @param date date + * @param level 保留级别,null保留到day + * @return date + */ + public static Date setDate(Date date, String level) { + if (null == date || null == level) { + return date; + } + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + switch (level) { + case LEVEL_DAY: // 保留到 Day + // cal.set(Calendar.HOUR, 0); // 12小时制 + cal.set(Calendar.HOUR_OF_DAY, 0); // 24小时制 + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + break; + case LEVEL_HOUR: // 保留到 Hour + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + break; + case LEVEL_MINUTE: // 保留到 MINUTE + cal.set(Calendar.SECOND, 0); + break; + case LEVEL_SECOND: // 保留到 SECOND + cal.set(Calendar.MILLISECOND, 0); + break; + default: + break; + } + return cal.getTime(); + } + + /** + * 比较两个日期的间隔(时间差绝对值,向下取整)(day、hour、minute). + * + * @param date1 date1 + * @param date2 date2 + * @param level 比较级别 + * @return int 无对应时间间隔级别 + */ + public static Integer getDateInterval(Date date1, Date date2, String level) { + Double num = dateInterval(date1, date2, level); + if (null == num) { + return null; + } + return (int) Math.floor(num); + } + + /** + * 比较两个日期的间隔(时间差绝对值,向上取整)(day、hour、minute). + * + * @param date1 date1 + * @param date2 date2 + * @param level 比较级别 + * @return int 无对应时间间隔级别 + */ + public static Integer getDateIntervalCeil(Date date1, Date date2, String level) { + Double num = dateInterval(date1, date2, level); + if (null == num) { + return null; + } + return (int) Math.ceil(num); + } + + /** + * 比较两个日期的间隔(day、hour、minute). + * + * @param date1 date1 + * @param date2 date2 + * @param level 比较级别 + * @return int 无对应时间间隔级别 + */ + private static Double dateInterval(Date date1, Date date2, String level) { + Double time = (double) (date1.getTime() - date2.getTime()); + if (time < 0) { + time = time * -1; + } + Double num = null; + switch (level) { + case LEVEL_DAY: // 天 + num = (Double) (time / TimeUnit.DAYS.toMillis(1)); + break; + case LEVEL_HOUR: // 小时 + num = (Double) (time / TimeUnit.HOURS.toMillis(1)); + break; + case LEVEL_MINUTE: // 分钟 + num = (Double) (time / TimeUnit.MINUTES.toMillis(1)); + break; + case LEVEL_SECOND: // 秒 + num = (Double) (time / TimeUnit.SECONDS.toMillis(1)); + break; + default: + break; + } + return num; + } + + /** + * 获取当前日期指定时间. + * + * @param date date + * @param time time + * @return date + * @throws ParseException ParseException + */ + public static Date dateToHms(Date date, String time) throws ParseException { + if (null == date || isNullOrEmpty(time)) { + return date; + } + StringBuilder timeBuf = new StringBuilder(); + String dateStr = formatYMd(date); + timeBuf.append(dateStr).append(" "); + time = time.trim(); + while (!time.matches(".*\\d")) { + time = time.substring(0, time.length() - 1); + } + timeBuf.append(time); + if (time.matches("\\d{1,2}:\\d{1,2}:\\d{1,2}")) { + timeBuf.append(".0"); + } else if (time.matches("\\d{1,2}:\\d{1,2}")) { + timeBuf.append(":00.0"); + } else if (time.matches("\\d{1,2}")) { + timeBuf.append(":00:00.0"); + } + return parse(timeBuf.toString(), ENUM_FORMAT_YMDS); + } + + /** + * 取N天之前的时间 + * + * @param beforeDays + * @return + */ + public static Date getDateBefore(int beforeDays) { + Calendar now = Calendar.getInstance(); + now.setTime(new Date()); + now.set(Calendar.DATE, now.get(Calendar.DATE) - beforeDays); + return now.getTime(); + } + + /** + * 将Date类转换为XMLGregorianCalendar. + * + * @param date date + * @return XMLGregorianCalendar + * @throws DatatypeConfigurationException DatatypeConfigurationException + */ + public static XMLGregorianCalendar dateToXmlDate(Date date) throws DatatypeConfigurationException { + XMLGregorianCalendar dateType = null; + if (null != date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + DatatypeFactory dtf = DatatypeFactory.newInstance(); + dateType = dtf.newXMLGregorianCalendar(); + if (null != dateType) { + dateType.setYear(cal.get(Calendar.YEAR)); + // 由于Calendar.MONTH取值范围为0~11,需要加1 + dateType.setMonth(cal.get(Calendar.MONTH) + 1); + dateType.setDay(cal.get(Calendar.DAY_OF_MONTH)); + dateType.setHour(cal.get(Calendar.HOUR_OF_DAY)); + dateType.setMinute(cal.get(Calendar.MINUTE)); + dateType.setSecond(cal.get(Calendar.SECOND)); + } + } + return dateType; + } + + /** + * 清理ThreadLocal(每次线程结束都应执行此操作). + */ + public static void clearThreadLocal() { + threadLocalDateFormat.remove(); + } + + /** + * 获取前一天的23点59分59秒,避免 + * + * @return + */ + public static String getYesterdayForSurvey() { + SimpleDateFormat dateFormat = getDateFormat(DateFormatEnum.COMMON1.getValue()); + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, -1); + c.set(Calendar.HOUR_OF_DAY, 23); + c.set(Calendar.MINUTE, 59); + c.set(Calendar.SECOND, 59); + return dateFormat.format(c.getTime()); + } + + /** + * 获取前一天时间yyyy-mm-dd + * + * @return + */ + public static String getYesterdayForYMD() { + SimpleDateFormat dateFormat = getDateFormat(DateFormatEnum.ENUM_FORMAT_YMD.getValue()); + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, -1); + return dateFormat.format(c.getTime()); + } + + public static Date getYesterday() { + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, -1); + c.set(Calendar.HOUR_OF_DAY, 23); + c.set(Calendar.MINUTE, 59); + c.set(Calendar.SECOND, 59); + return c.getTime(); + } + + public static String getLastDayMonthStr() { + LocalDate now = LocalDate.now(); + LocalDate lastDay = now.minusDays(1L); + return lastDay.format(DateTimeFormatter.ofPattern("yyyyMM")); + } + + private static final ConcurrentHashMap modifiedDate = new ConcurrentHashMap<>(); + + public static Date getModifiedDate(String key) { + return modifiedDate.get(key); + } + + public static Date setModifiedDate(String key, Date date) { + return modifiedDate.put(key, date); + } +} diff --git a/src/main/java/com/xiang/common/utils/OkHttpUtil.java b/src/main/java/com/xiang/common/utils/OkHttpUtil.java new file mode 100644 index 0000000..0b97cd9 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/OkHttpUtil.java @@ -0,0 +1,169 @@ +package com.xiang.common.utils; + +import cn.hutool.http.HttpRequest; +import com.xiang.common.handler.CallbackHandler; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import javax.net.ssl.*; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class OkHttpUtil { + + private static final String secretId = "o82qrnb4z0s8y6j9qvyw"; + private static final String signature = "p93f1o4fn6zht9sa6aryu9wpfp2el8dm"; + private static final String urlString = "https://dps.kdlapi.com/api/getdps/?secret_id=" + secretId + "&signature=" + signature + "&num=1&format=text&sep=1"; + // 用户名密码认证(私密代理/独享代理) + static final String username = "d2859987908"; + static final String password = "1auxzpkz"; + // 单例实例 + private static final OkHttpUtil INSTANCE; + + static { + try { + INSTANCE = new OkHttpUtil(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (KeyManagementException e) { + throw new RuntimeException(e); + } + } + + // OkHttpClient 实例 + private final OkHttpClient client; + + // 私有构造方法,初始化 OkHttpClient + private OkHttpUtil() throws NoSuchAlgorithmException, KeyManagementException { + // 创建一个信任所有证书的TrustManager + final TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + // 初始化SSLContext + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + + // 创建一个信任所有主机名的HostnameVerifier + HostnameVerifier allHostsValid = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + // 2. 配置连接池(关键参数) + ConnectionPool pool = new ConnectionPool( + 5, // 最大空闲连接数(根据服务器承受能力调整) + 5, // 存活时间(分钟) + TimeUnit.MINUTES + ); + + client = new OkHttpClient.Builder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]) + .hostnameVerifier(allHostsValid) + .connectionPool(pool) + .connectTimeout(60, TimeUnit.SECONDS) // 连接超时时间 + .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 + .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 + .retryOnConnectionFailure(true) // 自动重试 + .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) // 协议优先级 + .build(); + } + + public OkHttpUtil(OkHttpClient client){ + this.client = client; + } + + + + + // 获取单例实例 + public static OkHttpUtil getInstance() { + return INSTANCE; + } + + + /** + * 发送 GET 请求 + * + * @param url 请求URL + * @param headers 请求头(可以为空) + * @return 响应结果 + * @throws IOException 请求失败时抛出异常 + */ + public String get(String url, Map headers) throws IOException { + Request.Builder builder = new Request.Builder().url(url); + + // 添加请求头 + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + builder.addHeader(entry.getKey(), entry.getValue()); + } + } + + Request request = builder.build(); + try (Response response = client.newCall(request).execute()) { + String string = response.body().string(); + if (!response.isSuccessful()) { + log.error("{}okhttp请求失败: {}-->", url, string); + } + return string; + } + } + + /** + * 发送 POST 请求(JSON 格式) + * + * @param url 请求URL + * @param headers 请求头(可以为空) + * @param json JSON 请求体 + * @return 响应结果 + * @throws IOException 请求失败时抛出异常 + */ + public String postJson(String url, Map headers, String json) throws IOException { + RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8")); + Request.Builder builder = new Request.Builder() + .url(url) + .post(body) + .addHeader("User-Agent", "BookingClient/1.0"); + + // 添加请求头 + if (headers != null) { + for (Map.Entry entry : headers.entrySet()) { + builder.addHeader(entry.getKey(), entry.getValue()); + } + } + + Request request = builder.build(); + + try (Response response = client.newCall(request).execute()) { + String string = response.body().string(); + if (!response.isSuccessful()) { + log.error("{}okhttp请求失败: {}-->", url, string); + return string; + } + return string; + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java new file mode 100644 index 0000000..ca8f9fe --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java @@ -0,0 +1,49 @@ +package com.xiang.service.module.jntyzx.zlb.constants; + +public class ZlbUrlConstants { + /** + * 获取验证码接口 + */ + public static final String captchaUrl = "https://asian.hzsports.net/sendMessageServer/in/captcha/genCaptcha"; + /** + * 获取图片轨迹接口 + */ + public static final String getCaptchaUrl = "http://118.31.116.52:5700/captcha"; + /** + * 下单接口 + */ + public static final String newOrderUrl = "https://asian.hzsports.net/sportticketserver/site/sitePlaceOrder"; + /** + * 获取用户信息接口 + */ + public static final String getUserInfoUrl = "https://asian.hzsports.net/sportthirdserver/zjmanage/getUserInfo"; + /** + * 获取场地接口 + */ + public static final String getSiteInfoUrl = "https://asian.hzsports.net/sportticketserver/site/selectTicketListBy"; + /** + * 获取订单接口 + */ + public static final String getOrderInfoUrl = "https://asian.hzsports.net/sportticketserver/orderV2/listSiteAndTicket"; + /** + * 获取订单详情接口 + */ + public static final String getOrderDetailUrl = "https://asian.hzsports.net/sportticketserver/orderV2/siteDetail/%s"; + /** + * 退单接口 + */ + public static final String getOrderRefundUrl = "https://asian.hzsports.net/sportticketserver/orderV2/refund"; + /** + * 取消接口 + */ + public static final String getOrderCancelUrl = "https://asian.hzsports.net/sportticketserver/orderV2/cancel"; + + + public static final String siteStr = "{\"stadiumId\":\"49\",\"siteItemId\":1940,\"belongDate\":\"%s\",\"channelType\":6}"; + public static final String siteWqStr = "{\"stadiumId\":\"360112\",\"siteItemId\":1148,\"belongDate\":\"%s\",\"channelType\":6}"; + public static final String refundStr = "{\"detailOrderId\":%s,\"detailOrderIds\":[%s],\"type\":2}"; + public static final String cancelStr = "{\"orderId\":%s,\"type\":2}"; + + public static final String REDIS_PREFIX = "order:renew"; +} + diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java new file mode 100644 index 0000000..2803c55 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -0,0 +1,4 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +public class ZlbTaskConfig { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbService.java new file mode 100644 index 0000000..fe49841 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbService.java @@ -0,0 +1,43 @@ +package com.xiang.service.module.jntyzx.zlb.service; + + +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import com.xiang.common.utils.OkHttpUtil; + +import java.io.IOException; +import java.util.Map; + +/** + * @author caoliang + * @version 1.0 + * @date 2025-06-19 23:26:08 + * @Description:*** + */ +public interface ZlbService { + void queryZLbSiteInfo(String ymdDate,Integer type) throws Exception; + + String getKey(String tokenId, OkHttpUtil client) throws IOException; + + + void testJs(String token, String name) throws IOException; + + Map getHeaders(String tokenId); + + String buildSiteOrder(ZlbUserInfo zlbUserInfo, String secretKey, String day) throws Exception; + + void createOrder(ZlbUserInfo zlbUserInfo) throws Exception; + + String createOrderWq(ZlbUserInfo zlbUserInfo) throws Exception; + + String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException; + + void deleteRedis(String name); + + void installRedis(String name); + + void jianlou(String name, String day,long time) throws Exception; + + void refundOrder(String refundName, String day) throws Exception; + + void cancelOrder(String cancelName, String day) throws Exception; +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java new file mode 100644 index 0000000..32fb669 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -0,0 +1,600 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.pojo.jntyzx.zlb.*; +import com.xiang.common.utils.AESECBUtils; +import com.xiang.common.utils.DateUtils; +import com.xiang.common.utils.OkHttpUtil; +import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + + +@Service +@Slf4j +public class ZlbServiceImpl implements ZlbService { + + + @Resource + private ZlbSiteInfoService zlbSiteInfoService; + @Resource + private ZlbTokenInfoService zlbTokenInfoService; + @Autowired + private JntyzxDingTalkFactory jntyzxDingTalkFactory; + @Autowired + private ZlbUserInfoService zlbUserInfoService; + @Autowired + private RedisTemplate redisTemplate; + + + @Override + public void queryZLbSiteInfo(String ymdDate, Integer type) throws Exception { + log.info("开始查询场地信息"); + LambdaQueryWrapper wrapper1 = Wrappers.lambdaQuery(); + wrapper1.eq(ZlbSiteInfo::getType, type); + wrapper1.eq(ZlbSiteInfo::getBelongDate, ymdDate); + List zlbSiteInfoList1 = zlbSiteInfoService.list(wrapper1); + if (!zlbSiteInfoList1.isEmpty()) { + if (type == 1) { + dayinLog(zlbSiteInfoList1); + return; + } else { + zlbSiteInfoService.remove(wrapper1); + } + } + jntyzxDingTalkFactory.sendMsg(type + "没有查到" + ymdDate + "的场地,开始补齐"); + OkHttpUtil client = OkHttpUtil.getInstance(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbTokenInfo::getName, "Xiang"); + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.getOne(wrapper); + List zlbSiteInfoList = new ArrayList<>(); + String tokenId = zlbTokenInfo.getTokenId(); + String key = getKey(tokenId, client); + log.info("tokenId==>{}", tokenId); + String siteOrderDetailsStr = ""; + if (type.equals(1)) { + siteOrderDetailsStr = String.format(ZlbUrlConstants.siteStr, ymdDate); + } else if (type.equals(2)) { + siteOrderDetailsStr = String.format(ZlbUrlConstants.siteWqStr, ymdDate); + } + String reEncrypted = AESECBUtils.encrypt(siteOrderDetailsStr, key); + ZlbSiteRequest zlbSiteRequest = new ZlbSiteRequest(); + zlbSiteRequest.setDataStr(reEncrypted); + zlbSiteRequest.setStadiumId("49"); + zlbSiteRequest.setSiteItemId(1940); + String jsonString = JSON.toJSONString(zlbSiteRequest); + log.info("json:{}", jsonString); + //请求场地信息 + Map headers = getHeaders(tokenId); + String siteInfo = client.postJson(ZlbUrlConstants.getSiteInfoUrl, headers, jsonString); + log.info("请求场地返回信息: \n {}", siteInfo); + JSONObject jsonObject = JSONObject.parseObject(siteInfo); + JSONObject data = jsonObject.getJSONObject("data"); + String listString = data.getString("list"); + List zlbSiteInfos = JSONArray.parseArray(listString, ZlbSiteInfo.class); + for (ZlbSiteInfo zlbSiteInfo : zlbSiteInfos) { + Integer ticketType = zlbSiteInfo.getTicketType(); + if (ticketType == 1 && type == 1) {//代表可以抢的场地号 + zlbSiteInfo.setType(1); + zlbSiteInfoList.add(zlbSiteInfo); + } + if (ticketType == 4 && type == 1) {//代表可以抢的场地号 + zlbSiteInfo.setType(4); + zlbSiteInfoList.add(zlbSiteInfo); + } + if (type == 2) {//代表可以抢的场地号 + zlbSiteInfo.setType(2); + zlbSiteInfoList.add(zlbSiteInfo); + } + } + if (!zlbSiteInfoList.isEmpty()) { + + zlbSiteInfoService.saveBatch(zlbSiteInfoList); + } + if (type.equals(1)) { + dayinLog(zlbSiteInfoList); + } + } + + private void dayinLog(List zlbSiteInfoList1) { + Map> collect = zlbSiteInfoList1.stream().collect(Collectors.groupingBy(ZlbSiteInfo::getDayEffectiveTimes)); + List zlbSiteInfos6_8 = collect.get("18:00"); + List zlbSiteInfos8_10 = collect.get("20:00"); + List collect1 = zlbSiteInfos6_8.stream().map(ZlbSiteInfo::getPlaceName).collect(Collectors.toList()); + List collect2 = zlbSiteInfos8_10.stream().map(ZlbSiteInfo::getPlaceName).collect(Collectors.toList()); + jntyzxDingTalkFactory.sendMsg("查询到后天场地6-8" + collect1); + jntyzxDingTalkFactory.sendMsg("查询到后天场地8-10" + collect2); + } + + @Override + public String getKey(String tokenId, OkHttpUtil client) throws IOException { + Map headers = getHeaders(tokenId); + String response = client.postJson(ZlbUrlConstants.getUserInfoUrl, headers, "{}"); + log.info("获取用户信息返回:{}", response); + if (StringUtils.isNotBlank(response)) { + JSONObject jsonObject = JSONObject.parseObject(response); + String code = jsonObject.getString("code"); + if ("200".equals(code)) { + JSONObject data = jsonObject.getJSONObject("data"); + String secretKey = data.getString("secretKey"); + //截取密钥前16位 + return secretKey.substring(0, 16); + } + } + return "未登录"; + } + + @Override + public Map getHeaders(String tokenId) { + Map headers = new HashMap<>(); + headers.put("Host", "asian.hzsports.net"); + headers.put("Accept", "*/*"); + headers.put("Sec-Fetch-Site", "cross-site"); + headers.put("Accept-Language", "zh-CN,zh;q=0.9"); + headers.put("Accept-Encoding", "gzip, deflate, br"); + headers.put("token", tokenId); + headers.put("yayunToken", tokenId); + headers.put("Origin", "https://mapi.zjzwfw.gov.cn"); + headers.put("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/22G100 Ariver/1.0.10 Jupiter/1.0.0 000001@ZLB_iphone_7.28.0 hanweb_iphone_/hanweb/dtdreamweb/bundleVersion7.28.0"); + headers.put("Referer", "https://mapi.zjzwfw.gov.cn/"); + headers.put("Connection", "keep-alive"); + headers.put("Content-Type", "application/json;charset=UTF-8"); + headers.put("Sec-Fetch-Dest", "empty"); + headers.put("Sec-Fetch-Mode", "cors"); + return headers; + } + + @Override + public String buildSiteOrder(ZlbUserInfo zlbUserInfo, String secretKey, String day) throws Exception { + //获取配置的场地号 + log.info("secretKey:{}", secretKey); + String placeName = zlbUserInfo.getPlaceName(); + String siteTimeName = zlbUserInfo.getSiteTimeName(); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbSiteInfo::getPlaceName, placeName); + wrapper.eq(ZlbSiteInfo::getDayEffectiveTimes, siteTimeName); + wrapper.eq(ZlbSiteInfo::getBelongDate, day); + ZlbSiteInfo zlbSiteInfo = zlbSiteInfoService.getOne(wrapper); + if (zlbSiteInfo != null) { + ZlbOrderJson zlbOrderJson = new ZlbOrderJson(); + zlbOrderJson.setAmount(zlbSiteInfo.getAmount()); + zlbOrderJson.setBelongDate(zlbSiteInfo.getBelongDate()); + zlbOrderJson.setDayEffectiveTimes(zlbSiteInfo.getDayEffectiveTimes()); + zlbOrderJson.setDayOverdueTimes(zlbSiteInfo.getDayOverdueTimes()); + zlbOrderJson.setPlaceName(zlbSiteInfo.getPlaceName()); + zlbOrderJson.setSiteId(zlbSiteInfo.getSiteId()); + zlbOrderJson.setSiteItemId(zlbSiteInfo.getSiteItemId()); + zlbOrderJson.setSiteRuleId(zlbSiteInfo.getSiteRuleId()); + zlbOrderJson.setSiteTicketId(zlbSiteInfo.getSiteTicketId()); + zlbOrderJson.setPayChannel(2); + List zlbOrderJsons = new ArrayList<>(); + zlbOrderJsons.add(zlbOrderJson); + String jsonString = JSON.toJSONString(zlbOrderJsons); + return AESECBUtils.encrypt(jsonString, secretKey); + } + return ""; + } + + @Override + public void createOrder(ZlbUserInfo zlbUserInfo) throws Exception { + Date date = DateUtils.addDate(new Date(), 1); + String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD); + String name = zlbUserInfo.getName(); + String placeName = zlbUserInfo.getPlaceName(); + String siteTimeName = zlbUserInfo.getSiteTimeName(); + //获取Token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.queryByName(name); + OkHttpUtil client = OkHttpUtil.getInstance(); + String tokenId = zlbTokenInfo.getTokenId(); + String secretKey = getKey(tokenId, client); + //组装场地信息 + String siteOrderDetailsStr = buildSiteOrder(zlbUserInfo, secretKey, day); + //加密 + + for (int i = 1; i < 12; i++) { + String response1 = sendOrder(siteOrderDetailsStr, zlbTokenInfo.getTokenId(), client); + String str = buildOrder(name, response1, placeName, siteTimeName); + if ("下单成功".equals(str)) { + return; + } + if ("您选择场地已被售出".equals(str)) { + return; + } + } + } + + public String buildOrder(String name, String response, String placeName, String siteTimeName) throws InterruptedException { + String orderId = ""; + log.info("订单接口返回结果==> \n {}", response); + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getInteger("code") == 200) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); + JSONObject data = jsonObject.getJSONObject("data"); + orderId = data.getString("orderId"); + log.info("{}订单{}创建成功", name, orderId); + String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + orderId + "_" + name; + redisTemplate.opsForValue().set(redisKey, name); + redisTemplate.expire(redisKey, 123, TimeUnit.SECONDS); + return "下单成功"; + } else if (response.contains("场地不在可售时间内")) { + return "下单失败"; + } else if (response.contains("下单失败")) { + return "下单失败"; + } else if (response.contains("您选择场地已被售出")) { + return "您选择场地已被售出"; + } else if (response.contains("此票超过用户每日订场次数")) { + return "下单失败"; + } else if (response.contains("您有一笔待支付的订场订单")) { + Thread.sleep(1000); + return "您有一笔待支付的订场订单"; + } else if (response.contains("场地火爆")) { + log.info("{}场地火爆下单返回失败暂停1s下单 \n{}", name, response); + return "下单失败"; + } else { + log.info("{}订单接口下单返回失败 \n{}", name, response); + } + return orderId; + } + + @Override + public String createOrderWq(ZlbUserInfo zlbUserInfo) throws Exception { + String orderId = ""; + Date date = DateUtils.addDate(new Date(), 3); + String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD); + String name = zlbUserInfo.getName(); + String placeName = zlbUserInfo.getPlaceName(); + String siteTimeName = zlbUserInfo.getSiteTimeName(); + //获取Token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.queryByName(name); + String siteOrderDetailsStr = buildSiteOrderList(zlbUserInfo, zlbTokenInfo.getSecretKey(), day); + //组装参数加密 + for (int i = 1; i < 10; i++) { + String response = sendOrderWq(siteOrderDetailsStr, zlbTokenInfo.getTokenId()); + log.info("订单接口返回结果==> \n {}", response); + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getInteger("code") == 200) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); + JSONObject data = jsonObject.getJSONObject("data"); + orderId = data.getString("orderId"); + return orderId; + } else if (response.contains("下单失败")) { + jntyzxDingTalkFactory.sendMsg(response); + return ""; + } else if (response.contains("场地不在可售时间内")) { + jntyzxDingTalkFactory.sendMsg(response); + return ""; + } else if (response.contains("您选择场地已被售出")) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败 \n" + response); + return ""; + } else if (response.contains("此票超过用户每日订场次数")) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败 \n" + response); + return ""; + } else if (response.contains("您有一笔待支付的订场订单")) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败 \n" + response); + Thread.sleep(1500); + } else if (response.contains("场地火爆")) { + log.info("{}场地火爆下单返回失败暂停1s下单 \n{}", name, response); + Thread.sleep(200); + } else { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败请检查日志重试第" + i + "次××××××==> \n" + response); + } + } + return orderId; + } + + private String sendOrderWq(String siteOrderDetailsStr, String tokenId) throws IOException { + OkHttpUtil client = OkHttpUtil.getInstance(); + Map headers = getHeaders(tokenId); + ZlbOrderWqInfo zlbOrderInfo = new ZlbOrderWqInfo(); + zlbOrderInfo.setSiteOrderDetailsStr(siteOrderDetailsStr); + String jsonString = JSON.toJSONString(zlbOrderInfo); + log.info("json:\n {}", jsonString); + return client.postJson(ZlbUrlConstants.newOrderUrl, headers, jsonString); + } + + private String buildSiteOrderList(ZlbUserInfo zlbUserInfo, String secretKey, String day) throws Exception { + //获取配置的场地号 + log.info("secretKey:{}", secretKey); + String placeName = zlbUserInfo.getPlaceName(); + String siteTimeName = zlbUserInfo.getSiteTimeName(); + String[] siteTimeNameList = siteTimeName.split(","); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbSiteInfo::getPlaceName, placeName); + wrapper.in(ZlbSiteInfo::getDayEffectiveTimes, Arrays.asList(siteTimeNameList)); + wrapper.eq(ZlbSiteInfo::getBelongDate, day); + List zlbSiteInfoList = zlbSiteInfoService.list(wrapper); + if (CollectionUtils.isNotEmpty(zlbSiteInfoList)) { + List zlbOrderJsons = new ArrayList<>(); + for (ZlbSiteInfo zlbSiteInfo : zlbSiteInfoList) { + ZlbOrderWqJson zlbOrderJson = new ZlbOrderWqJson(); + zlbOrderJson.setIssueIds(null); + zlbOrderJson.setIssueAmount(0); + zlbOrderJson.setCardOrderId(null); + zlbOrderJson.setAmount(zlbSiteInfo.getAmount()); + zlbOrderJson.setBelongDate(zlbSiteInfo.getBelongDate()); + zlbOrderJson.setDayEffectiveTimes(zlbSiteInfo.getDayEffectiveTimes()); + zlbOrderJson.setDayOverdueTimes(zlbSiteInfo.getDayOverdueTimes()); + zlbOrderJson.setPlaceName(zlbSiteInfo.getPlaceName()); + zlbOrderJson.setSaasTicketId(zlbSiteInfo.getSaasTicketId()); + zlbOrderJson.setSiteId(zlbSiteInfo.getSiteId()); + zlbOrderJson.setSiteItemId(zlbSiteInfo.getSiteItemId()); + zlbOrderJson.setSiteRuleId(zlbSiteInfo.getSiteRuleId()); + zlbOrderJson.setSiteTicketId(zlbSiteInfo.getSiteTicketId()); + zlbOrderJson.setPayChannel(2); + zlbOrderJsons.add(zlbOrderJson); + } + String jsonString = com.alibaba.fastjson.JSON.toJSONString(zlbOrderJsons, SerializerFeature.WriteMapNullValue); + return AESECBUtils.encrypt(jsonString, secretKey); + } + return ""; + + } + + private String sendOrder(String siteOrderDetailsStr, String token, OkHttpUtil client) throws Exception { + //请求参数以及图片验证码 + String newOrderJson = buildNewOrder(siteOrderDetailsStr, client); + Map headers = getHeaders(token); + return client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); + } + + @Override + public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException { + //获取图片验证码 + String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); + //获取验证码轨迹 + String track = HttpRequest.post(ZlbUrlConstants.getCaptchaUrl).body(s).execute().body(); + ZlbOrderInfo zlbOrderInfo = JSONObject.parseObject(track, ZlbOrderInfo.class); + zlbOrderInfo.setSiteOrderDetailsStr(siteOrderDetailsStr); + String jsonString = JSON.toJSONString(zlbOrderInfo); + log.info("json:\n {}", jsonString); + return jsonString; + } + + @Override + public void deleteRedis(String name) { + // 定义模糊匹配模式 + String pattern = ZlbUrlConstants.REDIS_PREFIX + "_*_" + name; + // 查找所有匹配的键 + Set keys = redisTemplate.keys(pattern); + // 批量删除 + if (!keys.isEmpty()) { + redisTemplate.delete(keys); + } + } + + @Override + public void installRedis(String name) { + String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + 123456 + "_" + name; + redisTemplate.opsForValue().set(redisKey, name); + redisTemplate.expire(redisKey, 1234, TimeUnit.SECONDS); + } + + public static volatile boolean running = true; + + @Override + public void jianlou(String name, String day, long time) throws Exception { + jntyzxDingTalkFactory.sendMsg(name + "自定义捡漏开始捡漏时间:" + day + " 捡漏人:" + name + " 捡漏间隔:" + time + "ms"); + //获取Token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.queryByName(name); + String tokenId = zlbTokenInfo.getTokenId(); + //代理ip + OkHttpUtil client = OkHttpUtil.getInstance(); + String secretKey = getKey(tokenId, client); + if ("未登录".equals(secretKey)) { + jntyzxDingTalkFactory.sendMsg(name + "未登录请检查登录信息"); + return; + } + log.info("开始捡漏:{}", name); + String siteOrderDetailsStr = String.format(ZlbUrlConstants.siteStr, day); + String reEncrypted = AESECBUtils.encrypt(siteOrderDetailsStr, secretKey); + ZlbSiteRequest zlbSiteRequest = new ZlbSiteRequest(); + zlbSiteRequest.setDataStr(reEncrypted); + String jsonString = JSON.toJSONString(zlbSiteRequest); + //请求场地信息 + Map headers = new HashMap<>(); + headers.put("token", tokenId); + int count = 0; + long startTime = System.currentTimeMillis(); + // 可重用对象 + JSONObject jsonObject = null; + List zlbSiteInfos = null; + running = true; + while (running) { + try { + String siteInfo = client.postJson(ZlbUrlConstants.getSiteInfoUrl, headers, jsonString); + if (StringUtils.isEmpty(siteInfo)) { + return; + } + // log.info("请求场地返回信息: \n {}", siteInfo); + jsonObject = JSONObject.parseObject(siteInfo); + JSONObject data = jsonObject.getJSONObject("data"); + String listString = data.getString("list"); + zlbSiteInfos = JSONArray.parseArray(listString, ZlbSiteInfo.class); + for (ZlbSiteInfo zlbSiteInfo : zlbSiteInfos) { + Integer ticketType = zlbSiteInfo.getTicketType(); + if (ticketType == 1) {//代表可以抢的场地号 + jntyzxDingTalkFactory.sendMsg(day + "ZLb捡漏发现场地:" + zlbSiteInfo.getPlaceName() + "时间点:" + zlbSiteInfo.getDayEffectiveTimes()); + ZlbOrderJson zlbOrderJson = new ZlbOrderJson(); + zlbOrderJson.setAmount(zlbSiteInfo.getAmount()); + zlbOrderJson.setBelongDate(zlbSiteInfo.getBelongDate()); + zlbOrderJson.setDayEffectiveTimes(zlbSiteInfo.getDayEffectiveTimes()); + zlbOrderJson.setDayOverdueTimes(zlbSiteInfo.getDayOverdueTimes()); + zlbOrderJson.setPlaceName(zlbSiteInfo.getPlaceName()); + zlbOrderJson.setSiteId(zlbSiteInfo.getSiteId()); + zlbOrderJson.setSiteItemId(zlbSiteInfo.getSiteItemId()); + zlbOrderJson.setSiteRuleId(zlbSiteInfo.getSiteRuleId()); + zlbOrderJson.setSiteTicketId(zlbSiteInfo.getSiteTicketId()); + zlbOrderJson.setPayChannel(2); + List zlbOrderJsons = new ArrayList<>(); + zlbOrderJsons.add(zlbOrderJson); + String jsonStr = JSON.toJSONString(zlbOrderJsons); + String encrypt = AESECBUtils.encrypt(jsonStr, secretKey); + //拿到请求参数准备发请求 + String response = sendPost(client, headers, encrypt); + log.info("请求参数: \n {}", jsonStr); + JSONObject jsonObject22 = JSONObject.parseObject(response); + if (jsonObject22.getInteger("code") == 200) { + jntyzxDingTalkFactory.sendMsg(name + "ZLb捡漏场地成功请及时付款" + zlbSiteInfo.getPlaceName() + "时间:" + zlbSiteInfo.getDayEffectiveTimes()); + JSONObject data1 = jsonObject22.getJSONObject("data"); + String orderId = data1.getString("orderId"); + log.info("{}订单{}创建成功", name, orderId); + String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + orderId + "_" + name; + redisTemplate.opsForValue().set(redisKey, name); + redisTemplate.expire(redisKey, 120, TimeUnit.SECONDS); + running = false; + } else { + jntyzxDingTalkFactory.sendMsg(name + "ZLb捡漏场地失败请查看日志" + response); + } + } + } + count++; + if (count % 30 == 0) { + long duration = System.currentTimeMillis() - startTime; + log.info("监控循环性能 已访问接口次数:{} - 平均每次循环耗时: {}ms", count, duration / 30); + startTime = System.currentTimeMillis(); + } + //休息5s + Thread.sleep(time); + } catch (Exception e) { + log.error("请求场地信息异常:{}", e.getMessage()); + jntyzxDingTalkFactory.sendMsg(name + "ZLb自定义捡漏异常请查看日志"); + e.printStackTrace(); + } + + } + log.info("捡漏结束"); + jntyzxDingTalkFactory.sendMsg("自定义捡漏结束"); + } + + @Override + public void refundOrder(String refundName, String day) throws Exception { + //找到退款人的token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.getOne(new LambdaQueryWrapper().eq(ZlbTokenInfo::getName, refundName)); + if (zlbTokenInfo == null) { + jntyzxDingTalkFactory.sendMsg("退款失败,请检查是否录入退款人登录信息-->" + refundName); + } + OkHttpUtil client = OkHttpUtil.getInstance(); + //获取退款人的订单列表 + String s = client.postJson(ZlbUrlConstants.getOrderInfoUrl, getHeaders(zlbTokenInfo.getTokenId()), "{\"curPage\":1,\"maxPage\":10,\"state\":2,\"type\":2}"); + log.info("{}订单列表==> \n {}", refundName, s); + //获取当前日期下的orderId + JSONObject jsonObject = JSONObject.parseObject(s); + JSONObject data = jsonObject.getJSONObject("data"); + if (data.getInteger("total").equals(0)) { + jntyzxDingTalkFactory.sendMsg("退款失败,请检查是否存在待使用订单-->" + refundName); + return; + } + JSONArray list = data.getJSONArray("list"); + for (int i = 0; i < list.size(); i++) { + JSONObject jsonObject1 = list.getJSONObject(i); + String orderId = jsonObject1.getString("orderId"); + Integer stadiumId = jsonObject1.getInteger("stadiumId"); + String validity = jsonObject1.getString("validity"); + log.info("stadiumId:{} ,validity:{},orderId:{}", stadiumId, validity, orderId); + if (stadiumId.equals(49) && validity.contains(day)) { + //拿到订单详情 + String orderDetailStr = client.postJson(String.format(ZlbUrlConstants.getOrderDetailUrl, orderId), getHeaders(zlbTokenInfo.getTokenId()), "{}"); + log.info("{}订单详情==> \n {}", orderId, orderDetailStr); + JSONObject jsonObject2 = JSONObject.parseObject(orderDetailStr); + JSONObject data1 = jsonObject2.getJSONObject("data"); + JSONArray ticketInfos = data1.getJSONArray("ticketInfos"); + for (int j = 0; j < ticketInfos.size(); j++) { + JSONObject ticketInfo = ticketInfos.getJSONObject(j); + String detailOrderId = ticketInfo.getString("detailOrderId"); + //取消订单 + String cancelOrder = client.postJson(ZlbUrlConstants.getOrderRefundUrl, getHeaders(zlbTokenInfo.getTokenId()), String.format(ZlbUrlConstants.refundStr, orderId, detailOrderId)); + log.info("{}退款订单==> \n {}", refundName, cancelOrder); + if (cancelOrder.contains("退款成功")) { + jntyzxDingTalkFactory.sendMsg(refundName + "退款成功"); + } + } + } + + } + + } + + @Override + public void cancelOrder(String cancelName, String day) throws Exception { + //找到取消人的token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.getOne(new LambdaQueryWrapper().eq(ZlbTokenInfo::getName, cancelName)); + if (zlbTokenInfo == null) { + jntyzxDingTalkFactory.sendMsg("取消失败,请检查是否录入取消人登录信息-->" + cancelName); + } + OkHttpUtil client = OkHttpUtil.getInstance(); + //获取取消人的订单列表 + String s = client.postJson(ZlbUrlConstants.getOrderInfoUrl, getHeaders(zlbTokenInfo.getTokenId()), "{\"curPage\":1,\"maxPage\":10,\"state\":1,\"type\":2}"); + log.info("{}订单列表==> \n {}", cancelName, s); + //获取当前日期下的orderId + JSONObject jsonObject = JSONObject.parseObject(s); + JSONObject data = jsonObject.getJSONObject("data"); + if (data.getInteger("total").equals(0)) { + jntyzxDingTalkFactory.sendMsg("取消失败,请检查是否存在待使用订单-->" + cancelName); + return; + } + JSONArray list = data.getJSONArray("list"); + for (int i = 0; i < list.size(); i++) { + JSONObject jsonObject1 = list.getJSONObject(i); + String orderId = jsonObject1.getString("orderId"); + Integer stadiumId = jsonObject1.getInteger("stadiumId"); + String validity = jsonObject1.getString("validity"); + log.info("stadiumId:{} ,validity:{},orderId:{}", stadiumId, validity, orderId); + if (stadiumId.equals(49) && validity.contains(day)) { + //取消订单,并直接删除redis相关信息 + String cancelOrder = client.postJson(ZlbUrlConstants.getOrderCancelUrl, getHeaders(zlbTokenInfo.getTokenId()), String.format(ZlbUrlConstants.cancelStr, orderId)); + deleteRedis(orderId); + log.info("{}取消订单==> \n {}", cancelName, cancelOrder); + if (cancelOrder.contains("取消成功")) { + jntyzxDingTalkFactory.sendMsg(cancelName + "取消成功"); + } + } + } + } + + private String sendPost(OkHttpUtil client, Map headers, String encrypt) throws IOException { + String newOrderJson = buildNewOrder(encrypt, client); + String response = null; + try { + response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); + log.info("抢场地返回信息: \n {}", response); + } catch (Exception e) { + throw new RuntimeException(e); + } + return response; + } + + @Override + public void testJs(String token, String name) throws IOException { + log.info("获取到name:{},token:{}", name, token); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbTokenInfo::getName, name); + ZlbTokenInfo tokenInfo = zlbTokenInfoService.getOne(wrapper); + if (tokenInfo != null) { + tokenInfo.setTokenId(token); + tokenInfo.setUpdatedDate(new Date()); + zlbTokenInfoService.update(tokenInfo, wrapper); + jntyzxDingTalkFactory.sendMsg(name + "更新token为" + token); + } else { + jntyzxDingTalkFactory.sendMsg(name + "更新token失败没有该用户-->" + name); + } + } +} + + diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java new file mode 100644 index 0000000..e6d4844 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java @@ -0,0 +1,13 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; + +/** +* @author a123 +* @description 针对表【zlb_site_info】的数据库操作Service +* @createDate 2025-06-19 23:32:29 +*/ +public interface ZlbSiteInfoService extends IService { + +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java new file mode 100644 index 0000000..a4cdbf4 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java @@ -0,0 +1,21 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ZlbSiteInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; +import org.springframework.stereotype.Service; + +/** +* @author a123 +* @description 针对表【zlb_site_info】的数据库操作Service实现 +* @createDate 2025-06-19 23:32:29 +*/ +@Service +public class ZlbSiteInfoServiceImpl extends ServiceImpl + implements ZlbSiteInfoService { + +} + + + + diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java new file mode 100644 index 0000000..e775f29 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java @@ -0,0 +1,14 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; + +/** +* @author a123 +* @description 针对表【zlb_token_info】的数据库操作Service +* @createDate 2025-06-20 23:51:24 +*/ +public interface ZlbTokenInfoService extends IService { + + ZlbTokenInfo queryByName(String name); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java new file mode 100644 index 0000000..50134cd --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java @@ -0,0 +1,29 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ZlbTokenInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import org.springframework.stereotype.Service; + +/** +* @author a123 +* @description 针对表【zlb_token_info】的数据库操作Service实现 +* @createDate 2025-06-20 23:51:24 +*/ +@Service +public class ZlbTokenInfoServiceImpl extends ServiceImpl + implements ZlbTokenInfoService { + + @Override + public ZlbTokenInfo queryByName(String name) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbTokenInfo::getName, name); + return this.getOne(wrapper); + } +} + + + + diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java new file mode 100644 index 0000000..9c118d7 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java @@ -0,0 +1,8 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; + +public interface ZlbUserInfoService extends IService { + +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java new file mode 100644 index 0000000..ba779c8 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java @@ -0,0 +1,16 @@ +package com.xiang.service.module.jntyzx.zlb.service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ZlbUserInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import org.springframework.stereotype.Service; + +@Service +public class ZlbUserInfoServiceImpl extends ServiceImpl + implements ZlbUserInfoService { + +} + + + + -- 2.49.1 From 66a69a4968fc6836f5ad1b12f8d0ba39e6db3e88 Mon Sep 17 00:00:00 2001 From: xiang Date: Tue, 5 May 2026 17:28:14 +0800 Subject: [PATCH 06/32] =?UTF-8?q?feat:=E4=B8=8B=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/enums/ScheduleEnums.java | 10 ++ .../xiang/common/enums/YcCodeTypeEnum.java | 17 +++ .../schedule/BaseScheduleTaskTemplate.java | 3 +- .../common/pojo/code/YcCodeBaseResponse.java | 14 ++ .../common/pojo/code/YcCodeDataResp.java | 14 ++ .../xiang/common/pojo/code/YcCodeRequest.java | 24 +++ .../common/pojo/jntyzx/zlb/ZlbOrderInfo.java | 21 +-- .../xiang/common/service/CodeServiceImpl.java | 73 +++++++++ .../xiang/common/service/ICodeService.java | 14 ++ .../jntyzx/zlb/schedule/ZlbLoginTask.java | 138 ++++++++++++++++++ .../jntyzx/zlb/schedule/ZlbOrderTask.java | 137 +++++++++++++++++ .../jntyzx/zlb/schedule/ZlbSiteDayTask.java | 74 ++++++++++ .../jntyzx/zlb/schedule/ZlbSiteTask.java | 67 +++++++++ .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 44 ++++++ .../zlb/schedule/ZlbTokenRefreshTask.java | 79 ++++++++++ .../jntyzx/zlb/service/ZlbServiceImpl.java | 12 +- 16 files changed, 728 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java create mode 100644 src/main/java/com/xiang/common/pojo/code/YcCodeBaseResponse.java create mode 100644 src/main/java/com/xiang/common/pojo/code/YcCodeDataResp.java create mode 100644 src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java create mode 100644 src/main/java/com/xiang/common/service/CodeServiceImpl.java create mode 100644 src/main/java/com/xiang/common/service/ICodeService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 684c88f..45daa45 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -7,8 +7,18 @@ import lombok.Getter; @AllArgsConstructor public enum ScheduleEnums { + /** + * 0:glados 1:芬玩岛 2:江体小程序 3:江体zlb 4:DDNS + */ DOMAIN_DYNAMIC_ANALYSIS_TASK(4, "domain", "domainDynamicAnalysisTask"), GLADOS_CHECK_IN_TASK(0, "glados", "gladosCheckInTask"), + + ZLB_LOGIN_TASK(3, "zlb", "zlbLoginTask"), + ZLB_TOKEN_CHECK_TASK(3, "zlb", "zlbTokenCheckTask"), + ZLB_SITE_QUERY_TASK(3, "zlb", "zlbSiteQueryTask"), + ZLB_SITE_DAY_TASK(3, "zlb", "zlbSiteDayTask"), + ZLB_ORDER_CREATE_TASK(3, "zlb", "zlbOrderCreateTask"), + ; private final Integer modeleCode; diff --git a/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java b/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java new file mode 100644 index 0000000..d43c581 --- /dev/null +++ b/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java @@ -0,0 +1,17 @@ +package com.xiang.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum YcCodeTypeEnum { + + YC_10118(10118, "中文字符 1~4位 plus 其他类型准确率不满足 可使用本类型"), + YC_6246(30100, "通用中文点选1") + + ; + private final Integer type; + private final String desc; + +} diff --git a/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java index 1d07da0..c2277af 100644 --- a/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java +++ b/src/main/java/com/xiang/common/factory/schedule/BaseScheduleTaskTemplate.java @@ -9,6 +9,7 @@ import com.xiang.common.service.IScheduleRunLogService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Objects; @@ -112,7 +113,7 @@ public abstract class BaseScheduleTaskTemplate { * @param validatedParams 校验通过的参数 * @return 任务执行结果 */ - protected abstract TaskResult doExecute(Object validatedParams); + protected abstract TaskResult doExecute(Object validatedParams) throws Exception; /** * 记录成功结果 - 子类可选择性覆写 diff --git a/src/main/java/com/xiang/common/pojo/code/YcCodeBaseResponse.java b/src/main/java/com/xiang/common/pojo/code/YcCodeBaseResponse.java new file mode 100644 index 0000000..8afa6ad --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/code/YcCodeBaseResponse.java @@ -0,0 +1,14 @@ +package com.xiang.common.pojo.code; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class YcCodeBaseResponse { + private String msg; + private Integer code; + private T data; +} diff --git a/src/main/java/com/xiang/common/pojo/code/YcCodeDataResp.java b/src/main/java/com/xiang/common/pojo/code/YcCodeDataResp.java new file mode 100644 index 0000000..d8937cf --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/code/YcCodeDataResp.java @@ -0,0 +1,14 @@ +package com.xiang.common.pojo.code; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class YcCodeDataResp { + private Integer code; + private String data; + private Double time; +} diff --git a/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java new file mode 100644 index 0000000..de7142b --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java @@ -0,0 +1,24 @@ +package com.xiang.common.pojo.code; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class YcCodeRequest { + /** + * 图片 + */ + private String image; + /** + * token + */ + private String token; + /** + * 接口类型 + */ + private Integer type; + private String extra; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java index 0fa2ce7..f5b844f 100644 --- a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbOrderInfo.java @@ -12,24 +12,25 @@ import java.util.List; */ @Data public class ZlbOrderInfo { - private String id; - private ZlbData data; + private String id; + private ZlbData data; @Data public static class ZlbData { - private Integer bgImageWidth; - private Integer bgImageHeight; - private String startTime; - private String stopTime; + private Integer bgImageWidth; + private Integer bgImageHeight; + private String startTime; + private String stopTime; private List trackList; @Data public static class TrackList { - private Integer x; - private Integer y; - private Integer t; - private String type; + private Integer x; + private Integer y; + private Integer t; + private String type; } } + private String siteOrderDetailsStr; } diff --git a/src/main/java/com/xiang/common/service/CodeServiceImpl.java b/src/main/java/com/xiang/common/service/CodeServiceImpl.java new file mode 100644 index 0000000..6b44543 --- /dev/null +++ b/src/main/java/com/xiang/common/service/CodeServiceImpl.java @@ -0,0 +1,73 @@ +package com.xiang.common.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.google.common.collect.Maps; +import com.xiang.common.enums.YcCodeTypeEnum; +import com.xiang.common.pojo.code.YcCodeBaseResponse; +import com.xiang.common.pojo.code.YcCodeDataResp; +import com.xiang.common.pojo.code.YcCodeRequest; +import com.xiang.common.utils.HttpService; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Objects; + +@Service +@Slf4j +public class CodeServiceImpl implements ICodeService { + + // 云码平台接口api + private static final String YUN_CODE_API_URL = "http://api.jfbym.com/api/YmServer/customApi"; + + @Override + public String templateCodeResolve(String image) { + YcCodeBaseResponse resp = baseCodeResolve(YcCodeTypeEnum.YC_10118.getType(), image, null); + if (Objects.isNull(resp)) { + return null; + } + Integer code = resp.getCode(); + if (200 == code) { + String data = resp.getData().getData(); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < data.length(); i++) { + result.append(data.charAt(i)).append(","); + } + return result.substring(0, result.length() - 1); + } + return null; + + } + + @Override + public String codeResolve(String image, String extra) { + YcCodeBaseResponse resp = baseCodeResolve(YcCodeTypeEnum.YC_6246.getType(), image, extra); + if (Objects.isNull(resp)) { + return null; + } + Integer code = resp.getCode(); + if (200 == code) { + return resp.getData().getData(); + } + return null; + } + + @Override + public YcCodeBaseResponse baseCodeResolve(Integer type, String image, String extra) { + + HashMap header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + + YcCodeRequest ycCodeRequest = new YcCodeRequest(); + ycCodeRequest.setImage(image); + ycCodeRequest.setToken("9LQ1ATKVEeO8Arhq-HavXzpHvkzdZz_r7ydmqlYhp9c"); + ycCodeRequest.setType(type); + ycCodeRequest.setExtra(extra); + + String resp = HttpService.doPost(YUN_CODE_API_URL, header, JSON.toJSONString(ycCodeRequest)); + return JSON.parseObject(resp, new TypeReference>() { + }); + } +} diff --git a/src/main/java/com/xiang/common/service/ICodeService.java b/src/main/java/com/xiang/common/service/ICodeService.java new file mode 100644 index 0000000..b45de0f --- /dev/null +++ b/src/main/java/com/xiang/common/service/ICodeService.java @@ -0,0 +1,14 @@ +package com.xiang.common.service; + +import com.xiang.common.pojo.code.YcCodeBaseResponse; +import com.xiang.common.pojo.code.YcCodeDataResp; +import com.xiang.common.pojo.code.YcCodeRequest; + +public interface ICodeService { + + String templateCodeResolve(String image); + + String codeResolve(String image, String extra); + + YcCodeBaseResponse baseCodeResolve(Integer type, String image, String extra); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java new file mode 100644 index 0000000..087dc00 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java @@ -0,0 +1,138 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.mapper.ZlbLoginInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbLoginInfo; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class ZlbLoginTask extends BaseScheduleTaskTemplate { + + private final ZlbLoginInfoMapper zlbLoginInfoMapper; + private final ZlbTokenInfoService zlbTokenInfoService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public ZlbLoginTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + ZlbLoginInfoMapper zlbLoginInfoMapper, + ZlbTokenInfoService zlbTokenInfoService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbLoginInfoMapper = zlbLoginInfoMapper; + this.zlbTokenInfoService = zlbTokenInfoService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_LOGIN_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_LOGIN_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_LOGIN_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(true); + + List zlbLoginInfos = zlbLoginInfoMapper.selectList(new QueryWrapper<>()); + if (CollectionUtils.isEmpty(zlbLoginInfos)) { + taskResult.setSummary("暂无用户信息"); + return taskResult; + } + + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(60, TimeUnit.SECONDS) // 连接超时时间 + .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 + .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 + .build(); + StringBuilder sb = new StringBuilder(); + for (ZlbLoginInfo zlbLoginInfo : zlbLoginInfos) { + String name = zlbLoginInfo.getName(); + try { + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(mediaType, "{\"appId\":\"2002207307\"}"); + Request request = new Request.Builder() + .url("https://portal.zjzwfw.gov.cn/uc/sso/ticket") + .method("POST", body) + .addHeader("Host", "portal.zjzwfw.gov.cn") + .addHeader("X-App-Id", "") + .addHeader("X-Timestamp", "1761122479") + .addHeader("User-Agent", "000001@ZLB_iphone_7.28.0") + .addHeader("guc-accountType", "person") +// .addHeader("aliyungf_tc", zlbLoginInfo.getAliyungfTc()) + .addHeader("Biz-Session-Id", zlbLoginInfo.getBizSessionId()) + .addHeader("guc-platform", "app") + .addHeader("X-Access-Id", "szzj") + .addHeader("X-Device-Id", zlbLoginInfo.getXDeviceId()) + .addHeader("X-Sign-Type", "SHA256") + .addHeader("guc-endpoint", "C") + .addHeader("X-Sign-Value", zlbLoginInfo.getXSignValue()) + .addHeader("Connection", "keep-alive") + .addHeader("token", zlbLoginInfo.getToken()) + .addHeader("Accept-Language", "zh-Hans-CN;q=1") + .addHeader("guc-gsid", zlbLoginInfo.getGucGsid()) + .addHeader("X-Site-Code", zlbLoginInfo.getXSiteCode()) + .addHeader("Accept", "*/*") + .addHeader("OS-Version", "18.6.2") + .addHeader("Cookie", zlbLoginInfo.getCookie()) + .addHeader("Content-Type", "application/json") + .build(); + Response response = client.newCall(request).execute(); + String string = response.body().string(); + log.info("{}获取ticketId接口返回:{}", name,string); + JSONObject jsonObject = JSON.parseObject(string + ); + JSONObject data = jsonObject.getJSONObject("data"); + String ticketId = data.getString("ticketId"); + Request.Builder builder = new Request.Builder().url("https://asian.hzsports.net/sportthirdserver/zjmanage/getTydjtLoginInFo4?ticketId=" + ticketId); + // 添加请求头 + builder.addHeader("token", "9a422ab8b25d4634ae8efbb965945b46"); + Response response1 = client.newCall(builder.build()).execute(); + JSONObject jsonObject1 = JSON.parseObject(response1.body().string()); + String token = jsonObject1.getString("data"); + log.info("{}token=== {}", name, token); + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.queryByName(name); + zlbTokenInfo.setTokenId(token); + zlbTokenInfo.setUpdatedDate(new Date()); + zlbTokenInfoService.updateById(zlbTokenInfo); + } catch (Exception e) { + log.error("{}登录异常", name, e); + sb.append(name).append("用户登录异常.").append(e.getMessage()); + jntyzxDingTalkFactory.sendMsg("登录异常:" + name); + } + } + if (StringUtils.isNotBlank(sb)) { + taskResult.setSuccess(false); + taskResult.setSummary(sb.toString()); + } + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java new file mode 100644 index 0000000..d524a30 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -0,0 +1,137 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.common.utils.OkHttpUtil; +import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; +import com.xiang.service.module.jntyzx.zlb.service.ZlbService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbUserInfoService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.IOException; +import java.time.Duration; +import java.time.LocalTime; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class ZlbOrderTask extends BaseScheduleTaskTemplate { + + private final ZlbUserInfoService zlbUserInfoService; + private final ZlbService zlbService; + private final ZlbTokenInfoService zlbTokenInfoService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + private final RedisTemplate redisTemplate; + + public ZlbOrderTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + ZlbUserInfoService zlbUserInfoService, + ZlbService zlbService, + ZlbTokenInfoService zlbTokenInfoService, + JntyzxDingTalkFactory jntyzxDingTalkFactory, + RedisTemplate redisTemplate) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbUserInfoService = zlbUserInfoService; + this.zlbService = zlbService; + this.zlbTokenInfoService = zlbTokenInfoService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + this.redisTemplate = redisTemplate; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_ORDER_CREATE_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_ORDER_CREATE_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_ORDER_CREATE_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + + TaskResult taskResult = new TaskResult(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbUserInfo::getIsBook, 0); + wrapper.eq(ZlbUserInfo::getName, "xiang"); + Date date = DateUtils.addDate(new Date(), 0); + String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD); + wrapper.eq(ZlbUserInfo::getWeek, DateUtils.getWeekDayTwo(day)); + ZlbUserInfo zlbUserInfo = zlbUserInfoService.getOne(wrapper); + if (zlbUserInfo == null) { + taskResult.setSuccess(false); + taskResult.setSummary("用户不存在"); + return taskResult; + } + OkHttpUtil client = OkHttpUtil.getInstance(); + //使用代理ip + String name = zlbUserInfo.getName(); + String placeName = zlbUserInfo.getPlaceName(); + String siteTimeName = zlbUserInfo.getSiteTimeName(); + //获取Token + ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.queryByName(name); + String tokenId = zlbTokenInfo.getTokenId(); + String secretKey = zlbService.getKey(tokenId, client); + String siteOrderDetailsStr = zlbService.buildSiteOrder(zlbUserInfo, secretKey, day); + Map headers = zlbService.getHeaders(zlbTokenInfo.getTokenId()); + String newOrderJson = zlbService.buildNewOrder(siteOrderDetailsStr, client); + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + //计算9点到现在的时间差 + //获取江体当前时间 + LocalTime currentTime = LocalTime.now(); + LocalTime targetTime = LocalTime.parse("09:00:11.800"); + Duration duration = Duration.between(currentTime, targetTime); + long milliseconds = duration.toMillis(); +// executorService.schedule(() -> { + String response = null; + try { + response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); + } catch (Exception e) { + throw new RuntimeException(e); + } + buildOrder(name, response, placeName, siteTimeName); +// }, milliseconds, TimeUnit.MILLISECONDS); + + return taskResult; + } + + private void buildOrder(String name, String response, String placeName, String siteTimeName) { + log.info("订单接口返回结果==> \n {}", response); + JSONObject jsonObject = JSONObject.parseObject(response); + if (jsonObject.getInteger("code") == 200) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); + JSONObject data = jsonObject.getJSONObject("data"); + String orderId = data.getString("orderId"); + log.info("{}订单{}创建成功", name, orderId); + String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + orderId + "_" + name; + redisTemplate.opsForValue().set(redisKey, name); + redisTemplate.expire(redisKey, 120, TimeUnit.SECONDS); + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java new file mode 100644 index 0000000..1a17e02 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java @@ -0,0 +1,74 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.mapper.ZlbOrderInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbPayOrder; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +@Component +@Slf4j +public class ZlbSiteDayTask extends BaseScheduleTaskTemplate { + private final ZlbOrderInfoMapper zlbOrderInfoMapper; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public ZlbSiteDayTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + ZlbOrderInfoMapper zlbOrderInfoMapper, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbOrderInfoMapper = zlbOrderInfoMapper; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_SITE_DAY_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_SITE_DAY_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_SITE_DAY_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { + TaskResult taskResult = new TaskResult(); + + //获取当前时间的后一天 + Date date = DateUtils.addDate(new Date(), 1); + String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbPayOrder::getDay, day); + wrapper.orderByAsc(ZlbPayOrder::getTime); + List zlbPayOrders = zlbOrderInfoMapper.selectList(wrapper); + if (!zlbPayOrders.isEmpty()){ + StringBuilder sb = new StringBuilder(); + sb.append("预约日期:").append(day).append("\n"); + for (ZlbPayOrder zlbPayOrder : zlbPayOrders) { + sb.append("预约人:").append(zlbPayOrder.getName()).append("预约场地:").append(zlbPayOrder.getPlaceName()).append(",").append("预约时间:").append(zlbPayOrder.getTime()).append(",").append("\n"); + } + jntyzxDingTalkFactory.sendMsg(sb.toString()); + } + + taskResult.setSuccess(true); + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java new file mode 100644 index 0000000..093796d --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java @@ -0,0 +1,67 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.zlb.service.ZlbService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; + +@Component +@Slf4j +public class ZlbSiteTask extends BaseScheduleTaskTemplate { + + private final ZlbService zlbService; + + public ZlbSiteTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, ZlbService zlbService) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbService = zlbService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_SITE_QUERY_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_SITE_QUERY_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_SITE_QUERY_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { + + TaskResult taskResult = new TaskResult(); + //获取当前时间的后一天 + Date date1 = DateUtils.addDate(new Date(), 2); + Date date2 = DateUtils.addDate(new Date(), 3); + Date date3 = DateUtils.addDate(new Date(), 4); + String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD); + String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD); + String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD); + try { + zlbService.queryZLbSiteInfo(day1, 1); + zlbService.queryZLbSiteInfo(day2, 1); + zlbService.queryZLbSiteInfo(day3, 1); + } catch (Exception e) { + log.error("site task error", e); + taskResult.setSuccess(false); + taskResult.setSummary(e.getMessage()); + return taskResult; + } + taskResult.setSuccess(true); + taskResult.setSummary("site task success."); + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index 2803c55..8c3e1ac 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -1,4 +1,48 @@ package com.xiang.service.module.jntyzx.zlb.schedule; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@Component +@RequiredArgsConstructor +@RestController public class ZlbTaskConfig { + + private final ZlbLoginTask zlbLoginTask; + private final ZlbTokenRefreshTask zlbTokenRefreshTask; + private final ZlbSiteTask zlbSiteTask; + private final ZlbSiteDayTask zlbSiteDayTask; + private final ZlbOrderTask zlbOrderTask; + + @Scheduled(cron = "0 0/30 * * * ?") + @GetMapping("/zlbLoginTask") + public void zlbLoginTask() { + zlbLoginTask.run(); + } + + @Scheduled(cron = "0 0 8 * * *") + @GetMapping("/zlbTokenRefresh") + public void zlbTokenRefresh() { + zlbTokenRefreshTask.run(); + } + + @GetMapping("/zlbSiteTask") + public void zlbSiteTask() { + zlbSiteTask.run(); + } + + @GetMapping("/zlbSiteDayTask") + public void zlbSiteDayTask() { + zlbSiteDayTask.run(); + } + + @GetMapping("/zlbOrderCreateTask") + public void zlbOrderCreateTask() { + zlbOrderTask.run(); + } + + } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java new file mode 100644 index 0000000..17a360f --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java @@ -0,0 +1,79 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +@Component +@Slf4j +public class ZlbTokenRefreshTask extends BaseScheduleTaskTemplate { + + private final ZlbTokenInfoService zlbTokenInfoService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public ZlbTokenRefreshTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + ZlbTokenInfoService zlbTokenInfoService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbTokenInfoService = zlbTokenInfoService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_TOKEN_CHECK_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_TOKEN_CHECK_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_TOKEN_CHECK_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) { + + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(true); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(ZlbTokenInfo::getIsDel, 0); + List list = zlbTokenInfoService.list(wrapper); + StringBuilder sb = new StringBuilder(); + for (ZlbTokenInfo zlbTokenInfo : list) { + String name = zlbTokenInfo.getName(); + Date updatedDate = zlbTokenInfo.getUpdatedDate(); + //判断update时间是不是当前时间相差多少小时 + if (new Date().getTime() -updatedDate.getTime() > 1000 * 60 * 60 * 30) { + //超过30小时,则重新获取token + sb.append(name).append(" token已超过30小时请及时更新×××××").append("\n"); + }else{ + sb.append(name).append(" token可以正常使用无需更新√√√√√").append("\n"); + } + } + jntyzxDingTalkFactory.sendMsg(sb.toString()); + if (StringUtils.isNotBlank(sb)) { + taskResult.setSummary(sb.toString()); + taskResult.setSuccess(false); + } + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 32fb669..787bd3c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.pojo.jntyzx.zlb.*; +import com.xiang.common.service.ICodeService; import com.xiang.common.utils.AESECBUtils; import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.OkHttpUtil; @@ -42,6 +43,8 @@ public class ZlbServiceImpl implements ZlbService { private ZlbUserInfoService zlbUserInfoService; @Autowired private RedisTemplate redisTemplate; + @Autowired + private ICodeService codeService; @Override @@ -62,7 +65,7 @@ public class ZlbServiceImpl implements ZlbService { jntyzxDingTalkFactory.sendMsg(type + "没有查到" + ymdDate + "的场地,开始补齐"); OkHttpUtil client = OkHttpUtil.getInstance(); LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(ZlbTokenInfo::getName, "Xiang"); + wrapper.eq(ZlbTokenInfo::getName, "xiang"); ZlbTokenInfo zlbTokenInfo = zlbTokenInfoService.getOne(wrapper); List zlbSiteInfoList = new ArrayList<>(); String tokenId = zlbTokenInfo.getTokenId(); @@ -358,8 +361,13 @@ public class ZlbServiceImpl implements ZlbService { public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException { //获取图片验证码 String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); + JSONObject jsonObject = JSON.parseObject(s); + String templateImage = JSON.toJSONString(jsonObject.get("templateImage")); + String templeCode = codeService.templateCodeResolve(templateImage); + String captcha = JSON.toJSONString(jsonObject.get("captcha")); + String backgroundImage = JSON.toJSONString(JSON.parseObject(captcha).get("backgroundImage")); + String track = codeService.codeResolve(backgroundImage, templeCode); //获取验证码轨迹 - String track = HttpRequest.post(ZlbUrlConstants.getCaptchaUrl).body(s).execute().body(); ZlbOrderInfo zlbOrderInfo = JSONObject.parseObject(track, ZlbOrderInfo.class); zlbOrderInfo.setSiteOrderDetailsStr(siteOrderDetailsStr); String jsonString = JSON.toJSONString(zlbOrderInfo); -- 2.49.1 From a1d42a4b2728fb95b183bad3800878057281681c Mon Sep 17 00:00:00 2001 From: xiang Date: Tue, 5 May 2026 21:23:11 +0800 Subject: [PATCH 07/32] =?UTF-8?q?feat:=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiang/common/enums/YcCodeTypeEnum.java | 3 +- .../xiang/common/pojo/code/YcCodeRequest.java | 7 ++- .../xiang/common/service/CodeServiceImpl.java | 35 ++++++++++--- .../xiang/common/service/ICodeService.java | 4 +- .../jntyzx/zlb/service/ZlbServiceImpl.java | 49 ++++++++++++++++--- 5 files changed, 83 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java b/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java index d43c581..99c9088 100644 --- a/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java +++ b/src/main/java/com/xiang/common/enums/YcCodeTypeEnum.java @@ -8,7 +8,8 @@ import lombok.Getter; public enum YcCodeTypeEnum { YC_10118(10118, "中文字符 1~4位 plus 其他类型准确率不满足 可使用本类型"), - YC_6246(30100, "通用中文点选1") + YC_6246(30100, "通用中文点选1"), + YC_310700(310700, "适合Zlb使用的word click") ; private final Integer type; diff --git a/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java index de7142b..e0ae7da 100644 --- a/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java +++ b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java @@ -9,7 +9,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor public class YcCodeRequest { /** - * 图片 + * 图片 base64 */ private String image; /** @@ -21,4 +21,9 @@ public class YcCodeRequest { */ private Integer type; private String extra; + + /** + * 模板图片 base64 + */ + private String labelImage; } diff --git a/src/main/java/com/xiang/common/service/CodeServiceImpl.java b/src/main/java/com/xiang/common/service/CodeServiceImpl.java index 6b44543..5c52888 100644 --- a/src/main/java/com/xiang/common/service/CodeServiceImpl.java +++ b/src/main/java/com/xiang/common/service/CodeServiceImpl.java @@ -9,10 +9,13 @@ import com.xiang.common.pojo.code.YcCodeDataResp; import com.xiang.common.pojo.code.YcCodeRequest; import com.xiang.common.utils.HttpService; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Objects; @Service @@ -42,14 +45,34 @@ public class CodeServiceImpl implements ICodeService { } @Override - public String codeResolve(String image, String extra) { - YcCodeBaseResponse resp = baseCodeResolve(YcCodeTypeEnum.YC_6246.getType(), image, extra); - if (Objects.isNull(resp)) { + public List codeResolve(String image, String templateImage) { + HashMap header = Maps.newHashMap(); + header.put("Content-Type", "application/json"); + YcCodeRequest ycCodeRequest = new YcCodeRequest(); + ycCodeRequest.setImage(image); + ycCodeRequest.setToken("9LQ1ATKVEeO8Arhq-HavXzpHvkzdZz_r7ydmqlYhp9c"); + ycCodeRequest.setLabelImage(templateImage); + + String resp = HttpService.doPost(YUN_CODE_API_URL, header, JSON.toJSONString(ycCodeRequest)); + YcCodeBaseResponse response = JSON.parseObject(resp, new TypeReference>() { + }); + if (Objects.isNull(response)) { return null; } - Integer code = resp.getCode(); - if (200 == code) { - return resp.getData().getData(); + Integer code = response.getCode(); + if (10000 == code) { + YcCodeDataResp data = response.getData(); + if (Objects.isNull(data)) { + return null; + } + Integer dataCode = data.getCode(); + if (0 == dataCode) { + String dataData = data.getData(); + if (StringUtils.isNotBlank(dataData)) { + String[] split = dataData.split("\\|"); + return Arrays.asList(split); + } + } } return null; } diff --git a/src/main/java/com/xiang/common/service/ICodeService.java b/src/main/java/com/xiang/common/service/ICodeService.java index b45de0f..ed10cbc 100644 --- a/src/main/java/com/xiang/common/service/ICodeService.java +++ b/src/main/java/com/xiang/common/service/ICodeService.java @@ -4,11 +4,13 @@ import com.xiang.common.pojo.code.YcCodeBaseResponse; import com.xiang.common.pojo.code.YcCodeDataResp; import com.xiang.common.pojo.code.YcCodeRequest; +import java.util.List; + public interface ICodeService { String templateCodeResolve(String image); - String codeResolve(String image, String extra); + List codeResolve(String image, String extra); YcCodeBaseResponse baseCodeResolve(Integer type, String image, String extra); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 787bd3c..16fe71a 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.pojo.jntyzx.zlb.*; import com.xiang.common.service.ICodeService; @@ -17,6 +18,7 @@ import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.OkHttpUtil; import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -362,19 +364,54 @@ public class ZlbServiceImpl implements ZlbService { //获取图片验证码 String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); JSONObject jsonObject = JSON.parseObject(s); - String templateImage = JSON.toJSONString(jsonObject.get("templateImage")); - String templeCode = codeService.templateCodeResolve(templateImage); + String id = (String) jsonObject.get("id"); String captcha = JSON.toJSONString(jsonObject.get("captcha")); String backgroundImage = JSON.toJSONString(JSON.parseObject(captcha).get("backgroundImage")); - String track = codeService.codeResolve(backgroundImage, templeCode); + String templateImage = JSON.toJSONString(JSON.parseObject(captcha).get("templateImage")); + List trackList = codeService.codeResolve(backgroundImage, templateImage); + ZlbOrderInfo orderInfo = new ZlbOrderInfo(); + orderInfo.setId(id); //获取验证码轨迹 - ZlbOrderInfo zlbOrderInfo = JSONObject.parseObject(track, ZlbOrderInfo.class); - zlbOrderInfo.setSiteOrderDetailsStr(siteOrderDetailsStr); - String jsonString = JSON.toJSONString(zlbOrderInfo); + List trackListList = convert(trackList); + ZlbOrderInfo.ZlbData data = new ZlbOrderInfo.ZlbData(); +// data.setBgImageWidth(); +// data.setBgImageHeight(); +// data.setStartTime(); +// data.setStopTime(); + data.setTrackList(trackListList); + + orderInfo.setData(data); + orderInfo.setSiteOrderDetailsStr(siteOrderDetailsStr); + String jsonString = JSON.toJSONString(orderInfo); log.info("json:\n {}", jsonString); return jsonString; } + private List convert(List trackList) { + List result = Lists.newArrayList(); + + for (String track : trackList) { + String[] split = track.split(","); + String x = split[0]; + String y = split[1]; + ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList(); + 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"); + + } + + return result; + } + @Override public void deleteRedis(String name) { // 定义模糊匹配模式 -- 2.49.1 From 582beb13db1bb98dec2b0d40ec240c7bd5d6b829 Mon Sep 17 00:00:00 2001 From: Xiang Date: Thu, 7 May 2026 16:38:59 +0800 Subject: [PATCH 08/32] =?UTF-8?q?feat:zlb=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/pojo/TrackPoint.java | 29 +++ .../xiang/common/pojo/code/YcCodeRequest.java | 2 + .../common/pojo/jntyzx/zlb/ZlbCaptcha.java | 26 +++ .../pojo/jntyzx/zlb/ZlbCaptchaResp.java | 17 ++ .../xiang/common/service/CodeServiceImpl.java | 2 +- .../xiang/common/utils/Base64ImageScaler.java | 97 +++++++++ .../xiang/common/utils/TrajectoryUtil.java | 198 ++++++++++++++++++ .../jntyzx/zlb/schedule/ZlbSiteTask.java | 7 +- .../jntyzx/zlb/service/ZlbServiceImpl.java | 115 +++++++--- 9 files changed, 454 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/xiang/common/pojo/TrackPoint.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptcha.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptchaResp.java create mode 100644 src/main/java/com/xiang/common/utils/Base64ImageScaler.java create mode 100644 src/main/java/com/xiang/common/utils/TrajectoryUtil.java diff --git a/src/main/java/com/xiang/common/pojo/TrackPoint.java b/src/main/java/com/xiang/common/pojo/TrackPoint.java new file mode 100644 index 0000000..43ba8b2 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/TrackPoint.java @@ -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); + } +} diff --git a/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java index e0ae7da..2ab8493 100644 --- a/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java +++ b/src/main/java/com/xiang/common/pojo/code/YcCodeRequest.java @@ -1,5 +1,6 @@ package com.xiang.common.pojo.code; +import com.alibaba.fastjson.annotation.JSONField; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -25,5 +26,6 @@ public class YcCodeRequest { /** * 模板图片 base64 */ + @JSONField(name = "label_image") private String labelImage; } diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptcha.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptcha.java new file mode 100644 index 0000000..c49d0aa --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptcha.java @@ -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; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptchaResp.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptchaResp.java new file mode 100644 index 0000000..6be9574 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbCaptchaResp.java @@ -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; +} diff --git a/src/main/java/com/xiang/common/service/CodeServiceImpl.java b/src/main/java/com/xiang/common/service/CodeServiceImpl.java index 5c52888..d50203a 100644 --- a/src/main/java/com/xiang/common/service/CodeServiceImpl.java +++ b/src/main/java/com/xiang/common/service/CodeServiceImpl.java @@ -10,7 +10,6 @@ import com.xiang.common.pojo.code.YcCodeRequest; import com.xiang.common.utils.HttpService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; import java.util.Arrays; @@ -52,6 +51,7 @@ public class CodeServiceImpl implements ICodeService { ycCodeRequest.setImage(image); ycCodeRequest.setToken("9LQ1ATKVEeO8Arhq-HavXzpHvkzdZz_r7ydmqlYhp9c"); ycCodeRequest.setLabelImage(templateImage); + ycCodeRequest.setType(YcCodeTypeEnum.YC_310700.getType()); String resp = HttpService.doPost(YUN_CODE_API_URL, header, JSON.toJSONString(ycCodeRequest)); YcCodeBaseResponse response = JSON.parseObject(resp, new TypeReference>() { diff --git a/src/main/java/com/xiang/common/utils/Base64ImageScaler.java b/src/main/java/com/xiang/common/utils/Base64ImageScaler.java new file mode 100644 index 0000000..dcd3a12 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/Base64ImageScaler.java @@ -0,0 +1,97 @@ +package com.xiang.common.utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Base64; + +/** + * Base64图片缩放工具类 + */ +public class Base64ImageScaler { + /** + * 将Base64图片等比例缩小1倍(缩小到原来的50%) + * @param base64Image 原始Base64图片(支持带或不带data:image前缀) + * @return 缩放后的Base64图片(保持原格式) + */ + public static String scaleToHalf(String base64Image) { + try { + // 1. 解析Base64数据 + String base64Data = base64Image; + String prefix = ""; + + if (base64Image.contains(",")) { + String[] parts = base64Image.split(","); + prefix = parts[0] + ","; + base64Data = parts[1]; + } + + // 2. Base64解码为字节数组 + byte[] imageBytes = Base64.getDecoder().decode(base64Data); + + // 3. 字节数组转BufferedImage + ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes); + BufferedImage originalImage = ImageIO.read(bis); + + // 4. 计算新尺寸(缩小一半) + int originalWidth = originalImage.getWidth(); + int originalHeight = originalImage.getHeight(); + int newWidth = originalWidth / 2; + int newHeight = originalHeight / 2; + + // 5. 创建缩放后的图片 + BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, originalImage.getType()); + Graphics2D g2d = scaledImage.createGraphics(); + + // 设置高质量缩放算法 + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + // 绘制缩放后的图片 + g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null); + g2d.dispose(); + + // 6. BufferedImage转字节数组 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String format = getImageFormat(originalImage); + ImageIO.write(scaledImage, format, baos); + byte[] scaledBytes = baos.toByteArray(); + + // 7. 字节数组转Base64 + String scaledBase64 = Base64.getEncoder().encodeToString(scaledBytes); + + // 8. 返回带前缀的Base64(如果原图有前缀则保留) + return prefix.isEmpty() ? scaledBase64 : prefix + scaledBase64; + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("图片缩放失败: " + e.getMessage()); + } + } + + /** + * 获取图片格式 + */ + private static String getImageFormat(BufferedImage image) { + // 简单判断,默认返回png + return "png"; + } + + /** + * 使用示例 + */ + public static void main(String[] args) { + // 您的Base64图片 + String yourBase64Image = "data:image/jpeg;base64,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"; + + // 调用方法缩小1倍 + String scaledBase64 = scaleToHalf(yourBase64Image); + + System.out.println("缩放完成!"); + System.out.println("原始长度: " + yourBase64Image.length()); + System.out.println("缩放后长度: " + scaledBase64.length()); + System.out.println("缩放后的Base64: " + scaledBase64); + } +} \ No newline at end of file diff --git a/src/main/java/com/xiang/common/utils/TrajectoryUtil.java b/src/main/java/com/xiang/common/utils/TrajectoryUtil.java new file mode 100644 index 0000000..91951d1 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/TrajectoryUtil.java @@ -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 generateMove(int fromX, int fromY, int toX, int toY, int start ,int duration, int pointCount) { + List 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 generateClick(int x, int y, int startTime) { + List 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 getRandomPoints(int fromX, int fromY, int toX, int toY, int count) { + List 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 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 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 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 + ")"); + } + +} \ No newline at end of file diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java index 093796d..4a8ae9b 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java @@ -10,7 +10,6 @@ import com.xiang.service.module.jntyzx.zlb.service.ZlbService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import javax.annotation.Resource; import java.util.Date; @Component @@ -44,9 +43,9 @@ public class ZlbSiteTask extends BaseScheduleTaskTemplate { TaskResult taskResult = new TaskResult(); //获取当前时间的后一天 - Date date1 = DateUtils.addDate(new Date(), 2); - Date date2 = DateUtils.addDate(new Date(), 3); - Date date3 = DateUtils.addDate(new Date(), 4); + Date date1 = DateUtils.addDate(new Date(), 0); + Date date2 = DateUtils.addDate(new Date(), 1); + Date date3 = DateUtils.addDate(new Date(), 2); String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD); String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD); String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 16fe71a..e60d402 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -1,6 +1,5 @@ package com.xiang.service.module.jntyzx.zlb.service; -import cn.hutool.http.HttpRequest; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson2.JSON; 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.google.common.collect.Lists; 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.utils.AESECBUtils; +import com.xiang.common.utils.Base64ImageScaler; import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.OkHttpUtil; +import com.xiang.common.utils.TrajectoryUtil; import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; 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.stream.Collectors; @@ -35,6 +52,7 @@ import java.util.stream.Collectors; public class ZlbServiceImpl implements ZlbService { + public static volatile boolean running = true; @Resource private ZlbSiteInfoService zlbSiteInfoService; @Resource @@ -48,7 +66,6 @@ public class ZlbServiceImpl implements ZlbService { @Autowired private ICodeService codeService; - @Override public void queryZLbSiteInfo(String ymdDate, Integer type) throws Exception { log.info("开始查询场地信息"); @@ -361,23 +378,38 @@ public class ZlbServiceImpl implements ZlbService { @Override public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException { + LocalDateTime startTime = LocalDateTime.now(); + //获取图片验证码 String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); - JSONObject jsonObject = JSON.parseObject(s); - String id = (String) jsonObject.get("id"); - String captcha = JSON.toJSONString(jsonObject.get("captcha")); - String backgroundImage = JSON.toJSONString(JSON.parseObject(captcha).get("backgroundImage")); - String templateImage = JSON.toJSONString(JSON.parseObject(captcha).get("templateImage")); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + 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 trackList = codeService.codeResolve(backgroundImage, templateImage); + ZlbOrderInfo orderInfo = new ZlbOrderInfo(); orderInfo.setId(id); //获取验证码轨迹 List trackListList = convert(trackList); ZlbOrderInfo.ZlbData data = new ZlbOrderInfo.ZlbData(); -// data.setBgImageWidth(); -// data.setBgImageHeight(); -// data.setStartTime(); -// data.setStopTime(); + data.setBgImageWidth(zlbCaptchaResp.getCaptcha().getBackgroundImageWidth() / 2); + data.setBgImageHeight(zlbCaptchaResp.getCaptcha().getBackgroundImageHeight() / 2); + data.setStartTime(startTime.toString()); + data.setStopTime(LocalDateTime.now().toString()); data.setTrackList(trackListList); orderInfo.setData(data); @@ -388,27 +420,44 @@ public class ZlbServiceImpl implements ZlbService { } private List convert(List trackList) { + int t = 0; List result = Lists.newArrayList(); - for (String track : trackList) { - String[] split = track.split(","); - String x = split[0]; - String y = split[1]; - ZlbOrderInfo.ZlbData.TrackList data = new ZlbOrderInfo.ZlbData.TrackList(); - 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"); - + for (int i = 0; i < trackList.size(); i++) { + String currentTrack = trackList.get(i); + String nextTrack = null; + if (i != trackList.size() - 1) { + nextTrack = trackList.get(i + 1); + } + t += 100; + Integer currentX = Integer.parseInt(currentTrack.split(",")[0]); + Integer currentY = Integer.parseInt(currentTrack.split(",")[1]); + List 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 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; } @@ -431,8 +480,6 @@ public class ZlbServiceImpl implements ZlbService { redisTemplate.expire(redisKey, 1234, TimeUnit.SECONDS); } - public static volatile boolean running = true; - @Override public void jianlou(String name, String day, long time) throws Exception { jntyzxDingTalkFactory.sendMsg(name + "自定义捡漏开始捡漏时间:" + day + " 捡漏人:" + name + " 捡漏间隔:" + time + "ms"); -- 2.49.1 From c387f81225649fd115e9040da67137e0c36682d0 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 09:19:04 +0800 Subject: [PATCH 09/32] =?UTF-8?q?feat:zlb=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/utils/ImageUtils.java | 68 ++++ .../common/utils/ZlbCaptchaTrackDebugger.java | 361 ++++++++++++++++++ .../common/utils/ZlbCaptchaTrackUtil.java | 117 ++++++ .../jntyzx/zlb/schedule/ZlbOrderTask.java | 9 +- .../jntyzx/zlb/service/ZlbServiceImpl.java | 44 +-- src/main/resources/image.txt | 1 + 6 files changed, 555 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/xiang/common/utils/ImageUtils.java create mode 100644 src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java create mode 100644 src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java create mode 100644 src/main/resources/image.txt diff --git a/src/main/java/com/xiang/common/utils/ImageUtils.java b/src/main/java/com/xiang/common/utils/ImageUtils.java new file mode 100644 index 0000000..b4bce26 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/ImageUtils.java @@ -0,0 +1,68 @@ +package com.xiang.common.utils; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Base64; + +/** + * @Author: xiang + * @Date: 2026-05-08 08:56 + */ +public class ImageUtils { + /** + * 将 Base64 图片写入文件 + * @param base64String Base64 编码的图片字符串 + * @param outputPath 输出文件路径 + */ + public static void saveBase64Image(String base64String, String outputPath) { + try { + // 处理可能包含前缀的 Base64(如 data:image/png;base64,) + String base64Data = extractBase64Data(base64String); + + // 解码 Base64 + byte[] imageBytes = Base64.getDecoder().decode(base64Data); + + // 写入文件 + try (FileOutputStream fos = new FileOutputStream(outputPath)) { + fos.write(imageBytes); + } + + System.out.println("图片已保存至: " + outputPath); + + } catch (IllegalArgumentException e) { + System.err.println("Base64 解码失败: " + e.getMessage()); + } catch (IOException e) { + System.err.println("文件写入失败: " + e.getMessage()); + } + } + /** + * 提取纯 Base64 数据(去除前缀) + */ + private static String extractBase64Data(String base64String) { + if (base64String == null || base64String.isEmpty()) { + throw new IllegalArgumentException("Base64 字符串不能为空"); + } + + // 检查是否包含 data:image/xxx;base64, 前缀 + if (base64String.contains(",")) { + return base64String.split(",", 2)[1]; + } + + return base64String; + } + + /** + * 从 Base64 字符串中获取图片类型 + */ + public static String getImageType(String base64String) { + if (base64String.startsWith("data:image/")) { + String type = base64String.substring(11, base64String.indexOf(";")); + return type; // 返回 png, jpeg, gif 等 + } + return "png"; // 默认类型 + } + + public static void main(String[] args) { + + } +} diff --git a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java new file mode 100644 index 0000000..229346b --- /dev/null +++ b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java @@ -0,0 +1,361 @@ +package com.xiang.common.utils; + +import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderInfo; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +/** + * ZLB 验证码轨迹调试器。 + * 输入原始图片和轨迹点,输出控制台预览与带标注调试图。 + */ +@Slf4j +public class ZlbCaptchaTrackDebugger { + + private static final DateTimeFormatter FILE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS"); + private static final int DEFAULT_CONSOLE_WIDTH = 84; + private static final int DEFAULT_CONSOLE_HEIGHT = 24; + private static final Path OUTPUT_DIR = Paths.get("logs", "script", "track-debug"); + private static final String DEFAULT_REQUEST_ID = "debug-demo"; + private static final String DEFAULT_COORDINATE_TEXT = "226,89|76,62|125,101|26,114"; + private static final Path DEFAULT_IMAGE_FILE = Paths.get("src", "main", "resources", "image.txt"); + + private ZlbCaptchaTrackDebugger() { + } + + public static DebugResult debug(String base64Image, + List rawTrackList, + List generatedTrackList, + String requestId) { + try { + BufferedImage originalImage = decodeBase64Image(base64Image); + List rawPoints = parseRawTrackList(rawTrackList); + String consolePreview = buildConsolePreview(originalImage, rawPoints, generatedTrackList, requestId); + String debugImagePath = exportDebugImage(originalImage, rawPoints, generatedTrackList, requestId); + return new DebugResult(consolePreview, debugImagePath); + } catch (Exception e) { + log.warn("生成 ZLB 验证码轨迹调试信息失败", e); + return new DebugResult(null, null); + } + } + + public static ExecutionResult execute(String imageBase64, String coordinateText, String requestId) { + List rawTrackList = ZlbCaptchaTrackUtil.parseCoordinateText(coordinateText); + List generatedTrackList = ZlbCaptchaTrackUtil.generateBezierTrackList(rawTrackList); + DebugResult debugResult = debug(imageBase64, rawTrackList, generatedTrackList, requestId); + return new ExecutionResult(generatedTrackList, debugResult.getConsolePreview(), debugResult.getDebugImagePath()); + } + + public static void main(String[] args) { + if (args.length == 0) { + runLocalSample(); + return; + } + if (args.length < 3) { + System.out.println("Usage: ZlbCaptchaTrackDebugger "); + return; + } + ExecutionResult result = execute(args[2], args[1], args[0]); + System.out.println("trackList=" + result.getTrackListJson()); + if (result.getConsolePreview() != null) { + System.out.println(result.getConsolePreview()); + } + if (result.getDebugImagePath() != null) { + System.out.println("debugImagePath=" + result.getDebugImagePath()); + } + } + + public static void runLocalSample() { + try { + String imageBase64 = loadDefaultImageBase64(); + ImageUtils.saveBase64Image(imageBase64, OUTPUT_DIR.toFile() + "image.jpeg"); + ExecutionResult result = execute(imageBase64, DEFAULT_COORDINATE_TEXT, DEFAULT_REQUEST_ID); + System.out.println("trackList=" + result.getTrackListJson()); + if (result.getConsolePreview() != null) { + System.out.println(result.getConsolePreview()); + } + if (result.getDebugImagePath() != null) { + System.out.println("debugImagePath=" + result.getDebugImagePath()); + } + } catch (Exception e) { + throw new RuntimeException("运行本地样例失败,请先把 image base64 写入 " + DEFAULT_IMAGE_FILE.toAbsolutePath(), e); + } + } + + private static String loadDefaultImageBase64() throws IOException { + return new String(Files.readAllBytes(DEFAULT_IMAGE_FILE), StandardCharsets.UTF_8).trim(); + } + + private static BufferedImage decodeBase64Image(String base64Image) throws IOException { + String base64Data = base64Image; + if (base64Image != null && base64Image.contains(",")) { + base64Data = base64Image.substring(base64Image.indexOf(',') + 1); + } + byte[] imageBytes = Base64.getDecoder().decode(base64Data); + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes)) { + BufferedImage image = ImageIO.read(inputStream); + if (image == null) { + throw new IOException("base64 图片解码结果为空"); + } + return image; + } + } + + private static List parseRawTrackList(List rawTrackList) { + if (rawTrackList == null || rawTrackList.isEmpty()) { + return Collections.emptyList(); + } + List points = new ArrayList<>(rawTrackList.size()); + for (String item : rawTrackList) { + if (item == null || item.trim().isEmpty()) { + continue; + } + String[] split = item.split(","); + if (split.length < 2) { + continue; + } + points.add(new Point(Integer.parseInt(split[0].trim()), Integer.parseInt(split[1].trim()))); + } + return points; + } + + private static String buildConsolePreview(BufferedImage originalImage, + List rawPoints, + List generatedTrackList, + String requestId) { + int width = originalImage.getWidth(); + int height = originalImage.getHeight(); + int consoleWidth = Math.min(DEFAULT_CONSOLE_WIDTH, Math.max(24, width)); + int consoleHeight = Math.min(DEFAULT_CONSOLE_HEIGHT, Math.max(12, (int) Math.round((double) height / width * consoleWidth * 0.5d))); + + char[][] canvas = new char[consoleHeight][consoleWidth]; + for (int row = 0; row < consoleHeight; row++) { + for (int col = 0; col < consoleWidth; col++) { + canvas[row][col] = '.'; + } + } + + overlayRawPoints(canvas, rawPoints, width, height); + overlayGeneratedTrack(canvas, generatedTrackList, width, height); + + StringBuilder preview = new StringBuilder(); + preview.append('\n'); + preview.append("===== ZLB CAPTCHA DEBUG =====").append('\n'); + preview.append("requestId=").append(sanitizeFileName(requestId)) + .append(", image=").append(width).append("x").append(height) + .append(", rawPoints=").append(rawPoints.size()) + .append(", generatedPoints=").append(generatedTrackList == null ? 0 : generatedTrackList.size()) + .append('\n'); + preview.append("rawPoints=").append(buildRawPointSummary(rawPoints)).append('\n'); + preview.append("generatedTrack=").append(buildTrackSummary(generatedTrackList)).append('\n'); + preview.append("grid: R=raw point, C=click, M=move, B=overlap, .=empty").append('\n'); + for (char[] chars : canvas) { + preview.append(chars).append('\n'); + } + preview.append("================================").append('\n'); + return preview.toString(); + } + + private static String exportDebugImage(BufferedImage originalImage, + List rawPoints, + List generatedTrackList, + String requestId) throws IOException { + BufferedImage debugImage = new BufferedImage( + originalImage.getWidth(), + originalImage.getHeight(), + BufferedImage.TYPE_INT_ARGB + ); + Graphics2D graphics = debugImage.createGraphics(); + try { + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + graphics.drawImage(originalImage, 0, 0, null); + drawGeneratedTrack(graphics, generatedTrackList); + drawRawPoints(graphics, rawPoints); + drawLegend(graphics); + } finally { + graphics.dispose(); + } + + Files.createDirectories(OUTPUT_DIR); + String fileName = String.format( + "zlb_track_%s_%s.png", + sanitizeFileName(requestId), + FILE_TIME_FORMATTER.format(LocalDateTime.now()) + ); + Path outputPath = OUTPUT_DIR.resolve(fileName).toAbsolutePath(); + ImageIO.write(debugImage, "png", outputPath.toFile()); + return outputPath.toString(); + } + + private static void overlayRawPoints(char[][] canvas, List rawPoints, int width, int height) { + for (Point rawPoint : rawPoints) { + int col = scaleToConsole(rawPoint.x, width, canvas[0].length); + int row = scaleToConsole(rawPoint.y, height, canvas.length); + paintOverlay(canvas, row, col, 'R'); + } + } + + private static void overlayGeneratedTrack(char[][] canvas, + List generatedTrackList, + int width, + int height) { + if (generatedTrackList == null) { + return; + } + for (ZlbOrderInfo.ZlbData.TrackList trackPoint : generatedTrackList) { + int col = scaleToConsole(trackPoint.getX(), width, canvas[0].length); + int row = scaleToConsole(trackPoint.getY(), height, canvas.length); + char mark = "click".equalsIgnoreCase(trackPoint.getType()) ? 'C' : 'M'; + paintOverlay(canvas, row, col, mark); + } + } + + private static void paintOverlay(char[][] canvas, int row, int col, char mark) { + char current = canvas[row][col]; + if (current == 'R' || current == 'C' || current == 'M' || current == 'B') { + canvas[row][col] = current == mark ? current : 'B'; + return; + } + canvas[row][col] = mark; + } + + private static int scaleToConsole(int value, int originalSize, int consoleSize) { + if (originalSize <= 1) { + return 0; + } + int scaled = (int) Math.round(value * (consoleSize - 1d) / (originalSize - 1d)); + return Math.max(0, Math.min(consoleSize - 1, scaled)); + } + + private static String buildTrackSummary(List generatedTrackList) { + if (generatedTrackList == null || generatedTrackList.isEmpty()) { + return "[]"; + } + StringBuilder summary = new StringBuilder("["); + for (int i = 0; i < generatedTrackList.size(); i++) { + ZlbOrderInfo.ZlbData.TrackList trackPoint = generatedTrackList.get(i); + if (i > 0) { + summary.append(", "); + } + summary.append("{x=").append(trackPoint.getX()) + .append(", y=").append(trackPoint.getY()) + .append(", type=").append(trackPoint.getType()) + .append(", t=").append(trackPoint.getT()) + .append('}'); + } + summary.append(']'); + return summary.toString(); + } + + private static String buildRawPointSummary(List rawPoints) { + if (rawPoints == null || rawPoints.isEmpty()) { + return "[]"; + } + StringBuilder summary = new StringBuilder("["); + for (int i = 0; i < rawPoints.size(); i++) { + Point point = rawPoints.get(i); + if (i > 0) { + summary.append(", "); + } + summary.append("P").append(i) + .append("{x=").append(point.x) + .append(", y=").append(point.y) + .append('}'); + } + summary.append(']'); + return summary.toString(); + } + + private static void drawRawPoints(Graphics2D graphics, List rawPoints) { + graphics.setFont(new Font("SansSerif", Font.BOLD, 13)); + graphics.setStroke(new BasicStroke(2.5f)); + for (int i = 0; i < rawPoints.size(); i++) { + Point point = rawPoints.get(i); + graphics.setColor(new Color(30, 144, 255, 210)); + graphics.drawOval(point.x - 8, point.y - 8, 16, 16); + graphics.setColor(Color.WHITE); + graphics.drawString("P" + i, point.x + 8, point.y - 8); + } + } + + private static void drawGeneratedTrack(Graphics2D graphics, List generatedTrackList) { + if (generatedTrackList == null || generatedTrackList.isEmpty()) { + return; + } + graphics.setFont(new Font("SansSerif", Font.PLAIN, 12)); + graphics.setStroke(new BasicStroke(2.5f)); + for (int i = 0; i < generatedTrackList.size(); i++) { + ZlbOrderInfo.ZlbData.TrackList current = generatedTrackList.get(i); + if (i > 0) { + ZlbOrderInfo.ZlbData.TrackList previous = generatedTrackList.get(i - 1); + graphics.setColor(new Color(255, 99, 71, 180)); + graphics.drawLine(previous.getX(), previous.getY(), current.getX(), current.getY()); + } + Color pointColor = "click".equalsIgnoreCase(current.getType()) + ? new Color(220, 20, 60, 220) + : new Color(255, 165, 0, 220); + graphics.setColor(pointColor); + graphics.fillOval(current.getX() - 5, current.getY() - 5, 10, 10); + graphics.setColor(Color.WHITE); + graphics.drawOval(current.getX() - 5, current.getY() - 5, 10, 10); +// graphics.drawString(current.getType() + "(" + current.getT() + ")", current.getX() + 6, current.getY() + 16); + } + } + + private static void drawLegend(Graphics2D graphics) { + int boxX = 12; + int boxY = 12; + int boxWidth = 250; + int boxHeight = 62; +// graphics.setColor(new Color(0, 0, 0, 135)); +// graphics.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 12, 12); +// graphics.setColor(Color.WHITE); +// graphics.setFont(new Font("SansSerif", Font.BOLD, 13)); +// graphics.drawString("Blue: raw points", boxX + 12, boxY + 22); +// graphics.drawString("Red: click track", boxX + 12, boxY + 40); +// graphics.drawString("Orange: move track", boxX + 12, boxY + 58); + } + + private static String sanitizeFileName(String value) { + if (value == null || value.trim().isEmpty()) { + return "unknown"; + } + return value.replaceAll("[^a-zA-Z0-9_-]", "_"); + } + + @Data + @AllArgsConstructor + public static class DebugResult { + private String consolePreview; + private String debugImagePath; + } + + @Data + @AllArgsConstructor + public static class ExecutionResult { + private List trackList; + private String consolePreview; + private String debugImagePath; + + public String getTrackListJson() { + return com.alibaba.fastjson2.JSON.toJSONString(trackList); + } + } +} diff --git a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java new file mode 100644 index 0000000..e20d3c7 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java @@ -0,0 +1,117 @@ +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")); + } + } + } + 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); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java index d524a30..c762a2f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -2,8 +2,10 @@ package com.xiang.service.module.jntyzx.zlb.schedule; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.exception.BusinessException; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; @@ -18,12 +20,9 @@ import com.xiang.service.module.jntyzx.zlb.service.ZlbService; import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; import com.xiang.service.module.jntyzx.zlb.service.ZlbUserInfoService; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import javax.annotation.Resource; -import java.io.IOException; import java.time.Duration; import java.time.LocalTime; import java.util.Date; @@ -101,6 +100,10 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { String siteOrderDetailsStr = zlbService.buildSiteOrder(zlbUserInfo, secretKey, day); Map headers = zlbService.getHeaders(zlbTokenInfo.getTokenId()); String newOrderJson = zlbService.buildNewOrder(siteOrderDetailsStr, client); + if (StringUtils.isBlank(newOrderJson)) { + log.info("构建订单参数异常:{}", siteOrderDetailsStr); + throw new BusinessException(""); + } ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); //计算9点到现在的时间差 //获取江体当前时间 diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index e60d402..24c8798 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -8,9 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.google.common.collect.Lists; import com.xiang.common.factory.JntyzxDingTalkFactory; -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; @@ -25,7 +23,7 @@ import com.xiang.common.utils.AESECBUtils; import com.xiang.common.utils.Base64ImageScaler; import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.OkHttpUtil; -import com.xiang.common.utils.TrajectoryUtil; +import com.xiang.common.utils.ZlbCaptchaTrackUtil; import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -420,43 +418,7 @@ public class ZlbServiceImpl implements ZlbService { } private List convert(List trackList) { - int t = 0; - List result = Lists.newArrayList(); - - for (int i = 0; i < trackList.size(); i++) { - String currentTrack = trackList.get(i); - String nextTrack = null; - if (i != trackList.size() - 1) { - nextTrack = trackList.get(i + 1); - } - t += 100; - Integer currentX = Integer.parseInt(currentTrack.split(",")[0]); - Integer currentY = Integer.parseInt(currentTrack.split(",")[1]); - List 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 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); - } - } + List result = ZlbCaptchaTrackUtil.generateBezierTrackList(trackList); log.info("生成的轨迹点结果:{}", JSON.toJSONString(result)); return result; } @@ -688,5 +650,3 @@ public class ZlbServiceImpl implements ZlbService { } } } - - diff --git a/src/main/resources/image.txt b/src/main/resources/image.txt new file mode 100644 index 0000000..eb16686 --- /dev/null +++ b/src/main/resources/image.txt @@ -0,0 +1 @@ +data:image/jpeg;base64,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 \ No newline at end of file -- 2.49.1 From 3964547e8478d60e6e7e8066a6746eede2b8b824 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 09:23:50 +0800 Subject: [PATCH 10/32] =?UTF-8?q?debugger:zlb=E4=B8=8B=E5=8D=95=E8=B0=83?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/utils/ZlbCaptchaTrackUtil.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java index e20d3c7..d09c878 100644 --- a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java +++ b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackUtil.java @@ -33,11 +33,14 @@ public class ZlbCaptchaTrackUtil { 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")); - } +// 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; -- 2.49.1 From 8a302db65a70e347fe34bb83719255428045bdbb Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 12:02:45 +0800 Subject: [PATCH 11/32] =?UTF-8?q?feat:zlb=E4=BB=BB=E5=8A=A1=E8=B0=83?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/ZlbCaptchaTrackDebugger.java | 7 +++++++ .../jntyzx/zlb/constants/ZlbUrlConstants.java | 1 + .../jntyzx/zlb/schedule/ZlbOrderTask.java | 20 +++++++++---------- .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 3 +++ .../jntyzx/zlb/service/ZlbServiceImpl.java | 14 ++++++++++--- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java index 229346b..4a9ceee 100644 --- a/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java +++ b/src/main/java/com/xiang/common/utils/ZlbCaptchaTrackDebugger.java @@ -62,6 +62,13 @@ public class ZlbCaptchaTrackDebugger { return new ExecutionResult(generatedTrackList, debugResult.getConsolePreview(), debugResult.getDebugImagePath()); } + public static ExecutionResult execute(String imageBase64, List coordinateText, String requestId) { + List rawTrackList = coordinateText; + List generatedTrackList = ZlbCaptchaTrackUtil.generateBezierTrackList(rawTrackList); + DebugResult debugResult = debug(imageBase64, rawTrackList, generatedTrackList, requestId); + return new ExecutionResult(generatedTrackList, debugResult.getConsolePreview(), debugResult.getDebugImagePath()); + } + public static void main(String[] args) { if (args.length == 0) { runLocalSample(); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java index ca8f9fe..e58a6c9 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/constants/ZlbUrlConstants.java @@ -40,6 +40,7 @@ public class ZlbUrlConstants { public static final String siteStr = "{\"stadiumId\":\"49\",\"siteItemId\":1940,\"belongDate\":\"%s\",\"channelType\":6}"; + public static final String siteQMStr = "{\"stadiumId\":\"185\",\"siteItemId\":194,\"belongDate\":\"%s\",\"channelType\":6}"; public static final String siteWqStr = "{\"stadiumId\":\"360112\",\"siteItemId\":1148,\"belongDate\":\"%s\",\"channelType\":6}"; public static final String refundStr = "{\"detailOrderId\":%s,\"detailOrderIds\":[%s],\"type\":2}"; public static final String cancelStr = "{\"orderId\":%s,\"type\":2}"; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java index c762a2f..fc702ab 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -79,7 +79,7 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(ZlbUserInfo::getIsBook, 0); wrapper.eq(ZlbUserInfo::getName, "xiang"); - Date date = DateUtils.addDate(new Date(), 0); + Date date = DateUtils.addDate(new Date(), 1); String day = DateUtils.format(date, DateUtils.ENUM_FORMAT_YMD); wrapper.eq(ZlbUserInfo::getWeek, DateUtils.getWeekDayTwo(day)); ZlbUserInfo zlbUserInfo = zlbUserInfoService.getOne(wrapper); @@ -111,15 +111,15 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { LocalTime targetTime = LocalTime.parse("09:00:11.800"); Duration duration = Duration.between(currentTime, targetTime); long milliseconds = duration.toMillis(); -// executorService.schedule(() -> { - String response = null; - try { - response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); - } catch (Exception e) { - throw new RuntimeException(e); - } - buildOrder(name, response, placeName, siteTimeName); -// }, milliseconds, TimeUnit.MILLISECONDS); + executorService.schedule(() -> { + String response = null; + try { + response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); + } catch (Exception e) { + throw new RuntimeException(e); + } + buildOrder(name, response, placeName, siteTimeName); + }, milliseconds, TimeUnit.MILLISECONDS); return taskResult; } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index 8c3e1ac..7f5d0d6 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -30,16 +30,19 @@ public class ZlbTaskConfig { } @GetMapping("/zlbSiteTask") + @Scheduled(cron = "30 30 16 * * ?") public void zlbSiteTask() { zlbSiteTask.run(); } @GetMapping("/zlbSiteDayTask") + @Scheduled(cron = "0 00 17 * * ?") public void zlbSiteDayTask() { zlbSiteDayTask.run(); } @GetMapping("/zlbOrderCreateTask") + @Scheduled(cron = "55 59 8 * * ?") public void zlbOrderCreateTask() { zlbOrderTask.run(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 24c8798..32593fc 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -32,7 +32,8 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.IOException; -import java.time.LocalDateTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -376,7 +377,7 @@ public class ZlbServiceImpl implements ZlbService { @Override public String buildNewOrder(String siteOrderDetailsStr, OkHttpUtil client) throws IOException { - LocalDateTime startTime = LocalDateTime.now(); + Instant startTime = Instant.now().truncatedTo(ChronoUnit.MILLIS); //获取图片验证码 String s = client.postJson(ZlbUrlConstants.captchaUrl, null, "{}"); @@ -403,11 +404,18 @@ public class ZlbServiceImpl implements ZlbService { orderInfo.setId(id); //获取验证码轨迹 List trackListList = convert(trackList); + +// ZlbCaptchaTrackDebugger.ExecutionResult execute = ZlbCaptchaTrackDebugger.execute(backgroundImage, trackList, "zlb-captcha-debugger"); +// List trackListList = execute.getTrackList(); + + ZlbOrderInfo.ZlbData data = new ZlbOrderInfo.ZlbData(); data.setBgImageWidth(zlbCaptchaResp.getCaptcha().getBackgroundImageWidth() / 2); data.setBgImageHeight(zlbCaptchaResp.getCaptcha().getBackgroundImageHeight() / 2); data.setStartTime(startTime.toString()); - data.setStopTime(LocalDateTime.now().toString()); + + Integer t = trackListList.get(trackListList.size() - 1).getT(); + data.setStopTime(startTime.plus(t, ChronoUnit.MILLIS).truncatedTo(ChronoUnit.MILLIS).toString()); data.setTrackList(trackListList); orderInfo.setData(data); -- 2.49.1 From 8c4ea440b65caff699181610591ff451c31539e4 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:05:51 +0800 Subject: [PATCH 12/32] =?UTF-8?q?feat:zlb=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/enums/ScheduleEnums.java | 1 + .../common/mapper/ZlbUserInfoMapper.java | 3 + .../common/pojo/jntyzx/zlb/ZlbTokenInfo.java | 2 + .../common/pojo/jntyzx/zlb/ZlbUserInfo.java | 2 + .../jntyzx/zlb/schedule/ZlbSiteTask.java | 6 +- .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 5 + .../zlb/schedule/ZlbUserConfigTask.java | 158 ++++++++++++++++++ .../zlb/service/ZlbTokenInfoService.java | 4 + .../zlb/service/ZlbTokenInfoServiceImpl.java | 9 + .../zlb/service/ZlbUserInfoService.java | 12 ++ .../zlb/service/ZlbUserInfoServiceImpl.java | 12 ++ 11 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 45daa45..58a0dab 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -18,6 +18,7 @@ public enum ScheduleEnums { ZLB_SITE_QUERY_TASK(3, "zlb", "zlbSiteQueryTask"), ZLB_SITE_DAY_TASK(3, "zlb", "zlbSiteDayTask"), ZLB_ORDER_CREATE_TASK(3, "zlb", "zlbOrderCreateTask"), + ZLB_USER_CONFIG_TASK(3, "zlb", "zlbUserConfigTask"), ; diff --git a/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java b/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java index cc38bae..516b0e8 100644 --- a/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java +++ b/src/main/java/com/xiang/common/mapper/ZlbUserInfoMapper.java @@ -2,6 +2,7 @@ package com.xiang.common.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @@ -9,6 +10,8 @@ import org.springframework.stereotype.Repository; @Mapper public interface ZlbUserInfoMapper extends BaseMapper { + @Delete("delete from zlb_user_info where 1=1") + int deleteAll(); } diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java index 99000df..0f844d3 100644 --- a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbTokenInfo.java @@ -20,6 +20,8 @@ public class ZlbTokenInfo { @TableId(type = IdType.AUTO) private Integer id; + private Integer loginInfoId; + /** * 名称 */ diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java index 59b54c3..9e46d6c 100644 --- a/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java +++ b/src/main/java/com/xiang/common/pojo/jntyzx/zlb/ZlbUserInfo.java @@ -18,6 +18,8 @@ public class ZlbUserInfo { @TableId(type = IdType.AUTO) private Integer id; + private Integer loginInfoId; + /** * 名称 */ diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java index 4a8ae9b..81d8248 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java @@ -43,9 +43,9 @@ public class ZlbSiteTask extends BaseScheduleTaskTemplate { TaskResult taskResult = new TaskResult(); //获取当前时间的后一天 - Date date1 = DateUtils.addDate(new Date(), 0); - Date date2 = DateUtils.addDate(new Date(), 1); - Date date3 = DateUtils.addDate(new Date(), 2); + Date date1 = DateUtils.addDate(new Date(), 1); + Date date2 = DateUtils.addDate(new Date(), 2); + Date date3 = DateUtils.addDate(new Date(), 3); String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD); String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD); String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index 7f5d0d6..6ba76e2 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -47,5 +47,10 @@ public class ZlbTaskConfig { zlbOrderTask.run(); } + @Scheduled(cron = "30 30 16 * * ?") + public void zlbUserConfig() { + + } + } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java new file mode 100644 index 0000000..2658352 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -0,0 +1,158 @@ +package com.xiang.service.module.jntyzx.zlb.schedule; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; +import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.zlb.service.ZlbSiteInfoService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import com.xiang.service.module.jntyzx.zlb.service.ZlbUserInfoService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @Author: xiang + * @Date: 2026-05-08 12:03 + */ +@Component +@Slf4j +public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { + + private final ZlbSiteInfoService zlbSiteInfoService; + private final ZlbUserInfoService zlbUserInfoService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + private final ZlbTokenInfoService zlbTokenInfoService; + + public ZlbUserConfigTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + ZlbSiteInfoService zlbSiteInfoService, + ZlbUserInfoService zlbUserInfoService, + ZlbTokenInfoService zlbTokenInfoService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.zlbSiteInfoService = zlbSiteInfoService; + this.zlbUserInfoService = zlbUserInfoService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + this.zlbTokenInfoService = zlbTokenInfoService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.ZLB_USER_CONFIG_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.ZLB_USER_CONFIG_TASK.getModeleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.ZLB_USER_CONFIG_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + TaskResult taskResult = new TaskResult(); + LocalDate date = LocalDate.now().plusDays(1); + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(ZlbSiteInfo::getBelongDate, date); + List siteInfoList = zlbSiteInfoService.list(lambdaQueryWrapper); + if (CollectionUtils.isEmpty(siteInfoList)) { + log.info("日期:{}无场地信息", date); + jntyzxDingTalkFactory.sendMsg("日期:" + date + "无场地信息配置"); + taskResult.setSuccess(Boolean.FALSE); + taskResult.setSummary("日期:" + date + "无场地信息配置"); + return taskResult; + } + siteInfoList = filterSiteInfo(siteInfoList); + siteInfoList = sortSiteInfo(siteInfoList); + + + List users = zlbTokenInfoService.getAllUsers(); + if (CollectionUtils.isEmpty(users)) { + log.info("日期:{}无用户信息", date); + jntyzxDingTalkFactory.sendMsg("日期:" + date + "无用户信息"); + taskResult.setSuccess(Boolean.FALSE); + taskResult.setSummary("日期:" + date + "无用户信息"); + return taskResult; + } + zlbUserInfoService.delAll(); + List list = Lists.newArrayList(); + int i = 0; + for (ZlbTokenInfo user : users) { + ZlbSiteInfo zlbSiteInfo = siteInfoList.get(i); + ZlbUserInfo zlbUserInfo = new ZlbUserInfo(); + zlbUserInfo.setLoginInfoId(user.getLoginInfoId()); + zlbUserInfo.setName(user.getName()); + zlbUserInfo.setWeek(DateUtils.getWeekDay(zlbSiteInfo.getBelongDate())); + zlbUserInfo.setType("1"); + zlbUserInfo.setPlaceName(zlbSiteInfo.getPlaceName()); + zlbUserInfo.setSiteTimeName(zlbSiteInfo.getDayEffectiveTimes()); + zlbUserInfo.setIsBook(0); + list.add(zlbUserInfo); + i++; + if (i == siteInfoList.size()) { + i = 0; + } + } + if (CollectionUtils.isNotEmpty(list)) { + zlbUserInfoService.saveBatch(list); + StringBuilder stringBuilder = new StringBuilder(); + for (ZlbUserInfo user : list) { + stringBuilder.append(user.getName()).append("配置预约:").append(user.getPlaceName()).append("\n"); + } + jntyzxDingTalkFactory.sendMsg(stringBuilder.toString()); + taskResult.setSuccess(Boolean.TRUE); + taskResult.setSummary(stringBuilder.toString()); + } + return taskResult; + } + + private List filterSiteInfo(List siteInfoList) { + return siteInfoList.stream().filter(item -> Objects.equals(item.getDayEffectiveTimes(), "20:00")).toList(); + } + + private List sortSiteInfo(List siteInfoList) { + if (CollectionUtils.isEmpty(siteInfoList)) { + return Lists.newArrayList(); + } + return siteInfoList.stream().sorted(Comparator.comparing(this::sort)).collect(Collectors.toList()); + } + + private int sort(ZlbSiteInfo siteInfo) { + String placeName = siteInfo.getPlaceName(); + if (placeName.contains("十号")) { + return 0; + } + if (placeName.contains("九号")) { + return 1; + } + if (placeName.contains("二号")) { + return 2; + } + if (placeName.contains("八号")) { + return 3; + } + if (placeName.contains("七号")) { + return 4; + } + return 5; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java index e775f29..50ccc5d 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java @@ -3,6 +3,8 @@ package com.xiang.service.module.jntyzx.zlb.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; +import java.util.List; + /** * @author a123 * @description 针对表【zlb_token_info】的数据库操作Service @@ -11,4 +13,6 @@ import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; public interface ZlbTokenInfoService extends IService { ZlbTokenInfo queryByName(String name); + + List getAllUsers(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java index 50134cd..6cf6826 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java @@ -7,6 +7,8 @@ import com.xiang.common.mapper.ZlbTokenInfoMapper; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; import org.springframework.stereotype.Service; +import java.util.List; + /** * @author a123 * @description 针对表【zlb_token_info】的数据库操作Service实现 @@ -22,6 +24,13 @@ public class ZlbTokenInfoServiceImpl extends ServiceImpl getAllUsers() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(ZlbTokenInfo::getIsDel, 0); + return baseMapper.selectList(lambdaQueryWrapper); + } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java index 9c118d7..fc2b192 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java @@ -3,6 +3,18 @@ package com.xiang.service.module.jntyzx.zlb.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; +import java.time.LocalDate; +import java.util.List; + public interface ZlbUserInfoService extends IService { + /** + * 查询日期内未预订的用户 + * @param date + * @return + */ + List getNoBookUsers(LocalDate date); + + int delAll(); + } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java index ba779c8..8420323 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java @@ -5,10 +5,22 @@ import com.xiang.common.mapper.ZlbUserInfoMapper; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; import org.springframework.stereotype.Service; +import java.time.LocalDate; +import java.util.List; + @Service public class ZlbUserInfoServiceImpl extends ServiceImpl implements ZlbUserInfoService { + @Override + public List getNoBookUsers(LocalDate date) { + return List.of(); + } + + @Override + public int delAll() { + return baseMapper.deleteAll(); + } } -- 2.49.1 From c0647b69e2948592eae52e6bb2991b1e79818af9 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:12:38 +0800 Subject: [PATCH 13/32] =?UTF-8?q?feat:zlb=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/jntyzx/zlb/schedule/ZlbLoginTask.java | 11 +++++++---- .../module/jntyzx/zlb/service/ZlbServiceImpl.java | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java index 087dc00..f15a018 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java @@ -15,7 +15,11 @@ import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; import lombok.extern.slf4j.Slf4j; -import okhttp3.*; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -68,7 +72,7 @@ public class ZlbLoginTask extends BaseScheduleTaskTemplate { return taskResult; } - OkHttpClient client = new OkHttpClient.Builder() + OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS) // 连接超时时间 .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 @@ -87,7 +91,6 @@ public class ZlbLoginTask extends BaseScheduleTaskTemplate { .addHeader("X-Timestamp", "1761122479") .addHeader("User-Agent", "000001@ZLB_iphone_7.28.0") .addHeader("guc-accountType", "person") -// .addHeader("aliyungf_tc", zlbLoginInfo.getAliyungfTc()) .addHeader("Biz-Session-Id", zlbLoginInfo.getBizSessionId()) .addHeader("guc-platform", "app") .addHeader("X-Access-Id", "szzj") @@ -107,7 +110,7 @@ public class ZlbLoginTask extends BaseScheduleTaskTemplate { .build(); Response response = client.newCall(request).execute(); String string = response.body().string(); - log.info("{}获取ticketId接口返回:{}", name,string); + log.info("{}获取ticketId接口返回:{}", name, string); JSONObject jsonObject = JSON.parseObject(string ); JSONObject data = jsonObject.getJSONObject("data"); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 32593fc..7e9d97f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -135,6 +135,10 @@ public class ZlbServiceImpl implements ZlbService { } private void dayinLog(List zlbSiteInfoList1) { + if (org.apache.commons.collections4.CollectionUtils.isEmpty(zlbSiteInfoList1)) { + log.info("无场地信息"); + return; + } Map> collect = zlbSiteInfoList1.stream().collect(Collectors.groupingBy(ZlbSiteInfo::getDayEffectiveTimes)); List zlbSiteInfos6_8 = collect.get("18:00"); List zlbSiteInfos8_10 = collect.get("20:00"); -- 2.49.1 From 4eeacf8c52dd202ed6faad3576b656a8614d2087 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:26:57 +0800 Subject: [PATCH 14/32] =?UTF-8?q?feat:=E5=9C=BA=E5=9C=B0=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=8B=89=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/jntyzx/zlb/schedule/ZlbSiteTask.java | 10 ++-------- .../module/jntyzx/zlb/service/ZlbServiceImpl.java | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java index 81d8248..bf65ed1 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java @@ -42,17 +42,11 @@ public class ZlbSiteTask extends BaseScheduleTaskTemplate { protected TaskResult doExecute(Object validatedParams) { TaskResult taskResult = new TaskResult(); - //获取当前时间的后一天 - Date date1 = DateUtils.addDate(new Date(), 1); - Date date2 = DateUtils.addDate(new Date(), 2); - Date date3 = DateUtils.addDate(new Date(), 3); + // 获取+2时间的场地信息,明天需要的是后天的场地信息 + Date date1 = DateUtils.addDate(new Date(), 2); String day1 = DateUtils.format(date1, DateUtils.ENUM_FORMAT_YMD); - String day2 = DateUtils.format(date2, DateUtils.ENUM_FORMAT_YMD); - String day3 = DateUtils.format(date3, DateUtils.ENUM_FORMAT_YMD); try { zlbService.queryZLbSiteInfo(day1, 1); - zlbService.queryZLbSiteInfo(day2, 1); - zlbService.queryZLbSiteInfo(day3, 1); } catch (Exception e) { log.error("site task error", e); taskResult.setSuccess(false); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index 7e9d97f..fd10a36 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -126,7 +126,6 @@ public class ZlbServiceImpl implements ZlbService { } } if (!zlbSiteInfoList.isEmpty()) { - zlbSiteInfoService.saveBatch(zlbSiteInfoList); } if (type.equals(1)) { -- 2.49.1 From 0a8e8537535262cf64dd591ae6a6e55a034333c2 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:28:51 +0800 Subject: [PATCH 15/32] =?UTF-8?q?feat:=E9=85=8D=E7=BD=AE=E5=9C=BA=E5=9C=B0?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java | 3 ++- .../service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index 6ba76e2..d9edb9a 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -16,6 +16,7 @@ public class ZlbTaskConfig { private final ZlbSiteTask zlbSiteTask; private final ZlbSiteDayTask zlbSiteDayTask; private final ZlbOrderTask zlbOrderTask; + private final ZlbUserConfigTask zlbUserConfigTask; @Scheduled(cron = "0 0/30 * * * ?") @GetMapping("/zlbLoginTask") @@ -49,7 +50,7 @@ public class ZlbTaskConfig { @Scheduled(cron = "30 30 16 * * ?") public void zlbUserConfig() { - + zlbUserConfigTask.run(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java index 2658352..06db0b5 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -70,7 +70,7 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { @Override protected TaskResult doExecute(Object validatedParams) throws Exception { TaskResult taskResult = new TaskResult(); - LocalDate date = LocalDate.now().plusDays(1); + LocalDate date = LocalDate.now().plusDays(2); LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); lambdaQueryWrapper.eq(ZlbSiteInfo::getBelongDate, date); List siteInfoList = zlbSiteInfoService.list(lambdaQueryWrapper); -- 2.49.1 From 04862d861af0e84d194bee33f20b5323f3716106 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:57:50 +0800 Subject: [PATCH 16/32] =?UTF-8?q?feat:=E4=BB=BB=E5=8A=A1=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/xiang/ApplicationInit.java | 66 +++++++++++++++++++ .../IScheduleOpeningConfigService.java | 5 ++ .../ScheduleOpeningConfigServiceImpl.java | 7 ++ .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 1 + .../zlb/schedule/ZlbUserConfigTask.java | 2 +- 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/xiang/ApplicationInit.java diff --git a/src/main/java/com/xiang/ApplicationInit.java b/src/main/java/com/xiang/ApplicationInit.java new file mode 100644 index 0000000..ca65b5b --- /dev/null +++ b/src/main/java/com/xiang/ApplicationInit.java @@ -0,0 +1,66 @@ +package com.xiang; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; +import com.xiang.common.service.IScheduleOpeningConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Author: xiang + * @Date: 2026-05-08 14:32 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ApplicationInit implements ApplicationRunner { + + private final IScheduleOpeningConfigService scheduleOpeningConfigService; + + @Override + public void run(ApplicationArguments args) throws Exception { + + log.info("开始加载任务配置!"); + loadScheduleTask(); + log.info("任务配置加载完成!"); + } + + private void loadScheduleTask() { + List allSchedules = scheduleOpeningConfigService.getAll(); + Map map = Maps.newHashMap(); + if (CollectionUtils.isNotEmpty(allSchedules)) { + map.putAll(allSchedules.stream().collect(Collectors.toMap(ScheduleOpeningConfigDO::getBeanName, Function.identity(), (a, b) -> a))); + } + ScheduleEnums[] enums = ScheduleEnums.values(); + if (ArrayUtils.isEmpty(enums)) { + log.info("暂无需要配置的"); + return; + } + List list = Lists.newArrayList(); + for (ScheduleEnums scheduleEnum : enums) { + if (map.containsKey(scheduleEnum.getTaskName())) { + continue; + } + ScheduleOpeningConfigDO scheduleOpeningConfigDO = new ScheduleOpeningConfigDO(); + scheduleOpeningConfigDO.setModule(scheduleEnum.getModeleCode()); + scheduleOpeningConfigDO.setBeanName(scheduleEnum.getTaskName()); + scheduleOpeningConfigDO.setStatus(1); + list.add(scheduleOpeningConfigDO); + } + if (CollectionUtils.isNotEmpty(list)) { + scheduleOpeningConfigService.saveBatch(list); + } + } +} diff --git a/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java b/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java index 597bd24..95ad703 100644 --- a/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java +++ b/src/main/java/com/xiang/common/service/IScheduleOpeningConfigService.java @@ -7,6 +7,11 @@ import java.util.List; public interface IScheduleOpeningConfigService extends IService { + /** + * 获取所有未删除的任务 + * @return + */ + List getAll(); /** * 根据模块id和任务名称查询 * @param moduleCode diff --git a/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java b/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java index 69816e3..7b3df46 100644 --- a/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java +++ b/src/main/java/com/xiang/common/service/ScheduleOpeningConfigServiceImpl.java @@ -7,9 +7,16 @@ import com.xiang.common.mapper.ScheduleOpeningConfigDao; import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; import org.springframework.stereotype.Service; +import java.util.List; + @Service public class ScheduleOpeningConfigServiceImpl extends ServiceImpl implements IScheduleOpeningConfigService { + @Override + public List getAll() { + return baseMapper.selectList(Wrappers.lambdaQuery()); + } + @Override public ScheduleOpeningConfigDO getConfigByModule(Integer moduleCode, String taskName) { LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index d9edb9a..a267f4a 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -49,6 +49,7 @@ public class ZlbTaskConfig { } @Scheduled(cron = "30 30 16 * * ?") + @GetMapping("/zlbUserConfig") public void zlbUserConfig() { zlbUserConfigTask.run(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java index 06db0b5..7f10448 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -101,7 +101,7 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { ZlbUserInfo zlbUserInfo = new ZlbUserInfo(); zlbUserInfo.setLoginInfoId(user.getLoginInfoId()); zlbUserInfo.setName(user.getName()); - zlbUserInfo.setWeek(DateUtils.getWeekDay(zlbSiteInfo.getBelongDate())); + zlbUserInfo.setWeek(DateUtils.getWeekDayTwo(zlbSiteInfo.getBelongDate())); zlbUserInfo.setType("1"); zlbUserInfo.setPlaceName(zlbSiteInfo.getPlaceName()); zlbUserInfo.setSiteTimeName(zlbSiteInfo.getDayEffectiveTimes()); -- 2.49.1 From 833b6fc208e4bdf661c83fc13f0e77c2193f2b88 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 14:59:49 +0800 Subject: [PATCH 17/32] =?UTF-8?q?doc:=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/xiang/common/enums/ScheduleEnums.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 58a0dab..3ba9daf 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -10,9 +10,20 @@ public enum ScheduleEnums { /** * 0:glados 1:芬玩岛 2:江体小程序 3:江体zlb 4:DDNS */ + + /** + * Aliyun DDNS任务 + */ DOMAIN_DYNAMIC_ANALYSIS_TASK(4, "domain", "domainDynamicAnalysisTask"), + + /** + * Glados任务 + */ GLADOS_CHECK_IN_TASK(0, "glados", "gladosCheckInTask"), + /** + * 江体 ZLB任务 + */ ZLB_LOGIN_TASK(3, "zlb", "zlbLoginTask"), ZLB_TOKEN_CHECK_TASK(3, "zlb", "zlbTokenCheckTask"), ZLB_SITE_QUERY_TASK(3, "zlb", "zlbSiteQueryTask"), -- 2.49.1 From 9d3db5e1b310d6c7bac16149faed2d7522a372a8 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 15:02:58 +0800 Subject: [PATCH 18/32] =?UTF-8?q?feat:=E7=94=9F=E4=BA=A7=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 55 +++++++++++++++++++++++++ src/main/resources/application.yml | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/application-prod.yml diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..74b61c2 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,55 @@ +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://rm-bp15t34gqx62jm069ro.mysql.rds.aliyuncs.com:3306/script_common?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: cornucopia + password: cornucopia@123 + druid: + initial-size: 5 + min-idle: 5 + max-active: 20 + max-wait: 60000 + data: + redis: + host: r-bp1wt59a6nfyt4e3ltpd.redis.rds.aliyuncs.com + port: 6379 + password: Xiang0000 + database: 5 + timeout: 3000 + lettuce: + pool: + max-active: 20 + max-idle: 10 + min-idle: 2 + max-wait: 3000 + +aliyun: + dns: + RR: + - client + - file + - nexus + + +dingtalk: + robot: + properties: + venue: + name: 江南体育中心通知群 + token: 6a218646972c684c75832b0229ea93a234778af537d7469ce96bef290faf530e + secret: SEC9018755ba86d3e5c1ed2fbfa1d6953d84bb2a6c8ebe7ed4e318457bfed5e0465 + users: + - 450841600726084717 + script: + name: 脚本运行通知群 + token: 4709b708d961846e0aee523c5abc3b67e8fa424ee292501d85efd4e504f15a8b + secret: SEC768ed578c0fb31a9aec84b1c1db4f195f5aca203985bbb9d549e23e41c8874d1 + users: + - 450841600726084717 + xb: + name: 股票基金变化通知群 + token: 340a9d39a5b0b6a52ba2262f9c27179cf50e3c8cfe6883ca082649d306038f41 + secret: SECe10ade3058880b84df5c6f46ab072c11f4ac2a5ef9f134d684705c2a3b004de2 + users: + - 450841600726084717 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d58e053..1cddb19 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 8080 + port: 10086 spring: profiles: active: local -- 2.49.1 From f1f3268e841c5ccde43ef641e826c55d75c3fcc4 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 15:24:35 +0800 Subject: [PATCH 19/32] =?UTF-8?q?feat:=E7=94=9F=E4=BA=A7=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 23 +++++++++++++++++++++++ src/main/resources/application-prod.yml | 1 + src/main/resources/application.yml | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 365a067..e0ef867 100644 --- a/pom.xml +++ b/pom.xml @@ -170,4 +170,27 @@ + + + + org.springframework.boot + spring-boot-maven-plugin + 2.3.0.RELEASE + + exec + + com.xiang.Application + ZIP + + + + + repackage + + + + + + + \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 74b61c2..43e02e5 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -26,6 +26,7 @@ spring: aliyun: dns: + rootDomain: xiangtech.xyz RR: - client - file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1cddb19..a4cf599 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,6 @@ server: port: 10086 spring: profiles: - active: local + active: prod application: name: script \ No newline at end of file -- 2.49.1 From 3b596bfa91a8ec1f3bf2ffaeacd85030b55c3479 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 15:57:50 +0800 Subject: [PATCH 20/32] =?UTF-8?q?feat:glados=E7=A7=AF=E5=88=86=E6=95=B0?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../glados/resp/GladosPointsHistoryResp.java | 26 ++++++++++++ .../pojo/glados/resp/GladosPointsResp.java | 30 +++++++++++++ .../glados/constants/GladosConstants.java | 3 ++ .../glados/service/GLaDOSServiceImpl.java | 42 ++++++++++++++++--- .../module/glados/service/IGLaDOSService.java | 1 - 5 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsHistoryResp.java create mode 100644 src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsResp.java diff --git a/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsHistoryResp.java b/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsHistoryResp.java new file mode 100644 index 0000000..0891045 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsHistoryResp.java @@ -0,0 +1,26 @@ +package com.xiang.common.pojo.glados.resp; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author: xiang + * @Date: 2026-05-08 15:47 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GladosPointsHistoryResp { + + private String asset; + private String balance; + private String business; + private String change; + private String detail; + private Long id; + private Long time; + @JSONField(name = "user_id") + private Long userId; +} diff --git a/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsResp.java b/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsResp.java new file mode 100644 index 0000000..d26d03e --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/glados/resp/GladosPointsResp.java @@ -0,0 +1,30 @@ +package com.xiang.common.pojo.glados.resp; + +import lombok.Data; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-05-08 15:46 + */ +@Data +public class GladosPointsResp { + private Integer code; + private List history; + private Plans plans; + private String points; +} + +@Data +class Plans { + + private Plan plan100; + private Plan plan200; + private Plan plan500; +} +@Data +class Plan { + private Integer days; + private Integer points; +} \ No newline at end of file diff --git a/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java b/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java index 76330c6..74eee9f 100644 --- a/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java +++ b/src/main/java/com/xiang/service/module/glados/constants/GladosConstants.java @@ -20,4 +20,7 @@ public class GladosConstants { * 签到请求体 */ public static final String GLADOS_CHECK_IN_BODY = "{\"token\":\"glados.cloud\"}"; + + + public static final String GLADOS_POINTS_LIST_URL = GLADOS_URL_PREFIX + "/api/user/points"; } diff --git a/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java b/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java index fca9fdd..e6bc873 100644 --- a/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java +++ b/src/main/java/com/xiang/service/module/glados/service/GLaDOSServiceImpl.java @@ -2,17 +2,15 @@ package com.xiang.service.module.glados.service; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.TypeReference; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Maps; import com.xiang.common.factory.ScriptDingTalkFactory; -import com.xiang.common.pojo.glados.GladosRunLogDO; import com.xiang.common.pojo.glados.GladosUserDO; import com.xiang.common.pojo.glados.resp.CheckInResp; import com.xiang.common.pojo.glados.resp.GLaDOSResponse; +import com.xiang.common.pojo.glados.resp.GladosPointsResp; import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.utils.HttpService; import com.xiang.service.module.glados.constants.GladosConstants; -import com.xiang.service.module.glados.manage.IGladosRunLogManage; import com.xiang.service.module.glados.manage.IGladosUserManage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -86,14 +84,44 @@ public class GLaDOSServiceImpl implements IGLaDOSService { checkInV2(gladosUserDO, new StringBuilder()); } - public boolean checkInV2(GladosUserDO user, StringBuilder sb) { + private GladosPointsResp pointsList(GladosUserDO user) { Map header = Maps.newHashMap(); header.put("Cookie", user.getCookie()); String response = null; + try { + response = HttpService.doGet(GladosConstants.GLADOS_POINTS_LIST_URL, header, null); + } catch (Exception e) { + log.error("http请求异常:{}", user.getEmail()); + return null; + } + if (org.apache.commons.lang3.StringUtils.isBlank(response)) { + return null; + } + + GladosPointsResp gLaDOSResponse = JSONObject.parseObject(response, new TypeReference() { + }); + if (Objects.isNull(gLaDOSResponse)) { + return null; + } + + if (0 == gLaDOSResponse.getCode()) { + // 成功请求 + return gLaDOSResponse; + } + return null; + } + + public boolean checkInV2(GladosUserDO user, StringBuilder sb) { + Map header = Maps.newHashMap(); + header.put("Cookie", user.getCookie()); + + String response = null; + GladosPointsResp gladosPointsResp = null; try { response = HttpService.doPost(GladosConstants.GLADOS_CHECK_IN_URL, header, GladosConstants.GLADOS_CHECK_IN_BODY); + gladosPointsResp = pointsList(user); } catch (Exception e) { log.error("http请求异常:{}", user.getEmail()); return false; @@ -113,9 +141,13 @@ public class GLaDOSServiceImpl implements IGLaDOSService { if (0 == gLaDOSResponse.getCode()) { // 成功请求 if (Objects.nonNull(gLaDOSResponse.getPoints()) && 0 != gLaDOSResponse.getPoints()) { + String points = null; + if (Objects.nonNull(gladosPointsResp)) { + points = gladosPointsResp.getPoints(); + } // 签到成功 dingTalkService.sendMsg("[时间:" + LocalDateTime.now() + "] 用户: " + - user.getEmail() + "签到成功,获得积分:" + gLaDOSResponse.getPoints()); + user.getEmail() + "签到成功,获得积分:" + gLaDOSResponse.getPoints() + ",可用积分数量:" + points); return Boolean.TRUE; } } diff --git a/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java b/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java index 7dbe626..17071fd 100644 --- a/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java +++ b/src/main/java/com/xiang/service/module/glados/service/IGLaDOSService.java @@ -1,6 +1,5 @@ package com.xiang.service.module.glados.service; -import com.xiang.common.pojo.glados.GladosUserDO; import com.xiang.common.pojo.schedule.TaskResult; /** -- 2.49.1 From a78da44f23e867cbc968e67884a68674749de849 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 16:04:18 +0800 Subject: [PATCH 21/32] =?UTF-8?q?feat:glados=E7=A7=AF=E5=88=86=E6=95=B0?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/xiang/Application.java | 2 ++ src/main/resources/application.yml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/xiang/Application.java b/src/main/java/com/xiang/Application.java index 92972dd..be158b5 100644 --- a/src/main/java/com/xiang/Application.java +++ b/src/main/java/com/xiang/Application.java @@ -4,8 +4,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a4cf599..1cddb19 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,6 @@ server: port: 10086 spring: profiles: - active: prod + active: local application: name: script \ No newline at end of file -- 2.49.1 From c61343a23852990af2894d530d7c4a8c70dc29c1 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 16:42:24 +0800 Subject: [PATCH 22/32] =?UTF-8?q?fix:=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index a267f4a..812f2f8 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -48,7 +48,7 @@ public class ZlbTaskConfig { zlbOrderTask.run(); } - @Scheduled(cron = "30 30 16 * * ?") + @Scheduled(cron = "30 35 16 * * ?") @GetMapping("/zlbUserConfig") public void zlbUserConfig() { zlbUserConfigTask.run(); -- 2.49.1 From 76ec2c8b3c81c7d71b093e30cd02b37aacf46a64 Mon Sep 17 00:00:00 2001 From: Xiang Date: Fri, 8 May 2026 17:32:17 +0800 Subject: [PATCH 23/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pojo/jntyzx/miniapp/dto/VenueListDTO.java | 34 +++ .../pojo/jntyzx/miniapp/pojo/OrderInfoDO.java | 55 ++++ .../miniapp/pojo/UserRestrictionInfo.java | 29 ++ .../jntyzx/miniapp/pojo/UserTokenInfoDO.java | 59 ++++ .../pojo/jntyzx/miniapp/pojo/VenueInfoDO.java | 72 +++++ .../jntyzx/miniapp/req/SubscribeRequest.java | 23 ++ .../pojo/jntyzx/miniapp/req/SubscribeVo.java | 60 +++++ .../pojo/jntyzx/miniapp/req/UserAddReq.java | 33 +++ .../pojo/jntyzx/miniapp/req/UserQueryReq.java | 41 +++ .../miniapp/req/UserStatusUpdateReq.java | 18 ++ .../miniapp/req/UserTokenUpdateReq.java | 18 ++ .../pojo/jntyzx/miniapp/req/UsernameReq.java | 9 + .../miniapp/req/VenueInfoQueryRequest.java | 33 +++ .../req/VenueInfoSubscribeRequest.java | 11 + .../jntyzx/miniapp/resp/JntyzxResponse.java | 20 ++ .../pojo/jntyzx/miniapp/resp/JtUserVo.java | 64 +++++ .../jntyzx/miniapp/resp/OrderCreateResp.java | 18 ++ .../miniapp/resp/VenueInfoQueryResp.java | 53 ++++ .../resp/query/QueryVenueResponse.java | 15 ++ .../miniapp/resp/query/SitePositionList.java | 56 ++++ .../jntyzx/miniapp/resp/query/TimeList.java | 32 +++ .../miniapp/resp/query/UserInfoResponse.java | 109 ++++++++ .../jntyzx/miniapp/resp/query/VenueList.java | 20 ++ .../java/com/xiang/common/utils/Base64.java | 253 ++++++++++++++++++ .../com/xiang/common/utils/DateUtils.java | 59 ++++ .../miniapp/constants/RedisKeyConstant.java | 34 +++ .../jntyzx/miniapp/constants/UrlConstant.java | 47 ++++ .../miniapp/converts/UserConverter.java | 32 +++ .../miniapp/converts/VenueInfoConverter.java | 16 ++ .../manage/IOrderCreateInfoManage.java | 17 ++ .../manage/IUserRestrictionManage.java | 13 + .../miniapp/manage/IUserTokenInfoManage.java | 20 ++ .../miniapp/manage/IVenueInfoManage.java | 22 ++ .../manage/OrderCreateInfoManageImpl.java | 26 ++ .../manage/UserRestrictionManageImpl.java | 27 ++ .../manage/UserTokenInfoManageImpl.java | 69 +++++ .../miniapp/manage/VenueInfoManageImpl.java | 50 ++++ .../mapper/JntyzxOrderCreateInfoMapper.java | 15 ++ .../JntyzxUserRestrictionInfoMapper.java | 11 + .../mapper/JntyzxUserTokenInfoMapper.java | 15 ++ .../miniapp/mapper/JntyzxVenueInfoMapper.java | 15 ++ .../miniapp/service/IJntyzxHttpService.java | 60 +++++ .../miniapp/service/IJtOrderService.java | 19 ++ .../service/IUserTokenInfoService.java | 33 +++ .../jntyzx/miniapp/service/IVenueService.java | 44 +++ .../service/impl/JntyzxHttpServiceImpl.java | 225 ++++++++++++++++ .../service/impl/OrderInfoServiceImpl.java | 98 +++++++ .../impl/UserTokenInfoServiceImpl.java | 251 +++++++++++++++++ .../service/impl/VenueServiceImpl.java | 227 ++++++++++++++++ .../miniapp/utils/JntyzxSaltEncodeUtils.java | 36 +++ .../jntyzx/miniapp/utils/MsgSendUtils.java | 42 +++ .../jntyzx/miniapp/utils/VenueInfoUtils.java | 41 +++ .../jntyzx/miniapp/utils/WeekendUtils.java | 17 ++ 53 files changed, 2716 insertions(+) create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/dto/VenueListDTO.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/OrderInfoDO.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserRestrictionInfo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserTokenInfoDO.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/VenueInfoDO.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeVo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserAddReq.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserQueryReq.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserStatusUpdateReq.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserTokenUpdateReq.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UsernameReq.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoQueryRequest.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoSubscribeRequest.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JntyzxResponse.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JtUserVo.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/OrderCreateResp.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/VenueInfoQueryResp.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/SitePositionList.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/TimeList.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/UserInfoResponse.java create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/VenueList.java create mode 100644 src/main/java/com/xiang/common/utils/Base64.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJntyzxHttpService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJtOrderService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/JntyzxSaltEncodeUtils.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/WeekendUtils.java diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/dto/VenueListDTO.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/dto/VenueListDTO.java new file mode 100644 index 0000000..67352c2 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/dto/VenueListDTO.java @@ -0,0 +1,34 @@ +package com.xiang.common.pojo.jntyzx.miniapp.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author: xiang + * @Date: 2025-12-15 13:55 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class VenueListDTO { + /** + * 时间 + */ + private String date; + + /** + * 时间 + */ + private String sjName; + + /** + * 场地名称 + */ + private String placeName; + + /** + * 联系人 + */ + private String contacts; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/OrderInfoDO.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/OrderInfoDO.java new file mode 100644 index 0000000..3f2174a --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/OrderInfoDO.java @@ -0,0 +1,55 @@ +package com.xiang.common.pojo.jntyzx.miniapp.pojo; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:57 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("jntyzx_order_create_info") +public class OrderInfoDO { + private Long id; + /** + * 订单id + */ + private String orderId; + + /** + * 参数 + */ + private String params; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 订单创建人 + */ + private String username; + + /** + * 场地号 + */ + private String placeName; + + /** + * 所属日期 + */ + private LocalDate date; + + /** + * 订单状态 (0:待付款,1:已付款) + */ + private Integer orderStatus; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserRestrictionInfo.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserRestrictionInfo.java new file mode 100644 index 0000000..9c558e9 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserRestrictionInfo.java @@ -0,0 +1,29 @@ +package com.xiang.common.pojo.jntyzx.miniapp.pojo; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("jntyzx_user_restriction") +public class UserRestrictionInfo { + + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 封禁截止时间 + */ + private LocalDateTime restrictionDeadline; + /** + * 封禁原因 + */ + private String restrictionDesc; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserTokenInfoDO.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserTokenInfoDO.java new file mode 100644 index 0000000..d8c6d4c --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserTokenInfoDO.java @@ -0,0 +1,59 @@ +package com.xiang.common.pojo.jntyzx.miniapp.pojo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:18 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("jntyzx_user_token_info") +public class UserTokenInfoDO { + private Long id; + /** + * 用户名 + */ + private String name; + + /** + * token + */ + private String token; + + /** + * wx openid + */ + private String openId; + + /** + * 状态(0:禁用 1:启用) + */ + private Integer status; + + /** + * 是否可以下单 (0:否 1:是) + */ + private Integer isOrder; + + /** + * 会员卡号 + */ + @TableField("member_card_no") + private String memberCardNo; + + /** + * 是否封禁 0:否 1:是 + */ + @TableField("is_restriction") + private Integer isRestriction; + + private LocalDateTime updateTime; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/VenueInfoDO.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/VenueInfoDO.java new file mode 100644 index 0000000..adc15c0 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/VenueInfoDO.java @@ -0,0 +1,72 @@ +package com.xiang.common.pojo.jntyzx.miniapp.pojo; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2025-12-15 15:48 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName("jntyzx_venue_info") +public class VenueInfoDO { + private Long id; + /** + * 场地名称 + */ + private String placeName; + + /** + * 所属日期 + */ + private LocalDate date; + + /** + * 场地信息三方主键 + */ + private Long placeMainId; + + /** + * 场地id + */ + private Integer placeId; + + /** + * + */ + private Integer scheduleId; + + /** + * 时间范围 + */ + private String sjName; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 联系人 + */ + private String contacts; + + /** + * 状态 + */ + private Integer type; + + private BigDecimal money; + private String className; + private String classCode; + private String appointments; + private String cTypeCode; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java new file mode 100644 index 0000000..a048087 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java @@ -0,0 +1,23 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import com.alibaba.fastjson2.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-15 16:34 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SubscribeRequest { + private JSONObject jsonObject; + private List subscribeVos; + private String bookTime; + private Integer paymentMethod; + private String svCiphertext; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeVo.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeVo.java new file mode 100644 index 0000000..1e7d690 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeVo.java @@ -0,0 +1,60 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @Author: xiang + * @Date: 2025-12-15 16:35 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SubscribeVo { + private int id; + + private String ballCourtId; + + private String sjName; + + private String scheduleId; + + private String placeName; + + private int placeId; + + private String type; + + private String className; + + private String classCode; + + private BigDecimal money; + + private String contacts; + + private String contactNumber; + + private String memberNumber; + + private String appointments; + + private String operator; + + private String endTime; + + private String beginTime; + + private int specOneTimes; + + private String ctypeCode; + + private int isWhole; + + private String orderId; + + private int votesnum; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserAddReq.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserAddReq.java new file mode 100644 index 0000000..da0b607 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserAddReq.java @@ -0,0 +1,33 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; +import org.jetbrains.annotations.NotNull; + + +@Data +public class UserAddReq { + + /** + * 用户名称 + */ + private String name; + /** + * token + */ + private String token; + /** + * wx openId + */ + private String openId; + + /** + * 会员卡号 + */ + private String memberCardNo; + + /** + * 状态 0:禁用 1:启用 + */ + private Integer status; +} + diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserQueryReq.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserQueryReq.java new file mode 100644 index 0000000..7217b24 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserQueryReq.java @@ -0,0 +1,41 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2026-03-24 16:40 + */ +@Data +public class UserQueryReq { + + /** + * 用户名称 + */ + private String name; + + /** + * wx openId + */ + private String openId; + + /** + * 会员卡号 + */ + private String memberCardNo; + + /** + * 状态 0:禁用 1:启用 + */ + private Integer status; + + /** + * 是否可以下单 0:否 1:是 + */ + private Integer isOrder; + + /** + * 是否封禁 0:否 1:是 + */ + private Integer isRestriction; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserStatusUpdateReq.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserStatusUpdateReq.java new file mode 100644 index 0000000..96b1ceb --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserStatusUpdateReq.java @@ -0,0 +1,18 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; + + +@Data +public class UserStatusUpdateReq { + + /** + * 用户名称 + */ + private String username; + + /** + * status + */ + private Integer status; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserTokenUpdateReq.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserTokenUpdateReq.java new file mode 100644 index 0000000..d016730 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UserTokenUpdateReq.java @@ -0,0 +1,18 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; + + +@Data +public class UserTokenUpdateReq { + + /** + * 用户名称 + */ + private String username; + + /** + * token + */ + private String token; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UsernameReq.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UsernameReq.java new file mode 100644 index 0000000..bca2270 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/UsernameReq.java @@ -0,0 +1,9 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; + + +@Data +public class UsernameReq { + private String username; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoQueryRequest.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoQueryRequest.java new file mode 100644 index 0000000..b083df4 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoQueryRequest.java @@ -0,0 +1,33 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.time.LocalDate; + +/** + * @Author: xiang + * @Date: 2026-04-09 09:39 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class VenueInfoQueryRequest{ + + /** + * 日期 + */ + private LocalDate date; + /** + * 时间段 例如 20:00-21:00 + */ + private String sj; + + /** + * 场地名称 + */ + private String placeName; + +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoSubscribeRequest.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoSubscribeRequest.java new file mode 100644 index 0000000..7fa4250 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/VenueInfoSubscribeRequest.java @@ -0,0 +1,11 @@ +package com.xiang.common.pojo.jntyzx.miniapp.req; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2026-04-09 10:00 + */ +@Data +public class VenueInfoSubscribeRequest { +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JntyzxResponse.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JntyzxResponse.java new file mode 100644 index 0000000..549bc78 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JntyzxResponse.java @@ -0,0 +1,20 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:38 + */ +@Data +public class JntyzxResponse { + private Boolean success; + + private String message; + + private Integer code; + + private T result; + + private Long timestamp; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JtUserVo.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JtUserVo.java new file mode 100644 index 0000000..7d49341 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/JtUserVo.java @@ -0,0 +1,64 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +/** + * @Author: xiang + * @Date: 2026-03-24 16:40 + */ +@Data +public class JtUserVo { + + private Long userId; + + /** + * 用户名称 + */ + private String name; + /** + * token + */ + private String token; + /** + * wx:openId + */ + private String openId; + /** + * 账号状态: + * 状态(0:禁用 1:启用) + */ + private Integer status; + /** + * 修改时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd Hh:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd Hh:mm:ss") + private LocalDateTime updateTime; + /** + * 江南体育中心会员卡号 + */ + private String memberCardNo; + /** + * 是否可以下单 + */ + private Boolean isOrder; + /** + * 是否封禁 + */ + private Boolean isRestriction; + /** + * 封禁结束时间 + */ + @DateTimeFormat(pattern = "yyyy-MM-dd Hh:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd Hh:mm:ss") + private LocalDateTime restrictionDeadline; + /** + * 封禁缘由 + */ + private String restrictionDesc; + +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/OrderCreateResp.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/OrderCreateResp.java new file mode 100644 index 0000000..11463e6 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/OrderCreateResp.java @@ -0,0 +1,18 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:36 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OrderCreateResp { + + private String id; + private String countDownNum; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/VenueInfoQueryResp.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/VenueInfoQueryResp.java new file mode 100644 index 0000000..1ea2780 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/VenueInfoQueryResp.java @@ -0,0 +1,53 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * @Author: xiang + * @Date: 2026-04-09 09:42 + */ +@Data +@AllArgsConstructor +public class VenueInfoQueryResp { + + /** + * 场地名称 + */ + private String placeName; + + /** + * 日期 + */ + private LocalDate date; + + /** + * 时间范围 + */ + private String sjName; + + /** + * 价格 + */ + private BigDecimal money; + + /** + * 联系人 + */ + private String contacts; + + /** + * 0:可订购 2:zlb 4:已订购 + */ + private Integer type; + private Long placeMainId; + private Integer placeId; + private Integer scheduleId; + private String className; + private String classCode; + private String appointments; + private String cTypeCode; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java new file mode 100644 index 0000000..9a8cae5 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java @@ -0,0 +1,15 @@ + +import lombok.Data; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:37 + */ +@Data +public class QueryVenueResponse { + private List timeList; + + private List venue; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/SitePositionList.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/SitePositionList.java new file mode 100644 index 0000000..3a04d50 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/SitePositionList.java @@ -0,0 +1,56 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp.query; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:45 + */ +@Data +public class SitePositionList { + private Long id; + + private String ballCourtId; + + private String sjName; + + private String scheduleId; + + private String placeName; + + private Integer placeId; + + private Integer type; + + private String className; + + private String classCode; + + private BigDecimal money; + + private String contacts; + + private String contactNumber; + + private String memberNumber; + + private String appointments; + + private String operator; + + private String endTime; + + private String beginTime; + + private Integer specOneTimes; + + private String ctypeCode; + + private String isWhole; + + private Long orderId; + + private Integer votesnum; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/TimeList.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/TimeList.java new file mode 100644 index 0000000..2981845 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/TimeList.java @@ -0,0 +1,32 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp.query; + +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:39 + */ +@Data +public class TimeList { + private Long id; + private String name; + + private String beginTime; + + private String endTime; + + private String type; + + private String isenable; + + private String operator; + + private String createtime; + + private String remarks; + + private String default01; + private String default02; + private String default03; + private String votesnum; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/UserInfoResponse.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/UserInfoResponse.java new file mode 100644 index 0000000..a1fbaf8 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/UserInfoResponse.java @@ -0,0 +1,109 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp.query; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserInfoResponse { + + /** + * id + */ + private Long id; + /** + * 会员卡号 + */ + private String consCard; + /** + * 姓名 + */ + private String consName; + /** + * 性别 + */ + private String consSex; + /** + * 身份证号 + */ + private String consIdCard; + /** + * 固定电话 + */ + private String consTel; + /** + * 手机号码 + */ + private String consHandSet; + /** + * 单位 + */ + private String consUnit; + /** + * 照片 + */ + private String consPhoto; + private Integer consWaste; + /** + * 会员卡号 + */ + private String consNumber; + private BigDecimal consMin; + private Integer consProp; + /** + * 注册年 + */ + private String consYear; + /** + * 注册月 + */ + private String consMonth; + /** + * 注册日 + */ + private String consDay; + private boolean consIflag; + /** + * 注册时间 + */ + private LocalDateTime consTimes; + /** + * openId + */ + private String openId; + /** + * 头像 + */ + private String photoUrl; + /** + * 会员 + */ + private Integer consVip; + /** + * 会员等级号 + */ + private String consVipCode; + + private String eleCardNum; + private Integer appointmentEligibility; + /** + * 封禁截止日期 + */ + @JSONField(name = "restrictionDeadline") + private String restrictionDeadline; + /** + * 封禁原因 + */ + private String restrictionDescription; + /** + * 封禁截止日期 + */ + @JSONField(name = "RestrictionDeadline") + private String RestrictionDeadline2; +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/VenueList.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/VenueList.java new file mode 100644 index 0000000..61b1401 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/VenueList.java @@ -0,0 +1,20 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp.query; + +import lombok.Data; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:39 + */ +@Data +public class VenueList { + + private Integer placeId; + + private String placeName; + + private List sitePosition; + +} diff --git a/src/main/java/com/xiang/common/utils/Base64.java b/src/main/java/com/xiang/common/utils/Base64.java new file mode 100644 index 0000000..5df1063 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/Base64.java @@ -0,0 +1,253 @@ +package com.xiang.common.utils; + +/** + * Base64工具类 + * + * @author xiang + */ +public final class Base64 { + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static { + for (int i = 0; i < BASELENGTH; ++i) { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = '+'; + lookUpBase64Alphabet[63] = '/'; + } + + private static boolean isWhiteSpace(char octect) { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) { + return (octect == PAD); + } + + private static boolean isData(char octect) { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) { + if (binaryData == null) { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char[] encodedData = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } else if (fewerThan24bits == SIXTEENBIT) { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) { + if (encoded == null) { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) { + return new byte[0]; + } + + byte[] decodedData = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } else if (!isPad(d3) && isPad(d4)) { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } else { + return null; + } + } else { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) { + if (data == null) { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) { + if (!isWhiteSpace(data[i])) { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/src/main/java/com/xiang/common/utils/DateUtils.java b/src/main/java/com/xiang/common/utils/DateUtils.java index 4a2b3df..a00d5ea 100644 --- a/src/main/java/com/xiang/common/utils/DateUtils.java +++ b/src/main/java/com/xiang/common/utils/DateUtils.java @@ -3,6 +3,8 @@ package com.xiang.common.utils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.xiang.common.enums.DateFormatEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; @@ -11,6 +13,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.DayOfWeek; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -22,6 +26,9 @@ import java.util.concurrent.TimeUnit; */ public class DateUtils { + private static final String defaultDateFormatter = "yyyy-MM-dd"; + private static final String defaultDateTimeFormatter = "yyyy-MM-dd HH:mm:ss"; + /** * 构造函数. @@ -603,4 +610,56 @@ public class DateUtils { public static Date setModifiedDate(String key, Date date) { return modifiedDate.put(key, date); } + + public static LocalDateTime getDateTimeFromStr(String dateStr) { + return getDateTimeFromStr(dateStr, "yyyy-MM-dd HH:mm:ss"); + } + + public static LocalDateTime getDateTimeFromStr(String dateStr, String pattern) { + return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern)); + } + + public static LocalDate getDateFromStr(String dataStr) { + return getDateFromStr(dataStr, "yyyy-MM-dd"); + } + + public static LocalDate getDateFromStr(String dataStr, String pattern) { + return LocalDate.parse(dataStr, DateTimeFormatter.ofPattern(pattern)); + } + + public static String getDateFromDate(LocalDate date) { + return getDateFromDate(date, "yyyy-MM-dd"); + } + + public static String getDateFromDate(LocalDate date, String pattern) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + return date.format(formatter); + } + + public static String getDateTimeFromDateTime(LocalDateTime dateTime) { + return getDateTimeFromDateTime(dateTime, "yyyy-MM-dd HH:mm:ss"); + } + + public static String getDateTimeFromDateTime(LocalDateTime dateTime, String pattern) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + return dateTime.format(formatter); + } + + public static LocalDateTime getTimeFromStr(String date, String time) { + String dateTimeStr = date + " " + time; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return LocalDateTime.parse(dateTimeStr, formatter); + } + + public static Boolean validWeekTime() { + if (!Objects.equals(LocalDateTime.now().getDayOfWeek(), DayOfWeek.SATURDAY) && !Objects.equals(LocalDateTime.now().getDayOfWeek(), DayOfWeek.SUNDAY)) { + LocalTime now = LocalTime.now(); + boolean inMorning = now.isAfter(LocalTime.of(9, 29)) && now.isBefore(LocalTime.of(11, 31)); + boolean inAfternoon = now.isAfter(LocalTime.of(12, 59)) && now.isBefore(LocalTime.of(15, 1)); + return !inAfternoon && !inMorning ? true : false; + } else { + log.info("当前时间为:{}", LocalDateTime.now()); + return true; + } + } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java new file mode 100644 index 0000000..8159f6c --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java @@ -0,0 +1,34 @@ +package com.xiang.service.module.jntyzx.miniapp.constants; + + +import java.time.LocalDate; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:43 + */ +public class RedisKeyConstant { + + public static final String JNTYZX_ORDER_CREATE_KEY = "jntyzx:order:create:orderId:"; + + public static final String JNTUZX_ORDER_PEEK_KEY = "jntyzx:order:peek:user:"; + + public static final String JNTYZX_VENUE_MSG_SEND_KEY = "jntyzx:order:venue:msg:send"; + + private static final String JNTYZX_VENUE_SUBSCRIBE_KEY = "jntyzx:venue:subscribe:"; + + private static final String JNTYZX_ORDER_CLOSE_CARD_KEY = "jntyzx:order:close:card:"; + + public static String getCloseCardKey(String username) { + return JNTYZX_ORDER_CLOSE_CARD_KEY + username + ":" +getDate(); + } + + public static String getVenueSubscribeKey(String placeName) { + return JNTYZX_VENUE_SUBSCRIBE_KEY + placeName + ":" + getDate(); + } + + public static String getDate() { + LocalDate now = LocalDate.now(); + return ":" + DateUtils.getDateFromDate(now); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java new file mode 100644 index 0000000..227e072 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java @@ -0,0 +1,47 @@ +package com.xiang.service.module.jntyzx.miniapp.constants; + +/** + * @Author: xiang + * @Date: 2025-12-15 13:46 + */ +public class UrlConstant { + + /** + * 江南体育中心基础URL + */ + private final static String GNTYZX_BASE_URL = "https://jntyzx.cn:8443"; + + /** + * 查询当天的场地信息 + */ + public final static String QUERY_TODAY_SUBSCRIBE_URL = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/getSubscribeByToday"; + /** + * 查询明天场地信息 + */ + public final static String QUERY_TOMORROW_SUBSCRIBE_URL = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/getSubscribeByTomorrow"; + + /** + * 订阅场地 + */ + public final static String ADD_SUBSCRIBE = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/addSubscribe"; + + /** + * 订单信息 + */ + public final static String ORDER_INFO = GNTYZX_BASE_URL + "/GYM-JN/multi/busiOrder/queryOrderInfo"; + + /** + * 心跳监测接口 + */ + public final static String HEALTH_DECLARATION = GNTYZX_BASE_URL + "/GYM-JN//busi/healthDeclaration/addUserPrivacy"; + + /** + * 校验会员卡状态 + */ + public final static String CHECK_NUM = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/checkDefaultsNum"; + + /** + * 根据openId查询会员卡信息 + */ + public final static String QUERY_BY_OPEN_ID = GNTYZX_BASE_URL + "/GYM-JN/multi/xfConsumer/queryByOpenId"; +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java new file mode 100644 index 0000000..4ff851e --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java @@ -0,0 +1,32 @@ +package com.xiang.service.module.jntyzx.miniapp.converts; + +import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; +import com.xiang.service.module.jntyzx.miniapp.entity.pojo.UserTokenInfoDO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Objects; + +@Mapper(componentModel = "spring") +public interface UserConverter { + + UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); + @Mapping(source = "id", target = "userId") + @Mapping(source = "isOrder", target = "isOrder", qualifiedByName = "isOrder") + @Mapping(source = "isRestriction", target = "isRestriction", qualifiedByName = "isRestrict") + JtUserVo convert(UserTokenInfoDO userTokenInfoDO); + List convert(List userTokenInfoDOs); + + @Named("isOrder") + default Boolean isOrder(Integer value) { + return Objects.nonNull(value) && Objects.equals(value, 1); + } + + @Named("isRestrict") + default Boolean isRestrict(Integer value) { + return Objects.nonNull(value) && Objects.equals(value, 0); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java new file mode 100644 index 0000000..3f60aab --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java @@ -0,0 +1,16 @@ +package com.xiang.service.module.jntyzx.miniapp.converts; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; +import com.xiang.service.module.jntyzx.miniapp.entity.pojo.VenueInfoDO; +import org.mapstruct.Mapper; + +/** + * @Author: xiang + * @Date: 2026-04-09 09:54 + */ +@Mapper(componentModel = "spring") +public interface VenueInfoConverter { + + Page toPage(Page page); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java new file mode 100644 index 0000000..8757938 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java @@ -0,0 +1,17 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:59 + */ +public interface IOrderCreateInfoManage extends IService { + + + List queryNoPayOrder(); + +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java new file mode 100644 index 0000000..77462a3 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java @@ -0,0 +1,13 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; + +import java.util.List; + +public interface IUserRestrictionManage extends IService { + + UserRestrictionInfo queryByUserId(Long userId); + + List queryByIdList(List idList); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java new file mode 100644 index 0000000..24ee98a --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java @@ -0,0 +1,20 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:19 + */ +public interface IUserTokenInfoManage extends IService { + List listUser(); + UserTokenInfoDO getByName(String name); + + List listCanOrder(); + + List queryByList(UserQueryReq req); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java new file mode 100644 index 0000000..4cab8bf --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java @@ -0,0 +1,22 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; + +import java.time.LocalDate; +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-15 15:50 + */ +public interface IVenueInfoManage extends IService { + + List queryByDate(LocalDate date); + + List queryByType(LocalDate date, Integer type); + + Page page(VenueInfoQueryRequest request); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java new file mode 100644 index 0000000..a4ab30c --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java @@ -0,0 +1,26 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; +import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxOrderCreateInfoMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:59 + */ +@Service +public class OrderCreateInfoManageImpl extends ServiceImpl implements IOrderCreateInfoManage { + + + @Override + public List queryNoPayOrder() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(OrderInfoDO::getOrderStatus, 0); + return baseMapper.selectList(lambdaQueryWrapper); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java new file mode 100644 index 0000000..e373f21 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java @@ -0,0 +1,27 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; +import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxUserRestrictionInfoMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class UserRestrictionManageImpl extends ServiceImpl implements IUserRestrictionManage { + @Override + public UserRestrictionInfo queryByUserId(Long userId) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(UserRestrictionInfo::getUserId, userId); + return baseMapper.selectOne(lambdaQueryWrapper); + } + + @Override + public List queryByIdList(List idList) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.in(UserRestrictionInfo::getUserId, idList); + return baseMapper.selectList(lambdaQueryWrapper); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java new file mode 100644 index 0000000..c5eaaa6 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java @@ -0,0 +1,69 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; +import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxUserTokenInfoMapper; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:19 + */ +@Service +public class UserTokenInfoManageImpl extends ServiceImpl implements IUserTokenInfoManage { + @Override + public List listUser() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(UserTokenInfoDO::getStatus, 1); + return baseMapper.selectList(lambdaQueryWrapper); + } + + @Override + public UserTokenInfoDO getByName(String name) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(UserTokenInfoDO::getStatus, 1); + lambdaQueryWrapper.eq(UserTokenInfoDO::getName, name); + lambdaQueryWrapper.last("limit 1"); + return baseMapper.selectOne(lambdaQueryWrapper); + } + + @Override + public List listCanOrder() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(UserTokenInfoDO::getStatus, 1); + lambdaQueryWrapper.eq(UserTokenInfoDO::getIsOrder, 1); + lambdaQueryWrapper.eq(UserTokenInfoDO::getIsRestriction, 0); + return baseMapper.selectList(lambdaQueryWrapper); + } + + @Override + public List queryByList(UserQueryReq req) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + if (StringUtils.isNotBlank(req.getName())) { + lambdaQueryWrapper.like(UserTokenInfoDO::getName, req.getName()); + } + if (StringUtils.isNotBlank(req.getOpenId())) { + lambdaQueryWrapper.eq(UserTokenInfoDO::getOpenId, req.getOpenId()); + } + if (StringUtils.isNotBlank(req.getMemberCardNo())) { + lambdaQueryWrapper.eq(UserTokenInfoDO::getMemberCardNo, req.getMemberCardNo()); + } + if (Objects.nonNull(req.getStatus())) { + lambdaQueryWrapper.eq(UserTokenInfoDO::getStatus, req.getStatus()); + } + if (Objects.nonNull(req.getIsRestriction())) { + lambdaQueryWrapper.eq(UserTokenInfoDO::getIsRestriction, req.getIsRestriction()); + } + if (Objects.nonNull(req.getIsOrder())) { + lambdaQueryWrapper.eq(UserTokenInfoDO::getIsOrder, req.getIsOrder()); + } + return baseMapper.selectList(lambdaQueryWrapper); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java new file mode 100644 index 0000000..f0d6d1b --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java @@ -0,0 +1,50 @@ +package com.xiang.service.module.jntyzx.miniapp.manage; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; +import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxVenueInfoMapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-15 15:51 + */ +@Service +public class VenueInfoManageImpl extends ServiceImpl implements IVenueInfoManage { + + public List queryByDate(LocalDate date) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(VenueInfoDO::getDate, date); + return baseMapper.selectList(lqw); + } + + @Override + public List queryByType(LocalDate date, Integer type) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(VenueInfoDO::getDate, date); + lqw.eq(VenueInfoDO::getType, type); + return baseMapper.selectList(lqw); + } + + @Override + public Page page(VenueInfoQueryRequest request) { + Page page = new Page<>(request.getCurrent(), request.getPageSize()); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(VenueInfoDO::getDate, request.getDate()); + if (StringUtils.isNotBlank(request.getSj())) { + lqw.eq(VenueInfoDO::getSjName, request.getSj()); + } + if (StringUtils.isNotBlank(request.getPlaceName())) { + lqw.like(VenueInfoDO::getPlaceName, request.getPlaceName()); + } + return baseMapper.selectPage(page, lqw); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java new file mode 100644 index 0000000..c5c9aad --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java @@ -0,0 +1,15 @@ +package com.xiang.service.module.jntyzx.miniapp.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +/** + * @Author: xiang + * @Date: 2025-12-16 10:58 + */ +@Mapper +@Repository +public interface JntyzxOrderCreateInfoMapper extends BaseMapper { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java new file mode 100644 index 0000000..f492211 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java @@ -0,0 +1,11 @@ +package com.xiang.service.module.jntyzx.miniapp.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Mapper +@Repository +public interface JntyzxUserRestrictionInfoMapper extends BaseMapper { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java new file mode 100644 index 0000000..5b53abb --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java @@ -0,0 +1,15 @@ +package com.xiang.service.module.jntyzx.miniapp.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:18 + */ +@Mapper +@Repository +public interface JntyzxUserTokenInfoMapper extends BaseMapper { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java new file mode 100644 index 0000000..2ba47bf --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java @@ -0,0 +1,15 @@ +package com.xiang.service.module.jntyzx.miniapp.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +/** + * @Author: xiang + * @Date: 2025-12-15 15:48 + */ +@Mapper +@Repository +public interface JntyzxVenueInfoMapper extends BaseMapper { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJntyzxHttpService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJntyzxHttpService.java new file mode 100644 index 0000000..4abf048 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJntyzxHttpService.java @@ -0,0 +1,60 @@ +package com.xiang.service.module.jntyzx.miniapp.service; + + +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.OrderCreateResp; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.UserInfoResponse; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-15 14:47 + */ +public interface IJntyzxHttpService { + + /** + * 查询今日可用场地 + */ + List queryAvailable(String isWeekend, String token); + + /** + * 查询明日可用场地 + * @param isWeekend + * @param token + * @return + */ + List queryAvailableTomorrow(String isWeekend, String token); + + /** + * 订单创建 + * @return + */ + JntyzxResponse createOrder(List venueInfos, String token, String openId); + + /** + * 心跳监测 + * @param token token + * @param openId openid + * @return + */ + JntyzxResponse healthDeclaration(String token, String openId); + + /** + * 根据openid查询 + * @param token token + * @param openId openId + * @return + */ + JntyzxResponse queryByOpenId(String token, String openId); + + /** + * 校验会员卡状态 + * @param token token + * @param cardNo 会员卡号 + * @return + */ + JntyzxResponse checkDefaultNums(String token, String cardNo); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJtOrderService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJtOrderService.java new file mode 100644 index 0000000..902497a --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IJtOrderService.java @@ -0,0 +1,19 @@ +package com.xiang.service.module.jntyzx.miniapp.service; + + +import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-16 16:17 + */ +public interface IJtOrderService { + + boolean createOrder(List venueInfoDOS, UserTokenInfoDO userTokenInfoDO); + + List queryNoPayOrder(); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java new file mode 100644 index 0000000..5a6b0bc --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java @@ -0,0 +1,33 @@ +package com.xiang.service.module.jntyzx.miniapp.service; + + +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserAddReq; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:22 + */ +public interface IUserTokenInfoService { + + List getAvailableUser(); + List getCanOrderUser(); + String getToken(String name); + boolean flushSingleToken(String name); + boolean flushToken(); + boolean updateTokenByName(String name, String token); + + List list(UserQueryReq req); + + Boolean updateStatusByUserName(String username, Integer status); + + Boolean refreshToken(String username); + + Boolean save(UserAddReq req); + + JtUserVo info(Long userId); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java new file mode 100644 index 0000000..78733c8 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java @@ -0,0 +1,44 @@ +package com.xiang.service.module.jntyzx.miniapp.service; + + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; +import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2025-12-15 16:07 + */ +public interface IVenueService { + + List queryVenueService(); + List queryTomorrowVenue(); + List queryCanBuyVenue(); + List queryTomorrowCanBuyVenue(); + List queryToday6210VenueInfo(); + + /** + * 更新场地信息 + * @param sitePositionLists + * @return + */ + boolean saveOrUpdateTodayVenueInfo(List sitePositionLists); + + /** + * 更新第二天的场地信息 + * @param sitePositionLists + * @return + */ + boolean saveTomorrowVenueInfo(List sitePositionLists); + + /** + * 查询场地列表信息 + * @param request + * @return + */ + Page list(VenueInfoQueryRequest request); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java new file mode 100644 index 0000000..d1ff885 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java @@ -0,0 +1,225 @@ +package com.xiang.service.module.jntyzx.miniapp.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.SubscribeRequest; +import com.xiang.common.pojo.jntyzx.miniapp.req.SubscribeVo; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.OrderCreateResp; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.UserInfoResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.VenueList; +import com.xiang.common.utils.RedisService; +import com.xiang.service.module.jntyzx.miniapp.constants.UrlConstant; +import com.xiang.service.module.jntyzx.miniapp.manage.IOrderCreateInfoManage; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.utils.JntyzxSaltEncodeUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @Author: xiang + * @Date: 2025-05-14 14:07 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class JntyzxHttpServiceImpl implements IJntyzxHttpService { + + private final RedisService redisService; + private final IOrderCreateInfoManage orderCreateInfoManage; + + @Override + public List queryAvailable(String isWeekend, String token) { + String url = UrlConstant.QUERY_TODAY_SUBSCRIBE_URL; + return querySitePositionInfo(isWeekend, token, url); + } + + @NotNull + private static List querySitePositionInfo(String isWeekend, String token, String url) { + Map header = Maps.newHashMap(); + header.put("X-Access-Token", token); + String resp = null; + Map params = Maps.newHashMap(); + params.put("gid", "03"); + params.put("isWeekend", isWeekend); + try { + resp = HttpHelper.doGet(url, header, params); + } catch (Exception e) { + log.error("[doGet] 江南体育中心查询当天场地 请求失败, url:{}", url); + return Lists.newArrayList(); + } + if (StringUtils.isEmpty(resp)) { + log.warn("[查询场地] 江南体育中心查询当天场地 请求结果为空, url:{}, resp:{}", url, resp); + return Lists.newArrayList(); + } + JSONObject jsonObject = JSON.parseObject(resp); + if (Objects.isNull(jsonObject)) { + return Lists.newArrayList(); + } + String resultStr = JSON.toJSONString(jsonObject.get("result")); + if (StringUtils.isBlank(resultStr)) { + return Lists.newArrayList(); + } + JSONObject result = JSON.parseObject(resultStr); + if (Objects.isNull(result)) { + return Lists.newArrayList(); + } + String venueStr = JSON.toJSONString(result.get("venue")); + if (StringUtils.isBlank(venueStr)) { + return Lists.newArrayList(); + } + List venueLists = JSON.parseArray(venueStr, VenueList.class); + if (CollectionUtils.isEmpty(venueLists)) { + return Lists.newArrayList(); + } + List res = Lists.newArrayList(); + for (VenueList venueList : venueLists) { + List sitePositionList = venueList.getSitePosition(); + if (CollectionUtils.isEmpty(sitePositionList)) { + continue; + } + res.addAll(sitePositionList); + } + return res; + } + + @Override + public List queryAvailableTomorrow(String isWeekend, String token) { + String url = UrlConstant.QUERY_TOMORROW_SUBSCRIBE_URL; + return querySitePositionInfo(isWeekend, token, url); + } + + @Override + public JntyzxResponse createOrder(List venueInfos, String token, String openId) { + List vos = Lists.newArrayList(); + for (VenueInfoDO venueInfo : venueInfos) { + SubscribeVo subscribeVo = new SubscribeVo(); + subscribeVo.setId(0); + subscribeVo.setBallCourtId("03"); + subscribeVo.setSjName(venueInfo.getSjName()); + subscribeVo.setScheduleId(String.valueOf(venueInfo.getScheduleId())); + subscribeVo.setPlaceName(venueInfo.getPlaceName()); + subscribeVo.setPlaceId(venueInfo.getPlaceId()); + subscribeVo.setType("0"); + subscribeVo.setClassName(venueInfo.getClassName()); + subscribeVo.setClassCode(venueInfo.getClassCode()); + subscribeVo.setMoney(venueInfo.getMoney().setScale(0)); + subscribeVo.setContacts("0"); + subscribeVo.setContactNumber(null); + subscribeVo.setMemberNumber(null); + subscribeVo.setAppointments(venueInfo.getAppointments()); + subscribeVo.setOperator(null); + subscribeVo.setEndTime(null); + subscribeVo.setBeginTime(null); + subscribeVo.setSpecOneTimes(3); + subscribeVo.setCtypeCode(venueInfo.getCTypeCode()); + subscribeVo.setIsWhole(0); + subscribeVo.setOrderId(null); + subscribeVo.setVotesnum(1); + vos.add(subscribeVo); + } + + JSONObject jsonObject = buildParamJsonObj(openId); + SubscribeRequest subscribeRequest = new SubscribeRequest(); + + subscribeRequest.setSubscribeVos(vos); + subscribeRequest.setBookTime(venueInfos.get(0).getAppointments()); + subscribeRequest.setPaymentMethod(1); + subscribeRequest.setSvCiphertext(JntyzxSaltEncodeUtils.sonAddSalt(JsonUtils.toJsonString(vos))); + subscribeRequest.setJsonObject(jsonObject); + + Map params = Maps.newHashMap(); + params.put("X-Access-Token", token); + String resp = HttpHelper.doPost(UrlConstant.ADD_SUBSCRIBE, params, JsonUtils.toJsonString(subscribeRequest)); + log.info("[江体小程序] 羽毛球场地下单响应结果:{}", resp); + if (StringUtils.isBlank(resp)) { + log.info("[resp] 请求结果为空"); + return null; + } + JntyzxResponse response = JSON.parseObject(resp, new TypeReference>() { + }); + if (Objects.isNull(response)) { + log.info("[res ==> response] 请求结果为空"); + return null; + } + return response; + } + @Override + public JntyzxResponse healthDeclaration(String token, String openId) { + Map headers = Maps.newHashMap(); + headers.put("X-Access-Token", token); + Map params = Maps.newHashMap(); + params.put("openId", openId); + + String respStr = HttpHelper.doGet(UrlConstant.HEALTH_DECLARATION, headers, params); + if (StringUtils.isBlank(respStr)) { + return null; + } + return JSON.parseObject(respStr, JntyzxResponse.class); + } + + @Override + public JntyzxResponse queryByOpenId(String token, String openId) { + Map params = Maps.newHashMap(); + params.put("openId", openId); + Map headers = Maps.newHashMap(); + headers.put("X-Access-Token", token); + + String resp = HttpHelper.doGet(UrlConstant.QUERY_BY_OPEN_ID, headers, params); + JntyzxResponse response = JSON.parseObject(resp, new TypeReference>() { + }); + if (Objects.isNull(response)) { + log.info("请求结果为空!"); + return null; + } + return response; + } + + @Override + public JntyzxResponse checkDefaultNums(String token, String cardNo) { + Map params = Maps.newHashMap(); + params.put("consNumber", cardNo); + Map headers = Maps.newHashMap(); + headers.put("X-Access-Token", token); + + String resp = HttpHelper.doGet(UrlConstant.CHECK_NUM, headers, params); + if (StringUtils.isBlank(resp)) { + return null; + } + return JSON.parseObject(resp, JntyzxResponse.class); + } + + private static JSONObject buildParamJsonObj(String openId) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", "1702581215097257986"); + jsonObject.put("createBy", null); + jsonObject.put("createTime", "2023-09-15 15:12:48"); + jsonObject.put("updateBy", null); + jsonObject.put("updateTime", null); + jsonObject.put("sysOrgCode", null); + jsonObject.put("openId", openId); + jsonObject.put("nickName", "1"); + jsonObject.put("unionId", null); + jsonObject.put("avatarUrl", "https://thirdwx.qlogo.cn/mmopen/vi_32/POgEwh4mIHO4nibH0KlMECNjjGxQUq24ZEaGT4poC6icRiccVGKSyXwibcPq4BWmiaIGuG1icwxaQX6grC9VemZoJ8rg/132"); + jsonObject.put("remarks", null); + jsonObject.put("default01", null); + jsonObject.put("default02", null); + jsonObject.put("default03", null); + jsonObject.put("default04", null); + jsonObject.put("default05", null); + return jsonObject; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java new file mode 100644 index 0000000..5adff5a --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java @@ -0,0 +1,98 @@ +package com.xiang.service.module.jntyzx.miniapp.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.xiang.common.exception.BusinessException; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.OrderCreateResp; +import com.xiang.common.utils.RedisService; +import com.xiang.service.module.jntyzx.miniapp.constants.RedisKeyConstant; +import com.xiang.service.module.jntyzx.miniapp.manage.IOrderCreateInfoManage; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.service.IJtOrderService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +/** + * @Author: xiang + * @Date: 2025-12-16 16:17 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class OrderInfoServiceImpl implements IJtOrderService { + + private final IOrderCreateInfoManage orderCreateInfoManage; + private final IJntyzxHttpService jntyzxHttpService; + private final RedisService redisService; + private final JntyzxHttpServiceImpl dingTalkFactory; + @Override + public List queryNoPayOrder() { + return orderCreateInfoManage.queryNoPayOrder(); + } + + @Override + public boolean createOrder(List venueInfoDOS, UserTokenInfoDO userTokenInfoDO) { + + String order = (String) redisService.get(RedisKeyConstant.JNTYZX_ORDER_CREATE_KEY + userTokenInfoDO.getName() + LocalDate.now()); + if (StringUtils.isNotBlank(order)) { + log.info("用户:{}已经有成功预订了场地", userTokenInfoDO.getName()); + return true; + } + + String user = (String) redisService.get(RedisKeyConstant.getCloseCardKey(userTokenInfoDO.getName())); + if (StringUtils.isNotBlank(user)) { + log.info("用户:{}有锁卡风险,不在请求接口!"); + return true; + } + + JntyzxResponse orderResp = jntyzxHttpService.createOrder(venueInfoDOS, userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId()); + if (Objects.isNull(orderResp)) { + return false; + } + if (orderResp.getSuccess()) { + OrderCreateResp result = orderResp.getResult(); + if (Objects.nonNull(result)) { + String orderId = result.getId(); + if (StringUtils.isNotBlank(orderId)) { + redisService.set(RedisKeyConstant.JNTYZX_ORDER_CREATE_KEY + userTokenInfoDO.getName() + LocalDate.now(), orderId); + OrderInfoDO orderInfoDO = new OrderInfoDO(); + orderInfoDO.setOrderId(orderId); + orderInfoDO.setCreateTime(LocalDateTime.now()); + orderInfoDO.setUsername(userTokenInfoDO.getName()); + orderInfoDO.setPlaceName(venueInfoDOS.get(0).getPlaceName()); + orderInfoDO.setDate(LocalDate.now()); + orderInfoDO.setOrderStatus(0); + orderCreateInfoManage.save(orderInfoDO); + } + } + dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); + return true; + } else { + dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); + if (orderResp.getMessage().contains("锁卡")) { + log.info("有锁卡风险,不在请求,用户:{}", userTokenInfoDO.getName()); + throw new BusinessException("即将锁卡,不再请求"); + } + if (orderResp.getMessage().contains("限制")) { + log.info("改会员卡被限制,不在请求,用户:{}", userTokenInfoDO.getName()); + redisService.set(RedisKeyConstant.getCloseCardKey(userTokenInfoDO.getName()), "true"); + throw new BusinessException("会员卡被限制,不在请求"); + } + if (orderResp.getMessage().contains("已有人预订")) { + log.info("该场地已被人预定,更换场地, 用户:{}", userTokenInfoDO.getName()); + redisService.set(RedisKeyConstant.getVenueSubscribeKey(venueInfoDOS.get(0).getPlaceName()), "true"); + } + return false; + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java new file mode 100644 index 0000000..bdc94db --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java @@ -0,0 +1,251 @@ +package com.xiang.service.module.jntyzx.miniapp.service.impl; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.xiang.common.exception.BusinessException; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserAddReq; +import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.UserInfoResponse; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.converts.UserConverter; +import com.xiang.service.module.jntyzx.miniapp.manage.IUserRestrictionManage; +import com.xiang.service.module.jntyzx.miniapp.manage.IUserTokenInfoManage; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:22 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class UserTokenInfoServiceImpl implements IUserTokenInfoService { + + private final IUserTokenInfoManage userTokenInfoManage; + private final IJntyzxHttpService jntyzxHttpService; + private final JntyzxDingTalkFactory jtDingTalkFactory; + private final IUserRestrictionManage userRestrictionManage; + private final UserConverter userConverter; + + + @Override + public List getAvailableUser() { + return userTokenInfoManage.listUser(); + } + + @Override + public String getToken(String name) { + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(name); + if (Objects.isNull(userTokenInfoDO)) { + return null; + } + return userTokenInfoDO.getToken(); + } + + @Override + public List getCanOrderUser() { + return userTokenInfoManage.listCanOrder(); + } + + @Override + public boolean flushSingleToken(String name) { + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(name); + if (Objects.isNull(userTokenInfoDO)) { + log.info("用户信息不存在,无需进行监测!"); + return false; + } + return healthDeclaration(userTokenInfoDO); + + } + + @Override + public boolean flushToken() { + List userTokenInfoDOS = userTokenInfoManage.list(); + if (CollectionUtils.isEmpty(userTokenInfoDOS)) { + log.info("【心跳监测】查询用户信息为空,无需操作"); + return true; + } + userTokenInfoDOS.forEach(this::healthDeclaration); + // 信息更新 + userTokenInfoDOS = userTokenInfoManage.list(); + userTokenInfoDOS.forEach(this::queryMemberCardInfo); + return true; + } + + @Override + public boolean updateTokenByName(String name, String token) { + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(name); + if (Objects.isNull(userTokenInfoDO)) { + throw new BusinessException("用户信息不存在!"); + } + userTokenInfoDO.setToken(token); + boolean flag = userTokenInfoManage.updateById(userTokenInfoDO); + jtDingTalkFactory.sendMsg("用户:" + name + ",token更新成功!"); + return flag; + } + + private boolean healthDeclaration(UserTokenInfoDO userTokenInfoDO) { + JntyzxResponse jntyzxResponse = jntyzxHttpService.healthDeclaration(userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId()); + if (Objects.isNull(jntyzxResponse)) { + log.info("用户名:{}心跳监测失败!", userTokenInfoDO.getName()); + } + boolean flag = StringUtils.contains(jntyzxResponse.getMessage(), "已存在"); + if (flag) { + log.info("用户名:{}心跳成功✅✅✅✅✅✅", userTokenInfoDO.getName()); + userTokenInfoDO.setStatus(1); + userTokenInfoManage.updateById(userTokenInfoDO); + } else { + jtDingTalkFactory.sendMsg("用户名:" + userTokenInfoDO.getName() + "心跳失败,消息:" + jntyzxResponse.getMessage()); + userTokenInfoDO.setStatus(0); + userTokenInfoManage.updateById(userTokenInfoDO); + } + return flag; + } + + @Override + public List list(UserQueryReq req) { + List userTokenInfoDOS = userTokenInfoManage.queryByList(req); + if (CollectionUtils.isEmpty(userTokenInfoDOS)) { + return Lists.newArrayList(); + } + List idList = userTokenInfoDOS.stream().map(UserTokenInfoDO::getId).toList(); + List userRestrictionInfos = userRestrictionManage.queryByIdList(idList); + Map userRestrictionInfoMap = Maps.newHashMap(); + if (CollectionUtils.isNotEmpty(userRestrictionInfos)) { + userRestrictionInfoMap.putAll( + userRestrictionInfos.stream().collect(Collectors.toMap( + UserRestrictionInfo::getUserId, Function.identity(), (a, b) -> a))); + } + List jtUserVoList = userConverter.convert(userTokenInfoDOS); + jtUserVoList.forEach(item -> { + if (userRestrictionInfoMap.containsKey(item.getUserId())) { + UserRestrictionInfo userRestrictionInfo = userRestrictionInfoMap.get(item.getUserId()); + if (Objects.nonNull(userRestrictionInfo)) { + if (userRestrictionInfo.getRestrictionDeadline().isAfter(LocalDateTime.now())) { + item.setRestrictionDeadline(userRestrictionInfo.getRestrictionDeadline()); + item.setRestrictionDesc(userRestrictionInfo.getRestrictionDesc()); + } + } + } + }); + return jtUserVoList; + } + + @Override + public Boolean updateStatusByUserName(String username, Integer status) { + if (StringUtils.isEmpty(username)) { + log.error("用户名为空"); + return false; + } + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(username); + if (Objects.isNull(userTokenInfoDO)) { + throw new BusinessException("用户不存在!"); + } + userTokenInfoDO.setStatus(status); + return userTokenInfoManage.updateById(userTokenInfoDO); + } + + @Override + public Boolean refreshToken(String username) { + if (StringUtils.isEmpty(username)) { + log.error("用户名为空"); + return false; + } + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(username); + if (Objects.isNull(userTokenInfoDO)) { + throw new BusinessException("用户不存在!"); + } + return healthDeclaration(userTokenInfoDO); + } + + @Override + public Boolean save(UserAddReq req) { + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(req.getName()); + if (Objects.nonNull(userTokenInfoDO)) { + throw new BusinessException("用户名已存在!"); + } + userTokenInfoDO = new UserTokenInfoDO(); + userTokenInfoDO.setName(req.getName()); + userTokenInfoDO.setToken(req.getToken()); + userTokenInfoDO.setOpenId(req.getOpenId()); + userTokenInfoDO.setStatus(req.getStatus()); + userTokenInfoDO.setIsOrder(1); + userTokenInfoDO.setMemberCardNo(req.getMemberCardNo()); + userTokenInfoDO.setIsRestriction(0); + userTokenInfoDO.setUpdateTime(LocalDateTime.now()); + return userTokenInfoManage.save(userTokenInfoDO); + } + + @Override + public JtUserVo info(Long userId) { + UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getById(userId); + if (Objects.isNull(userTokenInfoDO)) { + throw new BusinessException("用户不存在!"); + } + JtUserVo jtUserVo = userConverter.convert(userTokenInfoDO); + UserRestrictionInfo userRestrictionInfo = userRestrictionManage.queryByUserId(userId); + if (Objects.nonNull(userRestrictionInfo)) { + if (userRestrictionInfo.getRestrictionDeadline().isAfter(LocalDateTime.now())) { + jtUserVo.setRestrictionDeadline(userRestrictionInfo.getRestrictionDeadline()); + jtUserVo.setRestrictionDesc(userRestrictionInfo.getRestrictionDesc()); + } + } + return jtUserVo; + } + + /** + * 查询用户信息 + * + * @param userTokenInfoDO 用户 + * + * @return + */ + private void queryMemberCardInfo(UserTokenInfoDO userTokenInfoDO) { + JntyzxResponse response = jntyzxHttpService.queryByOpenId(userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId()); + if (Objects.isNull(response)) { + return; + } + if (response.getSuccess()) { + UserInfoResponse userInfoResponse = response.getResult(); + userTokenInfoDO.setMemberCardNo(userInfoResponse.getConsCard()); + if (StringUtils.isNotBlank(userInfoResponse.getRestrictionDeadline2())) { + userTokenInfoDO.setIsRestriction(1); + userTokenInfoDO.setIsOrder(0); + UserRestrictionInfo userRestrictionInfo = userRestrictionManage.queryByUserId(userTokenInfoDO.getId()); + if (Objects.isNull(userRestrictionInfo)) { + userRestrictionInfo = new UserRestrictionInfo(); + userRestrictionInfo.setUserId(userTokenInfoDO.getId()); + userRestrictionInfo.setRestrictionDeadline(DateUtils.getDateTimeFromStr(userInfoResponse.getRestrictionDeadline2())); + userRestrictionInfo.setRestrictionDesc(userInfoResponse.getRestrictionDescription()); + userRestrictionManage.save(userRestrictionInfo); + } else { + userRestrictionInfo.setRestrictionDeadline(DateUtils.getDateTimeFromStr(userInfoResponse.getRestrictionDeadline2())); + userRestrictionInfo.setRestrictionDesc(userInfoResponse.getRestrictionDescription()); + userRestrictionManage.updateById(userRestrictionInfo); + } + } else { + userTokenInfoDO.setIsRestriction(0); + userTokenInfoDO.setIsOrder(1); + } + userTokenInfoManage.updateById(userTokenInfoDO); + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java new file mode 100644 index 0000000..058af34 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java @@ -0,0 +1,227 @@ +package com.xiang.service.module.jntyzx.miniapp.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; +import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.converts.VenueInfoConverter; +import com.xiang.service.module.jntyzx.miniapp.manage.IVenueInfoManage; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Author: xiang + * @Date: 2025-12-15 16:08 + */ +@Service +@RequiredArgsConstructor +public class VenueServiceImpl implements IVenueService { + + private final IJntyzxHttpService jntyzxHttpService; + private final IVenueInfoManage venueInfoManage; + private final IUserTokenInfoService userTokenInfoService; + private final VenueInfoConverter venueInfoConverter; + + @Override + public List queryVenueService() { + String token = userTokenInfoService.getToken("Xiang"); + if (StringUtils.isBlank(token)) { + return Lists.newArrayList(); + } + List sitePositionLists = jntyzxHttpService.queryAvailable("1", token); + if (CollectionUtils.isEmpty(sitePositionLists)) { + return Lists.newArrayList(); + } + updateDatabase(sitePositionLists, true); + return sitePositionLists; + } + + @Override + public List queryTomorrowVenue() { + String token = userTokenInfoService.getToken("Xiang"); + if (StringUtils.isBlank(token)) { + return Lists.newArrayList(); + } + List sitePositionLists = jntyzxHttpService.queryAvailableTomorrow("1", token); + if (CollectionUtils.isEmpty(sitePositionLists)) { + return Lists.newArrayList(); + } + updateDatabase(sitePositionLists, false); + return sitePositionLists; + } + + @Override + public List queryCanBuyVenue() { + return venueInfoManage.queryByType(LocalDate.now(), 0); + } + + @Override + public List queryTomorrowCanBuyVenue() { + return venueInfoManage.queryByType(LocalDate.now().plusDays(1), 0); + } + + @Override + public List queryToday6210VenueInfo() { + List venueInfoDOS = venueInfoManage.queryByDate(LocalDate.now()); + return venueInfoDOS.stream().filter(item -> VenueInfoUtils.get628VenueInfo(item) || VenueInfoUtils.get8210VenueInfo(item)).toList(); + } + + @Override + public boolean saveOrUpdateTodayVenueInfo(List sitePositionLists) { + List venueInfoDOS = venueInfoManage.queryByDate(LocalDate.now()); + Map> map = Maps.newHashMap(); + if (CollectionUtils.isNotEmpty(sitePositionLists)) { + Map> map1 = venueInfoDOS.stream().collect(Collectors.groupingBy(VenueInfoDO::getPlaceName)); + map1.forEach((placeName, venueInfos) -> { + Map venueInfoDOMap = venueInfoDOS.stream().collect(Collectors.toMap(VenueInfoDO::getSjName, Function.identity(), (a, b) -> a)); + map.put(placeName, venueInfoDOMap); + }); + } + + for (SitePositionList sitePositionList : sitePositionLists) { + if (map.containsKey(sitePositionList.getPlaceName())) { + Map venueInfoDOMap = map.get(sitePositionList.getPlaceName()); + if (venueInfoDOMap.containsKey(sitePositionList.getSjName())) { + VenueInfoDO venueInfoDO = venueInfoDOMap.get(sitePositionList.getSjName()); + venueInfoDO.setPlaceName(sitePositionList.getPlaceName()); + venueInfoDO.setPlaceMainId(sitePositionList.getId()); + venueInfoDO.setPlaceId(sitePositionList.getPlaceId()); + venueInfoDO.setScheduleId(Integer.valueOf(sitePositionList.getScheduleId())); + venueInfoDO.setSjName(sitePositionList.getSjName()); + venueInfoDO.setContacts(sitePositionList.getContacts()); + venueInfoDO.setType(sitePositionList.getType()); + venueInfoDO.setMoney(sitePositionList.getMoney()); + venueInfoDO.setClassName(sitePositionList.getClassName()); + venueInfoDO.setClassCode(sitePositionList.getClassCode()); + venueInfoDO.setAppointments(sitePositionList.getAppointments()); + venueInfoDO.setCTypeCode(sitePositionList.getCtypeCode()); + venueInfoManage.updateById(venueInfoDO); + } else { + VenueInfoDO venueInfoDO = new VenueInfoDO(); + venueInfoDO.setDate(LocalDate.now()); + venueInfoDO.setCreateTime(LocalDateTime.now()); + venueInfoDO.setPlaceName(sitePositionList.getPlaceName()); + venueInfoDO.setPlaceMainId(sitePositionList.getId()); + venueInfoDO.setPlaceId(sitePositionList.getPlaceId()); + venueInfoDO.setScheduleId(Integer.valueOf(sitePositionList.getScheduleId())); + venueInfoDO.setSjName(sitePositionList.getSjName()); + venueInfoDO.setContacts(sitePositionList.getContacts()); + venueInfoDO.setType(sitePositionList.getType()); + venueInfoDO.setMoney(sitePositionList.getMoney()); + venueInfoDO.setClassName(sitePositionList.getClassName()); + venueInfoDO.setClassCode(sitePositionList.getClassCode()); + venueInfoDO.setAppointments(sitePositionList.getAppointments()); + venueInfoDO.setCTypeCode(sitePositionList.getCtypeCode()); + venueInfoManage.save(venueInfoDO); + } + } + } + return true; + } + + @Override + public boolean saveTomorrowVenueInfo(List sitePositionLists) { + for (SitePositionList sitePositionList : sitePositionLists) { + VenueInfoDO venueInfoDO = new VenueInfoDO(); + venueInfoDO.setDate(LocalDate.now().plusDays(1)); + venueInfoDO.setCreateTime(LocalDateTime.now()); + venueInfoDO.setPlaceName(sitePositionList.getPlaceName()); + venueInfoDO.setPlaceMainId(sitePositionList.getId()); + venueInfoDO.setPlaceId(sitePositionList.getPlaceId()); + venueInfoDO.setScheduleId(Integer.valueOf(sitePositionList.getScheduleId())); + venueInfoDO.setSjName(sitePositionList.getSjName()); + venueInfoDO.setContacts(sitePositionList.getContacts()); + venueInfoDO.setType(sitePositionList.getType()); + venueInfoDO.setMoney(sitePositionList.getMoney()); + venueInfoDO.setClassName(sitePositionList.getClassName()); + venueInfoDO.setClassCode(sitePositionList.getClassCode()); + venueInfoDO.setAppointments(sitePositionList.getAppointments()); + venueInfoDO.setCTypeCode(sitePositionList.getCtypeCode()); + venueInfoManage.save(venueInfoDO); + } + return true; + } + + @Override + public Page list(VenueInfoQueryRequest request) { + Page page = venueInfoManage.page(request); + return venueInfoConverter.toPage(page); + } + + private void updateDatabase(List list, boolean isToday) { + List venueInfoDOS = Lists.newArrayList(); + if (isToday) { + venueInfoDOS.addAll(venueInfoManage.queryByDate(LocalDate.now())); + } else { + venueInfoDOS.addAll(venueInfoManage.queryByDate(LocalDate.now().plusDays(1))); + } + + Map> map = Maps.newHashMap(); + if (CollectionUtils.isNotEmpty(venueInfoDOS)) { + map.putAll(venueInfoDOS.stream().filter(Objects::nonNull) + .collect(Collectors.groupingBy(VenueInfoDO::getPlaceId))); + } + List insertList = Lists.newArrayList(); + for (SitePositionList sitePositionList : list) { + if (map.containsKey(sitePositionList.getPlaceId())) { + List venueInfoDOList = map.get(sitePositionList.getPlaceId()); + Map sjMap = venueInfoDOList.stream().collect(Collectors.toMap(VenueInfoDO::getSjName, Function.identity(), (a, b) -> a)); + if (sjMap.containsKey(sitePositionList.getSjName())) { + VenueInfoDO venueInfoDO = sjMap.get(sitePositionList.getSjName()); + if (!StringUtils.equals(venueInfoDO.getContacts(), sitePositionList.getContacts()) + || !Objects.equals(venueInfoDO.getType(), sitePositionList.getType()) + || !Objects.equals(venueInfoDO.getPlaceMainId(), sitePositionList.getId())) { + venueInfoDO.setContacts(sitePositionList.getContacts()); + venueInfoDO.setType(sitePositionList.getType()); + venueInfoDO.setPlaceMainId(sitePositionList.getId()); + venueInfoManage.updateById(venueInfoDO); + } + } else { + addIntoInsert(sitePositionList, insertList); + } + } else { + addIntoInsert(sitePositionList, insertList); + } + } + if (CollectionUtils.isNotEmpty(insertList)) { + venueInfoManage.saveBatch(insertList); + } + } + + private static void addIntoInsert(SitePositionList sitePositionList, List insertList) { + VenueInfoDO venueInfoDO = new VenueInfoDO(); + venueInfoDO.setPlaceName(sitePositionList.getPlaceName()); + venueInfoDO.setDate(DateUtils.getDateFromStr(sitePositionList.getAppointments())); + venueInfoDO.setPlaceMainId(sitePositionList.getId()); + venueInfoDO.setPlaceId(sitePositionList.getPlaceId()); + venueInfoDO.setScheduleId(Integer.valueOf(sitePositionList.getScheduleId())); + venueInfoDO.setSjName(sitePositionList.getSjName()); + venueInfoDO.setCreateTime(LocalDateTime.now()); + venueInfoDO.setContacts(sitePositionList.getContacts()); + venueInfoDO.setType(sitePositionList.getType()); + venueInfoDO.setMoney(sitePositionList.getMoney()); + venueInfoDO.setClassName(sitePositionList.getClassName()); + venueInfoDO.setClassCode(sitePositionList.getClassCode()); + venueInfoDO.setAppointments(sitePositionList.getAppointments()); + venueInfoDO.setCTypeCode(sitePositionList.getCtypeCode()); + insertList.add(venueInfoDO); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/JntyzxSaltEncodeUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/JntyzxSaltEncodeUtils.java new file mode 100644 index 0000000..5677f60 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/JntyzxSaltEncodeUtils.java @@ -0,0 +1,36 @@ +package com.xiang.service.module.jntyzx.miniapp.utils; + + +import com.xiang.common.utils.Base64; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; + +public class JntyzxSaltEncodeUtils { + + private static int[] getMonthAndDay() { + LocalDate currentDate = LocalDate.now(); + int month = currentDate.getMonthValue(); + int day = currentDate.getDayOfMonth(); + return new int[]{month, day}; + } + + public static String sonAddSalt(String json) { + String svCiphertext = ""; + String suiji = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + String token1 = String.valueOf(suiji.charAt((int) (Math.random() * (double) suiji.length()))); + String token2 = String.valueOf(suiji.charAt((int) (Math.random() * (double) suiji.length()))); + svCiphertext = Base64.encode(json.getBytes(StandardCharsets.UTF_8)); + int[] monthAndDay = getMonthAndDay(); + int month = monthAndDay[0]; + int day = monthAndDay[1]; + if (month == 1) { + svCiphertext = (svCiphertext = token1 + svCiphertext).substring(0, day - 1) + token2 + svCiphertext.substring(day - 1); + } else if (day == 1) { + svCiphertext = token2 + svCiphertext.substring(0, month - 1) + token1 + svCiphertext.substring(month - 1); + } else { + svCiphertext = (svCiphertext = svCiphertext.substring(0, month - 1) + token1 + svCiphertext.substring(month - 1)).substring(0, day - 1) + token2 + svCiphertext.substring(day - 1); + } + return svCiphertext; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java new file mode 100644 index 0000000..077e865 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java @@ -0,0 +1,42 @@ +package com.xiang.service.module.jntyzx.miniapp.utils; + +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.utils.RedisService; +import com.xiang.service.module.jntyzx.miniapp.constants.RedisKeyConstant; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @Author: xiang + * @Date: 2026-01-26 09:14 + */ +@Component +@RequiredArgsConstructor +public class MsgSendUtils { + + private final RedisService redisService; + private final JntyzxDingTalkFactory jtDingTalkFactory; + + /** + * 限制钉钉消息发送 1小时最多5次 + * @param redisKey redis缓存的key + * @param msgContent 消息内容 + */ + public void sendMsgRestrict1Hours(String redisKey, String msgContent) { + String key = RedisKeyConstant.JNTYZX_VENUE_MSG_SEND_KEY + RedisKeyConstant.getDate(); + String cache = (String) redisService.get(redisKey); + if (StringUtils.isNotBlank(cache)) { + int sendNum = Integer.parseInt(cache); + if (sendNum >= 0 && sendNum <= 5) { + jtDingTalkFactory.sendMsg(msgContent); + redisService.set(key, String.valueOf(++sendNum), 1, TimeUnit.HOURS); + } + } else { + jtDingTalkFactory.sendMsg(msgContent); + redisService.set(key, "0", 1, TimeUnit.HOURS); + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java new file mode 100644 index 0000000..464962d --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java @@ -0,0 +1,41 @@ +package com.xiang.service.module.jntyzx.miniapp.utils; + +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import org.apache.commons.lang3.StringUtils; + +/** + * @Author: xiang + * @Date: 2025-12-16 09:55 + */ +public class VenueInfoUtils { + + public static boolean get123VenueInfo4Mor(VenueInfoDO venueInfoDO) { + return StringUtils.equals(venueInfoDO.getSjName(), "13:00-14:00") || StringUtils.equals(venueInfoDO.getSjName(), "14:00-15:00"); + } + public static boolean get1221VenueInfo4Mor(VenueInfoDO venueInfoDO) { + return StringUtils.equals(venueInfoDO.getSjName(), "12:00-13:00") || StringUtils.equals(venueInfoDO.getSjName(), "13:00-14:00"); + } + public static boolean get628VenueInfo(VenueInfoDO venueInfoDO) { + return StringUtils.equals(venueInfoDO.getSjName(), "18:00-19:00") || StringUtils.equals(venueInfoDO.getSjName(), "19:00-20:00"); + } + public static boolean get8210VenueInfo(VenueInfoDO venueInfoDO) { + return StringUtils.equals(venueInfoDO.getSjName(), "20:00-21:00") || StringUtils.equals(venueInfoDO.getSjName(), "21:00-22:00"); + } + public static boolean get8210VenueInfo(SitePositionList sitePositionList) { + return StringUtils.equals(sitePositionList.getSjName(), "20:00-21:00") || StringUtils.equals(sitePositionList.getSjName(), "21:00-22:00"); + } + + public static int sortVenueInfo(String placeName) { + if (placeName.contains("十号")) { + return 0; + } + if (placeName.contains("二号")) { + return 1; + } + if (placeName.contains("九号")) { + return 2; + } + return 3; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/WeekendUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/WeekendUtils.java new file mode 100644 index 0000000..c319769 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/WeekendUtils.java @@ -0,0 +1,17 @@ +package com.xiang.service.module.jntyzx.miniapp.utils; + +import java.time.DayOfWeek; +import java.time.LocalDate; + +public class WeekendUtils { + + public static String isWeekend() { + LocalDate tomorrow = LocalDate.now().plusDays(1); + DayOfWeek dayOfWeek = tomorrow.getDayOfWeek(); + String isWeekend = "0"; + if (dayOfWeek.getValue() == 6 || dayOfWeek.getValue() == 7 ) { + isWeekend = "1"; + } + return isWeekend; + } +} -- 2.49.1 From b30af008e03a551481eea034dfa61406b7e580b6 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 10:21:03 +0800 Subject: [PATCH 24/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/xiang/ApplicationInit.java | 2 +- .../enums/JntyzxUrlConstant.java} | 4 +- .../enums}/RedisKeyConstant.java | 4 +- .../com/xiang/common/enums/ScheduleEnums.java | 9 +- .../miniapp}/IOrderCreateInfoManage.java | 2 +- .../jntyzx/miniapp/IUserInfoManage.java | 12 ++ .../miniapp}/IUserRestrictionManage.java | 2 +- .../jntyzx/miniapp}/IUserTokenInfoManage.java | 2 +- .../jntyzx/miniapp}/IVenueInfoManage.java | 6 +- .../miniapp}/OrderCreateInfoManageImpl.java | 4 +- .../jntyzx/miniapp/UserInfoManageImpl.java | 18 +++ .../miniapp}/UserRestrictionManageImpl.java | 4 +- .../miniapp}/UserTokenInfoManageImpl.java | 4 +- .../jntyzx/miniapp}/VenueInfoManageImpl.java | 21 +-- .../jntyzx/zlb}/ZlbSiteInfoService.java | 2 +- .../jntyzx/zlb}/ZlbSiteInfoServiceImpl.java | 2 +- .../jntyzx/zlb}/ZlbTokenInfoService.java | 2 +- .../jntyzx/zlb}/ZlbTokenInfoServiceImpl.java | 2 +- .../jntyzx/zlb}/ZlbUserInfoService.java | 2 +- .../jntyzx/zlb}/ZlbUserInfoServiceImpl.java | 2 +- .../mapper/JntyzxOrderCreateInfoMapper.java | 2 +- .../common/mapper/JntyzxUserInfoMapper.java | 20 +++ .../JntyzxUserRestrictionInfoMapper.java | 2 +- .../mapper/JntyzxUserTokenInfoMapper.java | 2 +- .../mapper/JntyzxVenueInfoMapper.java | 2 +- .../pojo/jntyzx/miniapp/pojo/UserInfoDO.java | 53 +++++++ .../jntyzx/miniapp/req/SubscribeRequest.java | 2 +- .../resp/query/QueryVenueResponse.java | 1 + .../com/xiang/common/utils/DateUtils.java | 64 +------- .../com/xiang/common/utils/JsonUtils.java | 18 +++ .../schedule/DomainDynamicAnalysisTask.java | 2 +- .../glados/schedule/GladosCheckInTask.java | 2 +- .../miniapp/converts/UserConverter.java | 32 ---- .../miniapp/converts/VenueInfoConverter.java | 16 -- .../schedule/JntyzxMiniappScheduleConfig.java | 53 +++++++ .../schedule/JntyzxUserInfoConfigTask.java | 116 ++++++++++++++ .../miniapp/schedule/JtTokenRefreshTask.java | 57 +++++++ .../miniapp/schedule/JtVenuePullTask.java | 144 ++++++++++++++++++ .../schedule/JtVenueSubscribeTask.java | 127 +++++++++++++++ .../schedule/JtVenueTomorrowPullTask.java | 121 +++++++++++++++ .../miniapp/service/IUserInfoService.java | 16 ++ .../service/IUserTokenInfoService.java | 13 -- .../jntyzx/miniapp/service/IVenueService.java | 10 -- .../service/impl/JntyzxHttpServiceImpl.java | 20 +-- .../service/impl/OrderInfoServiceImpl.java | 7 +- .../service/impl/UserInfoServiceImpl.java | 32 ++++ .../impl/UserTokenInfoServiceImpl.java | 115 ++------------ .../service/impl/VenueServiceImpl.java | 13 +- .../jntyzx/miniapp/utils/MsgSendUtils.java | 2 +- .../jntyzx/miniapp/utils/VenueInfoUtils.java | 12 +- .../jntyzx/zlb/schedule/ZlbLoginTask.java | 4 +- .../jntyzx/zlb/schedule/ZlbOrderTask.java | 40 +++-- .../jntyzx/zlb/schedule/ZlbSiteDayTask.java | 3 +- .../jntyzx/zlb/schedule/ZlbSiteTask.java | 2 +- .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 2 +- .../zlb/schedule/ZlbTokenRefreshTask.java | 5 +- .../zlb/schedule/ZlbUserConfigTask.java | 31 +--- .../jntyzx/zlb/service/ZlbServiceImpl.java | 3 + 58 files changed, 916 insertions(+), 356 deletions(-) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/constants/UrlConstant.java => common/enums/JntyzxUrlConstant.java} (93%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/constants => common/enums}/RedisKeyConstant.java (93%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/IOrderCreateInfoManage.java (85%) create mode 100644 src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserInfoManage.java rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/IUserRestrictionManage.java (86%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/IUserTokenInfoManage.java (90%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/IVenueInfoManage.java (62%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/OrderCreateInfoManageImpl.java (85%) create mode 100644 src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserInfoManageImpl.java rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/UserRestrictionManageImpl.java (88%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/UserTokenInfoManageImpl.java (95%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp/manage => common/manage/jntyzx/miniapp}/VenueInfoManageImpl.java (52%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbSiteInfoService.java (85%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbSiteInfoServiceImpl.java (90%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbTokenInfoService.java (88%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbTokenInfoServiceImpl.java (95%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbUserInfoService.java (88%) rename src/main/java/com/xiang/{service/module/jntyzx/zlb/service => common/manage/jntyzx/zlb}/ZlbUserInfoServiceImpl.java (91%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp => common}/mapper/JntyzxOrderCreateInfoMapper.java (86%) create mode 100644 src/main/java/com/xiang/common/mapper/JntyzxUserInfoMapper.java rename src/main/java/com/xiang/{service/module/jntyzx/miniapp => common}/mapper/JntyzxUserRestrictionInfoMapper.java (85%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp => common}/mapper/JntyzxUserTokenInfoMapper.java (87%) rename src/main/java/com/xiang/{service/module/jntyzx/miniapp => common}/mapper/JntyzxVenueInfoMapper.java (86%) create mode 100644 src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserInfoDO.java create mode 100644 src/main/java/com/xiang/common/utils/JsonUtils.java delete mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java delete mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java diff --git a/src/main/java/com/xiang/ApplicationInit.java b/src/main/java/com/xiang/ApplicationInit.java index ca65b5b..3246791 100644 --- a/src/main/java/com/xiang/ApplicationInit.java +++ b/src/main/java/com/xiang/ApplicationInit.java @@ -54,7 +54,7 @@ public class ApplicationInit implements ApplicationRunner { continue; } ScheduleOpeningConfigDO scheduleOpeningConfigDO = new ScheduleOpeningConfigDO(); - scheduleOpeningConfigDO.setModule(scheduleEnum.getModeleCode()); + scheduleOpeningConfigDO.setModule(scheduleEnum.getModuleCode()); scheduleOpeningConfigDO.setBeanName(scheduleEnum.getTaskName()); scheduleOpeningConfigDO.setStatus(1); list.add(scheduleOpeningConfigDO); diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java b/src/main/java/com/xiang/common/enums/JntyzxUrlConstant.java similarity index 93% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java rename to src/main/java/com/xiang/common/enums/JntyzxUrlConstant.java index 227e072..1fd1023 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/UrlConstant.java +++ b/src/main/java/com/xiang/common/enums/JntyzxUrlConstant.java @@ -1,10 +1,10 @@ -package com.xiang.service.module.jntyzx.miniapp.constants; +package com.xiang.common.enums; /** * @Author: xiang * @Date: 2025-12-15 13:46 */ -public class UrlConstant { +public class JntyzxUrlConstant { /** * 江南体育中心基础URL diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java b/src/main/java/com/xiang/common/enums/RedisKeyConstant.java similarity index 93% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java rename to src/main/java/com/xiang/common/enums/RedisKeyConstant.java index 8159f6c..6117a59 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/constants/RedisKeyConstant.java +++ b/src/main/java/com/xiang/common/enums/RedisKeyConstant.java @@ -1,6 +1,8 @@ -package com.xiang.service.module.jntyzx.miniapp.constants; +package com.xiang.common.enums; +import com.xiang.common.utils.DateUtils; + import java.time.LocalDate; /** diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 3ba9daf..6ea4c02 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -31,9 +31,16 @@ public enum ScheduleEnums { ZLB_ORDER_CREATE_TASK(3, "zlb", "zlbOrderCreateTask"), ZLB_USER_CONFIG_TASK(3, "zlb", "zlbUserConfigTask"), + + JNTYZX_TOKEN_REFRESH_TASK(4, "jt-miniApp", "jntyzxTokenRefreshTask"), + JNTYZX_VENUE_INFO_PULL_TASK(4, "jt-miniApp", "jntyzxVenuePullTask"), + JNTYZX_ORDER_SUBSCRIBE_TASK(4, "jt-miniApp", "jntyzxOrderSubscribeTask"), + JNTYZX_VENUE_TODAY_SUBSCRIBE_TASK(4, "jt-miniApp", "jntyzxVenueTodaySubscribeTask"), + JNTYZX_VENUE_TOMORROW_PULL_TASK(4, "jt-miniApp", "jntyzxVenueTodayPullTask"), + JNTYZX_USER_INFO_CONFIG(4, "jt-miniApp", "jntyzxUserInfoConfigTask"), ; - private final Integer modeleCode; + private final Integer moduleCode; private final String module; private final String taskName; } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IOrderCreateInfoManage.java similarity index 85% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/IOrderCreateInfoManage.java index 8757938..99315a7 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IOrderCreateInfoManage.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IOrderCreateInfoManage.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; diff --git a/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserInfoManage.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserInfoManage.java new file mode 100644 index 0000000..27ce953 --- /dev/null +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserInfoManage.java @@ -0,0 +1,12 @@ +package com.xiang.common.manage.jntyzx.miniapp; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; + +/** + * @Author: xiang + * @Date: 2026-05-09 10:17 + */ +public interface IUserInfoManage extends IService { + boolean delAll(); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserRestrictionManage.java similarity index 86% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserRestrictionManage.java index 77462a3..6f7f53d 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserRestrictionManage.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserRestrictionManage.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserTokenInfoManage.java similarity index 90% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserTokenInfoManage.java index 24ee98a..6f702ce 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IUserTokenInfoManage.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IUserTokenInfoManage.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IVenueInfoManage.java similarity index 62% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/IVenueInfoManage.java index 4cab8bf..7e3a517 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/IVenueInfoManage.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/IVenueInfoManage.java @@ -1,9 +1,7 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; import java.time.LocalDate; import java.util.List; @@ -17,6 +15,4 @@ public interface IVenueInfoManage extends IService { List queryByDate(LocalDate date); List queryByType(LocalDate date, Integer type); - - Page page(VenueInfoQueryRequest request); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/OrderCreateInfoManageImpl.java similarity index 85% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/OrderCreateInfoManageImpl.java index a4ab30c..a3aee2c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/OrderCreateInfoManageImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/OrderCreateInfoManageImpl.java @@ -1,10 +1,10 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; -import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxOrderCreateInfoMapper; +import com.xiang.common.mapper.JntyzxOrderCreateInfoMapper; import org.springframework.stereotype.Service; import java.util.List; diff --git a/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserInfoManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserInfoManageImpl.java new file mode 100644 index 0000000..db94e14 --- /dev/null +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserInfoManageImpl.java @@ -0,0 +1,18 @@ +package com.xiang.common.manage.jntyzx.miniapp; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.JntyzxUserInfoMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; +import org.springframework.stereotype.Service; + +/** + * @Author: xiang + * @Date: 2026-05-09 10:17 + */ +@Service +public class UserInfoManageImpl extends ServiceImpl implements IUserInfoManage { + @Override + public boolean delAll() { + return baseMapper.delAll() > 0; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserRestrictionManageImpl.java similarity index 88% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserRestrictionManageImpl.java index e373f21..a5a0db7 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserRestrictionManageImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserRestrictionManageImpl.java @@ -1,10 +1,10 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; -import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxUserRestrictionInfoMapper; +import com.xiang.common.mapper.JntyzxUserRestrictionInfoMapper; import org.springframework.stereotype.Service; import java.util.List; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserTokenInfoManageImpl.java similarity index 95% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserTokenInfoManageImpl.java index c5eaaa6..d752e95 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/UserTokenInfoManageImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/UserTokenInfoManageImpl.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; @@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; -import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxUserTokenInfoMapper; +import com.xiang.common.mapper.JntyzxUserTokenInfoMapper; import org.springframework.stereotype.Service; import java.util.List; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/VenueInfoManageImpl.java similarity index 52% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/miniapp/VenueInfoManageImpl.java index f0d6d1b..b949ad5 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/manage/VenueInfoManageImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/miniapp/VenueInfoManageImpl.java @@ -1,13 +1,10 @@ -package com.xiang.service.module.jntyzx.miniapp.manage; +package com.xiang.common.manage.jntyzx.miniapp; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; -import com.xiang.service.module.jntyzx.miniapp.mapper.JntyzxVenueInfoMapper; -import org.apache.commons.lang3.StringUtils; +import com.xiang.common.mapper.JntyzxVenueInfoMapper; import org.springframework.stereotype.Service; import java.time.LocalDate; @@ -33,18 +30,4 @@ public class VenueInfoManageImpl extends ServiceImpl page(VenueInfoQueryRequest request) { - Page page = new Page<>(request.getCurrent(), request.getPageSize()); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(VenueInfoDO::getDate, request.getDate()); - if (StringUtils.isNotBlank(request.getSj())) { - lqw.eq(VenueInfoDO::getSjName, request.getSj()); - } - if (StringUtils.isNotBlank(request.getPlaceName())) { - lqw.like(VenueInfoDO::getPlaceName, request.getPlaceName()); - } - return baseMapper.selectPage(page, lqw); - } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoService.java similarity index 85% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoService.java index e6d4844..9f10a0b 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoService.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoService.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoServiceImpl.java similarity index 90% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoServiceImpl.java index a4cdbf4..f7d394c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbSiteInfoServiceImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbSiteInfoServiceImpl.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.mapper.ZlbSiteInfoMapper; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoService.java similarity index 88% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoService.java index 50ccc5d..9cd6008 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoService.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoService.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoServiceImpl.java similarity index 95% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoServiceImpl.java index 6cf6826..5d23605 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbTokenInfoServiceImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbTokenInfoServiceImpl.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoService.java similarity index 88% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoService.java index fc2b192..557faa6 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoService.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoService.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.extension.service.IService; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoServiceImpl.java similarity index 91% rename from src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java rename to src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoServiceImpl.java index 8420323..04fd5e6 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbUserInfoServiceImpl.java +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbUserInfoServiceImpl.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.zlb.service; +package com.xiang.common.manage.jntyzx.zlb; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiang.common.mapper.ZlbUserInfoMapper; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java b/src/main/java/com/xiang/common/mapper/JntyzxOrderCreateInfoMapper.java similarity index 86% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java rename to src/main/java/com/xiang/common/mapper/JntyzxOrderCreateInfoMapper.java index c5c9aad..0bfaa6f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxOrderCreateInfoMapper.java +++ b/src/main/java/com/xiang/common/mapper/JntyzxOrderCreateInfoMapper.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.mapper; +package com.xiang.common.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; diff --git a/src/main/java/com/xiang/common/mapper/JntyzxUserInfoMapper.java b/src/main/java/com/xiang/common/mapper/JntyzxUserInfoMapper.java new file mode 100644 index 0000000..cfe1b00 --- /dev/null +++ b/src/main/java/com/xiang/common/mapper/JntyzxUserInfoMapper.java @@ -0,0 +1,20 @@ +package com.xiang.common.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +/** + * @Author: xiang + * @Date: 2026-05-09 10:16 + */ +@Mapper +@Repository +public interface JntyzxUserInfoMapper extends BaseMapper { + + @Delete("delete from jntyzx_user_info where 1=1") + int delAll(); + +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java b/src/main/java/com/xiang/common/mapper/JntyzxUserRestrictionInfoMapper.java similarity index 85% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java rename to src/main/java/com/xiang/common/mapper/JntyzxUserRestrictionInfoMapper.java index f492211..bde072c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserRestrictionInfoMapper.java +++ b/src/main/java/com/xiang/common/mapper/JntyzxUserRestrictionInfoMapper.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.mapper; +package com.xiang.common.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java b/src/main/java/com/xiang/common/mapper/JntyzxUserTokenInfoMapper.java similarity index 87% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java rename to src/main/java/com/xiang/common/mapper/JntyzxUserTokenInfoMapper.java index 5b53abb..447f82e 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxUserTokenInfoMapper.java +++ b/src/main/java/com/xiang/common/mapper/JntyzxUserTokenInfoMapper.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.mapper; +package com.xiang.common.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java b/src/main/java/com/xiang/common/mapper/JntyzxVenueInfoMapper.java similarity index 86% rename from src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java rename to src/main/java/com/xiang/common/mapper/JntyzxVenueInfoMapper.java index 2ba47bf..c16fa62 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/mapper/JntyzxVenueInfoMapper.java +++ b/src/main/java/com/xiang/common/mapper/JntyzxVenueInfoMapper.java @@ -1,4 +1,4 @@ -package com.xiang.service.module.jntyzx.miniapp.mapper; +package com.xiang.common.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserInfoDO.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserInfoDO.java new file mode 100644 index 0000000..0036631 --- /dev/null +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/pojo/UserInfoDO.java @@ -0,0 +1,53 @@ +package com.xiang.common.pojo.jntyzx.miniapp.pojo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * @Author: xiang + * @Date: 2026-05-09 09:54 + */ +@Data +@TableName("jntyzx_user_info") +public class UserInfoDO { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + private Integer loginInfoId; + + /** + * 名称 + */ + private String name; + + /** + * 星期几 + */ + private String week; + + /** + * 分配的任务参数 + */ + private String type; + + /** + * 场地信息 + */ + private String placeName; + + /** + * 时间id111 + */ + private String siteTimeName; + + /** + * 是否开抢0-抢,1-不抢 + */ + private Integer isBook; + +} diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java index a048087..bd3c51a 100644 --- a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/req/SubscribeRequest.java @@ -16,7 +16,7 @@ import java.util.List; @NoArgsConstructor public class SubscribeRequest { private JSONObject jsonObject; - private List subscribeVos; + private List subscribeVos; private String bookTime; private Integer paymentMethod; private String svCiphertext; diff --git a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java index 9a8cae5..3d9dec0 100644 --- a/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java +++ b/src/main/java/com/xiang/common/pojo/jntyzx/miniapp/resp/query/QueryVenueResponse.java @@ -1,3 +1,4 @@ +package com.xiang.common.pojo.jntyzx.miniapp.resp.query; import lombok.Data; diff --git a/src/main/java/com/xiang/common/utils/DateUtils.java b/src/main/java/com/xiang/common/utils/DateUtils.java index a00d5ea..b040f36 100644 --- a/src/main/java/com/xiang/common/utils/DateUtils.java +++ b/src/main/java/com/xiang/common/utils/DateUtils.java @@ -26,75 +26,20 @@ import java.util.concurrent.TimeUnit; */ public class DateUtils { - private static final String defaultDateFormatter = "yyyy-MM-dd"; - private static final String defaultDateTimeFormatter = "yyyy-MM-dd HH:mm:ss"; + private static final Logger logger = LoggerFactory.getLogger(DateUtils.class); - - /** - * 构造函数. - */ - public void DateUtil() { - throw new RuntimeException("this is a util class,can not instance!"); - } - - /** - * 添加字段注释. - */ public static final String ENUM_FORMAT = "yyyy-MM-dd HH:mm:ss"; - /** - * 添加字段注释. - */ public static final String ASCM_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - - /** - * 添加字段注释. - */ public static final String ENUM_FORMAT_YMD = "yyyy-MM-dd"; public static final String ENUM_FORMAT_YMD_1 = "yyyyMMdd"; - - /** - * 添加字段注释. - */ public static final String ENUM_FORMAT_YMDS = "yyyy-MM-dd HH:mm:ss.S"; - - /** - * 添加字段注释. - */ public static final String ENUM_FORMAT_SLASH = "yyyy/MM/dd HH:mm:ss"; - - /** - * 添加字段注释. - */ public static final String ENUM_FORMAT_YMDS_SLASH = "yyyy/MM/dd HH:mm:ss.S"; - - /** - * 添加字段注释. - */ public static final String LEVEL_DAY = "day"; // 粒度级别 - - /** - * 添加字段注释. - */ public static final String LEVEL_HOUR = "hour"; - - /** - * 添加字段注释. - */ public static final String LEVEL_MINUTE = "minute"; - - /** - * 添加字段注释. - */ public static final String LEVEL_SECOND = "second"; - - /** - * 日期特殊字符对应. - */ private static Map mapSign = new HashMap<>(); - - /** - * 使用ThreadLocal保证SimpleDateFormat线程安全. - */ private static ThreadLocal> threadLocalDateFormat = new ThreadLocal<>(); /** @@ -141,6 +86,11 @@ public class DateUtils { return convertToChinese(dayOfWeek); } + public static String getWeekDay(LocalDate date) { + DayOfWeek dayOfWeek = date.getDayOfWeek(); + return convertToChinese(dayOfWeek); + } + private static String convertToChinese(DayOfWeek dayOfWeek) { switch (dayOfWeek) { case MONDAY: @@ -658,7 +608,7 @@ public class DateUtils { boolean inAfternoon = now.isAfter(LocalTime.of(12, 59)) && now.isBefore(LocalTime.of(15, 1)); return !inAfternoon && !inMorning ? true : false; } else { - log.info("当前时间为:{}", LocalDateTime.now()); + logger.info("当前时间为:{}", LocalDateTime.now()); return true; } } diff --git a/src/main/java/com/xiang/common/utils/JsonUtils.java b/src/main/java/com/xiang/common/utils/JsonUtils.java new file mode 100644 index 0000000..19814d2 --- /dev/null +++ b/src/main/java/com/xiang/common/utils/JsonUtils.java @@ -0,0 +1,18 @@ +package com.xiang.common.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; + +/** + * @Author: xiang + * @Date: 2025-07-25 15:58 + */ +public class JsonUtils { + public static String toJsonString(Object obj) { + return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue); + } + + public static T parse(String json, Class clazz) { + return JSON.parseObject(json, clazz); + } +} diff --git a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java index b41777f..648a0b3 100644 --- a/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java +++ b/src/main/java/com/xiang/service/module/domain/schedule/DomainDynamicAnalysisTask.java @@ -23,7 +23,7 @@ public class DomainDynamicAnalysisTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getModeleCode(); + return ScheduleEnums.DOMAIN_DYNAMIC_ANALYSIS_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java b/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java index 60be5e7..9be0cf2 100644 --- a/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java +++ b/src/main/java/com/xiang/service/module/glados/schedule/GladosCheckInTask.java @@ -31,7 +31,7 @@ public class GladosCheckInTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.GLADOS_CHECK_IN_TASK.getModeleCode(); + return ScheduleEnums.GLADOS_CHECK_IN_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java deleted file mode 100644 index 4ff851e..0000000 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/UserConverter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.xiang.service.module.jntyzx.miniapp.converts; - -import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; -import com.xiang.service.module.jntyzx.miniapp.entity.pojo.UserTokenInfoDO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Objects; - -@Mapper(componentModel = "spring") -public interface UserConverter { - - UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); - @Mapping(source = "id", target = "userId") - @Mapping(source = "isOrder", target = "isOrder", qualifiedByName = "isOrder") - @Mapping(source = "isRestriction", target = "isRestriction", qualifiedByName = "isRestrict") - JtUserVo convert(UserTokenInfoDO userTokenInfoDO); - List convert(List userTokenInfoDOs); - - @Named("isOrder") - default Boolean isOrder(Integer value) { - return Objects.nonNull(value) && Objects.equals(value, 1); - } - - @Named("isRestrict") - default Boolean isRestrict(Integer value) { - return Objects.nonNull(value) && Objects.equals(value, 0); - } -} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java deleted file mode 100644 index 3f60aab..0000000 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/converts/VenueInfoConverter.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.xiang.service.module.jntyzx.miniapp.converts; - -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; -import com.xiang.service.module.jntyzx.miniapp.entity.pojo.VenueInfoDO; -import org.mapstruct.Mapper; - -/** - * @Author: xiang - * @Date: 2026-04-09 09:54 - */ -@Mapper(componentModel = "spring") -public interface VenueInfoConverter { - - Page toPage(Page page); -} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java new file mode 100644 index 0000000..aff3a11 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java @@ -0,0 +1,53 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Author: xiang + * @Date: 2026-05-09 08:56 + */ +@Component +@RequiredArgsConstructor +@RestController +public class JntyzxMiniappScheduleConfig { + + private final JtTokenRefreshTask jtTokenRefreshTask; + private final JtVenuePullTask jtVenuePullTask; + private final JtVenueSubscribeTask jtVenueSubscribeTask; + private final JtVenueTomorrowPullTask jtVenueTomorrowPullTask; + + + @Scheduled(cron = "0 20,50 * * * ?") + @GetMapping("/jtTokenRefreshTask") + public void jtTokenRefreshTask() { + jtTokenRefreshTask.run(); + } + + @Scheduled(cron = "0 0/1 10-18 * * ?") + @GetMapping("/jtVenuePullTask") + public void jtVenuePullTask() { + jtVenuePullTask.run(); + } + + @Scheduled(cron = "0 30 8 * * ?") + @GetMapping("/jtVenueTomorrowPullTask") + public void jtVenueTomorrowPullTask() { + jtVenueTomorrowPullTask.run(); + } + + @Scheduled(cron = "0 40 8 * * ?") + @GetMapping("/jtUserInfoConfig") + public void jtUserInfoConfig() { + + } + + @Scheduled(cron = "5 0 9 * * ?") + @GetMapping("/jtVenueSubscribeTask") + public void jtVenueSubscribeTask() { + jtVenueSubscribeTask.run(); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java new file mode 100644 index 0000000..9f6dd70 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java @@ -0,0 +1,116 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.google.common.collect.Lists; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.service.IUserInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-05-09 09:58 + */ +@Component +@Slf4j +public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { + + private final IUserTokenInfoService userTokenInfoService; + private final IVenueService venueService; + private final IUserInfoService userInfoService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public JntyzxUserInfoConfigTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IUserTokenInfoService userTokenInfoService, + IVenueService venueService, + IUserInfoService userInfoService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.userTokenInfoService = userTokenInfoService; + this.venueService = venueService; + this.userInfoService = userInfoService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_USER_INFO_CONFIG.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_USER_INFO_CONFIG.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_USER_INFO_CONFIG.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(false); + List venueInfoDOS = venueService.queryCanBuyVenue(); + if (CollectionUtils.isEmpty(venueInfoDOS)) { + taskResult.setSummary("无可用场地"); + return taskResult; + } + venueInfoDOS = venueInfoDOS.stream().filter(VenueInfoUtils::get628VenueInfo).toList(); + if (CollectionUtils.isEmpty(venueInfoDOS)) { + taskResult.setSummary("无可用场地"); + return taskResult; + } + + List users = userTokenInfoService.getCanOrderUser(); + if (CollectionUtils.isEmpty(users)) { + taskResult.setSummary("无可用用户"); + return taskResult; + } + + List list = Lists.newArrayList(); + int i = 0; + for (UserTokenInfoDO user : users) { + VenueInfoDO venueInfoDO = venueInfoDOS.get(i); + UserInfoDO userInfoDO = new UserInfoDO(); + userInfoDO.setName(user.getName()); + userInfoDO.setWeek(DateUtils.getWeekDay(venueInfoDO.getDate())); + userInfoDO.setType("1"); + userInfoDO.setPlaceName(venueInfoDO.getPlaceName()); + userInfoDO.setSiteTimeName(venueInfoDO.getSjName().split("-")[0]); + userInfoDO.setIsBook(0); + list.add(userInfoDO); + i++; + if (i == venueInfoDOS.size()) { + i = 0; + } + } + + if (CollectionUtils.isNotEmpty(list)) { + userInfoService.batchSave(list); + StringBuilder stringBuilder = new StringBuilder(); + for (UserInfoDO user : list) { + stringBuilder.append(user.getName()).append("配置预约:").append(user.getPlaceName()).append("\n"); + } + jntyzxDingTalkFactory.sendMsg(stringBuilder.toString()); + taskResult.setSuccess(Boolean.TRUE); + taskResult.setSummary(stringBuilder.toString()); + } + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java new file mode 100644 index 0000000..db89af1 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java @@ -0,0 +1,57 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Author: xiang + * @Date: 2026-01-15 17:29 + */ +@Slf4j +@Component +@RestController +public class JtTokenRefreshTask extends BaseScheduleTaskTemplate { + + private final IUserTokenInfoService userTokenInfoService; + + public JtTokenRefreshTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IUserTokenInfoService userTokenInfoService) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.userTokenInfoService = userTokenInfoService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_TOKEN_REFRESH_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_TOKEN_REFRESH_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_TOKEN_REFRESH_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + TaskResult taskResult = new TaskResult(); + + log.info("【Token】江南体育中心token续期定时任务启动!!!time:{}", System.currentTimeMillis()); + userTokenInfoService.flushToken(); + + taskResult.setSuccess(true); + taskResult.setSummary("江体小程序token刷新成功"); + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java new file mode 100644 index 0000000..ecde99a --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java @@ -0,0 +1,144 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.google.common.collect.Maps; +import com.xiang.common.enums.RedisKeyConstant; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.MsgSendUtils; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import com.xiang.service.module.jntyzx.miniapp.utils.WeekendUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 每日9:00-19:00场地更新信息查询 + */ +@Component +@Slf4j +@RestController +public class JtVenuePullTask extends BaseScheduleTaskTemplate { + + private final IUserTokenInfoService userTokenInfoService; + private final IJntyzxHttpService jntyzxHttpService; + private final IVenueService venueService; + private final MsgSendUtils msgSendUtils; + + public JtVenuePullTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IUserTokenInfoService userTokenInfoService, + IJntyzxHttpService jntyzxHttpService, + IVenueService venueService, + MsgSendUtils msgSendUtils) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.userTokenInfoService = userTokenInfoService; + this.jntyzxHttpService = jntyzxHttpService; + this.venueService = venueService; + this.msgSendUtils = msgSendUtils; + } + + private List handleMsgSendList(List sitePositionLists, int dayOfWeek) { + // 过滤出来8-10的未订购的场地信息 + sitePositionLists = sitePositionLists.stream() + .filter(VenueInfoUtils::get8210VenueInfo) + .filter(item -> StringUtils.equals(item.getContacts(), "0")).toList(); + // 周六周日过滤小馆,不查询当天小馆信息 + if (dayOfWeek == 6 || dayOfWeek == 7) { + return sitePositionLists.stream() + .filter(item -> !item.getPlaceName().contains("小馆")) + .toList(); + } + Map mapByName = Maps.newLinkedHashMap(); + for (SitePositionList sitePositionList : sitePositionLists) { + if (!mapByName.containsKey(sitePositionList.getPlaceName())) { + mapByName.put(sitePositionList.getPlaceName(), sitePositionList); + } + } + return mapByName.values().stream().toList(); + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_PULL_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_VENUE_INFO_PULL_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_PULL_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(false); + log.info("【Venue】江体小程序场地数据拉取定时任务启动!!!time:{}", System.currentTimeMillis()); + List availableUser = userTokenInfoService.getAvailableUser(); + if (CollectionUtils.isEmpty(availableUser)) { + log.info("当前无可用用户查询场地信息!"); + taskResult.setSuccess(true); + taskResult.setSummary("当前无可用用户查询场地信息!"); + return taskResult; + } + String token; + LocalDateTime now = LocalDateTime.now(); + int dayOfWeek = now.getDayOfWeek().getValue(); + + for (UserTokenInfoDO userTokenInfoDO : availableUser) { + if (Objects.isNull(userTokenInfoDO)) { + continue; + } + token = userTokenInfoDO.getToken(); + if (StringUtils.isBlank(token)) { + continue; + } + List sitePositionLists = jntyzxHttpService.queryAvailable(WeekendUtils.isWeekend(), token); + if (CollectionUtils.isEmpty(sitePositionLists)) { + continue; + } + venueService.saveOrUpdateTodayVenueInfo(sitePositionLists); + + sitePositionLists = handleMsgSendList(sitePositionLists, dayOfWeek); + if (CollectionUtils.isEmpty(sitePositionLists)) { + taskResult.setSuccess(true); + taskResult.setSummary("当前无场地信息!"); + return taskResult; + } + + StringBuffer msg = new StringBuffer( + "查询到20:00-22:00空闲场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now()) + "\n"); + sitePositionLists.forEach(item -> { + msg.append(item.getPlaceName()).append("\n"); + }); + + String key = RedisKeyConstant.JNTYZX_VENUE_MSG_SEND_KEY + RedisKeyConstant.getDate(); + msgSendUtils.sendMsgRestrict1Hours(key, msg.toString()); + taskResult.setSuccess(true); + taskResult.setSummary("查询场地信息成功!时间:" + now); + return taskResult; + } + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java new file mode 100644 index 0000000..e1c0989 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java @@ -0,0 +1,127 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.alibaba.fastjson.JSON; +import com.xiang.common.enums.RedisKeyConstant; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.RedisService; +import com.xiang.service.module.jntyzx.miniapp.service.IJtOrderService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RestController +public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { + + private final IUserTokenInfoService userTokenInfoService; + private final IJtOrderService jtOrderService; + private final IVenueService venueService; + private final JntyzxDingTalkFactory jtDingTalkFactory; + private final RedisService redisService; + + public JtVenueSubscribeTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IUserTokenInfoService userTokenInfoService, + IJtOrderService jtOrderService, + IVenueService venueService, + JntyzxDingTalkFactory jtDingTalkFactory, + RedisService redisService) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.userTokenInfoService = userTokenInfoService; + this.jtOrderService = jtOrderService; + this.venueService = venueService; + this.jtDingTalkFactory = jtDingTalkFactory; + this.redisService = redisService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_ORDER_SUBSCRIBE_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_ORDER_SUBSCRIBE_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_ORDER_SUBSCRIBE_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(false); + + log.info("【Subscribe】 江体场地预定定时任务启动!!! time:{}", System.currentTimeMillis()); + List users = userTokenInfoService.getCanOrderUser(); + if (CollectionUtils.isEmpty(users)) { + log.info("暂无可下单用户, time:{}", System.currentTimeMillis()); + jtDingTalkFactory.sendMsg("暂无可下单用户, time:" + System.currentTimeMillis()); + taskResult.setSummary("无可下单用户"); + return taskResult; + } + List venueInfoDOS = venueService.queryTomorrowCanBuyVenue(); + Map> venueInfoMap = venueInfoDOS.stream() + .filter(VenueInfoUtils::get8210VenueInfo) + .filter(item -> !StringUtils.contains(item.getPlaceName(), "小馆")) + .collect(Collectors.groupingByConcurrent(VenueInfoDO::getPlaceName)); + if (MapUtils.isEmpty(venueInfoMap)) { + log.info("暂无可下单场地,time:{}", System.currentTimeMillis()); + taskResult.setSummary("无下单场地"); + return taskResult; + } + + users.parallelStream().forEach(user -> { + try { + List placeNameList = venueInfoMap.keySet().stream().sorted(Comparator.comparing(VenueInfoUtils::sortVenueInfo)).toList(); + log.info("场地排序后的集合:{}", JSON.toJSONString(placeNameList)); + for (String placeName : placeNameList) { + List venueInfoDOList = venueInfoMap.get(placeName); + for (int i = 0; i < 10; i++) { + String valid = (String) redisService.get(RedisKeyConstant.getVenueSubscribeKey(placeName)); + if (StringUtils.isNotBlank(valid)) { + break; + } + boolean order = jtOrderService.createOrder(venueInfoDOList, user); + if (order) { + return; + } + try { + Thread.sleep(1250); + } catch (InterruptedException e) { + log.error("睡眠失败~~~"); + } + } + } + } catch (Exception e) { + // 关键点:异常只影响当前 user + log.error("createOrder 异常,user={}", user.getId(), e); + return; // 结束这个 user,不影响其他 user + } + }); + taskResult.setSuccess(true); + taskResult.setSummary("下单执行成功!"); + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java new file mode 100644 index 0000000..be08f35 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java @@ -0,0 +1,121 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + + +import com.google.common.collect.Maps; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; +import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import com.xiang.service.module.jntyzx.miniapp.utils.WeekendUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Slf4j +@Component +public class JtVenueTomorrowPullTask extends BaseScheduleTaskTemplate { + private final IUserTokenInfoService userTokenInfoService; + private final IJntyzxHttpService jntyzxHttpService; + private final JntyzxDingTalkFactory jtDingTalkFactory; + private final IVenueService venueService; + + public JtVenueTomorrowPullTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IUserTokenInfoService userTokenInfoService, + IJntyzxHttpService jntyzxHttpService, + JntyzxDingTalkFactory jtDingTalkFactory, + IVenueService venueService) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.userTokenInfoService = userTokenInfoService; + this.jntyzxHttpService = jntyzxHttpService; + this.jtDingTalkFactory = jtDingTalkFactory; + this.venueService = venueService; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_VENUE_TOMORROW_PULL_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_VENUE_TOMORROW_PULL_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_VENUE_TOMORROW_PULL_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(Boolean.FALSE); + log.info("【Venue】江体小程序场地拉取定时任务启动!!!time:{}", System.currentTimeMillis()); + List availableUser = userTokenInfoService.getAvailableUser(); + if (CollectionUtils.isEmpty(availableUser)) { + log.info("当前无可用用户查询场地信息!"); + taskResult.setSummary("当前无可用用户查询场地信息"); + return taskResult; + } + // 用户信息 + StringBuffer userMsg = new StringBuffer(); + availableUser.forEach(item -> { + JntyzxResponse jntyzxResponse = jntyzxHttpService.checkDefaultNums(item.getToken(), item.getMemberCardNo()); + if (Objects.nonNull(jntyzxResponse)) { + if (jntyzxResponse.getSuccess()) { + userMsg.append("订购人:").append(item.getName()).append("正常下单\n"); + } else { + userMsg.append("订购人:").append(item.getName()).append(jntyzxResponse.getMessage()).append("\n"); + } + } + }); + jtDingTalkFactory.sendMsg(userMsg.toString()); + + // 场地信息 + UserTokenInfoDO userTokenInfoDO = availableUser.get(0); + String token = userTokenInfoDO.getToken(); + List sitePositionLists = jntyzxHttpService.queryAvailableTomorrow(WeekendUtils.isWeekend(), token); + if (CollectionUtils.isEmpty(sitePositionLists)) { + taskResult.setSummary("当前无可用场地信息"); + return taskResult; + } + venueService.saveTomorrowVenueInfo(sitePositionLists); + sitePositionLists = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList(); + if (CollectionUtils.isEmpty(sitePositionLists)) { + taskResult.setSummary("当前无可用场地信息"); + return taskResult; + } + Map map = Maps.newLinkedHashMap(); + for (SitePositionList sitePositionList : sitePositionLists) { + if (map.containsKey(sitePositionList.getPlaceName())) { + continue; + } + map.put(sitePositionList.getPlaceName(), sitePositionList); + } + StringBuffer msg = new StringBuffer("查询江体场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + " 20:00-22:00\n"); + map.forEach((placeName, sitePositionList) -> { + msg.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n"); + }); + jtDingTalkFactory.sendMsg(msg.toString()); + + taskResult.setSuccess(Boolean.TRUE); + taskResult.setSummary("场地信息获取成功!"); + return taskResult; + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java new file mode 100644 index 0000000..bf7eaea --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java @@ -0,0 +1,16 @@ +package com.xiang.service.module.jntyzx.miniapp.service; + +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-05-09 10:00 + */ +public interface IUserInfoService { + + boolean delAll(); + + boolean batchSave(List list); +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java index 5a6b0bc..a28abc8 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserTokenInfoService.java @@ -2,9 +2,6 @@ package com.xiang.service.module.jntyzx.miniapp.service; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.UserAddReq; -import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; -import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; import java.util.List; @@ -20,14 +17,4 @@ public interface IUserTokenInfoService { boolean flushSingleToken(String name); boolean flushToken(); boolean updateTokenByName(String name, String token); - - List list(UserQueryReq req); - - Boolean updateStatusByUserName(String username, Integer status); - - Boolean refreshToken(String username); - - Boolean save(UserAddReq req); - - JtUserVo info(Long userId); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java index 78733c8..4a82ea1 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IVenueService.java @@ -1,10 +1,7 @@ package com.xiang.service.module.jntyzx.miniapp.service; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; -import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; import java.util.List; @@ -34,11 +31,4 @@ public interface IVenueService { * @return */ boolean saveTomorrowVenueInfo(List sitePositionLists); - - /** - * 查询场地列表信息 - * @param request - * @return - */ - Page list(VenueInfoQueryRequest request); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java index d1ff885..7f825d4 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/JntyzxHttpServiceImpl.java @@ -13,9 +13,11 @@ import com.xiang.common.pojo.jntyzx.miniapp.resp.OrderCreateResp; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.UserInfoResponse; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.VenueList; +import com.xiang.common.utils.HttpService; +import com.xiang.common.utils.JsonUtils; import com.xiang.common.utils.RedisService; -import com.xiang.service.module.jntyzx.miniapp.constants.UrlConstant; -import com.xiang.service.module.jntyzx.miniapp.manage.IOrderCreateInfoManage; +import com.xiang.common.enums.JntyzxUrlConstant; +import com.xiang.common.manage.jntyzx.miniapp.IOrderCreateInfoManage; import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; import com.xiang.service.module.jntyzx.miniapp.utils.JntyzxSaltEncodeUtils; import lombok.RequiredArgsConstructor; @@ -43,7 +45,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { @Override public List queryAvailable(String isWeekend, String token) { - String url = UrlConstant.QUERY_TODAY_SUBSCRIBE_URL; + String url = JntyzxUrlConstant.QUERY_TODAY_SUBSCRIBE_URL; return querySitePositionInfo(isWeekend, token, url); } @@ -56,7 +58,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { params.put("gid", "03"); params.put("isWeekend", isWeekend); try { - resp = HttpHelper.doGet(url, header, params); + resp = HttpService.doGet(url, header, params); } catch (Exception e) { log.error("[doGet] 江南体育中心查询当天场地 请求失败, url:{}", url); return Lists.newArrayList(); @@ -98,7 +100,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { @Override public List queryAvailableTomorrow(String isWeekend, String token) { - String url = UrlConstant.QUERY_TOMORROW_SUBSCRIBE_URL; + String url = JntyzxUrlConstant.QUERY_TOMORROW_SUBSCRIBE_URL; return querySitePositionInfo(isWeekend, token, url); } @@ -143,7 +145,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { Map params = Maps.newHashMap(); params.put("X-Access-Token", token); - String resp = HttpHelper.doPost(UrlConstant.ADD_SUBSCRIBE, params, JsonUtils.toJsonString(subscribeRequest)); + String resp = HttpService.doPost(JntyzxUrlConstant.ADD_SUBSCRIBE, params, JsonUtils.toJsonString(subscribeRequest)); log.info("[江体小程序] 羽毛球场地下单响应结果:{}", resp); if (StringUtils.isBlank(resp)) { log.info("[resp] 请求结果为空"); @@ -164,7 +166,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { Map params = Maps.newHashMap(); params.put("openId", openId); - String respStr = HttpHelper.doGet(UrlConstant.HEALTH_DECLARATION, headers, params); + String respStr = HttpService.doGet(JntyzxUrlConstant.HEALTH_DECLARATION, headers, params); if (StringUtils.isBlank(respStr)) { return null; } @@ -178,7 +180,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { Map headers = Maps.newHashMap(); headers.put("X-Access-Token", token); - String resp = HttpHelper.doGet(UrlConstant.QUERY_BY_OPEN_ID, headers, params); + String resp = HttpService.doGet(JntyzxUrlConstant.QUERY_BY_OPEN_ID, headers, params); JntyzxResponse response = JSON.parseObject(resp, new TypeReference>() { }); if (Objects.isNull(response)) { @@ -195,7 +197,7 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService { Map headers = Maps.newHashMap(); headers.put("X-Access-Token", token); - String resp = HttpHelper.doGet(UrlConstant.CHECK_NUM, headers, params); + String resp = HttpService.doGet(JntyzxUrlConstant.CHECK_NUM, headers, params); if (StringUtils.isBlank(resp)) { return null; } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java index 5adff5a..004e686 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java @@ -2,14 +2,15 @@ package com.xiang.service.module.jntyzx.miniapp.service.impl; import com.alibaba.fastjson2.JSON; import com.xiang.common.exception.BusinessException; +import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.pojo.jntyzx.miniapp.pojo.OrderInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; import com.xiang.common.pojo.jntyzx.miniapp.resp.OrderCreateResp; import com.xiang.common.utils.RedisService; -import com.xiang.service.module.jntyzx.miniapp.constants.RedisKeyConstant; -import com.xiang.service.module.jntyzx.miniapp.manage.IOrderCreateInfoManage; +import com.xiang.common.enums.RedisKeyConstant; +import com.xiang.common.manage.jntyzx.miniapp.IOrderCreateInfoManage; import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; import com.xiang.service.module.jntyzx.miniapp.service.IJtOrderService; import lombok.RequiredArgsConstructor; @@ -34,7 +35,7 @@ public class OrderInfoServiceImpl implements IJtOrderService { private final IOrderCreateInfoManage orderCreateInfoManage; private final IJntyzxHttpService jntyzxHttpService; private final RedisService redisService; - private final JntyzxHttpServiceImpl dingTalkFactory; + private final JntyzxDingTalkFactory dingTalkFactory; @Override public List queryNoPayOrder() { return orderCreateInfoManage.queryNoPayOrder(); diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..7f3c316 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,32 @@ +package com.xiang.service.module.jntyzx.miniapp.service.impl; + +import com.xiang.common.manage.jntyzx.miniapp.IUserInfoManage; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; +import com.xiang.service.module.jntyzx.miniapp.service.IUserInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Author: xiang + * @Date: 2026-05-09 10:18 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class UserInfoServiceImpl implements IUserInfoService { + + private final IUserInfoManage userInfoManage; + + @Override + public boolean delAll() { + return userInfoManage.delAll(); + } + + @Override + public boolean batchSave(List list) { + return userInfoManage.saveBatch(list); + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java index bdc94db..2e15e50 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserTokenInfoServiceImpl.java @@ -1,20 +1,15 @@ package com.xiang.service.module.jntyzx.miniapp.service.impl; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import com.alibaba.fastjson.JSONObject; import com.xiang.common.exception.BusinessException; import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.manage.jntyzx.miniapp.IUserRestrictionManage; +import com.xiang.common.manage.jntyzx.miniapp.IUserTokenInfoManage; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserRestrictionInfo; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.UserAddReq; -import com.xiang.common.pojo.jntyzx.miniapp.req.UserQueryReq; import com.xiang.common.pojo.jntyzx.miniapp.resp.JntyzxResponse; -import com.xiang.common.pojo.jntyzx.miniapp.resp.JtUserVo; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.UserInfoResponse; import com.xiang.common.utils.DateUtils; -import com.xiang.service.module.jntyzx.miniapp.converts.UserConverter; -import com.xiang.service.module.jntyzx.miniapp.manage.IUserRestrictionManage; -import com.xiang.service.module.jntyzx.miniapp.manage.IUserTokenInfoManage; import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; import lombok.RequiredArgsConstructor; @@ -23,12 +18,8 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Collectors; /** * @Author: xiang @@ -43,7 +34,6 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService { private final IJntyzxHttpService jntyzxHttpService; private final JntyzxDingTalkFactory jtDingTalkFactory; private final IUserRestrictionManage userRestrictionManage; - private final UserConverter userConverter; @Override @@ -103,6 +93,10 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService { } private boolean healthDeclaration(UserTokenInfoDO userTokenInfoDO) { + if (StringUtils.isBlank(userTokenInfoDO.getToken()) || StringUtils.isBlank(userTokenInfoDO.getOpenId())) { + log.info("用户信息异常:{}", JSONObject.toJSONString(userTokenInfoDO)); + return false; + } JntyzxResponse jntyzxResponse = jntyzxHttpService.healthDeclaration(userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId()); if (Objects.isNull(jntyzxResponse)) { log.info("用户名:{}心跳监测失败!", userTokenInfoDO.getName()); @@ -120,97 +114,6 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService { return flag; } - @Override - public List list(UserQueryReq req) { - List userTokenInfoDOS = userTokenInfoManage.queryByList(req); - if (CollectionUtils.isEmpty(userTokenInfoDOS)) { - return Lists.newArrayList(); - } - List idList = userTokenInfoDOS.stream().map(UserTokenInfoDO::getId).toList(); - List userRestrictionInfos = userRestrictionManage.queryByIdList(idList); - Map userRestrictionInfoMap = Maps.newHashMap(); - if (CollectionUtils.isNotEmpty(userRestrictionInfos)) { - userRestrictionInfoMap.putAll( - userRestrictionInfos.stream().collect(Collectors.toMap( - UserRestrictionInfo::getUserId, Function.identity(), (a, b) -> a))); - } - List jtUserVoList = userConverter.convert(userTokenInfoDOS); - jtUserVoList.forEach(item -> { - if (userRestrictionInfoMap.containsKey(item.getUserId())) { - UserRestrictionInfo userRestrictionInfo = userRestrictionInfoMap.get(item.getUserId()); - if (Objects.nonNull(userRestrictionInfo)) { - if (userRestrictionInfo.getRestrictionDeadline().isAfter(LocalDateTime.now())) { - item.setRestrictionDeadline(userRestrictionInfo.getRestrictionDeadline()); - item.setRestrictionDesc(userRestrictionInfo.getRestrictionDesc()); - } - } - } - }); - return jtUserVoList; - } - - @Override - public Boolean updateStatusByUserName(String username, Integer status) { - if (StringUtils.isEmpty(username)) { - log.error("用户名为空"); - return false; - } - UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(username); - if (Objects.isNull(userTokenInfoDO)) { - throw new BusinessException("用户不存在!"); - } - userTokenInfoDO.setStatus(status); - return userTokenInfoManage.updateById(userTokenInfoDO); - } - - @Override - public Boolean refreshToken(String username) { - if (StringUtils.isEmpty(username)) { - log.error("用户名为空"); - return false; - } - UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(username); - if (Objects.isNull(userTokenInfoDO)) { - throw new BusinessException("用户不存在!"); - } - return healthDeclaration(userTokenInfoDO); - } - - @Override - public Boolean save(UserAddReq req) { - UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(req.getName()); - if (Objects.nonNull(userTokenInfoDO)) { - throw new BusinessException("用户名已存在!"); - } - userTokenInfoDO = new UserTokenInfoDO(); - userTokenInfoDO.setName(req.getName()); - userTokenInfoDO.setToken(req.getToken()); - userTokenInfoDO.setOpenId(req.getOpenId()); - userTokenInfoDO.setStatus(req.getStatus()); - userTokenInfoDO.setIsOrder(1); - userTokenInfoDO.setMemberCardNo(req.getMemberCardNo()); - userTokenInfoDO.setIsRestriction(0); - userTokenInfoDO.setUpdateTime(LocalDateTime.now()); - return userTokenInfoManage.save(userTokenInfoDO); - } - - @Override - public JtUserVo info(Long userId) { - UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getById(userId); - if (Objects.isNull(userTokenInfoDO)) { - throw new BusinessException("用户不存在!"); - } - JtUserVo jtUserVo = userConverter.convert(userTokenInfoDO); - UserRestrictionInfo userRestrictionInfo = userRestrictionManage.queryByUserId(userId); - if (Objects.nonNull(userRestrictionInfo)) { - if (userRestrictionInfo.getRestrictionDeadline().isAfter(LocalDateTime.now())) { - jtUserVo.setRestrictionDeadline(userRestrictionInfo.getRestrictionDeadline()); - jtUserVo.setRestrictionDesc(userRestrictionInfo.getRestrictionDesc()); - } - } - return jtUserVo; - } - /** * 查询用户信息 * @@ -219,6 +122,10 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService { * @return */ private void queryMemberCardInfo(UserTokenInfoDO userTokenInfoDO) { + if (StringUtils.isBlank(userTokenInfoDO.getToken()) || StringUtils.isBlank(userTokenInfoDO.getOpenId())) { + log.info("用户信息异常:{}", JSONObject.toJSONString(userTokenInfoDO)); + return; + } JntyzxResponse response = jntyzxHttpService.queryByOpenId(userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId()); if (Objects.isNull(response)) { return; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java index 058af34..82dc080 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java @@ -1,15 +1,11 @@ package com.xiang.service.module.jntyzx.miniapp.service.impl; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; -import com.xiang.common.pojo.jntyzx.miniapp.req.VenueInfoQueryRequest; -import com.xiang.common.pojo.jntyzx.miniapp.resp.VenueInfoQueryResp; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; import com.xiang.common.utils.DateUtils; -import com.xiang.service.module.jntyzx.miniapp.converts.VenueInfoConverter; -import com.xiang.service.module.jntyzx.miniapp.manage.IVenueInfoManage; +import com.xiang.common.manage.jntyzx.miniapp.IVenueInfoManage; import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; @@ -38,7 +34,6 @@ public class VenueServiceImpl implements IVenueService { private final IJntyzxHttpService jntyzxHttpService; private final IVenueInfoManage venueInfoManage; private final IUserTokenInfoService userTokenInfoService; - private final VenueInfoConverter venueInfoConverter; @Override public List queryVenueService() { @@ -160,12 +155,6 @@ public class VenueServiceImpl implements IVenueService { return true; } - @Override - public Page list(VenueInfoQueryRequest request) { - Page page = venueInfoManage.page(request); - return venueInfoConverter.toPage(page); - } - private void updateDatabase(List list, boolean isToday) { List venueInfoDOS = Lists.newArrayList(); if (isToday) { diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java index 077e865..ab0b174 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java @@ -2,7 +2,7 @@ package com.xiang.service.module.jntyzx.miniapp.utils; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.utils.RedisService; -import com.xiang.service.module.jntyzx.miniapp.constants.RedisKeyConstant; +import com.xiang.common.enums.RedisKeyConstant; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java index 464962d..f621f25 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java @@ -30,12 +30,18 @@ public class VenueInfoUtils { if (placeName.contains("十号")) { return 0; } - if (placeName.contains("二号")) { + if (placeName.contains("九号")) { return 1; } - if (placeName.contains("九号")) { + if (placeName.contains("二号")) { return 2; } - return 3; + if (placeName.contains("八号")) { + return 3; + } + if (placeName.contains("七号")) { + return 4; + } + return 5; } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java index f15a018..bade2eb 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbLoginTask.java @@ -13,7 +13,7 @@ import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; import lombok.extern.slf4j.Slf4j; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -53,7 +53,7 @@ public class ZlbLoginTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_LOGIN_TASK.getModeleCode(); + return ScheduleEnums.ZLB_LOGIN_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java index fc702ab..8f83c64 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -17,8 +17,8 @@ import com.xiang.common.utils.DateUtils; import com.xiang.common.utils.OkHttpUtil; import com.xiang.service.module.jntyzx.zlb.constants.ZlbUrlConstants; import com.xiang.service.module.jntyzx.zlb.service.ZlbService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbUserInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbUserInfoService; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @@ -63,7 +63,7 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_ORDER_CREATE_TASK.getModeleCode(); + return ScheduleEnums.ZLB_ORDER_CREATE_TASK.getModuleCode(); } @Override @@ -108,23 +108,33 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { //计算9点到现在的时间差 //获取江体当前时间 LocalTime currentTime = LocalTime.now(); - LocalTime targetTime = LocalTime.parse("09:00:11.800"); + LocalTime targetTime = LocalTime.parse("09:00:12.500"); Duration duration = Duration.between(currentTime, targetTime); long milliseconds = duration.toMillis(); executorService.schedule(() -> { - String response = null; - try { - response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); - } catch (Exception e) { - throw new RuntimeException(e); + for (int i = 0; i < 3; i++) { + String response = null; + try { + response = client.postJson(ZlbUrlConstants.newOrderUrl, headers, newOrderJson); + } catch (Exception e) { + throw new RuntimeException(e); + } + boolean b = buildOrder(name, response, placeName, siteTimeName); + if (b) { + break; + } + try { + Thread.sleep(1250); + } catch (InterruptedException e) { + logger.error("线程暂停异常"); + } } - buildOrder(name, response, placeName, siteTimeName); }, milliseconds, TimeUnit.MILLISECONDS); return taskResult; } - private void buildOrder(String name, String response, String placeName, String siteTimeName) { + private boolean buildOrder(String name, String response, String placeName, String siteTimeName) { log.info("订单接口返回结果==> \n {}", response); JSONObject jsonObject = JSONObject.parseObject(response); if (jsonObject.getInteger("code") == 200) { @@ -135,6 +145,14 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + orderId + "_" + name; redisTemplate.opsForValue().set(redisKey, name); redisTemplate.expire(redisKey, 120, TimeUnit.SECONDS); + return true; } + if (jsonObject.getInteger("code") == 500) { + if (jsonObject.getString("message").contains("已被售出")) { + jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败❌❌❌场地号:" + placeName + "已被售出"); + return true; + } + } + return false; } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java index 1a17e02..c4127c0 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteDayTask.java @@ -14,7 +14,6 @@ import com.xiang.common.utils.DateUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import javax.annotation.Resource; import java.util.Date; import java.util.List; @@ -40,7 +39,7 @@ public class ZlbSiteDayTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_SITE_DAY_TASK.getModeleCode(); + return ScheduleEnums.ZLB_SITE_DAY_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java index bf65ed1..dcb236f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbSiteTask.java @@ -30,7 +30,7 @@ public class ZlbSiteTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_SITE_QUERY_TASK.getModeleCode(); + return ScheduleEnums.ZLB_SITE_QUERY_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index 812f2f8..b8f5040 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -43,7 +43,7 @@ public class ZlbTaskConfig { } @GetMapping("/zlbOrderCreateTask") - @Scheduled(cron = "55 59 8 * * ?") + @Scheduled(cron = "2 0 9 * * ?") public void zlbOrderCreateTask() { zlbOrderTask.run(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java index 17a360f..df98213 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTokenRefreshTask.java @@ -9,12 +9,11 @@ import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import javax.annotation.Resource; import java.util.Date; import java.util.List; @@ -41,7 +40,7 @@ public class ZlbTokenRefreshTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_TOKEN_CHECK_TASK.getModeleCode(); + return ScheduleEnums.ZLB_TOKEN_CHECK_TASK.getModuleCode(); } @Override diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java index 7f10448..6685db4 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -13,9 +13,10 @@ import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; import com.xiang.common.utils.DateUtils; -import com.xiang.service.module.jntyzx.zlb.service.ZlbSiteInfoService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbTokenInfoService; -import com.xiang.service.module.jntyzx.zlb.service.ZlbUserInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbSiteInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbUserInfoService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; @@ -59,7 +60,7 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { @Override protected Integer getModule() { - return ScheduleEnums.ZLB_USER_CONFIG_TASK.getModeleCode(); + return ScheduleEnums.ZLB_USER_CONFIG_TASK.getModuleCode(); } @Override @@ -133,26 +134,6 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { if (CollectionUtils.isEmpty(siteInfoList)) { return Lists.newArrayList(); } - return siteInfoList.stream().sorted(Comparator.comparing(this::sort)).collect(Collectors.toList()); - } - - private int sort(ZlbSiteInfo siteInfo) { - String placeName = siteInfo.getPlaceName(); - if (placeName.contains("十号")) { - return 0; - } - if (placeName.contains("九号")) { - return 1; - } - if (placeName.contains("二号")) { - return 2; - } - if (placeName.contains("八号")) { - return 3; - } - if (placeName.contains("七号")) { - return 4; - } - return 5; + return siteInfoList.stream().sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))).collect(Collectors.toList()); } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java index fd10a36..1dc4cab 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/service/ZlbServiceImpl.java @@ -9,6 +9,9 @@ import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.manage.jntyzx.zlb.ZlbSiteInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbUserInfoService; import com.xiang.common.pojo.jntyzx.zlb.ZlbCaptchaResp; import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderInfo; import com.xiang.common.pojo.jntyzx.zlb.ZlbOrderJson; -- 2.49.1 From fea069d795ce8c3d64b1409a6320ba37bcdd5464 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 10:56:26 +0800 Subject: [PATCH 25/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiang/common/config/RedisProperties.java | 2 +- .../schedule/JntyzxMiniappScheduleConfig.java | 5 +- .../schedule/JntyzxUserInfoConfigTask.java | 8 ++- .../schedule/JtVenueSubscribeTask.java | 72 +++++++++++++------ .../miniapp/service/IUserInfoService.java | 2 + .../service/impl/UserInfoServiceImpl.java | 5 ++ .../jntyzx/miniapp/utils/VenueInfoUtils.java | 36 ++++++++++ 7 files changed, 102 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/xiang/common/config/RedisProperties.java b/src/main/java/com/xiang/common/config/RedisProperties.java index 294b887..05f1dc8 100644 --- a/src/main/java/com/xiang/common/config/RedisProperties.java +++ b/src/main/java/com/xiang/common/config/RedisProperties.java @@ -14,7 +14,7 @@ public class RedisProperties { private String host; private String port; private String password; - private Integer database = 0; + private Integer database; public String getAddress() { return "redis://" + host + ":" + port; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java index aff3a11..af70e5a 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java @@ -19,6 +19,7 @@ public class JntyzxMiniappScheduleConfig { private final JtVenuePullTask jtVenuePullTask; private final JtVenueSubscribeTask jtVenueSubscribeTask; private final JtVenueTomorrowPullTask jtVenueTomorrowPullTask; + private final JntyzxUserInfoConfigTask jntyzxUserInfoConfigTask; @Scheduled(cron = "0 20,50 * * * ?") @@ -27,7 +28,7 @@ public class JntyzxMiniappScheduleConfig { jtTokenRefreshTask.run(); } - @Scheduled(cron = "0 0/1 10-18 * * ?") +// @Scheduled(cron = "0 0/1 10-18 * * ?") @GetMapping("/jtVenuePullTask") public void jtVenuePullTask() { jtVenuePullTask.run(); @@ -42,7 +43,7 @@ public class JntyzxMiniappScheduleConfig { @Scheduled(cron = "0 40 8 * * ?") @GetMapping("/jtUserInfoConfig") public void jtUserInfoConfig() { - + jntyzxUserInfoConfigTask.run(); } @Scheduled(cron = "5 0 9 * * ?") diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java index 9f6dd70..c232807 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; +import java.util.Comparator; import java.util.List; /** @@ -66,12 +67,15 @@ public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { protected TaskResult doExecute(Object validatedParams) throws Exception { TaskResult taskResult = new TaskResult(); taskResult.setSuccess(false); - List venueInfoDOS = venueService.queryCanBuyVenue(); + List venueInfoDOS = venueService.queryTomorrowCanBuyVenue(); if (CollectionUtils.isEmpty(venueInfoDOS)) { taskResult.setSummary("无可用场地"); return taskResult; } - venueInfoDOS = venueInfoDOS.stream().filter(VenueInfoUtils::get628VenueInfo).toList(); + venueInfoDOS = venueInfoDOS.stream() + .filter(VenueInfoUtils::get11213VenueInfo4Mor) + .sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))) + .toList(); if (CollectionUtils.isEmpty(venueInfoDOS)) { taskResult.setSummary("无可用场地"); return taskResult; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java index e1c0989..6ed10b6 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java @@ -1,10 +1,10 @@ package com.xiang.service.module.jntyzx.miniapp.schedule; -import com.alibaba.fastjson.JSON; import com.xiang.common.enums.RedisKeyConstant; import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.pojo.UserTokenInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; import com.xiang.common.pojo.schedule.TaskResult; @@ -12,19 +12,23 @@ import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; import com.xiang.common.utils.RedisService; import com.xiang.service.module.jntyzx.miniapp.service.IJtOrderService; +import com.xiang.service.module.jntyzx.miniapp.service.IUserInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RestController; -import java.util.Comparator; +import java.time.Duration; +import java.time.LocalTime; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; import java.util.stream.Collectors; @Slf4j @@ -37,6 +41,7 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { private final IVenueService venueService; private final JntyzxDingTalkFactory jtDingTalkFactory; private final RedisService redisService; + private final IUserInfoService userInfoService; public JtVenueSubscribeTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, @@ -44,13 +49,15 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { IJtOrderService jtOrderService, IVenueService venueService, JntyzxDingTalkFactory jtDingTalkFactory, - RedisService redisService) { + RedisService redisService, + IUserInfoService userInfoService) { super(scheduleOpeningConfigService, scheduleRunLogService); this.userTokenInfoService = userTokenInfoService; this.jtOrderService = jtOrderService; this.venueService = venueService; this.jtDingTalkFactory = jtDingTalkFactory; this.redisService = redisService; + this.userInfoService = userInfoService; } @Override @@ -81,31 +88,53 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { taskResult.setSummary("无可下单用户"); return taskResult; } + Map userMap = users.stream().collect(Collectors.toMap(UserTokenInfoDO::getName, Function.identity(), (a, b) -> a)); + List venueInfoDOS = venueService.queryTomorrowCanBuyVenue(); Map> venueInfoMap = venueInfoDOS.stream() - .filter(VenueInfoUtils::get8210VenueInfo) - .filter(item -> !StringUtils.contains(item.getPlaceName(), "小馆")) .collect(Collectors.groupingByConcurrent(VenueInfoDO::getPlaceName)); - if (MapUtils.isEmpty(venueInfoMap)) { - log.info("暂无可下单场地,time:{}", System.currentTimeMillis()); - taskResult.setSummary("无下单场地"); + + List userInfoDOS = userInfoService.selectAll(); + if (CollectionUtils.isEmpty(userInfoDOS)) { + log.info("暂无可下单用户, time:{}", System.currentTimeMillis()); + jtDingTalkFactory.sendMsg("暂无可下单用户配置信息, time:" + System.currentTimeMillis()); + taskResult.setSummary("无可下单用户配置信息"); return taskResult; } - users.parallelStream().forEach(user -> { - try { - List placeNameList = venueInfoMap.keySet().stream().sorted(Comparator.comparing(VenueInfoUtils::sortVenueInfo)).toList(); - log.info("场地排序后的集合:{}", JSON.toJSONString(placeNameList)); - for (String placeName : placeNameList) { - List venueInfoDOList = venueInfoMap.get(placeName); + + for (UserInfoDO userInfoDO : userInfoDOS) { + if (venueInfoMap.containsKey(userInfoDO.getPlaceName()) && userMap.containsKey(userInfoDO.getName())) { + UserTokenInfoDO userTokenInfoDO = userMap.get(userInfoDO.getName()); + List venueInfoDOList = venueInfoMap.get(userInfoDO.getPlaceName()); + if (CollectionUtils.isEmpty(venueInfoDOList)) { + continue; + } + venueInfoDOList = VenueInfoUtils.filterVenueList(userInfoDO.getSiteTimeName(), venueInfoDOS); + if (CollectionUtils.isEmpty(venueInfoDOList)) { + logger.info("用户:{}无场地信息:{},时间:{}", userInfoDO.getName(), userInfoDO.getPlaceName(), userInfoDO.getSiteTimeName()); + continue; + } + + String placeName = userInfoDO.getPlaceName(); + + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + //计算9点到现在的时间差 + //获取江体当前时间 + LocalTime currentTime = LocalTime.now(); + LocalTime targetTime = LocalTime.parse("09:00:10"); + Duration duration = Duration.between(currentTime, targetTime); + long milliseconds = duration.toMillis(); + List finalVenueInfoDOList = venueInfoDOList; +// executorService.schedule(() -> { for (int i = 0; i < 10; i++) { String valid = (String) redisService.get(RedisKeyConstant.getVenueSubscribeKey(placeName)); if (StringUtils.isNotBlank(valid)) { break; } - boolean order = jtOrderService.createOrder(venueInfoDOList, user); + boolean order = jtOrderService.createOrder(finalVenueInfoDOList, userTokenInfoDO); if (order) { - return; + break; } try { Thread.sleep(1250); @@ -113,13 +142,10 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { log.error("睡眠失败~~~"); } } - } - } catch (Exception e) { - // 关键点:异常只影响当前 user - log.error("createOrder 异常,user={}", user.getId(), e); - return; // 结束这个 user,不影响其他 user +// }, milliseconds, TimeUnit.MILLISECONDS); } - }); + } + taskResult.setSuccess(true); taskResult.setSummary("下单执行成功!"); return taskResult; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java index bf7eaea..72e5845 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/IUserInfoService.java @@ -13,4 +13,6 @@ public interface IUserInfoService { boolean delAll(); boolean batchSave(List list); + + List selectAll(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java index 7f3c316..f162388 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/UserInfoServiceImpl.java @@ -29,4 +29,9 @@ public class UserInfoServiceImpl implements IUserInfoService { public boolean batchSave(List list) { return userInfoManage.saveBatch(list); } + + @Override + public List selectAll() { + return userInfoManage.list(); + } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java index f621f25..8a3ca0a 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java @@ -4,12 +4,20 @@ import com.xiang.common.pojo.jntyzx.miniapp.pojo.VenueInfoDO; import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; import org.apache.commons.lang3.StringUtils; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + /** * @Author: xiang * @Date: 2025-12-16 09:55 */ public class VenueInfoUtils { + public static boolean get11213VenueInfo4Mor(VenueInfoDO venueInfoDO) { + return StringUtils.equals(venueInfoDO.getSjName(), "11:00-12:00") || StringUtils.equals(venueInfoDO.getSjName(), "12:00-13:00"); + } + public static boolean get123VenueInfo4Mor(VenueInfoDO venueInfoDO) { return StringUtils.equals(venueInfoDO.getSjName(), "13:00-14:00") || StringUtils.equals(venueInfoDO.getSjName(), "14:00-15:00"); } @@ -44,4 +52,32 @@ public class VenueInfoUtils { } return 5; } + + public static List filterVenueList(String siteTimeName, List venueInfoDOS) { + if (StringUtils.equals(siteTimeName, "18:00")) { + return venueInfoDOS.stream().filter(VenueInfoUtils::get628VenueInfo).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "20:00")) { + return venueInfoDOS.stream().filter(VenueInfoUtils::get8210VenueInfo).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "12:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "12:00-13:00")).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "13:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "13:00-14:00")).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "14:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "14:00-15:00")).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "15:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "15:00-16:00")).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "16:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "16:00-17:00")).collect(Collectors.toList()); + } + if (StringUtils.equals(siteTimeName, "17:00")) { + return venueInfoDOS.stream().filter(item -> Objects.equals(item.getSjName(), "17:00-18:00")).collect(Collectors.toList()); + } + return null; + } } -- 2.49.1 From d1584184ae52e79f1cdc87093379c065a710e2d0 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 11:41:03 +0800 Subject: [PATCH 26/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/xiang/ApplicationInit.java | 11 ++++++++++ .../com/xiang/common/config/RedisConfig.java | 21 +++++++++++++++++- .../xiang/common/enums/RedisKeyConstant.java | 2 ++ .../schedule/JntyzxUserInfoConfigTask.java | 22 ++++++++++++++----- .../schedule/JtVenueSubscribeTask.java | 15 ++++++++----- .../service/impl/OrderInfoServiceImpl.java | 8 +++++-- .../jntyzx/zlb/schedule/ZlbOrderTask.java | 4 ++-- .../zlb/schedule/ZlbUserConfigTask.java | 21 +++++++++++++----- 8 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/xiang/ApplicationInit.java b/src/main/java/com/xiang/ApplicationInit.java index 3246791..34c512e 100644 --- a/src/main/java/com/xiang/ApplicationInit.java +++ b/src/main/java/com/xiang/ApplicationInit.java @@ -2,9 +2,11 @@ package com.xiang; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.xiang.common.enums.RedisKeyConstant; import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.pojo.schedule.ScheduleOpeningConfigDO; import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.utils.RedisService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -28,6 +30,7 @@ import java.util.stream.Collectors; public class ApplicationInit implements ApplicationRunner { private final IScheduleOpeningConfigService scheduleOpeningConfigService; + private final RedisService redisService; @Override public void run(ApplicationArguments args) throws Exception { @@ -35,6 +38,10 @@ public class ApplicationInit implements ApplicationRunner { log.info("开始加载任务配置!"); loadScheduleTask(); log.info("任务配置加载完成!"); + + log.info("redis key 加载开始!"); + loadRedisKey(); + log.info("redis key 加载结束!"); } private void loadScheduleTask() { @@ -63,4 +70,8 @@ public class ApplicationInit implements ApplicationRunner { scheduleOpeningConfigService.saveBatch(list); } } + + private void loadRedisKey() { + redisService.set(RedisKeyConstant.JNTYZX_SUBSCRIBE_TIME_KEY, "18:00"); + } } diff --git a/src/main/java/com/xiang/common/config/RedisConfig.java b/src/main/java/com/xiang/common/config/RedisConfig.java index ad5657a..2d007f6 100644 --- a/src/main/java/com/xiang/common/config/RedisConfig.java +++ b/src/main/java/com/xiang/common/config/RedisConfig.java @@ -7,11 +7,17 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import java.time.Duration; + @Configuration public class RedisConfig { @Bean @@ -35,7 +41,6 @@ public class RedisConfig { template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); - template.afterPropertiesSet(); return template; } @@ -44,4 +49,18 @@ public class RedisConfig { public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { return new StringRedisTemplate(factory); } + + @Bean + public RedisConnectionFactory redisConnectionFactory(RedisProperties props) { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setHostName(props.getHost()); + config.setPort(Integer.parseInt(props.getPort())); + config.setPassword(RedisPassword.of(props.getPassword())); + config.setDatabase(props.getDatabase()); + + LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() + .commandTimeout(Duration.ofSeconds(3)) + .build(); + return new LettuceConnectionFactory(config, clientConfig); + } } diff --git a/src/main/java/com/xiang/common/enums/RedisKeyConstant.java b/src/main/java/com/xiang/common/enums/RedisKeyConstant.java index 6117a59..1e2a78a 100644 --- a/src/main/java/com/xiang/common/enums/RedisKeyConstant.java +++ b/src/main/java/com/xiang/common/enums/RedisKeyConstant.java @@ -29,6 +29,8 @@ public class RedisKeyConstant { return JNTYZX_VENUE_SUBSCRIBE_KEY + placeName + ":" + getDate(); } + public static final String JNTYZX_SUBSCRIBE_TIME_KEY = "jntyzx:subscribe:time"; + public static String getDate() { LocalDate now = LocalDate.now(); return ":" + DateUtils.getDateFromDate(now); diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java index c232807..066d356 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java @@ -1,6 +1,8 @@ package com.xiang.service.module.jntyzx.miniapp.schedule; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.google.common.collect.Lists; +import com.xiang.common.enums.RedisKeyConstant; import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; @@ -11,6 +13,7 @@ import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; import com.xiang.common.utils.DateUtils; +import com.xiang.common.utils.RedisService; import com.xiang.service.module.jntyzx.miniapp.service.IUserInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; @@ -34,18 +37,21 @@ public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { private final IVenueService venueService; private final IUserInfoService userInfoService; private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + private final RedisService redisService; public JntyzxUserInfoConfigTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, IUserTokenInfoService userTokenInfoService, IVenueService venueService, IUserInfoService userInfoService, - JntyzxDingTalkFactory jntyzxDingTalkFactory) { + JntyzxDingTalkFactory jntyzxDingTalkFactory, + RedisService redisService) { super(scheduleOpeningConfigService, scheduleRunLogService); this.userTokenInfoService = userTokenInfoService; this.venueService = venueService; this.userInfoService = userInfoService; this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + this.redisService = redisService; } @Override @@ -72,14 +78,20 @@ public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { taskResult.setSummary("无可用场地"); return taskResult; } - venueInfoDOS = venueInfoDOS.stream() - .filter(VenueInfoUtils::get11213VenueInfo4Mor) - .sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))) - .toList(); + + String time = (String) redisService.get(RedisKeyConstant.JNTYZX_SUBSCRIBE_TIME_KEY); + if (StringUtils.isBlank(time)) { + time = "18:00"; + } + String finalTime = time; + venueInfoDOS = VenueInfoUtils.filterVenueList(finalTime, venueInfoDOS); if (CollectionUtils.isEmpty(venueInfoDOS)) { taskResult.setSummary("无可用场地"); return taskResult; } + venueInfoDOS = venueInfoDOS.stream() + .sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))) + .toList(); List users = userTokenInfoService.getCanOrderUser(); if (CollectionUtils.isEmpty(users)) { diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java index 6ed10b6..3785e3f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -108,9 +109,10 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { UserTokenInfoDO userTokenInfoDO = userMap.get(userInfoDO.getName()); List venueInfoDOList = venueInfoMap.get(userInfoDO.getPlaceName()); if (CollectionUtils.isEmpty(venueInfoDOList)) { + logger.info("用户:{}查询不到场地信息:{}", userInfoDO.getName(), userInfoDO.getPlaceName()); continue; } - venueInfoDOList = VenueInfoUtils.filterVenueList(userInfoDO.getSiteTimeName(), venueInfoDOS); + venueInfoDOList = VenueInfoUtils.filterVenueList(userInfoDO.getSiteTimeName(), venueInfoDOList); if (CollectionUtils.isEmpty(venueInfoDOList)) { logger.info("用户:{}无场地信息:{},时间:{}", userInfoDO.getName(), userInfoDO.getPlaceName(), userInfoDO.getSiteTimeName()); continue; @@ -125,9 +127,12 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { LocalTime targetTime = LocalTime.parse("09:00:10"); Duration duration = Duration.between(currentTime, targetTime); long milliseconds = duration.toMillis(); + if (milliseconds <= 0) { + milliseconds = 0; + } List finalVenueInfoDOList = venueInfoDOList; -// executorService.schedule(() -> { - for (int i = 0; i < 10; i++) { + executorService.schedule(() -> { + for (int i = 0; i < 3; i++) { String valid = (String) redisService.get(RedisKeyConstant.getVenueSubscribeKey(placeName)); if (StringUtils.isNotBlank(valid)) { break; @@ -137,12 +142,12 @@ public class JtVenueSubscribeTask extends BaseScheduleTaskTemplate { break; } try { - Thread.sleep(1250); + Thread.sleep(2000); } catch (InterruptedException e) { log.error("睡眠失败~~~"); } } -// }, milliseconds, TimeUnit.MILLISECONDS); + }, milliseconds, TimeUnit.MILLISECONDS); } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java index 004e686..d2729d4 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/OrderInfoServiceImpl.java @@ -76,10 +76,10 @@ public class OrderInfoServiceImpl implements IJtOrderService { orderCreateInfoManage.save(orderInfoDO); } } - dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); + dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "小程序预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); return true; } else { - dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); + dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "小程序预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp)); if (orderResp.getMessage().contains("锁卡")) { log.info("有锁卡风险,不在请求,用户:{}", userTokenInfoDO.getName()); throw new BusinessException("即将锁卡,不再请求"); @@ -93,6 +93,10 @@ public class OrderInfoServiceImpl implements IJtOrderService { log.info("该场地已被人预定,更换场地, 用户:{}", userTokenInfoDO.getName()); redisService.set(RedisKeyConstant.getVenueSubscribeKey(venueInfoDOS.get(0).getPlaceName()), "true"); } + if (orderResp.getMessage().contains("预约场地不能超过2个")) { + log.info("用户:{}已经预约场地", userTokenInfoDO.getName()); + throw new BusinessException("用户已经预约场地"); + } return false; } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java index 8f83c64..e5de3d5 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -138,7 +138,7 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { log.info("订单接口返回结果==> \n {}", response); JSONObject jsonObject = JSONObject.parseObject(response); if (jsonObject.getInteger("code") == 200) { - jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); + jntyzxDingTalkFactory.sendMsg(name + "zlb订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); JSONObject data = jsonObject.getJSONObject("data"); String orderId = data.getString("orderId"); log.info("{}订单{}创建成功", name, orderId); @@ -149,7 +149,7 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { } if (jsonObject.getInteger("code") == 500) { if (jsonObject.getString("message").contains("已被售出")) { - jntyzxDingTalkFactory.sendMsg(name + "订单接口下单返回失败❌❌❌场地号:" + placeName + "已被售出"); + jntyzxDingTalkFactory.sendMsg(name + "zlb订单接口下单返回失败❌❌❌场地号:" + placeName + "已被售出"); return true; } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java index 6685db4..7683bcf 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -1,11 +1,16 @@ package com.xiang.service.module.jntyzx.zlb.schedule; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.Lists; +import com.xiang.common.enums.RedisKeyConstant; import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.manage.jntyzx.zlb.ZlbSiteInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; +import com.xiang.common.manage.jntyzx.zlb.ZlbUserInfoService; import com.xiang.common.pojo.jntyzx.zlb.ZlbSiteInfo; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; @@ -13,9 +18,7 @@ import com.xiang.common.pojo.schedule.TaskResult; import com.xiang.common.service.IScheduleOpeningConfigService; import com.xiang.common.service.IScheduleRunLogService; import com.xiang.common.utils.DateUtils; -import com.xiang.common.manage.jntyzx.zlb.ZlbSiteInfoService; -import com.xiang.common.manage.jntyzx.zlb.ZlbTokenInfoService; -import com.xiang.common.manage.jntyzx.zlb.ZlbUserInfoService; +import com.xiang.common.utils.RedisService; import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -39,18 +42,21 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { private final ZlbUserInfoService zlbUserInfoService; private final JntyzxDingTalkFactory jntyzxDingTalkFactory; private final ZlbTokenInfoService zlbTokenInfoService; + private final RedisService redisService; public ZlbUserConfigTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, ZlbSiteInfoService zlbSiteInfoService, ZlbUserInfoService zlbUserInfoService, ZlbTokenInfoService zlbTokenInfoService, - JntyzxDingTalkFactory jntyzxDingTalkFactory) { + JntyzxDingTalkFactory jntyzxDingTalkFactory, + RedisService redisService) { super(scheduleOpeningConfigService, scheduleRunLogService); this.zlbSiteInfoService = zlbSiteInfoService; this.zlbUserInfoService = zlbUserInfoService; this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; this.zlbTokenInfoService = zlbTokenInfoService; + this.redisService = redisService; } @Override @@ -127,7 +133,12 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { } private List filterSiteInfo(List siteInfoList) { - return siteInfoList.stream().filter(item -> Objects.equals(item.getDayEffectiveTimes(), "20:00")).toList(); + String time = (String) redisService.get(RedisKeyConstant.JNTYZX_SUBSCRIBE_TIME_KEY); + if (StringUtils.isBlank(time)) { + time = "18:00"; + } + String finalTime = time; + return siteInfoList.stream().filter(item -> Objects.equals(item.getDayEffectiveTimes(), finalTime)).toList(); } private List sortSiteInfo(List siteInfoList) { -- 2.49.1 From 268b63e607875205e8271b2482f052801723cb39 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 14:35:54 +0800 Subject: [PATCH 27/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/enums/ScheduleEnums.java | 3 + .../schedule/JntyzxMiniappScheduleConfig.java | 42 ++++++++- .../schedule/JntyzxUserInfoConfigTask.java | 2 + .../miniapp/schedule/JtTokenRefreshTask.java | 2 + .../schedule/JtVenueInfoTodayResultTask.java | 94 +++++++++++++++++++ .../JtVenueInfoTomorrowResultTask.java | 94 +++++++++++++++++++ .../miniapp/schedule/JtVenuePullTask.java | 36 ++++--- .../schedule/JtVenueSubscribeTask.java | 3 + .../schedule/JtVenueTomorrowPullTask.java | 27 ++++-- .../service/impl/VenueServiceImpl.java | 9 +- .../jntyzx/miniapp/utils/MsgSendUtils.java | 2 +- .../jntyzx/miniapp/utils/VenueInfoUtils.java | 3 + .../jntyzx/zlb/schedule/ZlbTaskConfig.java | 20 +++- 13 files changed, 301 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTodayResultTask.java create mode 100644 src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTomorrowResultTask.java diff --git a/src/main/java/com/xiang/common/enums/ScheduleEnums.java b/src/main/java/com/xiang/common/enums/ScheduleEnums.java index 6ea4c02..bbe9742 100644 --- a/src/main/java/com/xiang/common/enums/ScheduleEnums.java +++ b/src/main/java/com/xiang/common/enums/ScheduleEnums.java @@ -38,6 +38,9 @@ public enum ScheduleEnums { JNTYZX_VENUE_TODAY_SUBSCRIBE_TASK(4, "jt-miniApp", "jntyzxVenueTodaySubscribeTask"), JNTYZX_VENUE_TOMORROW_PULL_TASK(4, "jt-miniApp", "jntyzxVenueTodayPullTask"), JNTYZX_USER_INFO_CONFIG(4, "jt-miniApp", "jntyzxUserInfoConfigTask"), + JNTYZX_VENUE_INFO_TODAY_RESULT_TASK(4, "jt-miniApp", "jtVenueInfoTodayResultTask"), + JNTYZX_VENUE_INFO_TOMORROW_RESULT_TASK(4, "jt-miniApp", "jtVenueInfoTomorrowResultTask"), + ; private final Integer moduleCode; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java index af70e5a..72a4865 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxMiniappScheduleConfig.java @@ -7,6 +7,8 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** + * 定时任务配置器 + * * @Author: xiang * @Date: 2026-05-09 08:56 */ @@ -20,35 +22,69 @@ public class JntyzxMiniappScheduleConfig { private final JtVenueSubscribeTask jtVenueSubscribeTask; private final JtVenueTomorrowPullTask jtVenueTomorrowPullTask; private final JntyzxUserInfoConfigTask jntyzxUserInfoConfigTask; + private final JtVenueInfoTodayResultTask jtVenueInfoTodayResultTask; + private final JtVenueInfoTomorrowResultTask jtVenueInfoTomorrowResultTask; - + /** + * token刷新 + */ @Scheduled(cron = "0 20,50 * * * ?") @GetMapping("/jtTokenRefreshTask") public void jtTokenRefreshTask() { jtTokenRefreshTask.run(); } -// @Scheduled(cron = "0 0/1 10-18 * * ?") + /** + * 每分钟场地信息更新 + */ + @Scheduled(cron = "0 0/1 10-18 * * ?") @GetMapping("/jtVenuePullTask") public void jtVenuePullTask() { jtVenuePullTask.run(); } + /** + * 拉取第二天场地信息 + */ @Scheduled(cron = "0 30 8 * * ?") @GetMapping("/jtVenueTomorrowPullTask") public void jtVenueTomorrowPullTask() { jtVenueTomorrowPullTask.run(); } + /** + * 配置下单用户场地数据 + */ @Scheduled(cron = "0 40 8 * * ?") @GetMapping("/jtUserInfoConfig") public void jtUserInfoConfig() { jntyzxUserInfoConfigTask.run(); } - @Scheduled(cron = "5 0 9 * * ?") + /** + * 下单定时任务 + */ + @Scheduled(cron = "0 0 9 * * ?") @GetMapping("/jtVenueSubscribeTask") public void jtVenueSubscribeTask() { jtVenueSubscribeTask.run(); } + + /** + * 当天场地订阅结果 + */ + @Scheduled(cron = "0 0 17 * * ?") + @GetMapping("/jtVenueInfoTodayResultTask") + public void jtVenueInfoTodayResultTask() { + jtVenueInfoTodayResultTask.run(); + } + + /** + * 第二天场地订阅结果 + */ + @Scheduled(cron = "0 10 9 * * ?") + @GetMapping("/jtVenueInfoTomorrowResultTask") + public void jtVenueInfoTomorrowResultTask() { + jtVenueInfoTomorrowResultTask.run(); + } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java index 066d356..a17a291 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java @@ -26,6 +26,8 @@ import java.util.Comparator; import java.util.List; /** + * 用户场地配置任务 每日8:40运行 + * * @Author: xiang * @Date: 2026-05-09 09:58 */ diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java index db89af1..08f2d5e 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtTokenRefreshTask.java @@ -11,6 +11,8 @@ import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RestController; /** + * 用户token刷新定时任务 每半个小时运行一次 在每个小时的20和50分 + * * @Author: xiang * @Date: 2026-01-15 17:29 */ diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTodayResultTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTodayResultTask.java new file mode 100644 index 0000000..f745d31 --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTodayResultTask.java @@ -0,0 +1,94 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.google.common.collect.Maps; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +/** + * 17:00查询当天的场地预定结果 + * + * @Author: xiang + * @Date: 2026-05-09 14:17 + */ +@Component +@Slf4j +public class JtVenueInfoTodayResultTask extends BaseScheduleTaskTemplate { + + private final IVenueService venueService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public JtVenueInfoTodayResultTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IVenueService venueService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.venueService = venueService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TODAY_RESULT_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TODAY_RESULT_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TODAY_RESULT_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(false); + List sitePositionLists = venueService.queryVenueService(); + if (CollectionUtils.isEmpty(sitePositionLists)) { + taskResult.setSummary("场地信息为空"); + return taskResult; + } + List positionListList6_8 = sitePositionLists.stream().filter(VenueInfoUtils::get628VenueInfo).toList(); + sendMsg(positionListList6_8, "18:00-20:00"); + List positionListList8_10 = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList(); + sendMsg(positionListList8_10, "20:00-22:00"); + taskResult.setSuccess(true); + taskResult.setSummary("场地信息查询定时任务成功"); + return taskResult; + } + + private void sendMsg(List positionListList, String time) { + if (CollectionUtils.isNotEmpty(positionListList)) { + Map map = Maps.newLinkedHashMap(); + for (SitePositionList sitePositionList : positionListList) { + if (map.containsKey(sitePositionList.getPlaceName())) { + continue; + } + map.put(sitePositionList.getPlaceName(), sitePositionList); + } + StringBuilder sb = new StringBuilder(DateUtils.getDateFromDate(LocalDate.now()) + "==>"+ time +"场地信息如下:\n"); + map.forEach((placeName, sitePositionList) -> { + sb.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n"); + }); + jntyzxDingTalkFactory.sendMsg(sb.toString()); + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTomorrowResultTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTomorrowResultTask.java new file mode 100644 index 0000000..265595c --- /dev/null +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueInfoTomorrowResultTask.java @@ -0,0 +1,94 @@ +package com.xiang.service.module.jntyzx.miniapp.schedule; + +import com.google.common.collect.Maps; +import com.xiang.common.enums.ScheduleEnums; +import com.xiang.common.factory.JntyzxDingTalkFactory; +import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.pojo.jntyzx.miniapp.resp.query.SitePositionList; +import com.xiang.common.pojo.schedule.TaskResult; +import com.xiang.common.service.IScheduleOpeningConfigService; +import com.xiang.common.service.IScheduleRunLogService; +import com.xiang.common.utils.DateUtils; +import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; +import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +/** + * 9:10分查询第二天的场地预定结果 + * + * @Author: xiang + * @Date: 2026-05-09 14:17 + */ +@Component +@Slf4j +public class JtVenueInfoTomorrowResultTask extends BaseScheduleTaskTemplate { + + private final IVenueService venueService; + private final JntyzxDingTalkFactory jntyzxDingTalkFactory; + + public JtVenueInfoTomorrowResultTask(IScheduleOpeningConfigService scheduleOpeningConfigService, + IScheduleRunLogService scheduleRunLogService, + IVenueService venueService, + JntyzxDingTalkFactory jntyzxDingTalkFactory) { + super(scheduleOpeningConfigService, scheduleRunLogService); + this.venueService = venueService; + this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; + } + + @Override + protected String getTaskName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TOMORROW_RESULT_TASK.getTaskName(); + } + + @Override + protected Integer getModule() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TOMORROW_RESULT_TASK.getModuleCode(); + } + + @Override + protected String getModuleName() { + return ScheduleEnums.JNTYZX_VENUE_INFO_TOMORROW_RESULT_TASK.getModule(); + } + + @Override + protected TaskResult doExecute(Object validatedParams) throws Exception { + + TaskResult taskResult = new TaskResult(); + taskResult.setSuccess(false); + List sitePositionLists = venueService.queryTomorrowVenue(); + if (CollectionUtils.isEmpty(sitePositionLists)) { + taskResult.setSummary("场地信息为空"); + return taskResult; + } + List positionListList6_8 = sitePositionLists.stream().filter(VenueInfoUtils::get628VenueInfo).toList(); + sendMsg(positionListList6_8, "18:00-20:00"); + List positionListList8_10 = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList(); + sendMsg(positionListList8_10, "20:00-22:00"); + taskResult.setSuccess(true); + taskResult.setSummary("场地信息查询定时任务成功"); + return taskResult; + } + + private void sendMsg(List positionListList, String time) { + if (CollectionUtils.isNotEmpty(positionListList)) { + Map map = Maps.newLinkedHashMap(); + for (SitePositionList sitePositionList : positionListList) { + if (map.containsKey(sitePositionList.getPlaceName())) { + continue; + } + map.put(sitePositionList.getPlaceName(), sitePositionList); + } + StringBuilder sb = new StringBuilder(DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + "==>"+ time +"场地信息如下:\n"); + map.forEach((placeName, sitePositionList) -> { + sb.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n"); + }); + jntyzxDingTalkFactory.sendMsg(sb.toString()); + } + } +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java index ecde99a..0fcf618 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java @@ -54,15 +54,18 @@ public class JtVenuePullTask extends BaseScheduleTaskTemplate { this.msgSendUtils = msgSendUtils; } - private List handleMsgSendList(List sitePositionLists, int dayOfWeek) { - // 过滤出来8-10的未订购的场地信息 - sitePositionLists = sitePositionLists.stream() - .filter(VenueInfoUtils::get8210VenueInfo) - .filter(item -> StringUtils.equals(item.getContacts(), "0")).toList(); - // 周六周日过滤小馆,不查询当天小馆信息 - if (dayOfWeek == 6 || dayOfWeek == 7) { - return sitePositionLists.stream() + private List handleMsgSendList(List sitePositionLists, Integer type) { + if (type == 1) { + sitePositionLists = sitePositionLists.stream() .filter(item -> !item.getPlaceName().contains("小馆")) + .filter(VenueInfoUtils::get628VenueInfo) + .filter(item -> StringUtils.equals(item.getContacts(), "0")) + .toList(); + } else { + sitePositionLists = sitePositionLists.stream() + .filter(item -> !item.getPlaceName().contains("小馆")) + .filter(VenueInfoUtils::get8210VenueInfo) + .filter(item -> StringUtils.equals(item.getContacts(), "0")) .toList(); } Map mapByName = Maps.newLinkedHashMap(); @@ -91,7 +94,6 @@ public class JtVenuePullTask extends BaseScheduleTaskTemplate { @Override protected TaskResult doExecute(Object validatedParams) throws Exception { - TaskResult taskResult = new TaskResult(); taskResult.setSuccess(false); log.info("【Venue】江体小程序场地数据拉取定时任务启动!!!time:{}", System.currentTimeMillis()); @@ -104,7 +106,6 @@ public class JtVenuePullTask extends BaseScheduleTaskTemplate { } String token; LocalDateTime now = LocalDateTime.now(); - int dayOfWeek = now.getDayOfWeek().getValue(); for (UserTokenInfoDO userTokenInfoDO : availableUser) { if (Objects.isNull(userTokenInfoDO)) { @@ -120,18 +121,15 @@ public class JtVenuePullTask extends BaseScheduleTaskTemplate { } venueService.saveOrUpdateTodayVenueInfo(sitePositionLists); - sitePositionLists = handleMsgSendList(sitePositionLists, dayOfWeek); - if (CollectionUtils.isEmpty(sitePositionLists)) { - taskResult.setSuccess(true); - taskResult.setSummary("当前无场地信息!"); - return taskResult; - } - + List sitePositionLists6_8 = handleMsgSendList(sitePositionLists, 1); StringBuffer msg = new StringBuffer( - "查询到20:00-22:00空闲场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now()) + "\n"); - sitePositionLists.forEach(item -> { + "查询到18:00-20:00空闲场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now()) + "\n"); + sitePositionLists6_8.forEach(item -> { msg.append(item.getPlaceName()).append("\n"); }); + List sitePositionLists8_10 = handleMsgSendList(sitePositionLists, 2); + msg.append("查询到18:00-20:00空闲场地信息=====>\n时间:").append(DateUtils.getDateFromDate(LocalDate.now())).append("\n"); + sitePositionLists8_10.forEach(item -> msg.append(item.getPlaceName()).append("\n")); String key = RedisKeyConstant.JNTYZX_VENUE_MSG_SEND_KEY + RedisKeyConstant.getDate(); msgSendUtils.sendMsgRestrict1Hours(key, msg.toString()); diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java index 3785e3f..cb5ca7f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueSubscribeTask.java @@ -32,6 +32,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; +/** + * 场地订阅定时任务 每日9:00:00 + */ @Slf4j @Component @RestController diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java index be08f35..fce854c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java @@ -26,6 +26,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; +/** + * 场地信息获取定时任务 每日8:30拉取第二天的场地信息 + */ @Slf4j @Component public class JtVenueTomorrowPullTask extends BaseScheduleTaskTemplate { @@ -96,11 +99,25 @@ public class JtVenueTomorrowPullTask extends BaseScheduleTaskTemplate { return taskResult; } venueService.saveTomorrowVenueInfo(sitePositionLists); - sitePositionLists = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList(); - if (CollectionUtils.isEmpty(sitePositionLists)) { + List sitePositionLists6_8 = sitePositionLists.stream().filter(VenueInfoUtils::get628VenueInfo).toList(); + if (CollectionUtils.isEmpty(sitePositionLists6_8)) { taskResult.setSummary("当前无可用场地信息"); return taskResult; } + buildMsg(sitePositionLists6_8, "18:00-20:00"); + List sitePositionLists8_10 = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList(); + if (CollectionUtils.isEmpty(sitePositionLists8_10)) { + taskResult.setSummary("当前无可用场地信息"); + return taskResult; + } + buildMsg(sitePositionLists8_10, "20:00-22:00"); + + taskResult.setSuccess(Boolean.TRUE); + taskResult.setSummary("场地信息获取成功!"); + return taskResult; + } + + private void buildMsg(List sitePositionLists, String time) { Map map = Maps.newLinkedHashMap(); for (SitePositionList sitePositionList : sitePositionLists) { if (map.containsKey(sitePositionList.getPlaceName())) { @@ -108,14 +125,10 @@ public class JtVenueTomorrowPullTask extends BaseScheduleTaskTemplate { } map.put(sitePositionList.getPlaceName(), sitePositionList); } - StringBuffer msg = new StringBuffer("查询江体场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + " 20:00-22:00\n"); + StringBuffer msg = new StringBuffer("查询江体场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + time + "\n"); map.forEach((placeName, sitePositionList) -> { msg.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n"); }); jtDingTalkFactory.sendMsg(msg.toString()); - - taskResult.setSuccess(Boolean.TRUE); - taskResult.setSummary("场地信息获取成功!"); - return taskResult; } } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java index 82dc080..6a02e4f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/service/impl/VenueServiceImpl.java @@ -10,6 +10,7 @@ import com.xiang.service.module.jntyzx.miniapp.service.IJntyzxHttpService; import com.xiang.service.module.jntyzx.miniapp.service.IUserTokenInfoService; import com.xiang.service.module.jntyzx.miniapp.service.IVenueService; import com.xiang.service.module.jntyzx.miniapp.utils.VenueInfoUtils; +import com.xiang.service.module.jntyzx.miniapp.utils.WeekendUtils; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -37,11 +38,11 @@ public class VenueServiceImpl implements IVenueService { @Override public List queryVenueService() { - String token = userTokenInfoService.getToken("Xiang"); + String token = userTokenInfoService.getToken("xiang"); if (StringUtils.isBlank(token)) { return Lists.newArrayList(); } - List sitePositionLists = jntyzxHttpService.queryAvailable("1", token); + List sitePositionLists = jntyzxHttpService.queryAvailable(WeekendUtils.isWeekend(), token); if (CollectionUtils.isEmpty(sitePositionLists)) { return Lists.newArrayList(); } @@ -51,11 +52,11 @@ public class VenueServiceImpl implements IVenueService { @Override public List queryTomorrowVenue() { - String token = userTokenInfoService.getToken("Xiang"); + String token = userTokenInfoService.getToken("xiang"); if (StringUtils.isBlank(token)) { return Lists.newArrayList(); } - List sitePositionLists = jntyzxHttpService.queryAvailableTomorrow("1", token); + List sitePositionLists = jntyzxHttpService.queryAvailableTomorrow(WeekendUtils.isWeekend(), token); if (CollectionUtils.isEmpty(sitePositionLists)) { return Lists.newArrayList(); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java index ab0b174..a48ea23 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/MsgSendUtils.java @@ -30,7 +30,7 @@ public class MsgSendUtils { String cache = (String) redisService.get(redisKey); if (StringUtils.isNotBlank(cache)) { int sendNum = Integer.parseInt(cache); - if (sendNum >= 0 && sendNum <= 5) { + if (sendNum >= 0 && sendNum < 5) { jtDingTalkFactory.sendMsg(msgContent); redisService.set(key, String.valueOf(++sendNum), 1, TimeUnit.HOURS); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java index 8a3ca0a..8d73cf8 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java @@ -27,6 +27,9 @@ public class VenueInfoUtils { public static boolean get628VenueInfo(VenueInfoDO venueInfoDO) { return StringUtils.equals(venueInfoDO.getSjName(), "18:00-19:00") || StringUtils.equals(venueInfoDO.getSjName(), "19:00-20:00"); } + public static boolean get628VenueInfo(SitePositionList sitePositionList) { + return StringUtils.equals(sitePositionList.getSjName(), "18:00-19:00") || StringUtils.equals(sitePositionList.getSjName(), "19:00-20:00"); + } public static boolean get8210VenueInfo(VenueInfoDO venueInfoDO) { return StringUtils.equals(venueInfoDO.getSjName(), "20:00-21:00") || StringUtils.equals(venueInfoDO.getSjName(), "21:00-22:00"); } diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java index b8f5040..40ffcd8 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbTaskConfig.java @@ -18,41 +18,57 @@ public class ZlbTaskConfig { private final ZlbOrderTask zlbOrderTask; private final ZlbUserConfigTask zlbUserConfigTask; + /** + * token续期 + */ @Scheduled(cron = "0 0/30 * * * ?") @GetMapping("/zlbLoginTask") public void zlbLoginTask() { zlbLoginTask.run(); } + /** + * token校验 + */ @Scheduled(cron = "0 0 8 * * *") @GetMapping("/zlbTokenRefresh") public void zlbTokenRefresh() { zlbTokenRefreshTask.run(); } + /** + * 场地信息拉取 拉取后天的场地信息 + */ @GetMapping("/zlbSiteTask") @Scheduled(cron = "30 30 16 * * ?") public void zlbSiteTask() { zlbSiteTask.run(); } + /** + * 当天场地订阅信息--数据库数据 + */ @GetMapping("/zlbSiteDayTask") @Scheduled(cron = "0 00 17 * * ?") public void zlbSiteDayTask() { zlbSiteDayTask.run(); } + /** + * 下单 + */ @GetMapping("/zlbOrderCreateTask") @Scheduled(cron = "2 0 9 * * ?") public void zlbOrderCreateTask() { zlbOrderTask.run(); } + /** + * 下单用户场地配置信息 + */ @Scheduled(cron = "30 35 16 * * ?") @GetMapping("/zlbUserConfig") public void zlbUserConfig() { zlbUserConfigTask.run(); } - - } -- 2.49.1 From cb21b38287ee199cf4011878f96cfaeccb305a05 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 14:58:07 +0800 Subject: [PATCH 28/32] =?UTF-8?q?feat:=E6=B1=9F=E4=BD=93=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/JntyzxUserInfoConfigTask.java | 2 ++ .../miniapp/schedule/JtVenuePullTask.java | 27 +++++++++++-------- .../schedule/JtVenueTomorrowPullTask.java | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java index a17a291..0157db6 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JntyzxUserInfoConfigTask.java @@ -92,6 +92,7 @@ public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { return taskResult; } venueInfoDOS = venueInfoDOS.stream() + .filter(item -> !item.getPlaceName().contains("小馆")) .sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))) .toList(); @@ -103,6 +104,7 @@ public class JntyzxUserInfoConfigTask extends BaseScheduleTaskTemplate { List list = Lists.newArrayList(); int i = 0; + userInfoService.delAll(); for (UserTokenInfoDO user : users) { VenueInfoDO venueInfoDO = venueInfoDOS.get(i); UserInfoDO userInfoDO = new UserInfoDO(); diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java index 0fcf618..594212f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenuePullTask.java @@ -121,18 +121,23 @@ public class JtVenuePullTask extends BaseScheduleTaskTemplate { } venueService.saveOrUpdateTodayVenueInfo(sitePositionLists); - List sitePositionLists6_8 = handleMsgSendList(sitePositionLists, 1); - StringBuffer msg = new StringBuffer( - "查询到18:00-20:00空闲场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now()) + "\n"); - sitePositionLists6_8.forEach(item -> { - msg.append(item.getPlaceName()).append("\n"); - }); - List sitePositionLists8_10 = handleMsgSendList(sitePositionLists, 2); - msg.append("查询到18:00-20:00空闲场地信息=====>\n时间:").append(DateUtils.getDateFromDate(LocalDate.now())).append("\n"); - sitePositionLists8_10.forEach(item -> msg.append(item.getPlaceName()).append("\n")); + StringBuffer msg = new StringBuffer(); - String key = RedisKeyConstant.JNTYZX_VENUE_MSG_SEND_KEY + RedisKeyConstant.getDate(); - msgSendUtils.sendMsgRestrict1Hours(key, msg.toString()); + List sitePositionLists6_8 = handleMsgSendList(sitePositionLists, 1); + if (CollectionUtils.isNotEmpty(sitePositionLists6_8)) { + msg.append("查询到18:00-20:00空闲场地信息=====>\n时间:").append(DateUtils.getDateFromDate(LocalDate.now())).append("\n"); + sitePositionLists6_8.forEach(item -> msg.append(item.getPlaceName()).append("\n")); + } + List sitePositionLists8_10 = handleMsgSendList(sitePositionLists, 2); + if (CollectionUtils.isNotEmpty(sitePositionLists8_10)) { + msg.append("查询到20:00-22:00空闲场地信息=====>\n时间:").append(DateUtils.getDateFromDate(LocalDate.now())).append("\n"); + sitePositionLists8_10.forEach(item -> msg.append(item.getPlaceName()).append("\n")); + } + + if (StringUtils.isNotBlank(msg)) { + String key = RedisKeyConstant.JNTYZX_VENUE_MSG_SEND_KEY + RedisKeyConstant.getDate(); + msgSendUtils.sendMsgRestrict1Hours(key, msg.toString()); + } taskResult.setSuccess(true); taskResult.setSummary("查询场地信息成功!时间:" + now); return taskResult; diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java index fce854c..7e3072f 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/schedule/JtVenueTomorrowPullTask.java @@ -125,7 +125,7 @@ public class JtVenueTomorrowPullTask extends BaseScheduleTaskTemplate { } map.put(sitePositionList.getPlaceName(), sitePositionList); } - StringBuffer msg = new StringBuffer("查询江体场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + time + "\n"); + StringBuffer msg = new StringBuffer("查询江体场地信息=====>\n时间:" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + " " + time + "\n"); map.forEach((placeName, sitePositionList) -> { msg.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n"); }); -- 2.49.1 From 60992dc4f6738ef091d8dac8981392036b1925d5 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 15:17:12 +0800 Subject: [PATCH 29/32] =?UTF-8?q?feat:zlb=E6=B1=9F=E4=BD=93=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jntyzx/zlb/IZlbOrderInfoManage.java | 11 ++++++++ .../jntyzx/zlb/ZlbOrderInfoManageImpl.java | 15 +++++++++++ .../jntyzx/zlb/schedule/ZlbOrderTask.java | 26 ++++++++++++++++--- 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/xiang/common/manage/jntyzx/zlb/IZlbOrderInfoManage.java create mode 100644 src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbOrderInfoManageImpl.java diff --git a/src/main/java/com/xiang/common/manage/jntyzx/zlb/IZlbOrderInfoManage.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/IZlbOrderInfoManage.java new file mode 100644 index 0000000..ce20403 --- /dev/null +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/IZlbOrderInfoManage.java @@ -0,0 +1,11 @@ +package com.xiang.common.manage.jntyzx.zlb; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.xiang.common.pojo.jntyzx.zlb.ZlbPayOrder; + +/** + * @Author: xiang + * @Date: 2026-05-09 15:11 + */ +public interface IZlbOrderInfoManage extends IService { +} diff --git a/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbOrderInfoManageImpl.java b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbOrderInfoManageImpl.java new file mode 100644 index 0000000..7e49403 --- /dev/null +++ b/src/main/java/com/xiang/common/manage/jntyzx/zlb/ZlbOrderInfoManageImpl.java @@ -0,0 +1,15 @@ +package com.xiang.common.manage.jntyzx.zlb; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.xiang.common.mapper.ZlbOrderInfoMapper; +import com.xiang.common.pojo.jntyzx.zlb.ZlbPayOrder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @Author: xiang + * @Date: 2026-05-09 15:11 + */ +@Service +public class ZlbOrderInfoManageImpl extends ServiceImpl implements IZlbOrderInfoManage { +} diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java index e5de3d5..21d48e5 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbOrderTask.java @@ -8,6 +8,8 @@ import com.xiang.common.enums.ScheduleEnums; import com.xiang.common.exception.BusinessException; import com.xiang.common.factory.JntyzxDingTalkFactory; import com.xiang.common.factory.schedule.BaseScheduleTaskTemplate; +import com.xiang.common.manage.jntyzx.zlb.IZlbOrderInfoManage; +import com.xiang.common.pojo.jntyzx.zlb.ZlbPayOrder; import com.xiang.common.pojo.jntyzx.zlb.ZlbTokenInfo; import com.xiang.common.pojo.jntyzx.zlb.ZlbUserInfo; import com.xiang.common.pojo.schedule.TaskResult; @@ -24,6 +26,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.time.Duration; +import java.time.LocalDate; import java.time.LocalTime; import java.util.Date; import java.util.Map; @@ -40,6 +43,7 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { private final ZlbTokenInfoService zlbTokenInfoService; private final JntyzxDingTalkFactory jntyzxDingTalkFactory; private final RedisTemplate redisTemplate; + private final IZlbOrderInfoManage zlbOrderInfoManage; public ZlbOrderTask(IScheduleOpeningConfigService scheduleOpeningConfigService, IScheduleRunLogService scheduleRunLogService, @@ -47,13 +51,15 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { ZlbService zlbService, ZlbTokenInfoService zlbTokenInfoService, JntyzxDingTalkFactory jntyzxDingTalkFactory, - RedisTemplate redisTemplate) { + RedisTemplate redisTemplate, + IZlbOrderInfoManage zlbOrderInfoManage) { super(scheduleOpeningConfigService, scheduleRunLogService); this.zlbUserInfoService = zlbUserInfoService; this.zlbService = zlbService; this.zlbTokenInfoService = zlbTokenInfoService; this.jntyzxDingTalkFactory = jntyzxDingTalkFactory; this.redisTemplate = redisTemplate; + this.zlbOrderInfoManage = zlbOrderInfoManage; } @Override @@ -98,11 +104,15 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { String tokenId = zlbTokenInfo.getTokenId(); String secretKey = zlbService.getKey(tokenId, client); String siteOrderDetailsStr = zlbService.buildSiteOrder(zlbUserInfo, secretKey, day); + if (StringUtils.isEmpty(siteOrderDetailsStr)) { + log.info("构建订单参数异常:{}", siteOrderDetailsStr); + throw new BusinessException("构建订单参数异常"); + } Map headers = zlbService.getHeaders(zlbTokenInfo.getTokenId()); String newOrderJson = zlbService.buildNewOrder(siteOrderDetailsStr, client); if (StringUtils.isBlank(newOrderJson)) { log.info("构建订单参数异常:{}", siteOrderDetailsStr); - throw new BusinessException(""); + throw new BusinessException("构建订单参数异常"); } ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); //计算9点到现在的时间差 @@ -138,18 +148,26 @@ public class ZlbOrderTask extends BaseScheduleTaskTemplate { log.info("订单接口返回结果==> \n {}", response); JSONObject jsonObject = JSONObject.parseObject(response); if (jsonObject.getInteger("code") == 200) { - jntyzxDingTalkFactory.sendMsg(name + "zlb订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); + jntyzxDingTalkFactory.sendMsg(name + ":zlb订单接口下单返回成功请2分钟内付款√√√√√√场地号:" + placeName + "时间:" + siteTimeName); JSONObject data = jsonObject.getJSONObject("data"); String orderId = data.getString("orderId"); log.info("{}订单{}创建成功", name, orderId); String redisKey = ZlbUrlConstants.REDIS_PREFIX + "_" + orderId + "_" + name; redisTemplate.opsForValue().set(redisKey, name); redisTemplate.expire(redisKey, 120, TimeUnit.SECONDS); + ZlbPayOrder zlbPayOrder = new ZlbPayOrder(); + zlbPayOrder.setName(name); + zlbPayOrder.setDay(DateUtils.getDateFromDate(LocalDate.now().plusDays(1))); + zlbPayOrder.setVenues("江体"); + zlbPayOrder.setPlaceName(placeName); + zlbPayOrder.setTime(siteTimeName); + zlbPayOrder.setIsPay(0); + zlbOrderInfoManage.save(zlbPayOrder); return true; } if (jsonObject.getInteger("code") == 500) { if (jsonObject.getString("message").contains("已被售出")) { - jntyzxDingTalkFactory.sendMsg(name + "zlb订单接口下单返回失败❌❌❌场地号:" + placeName + "已被售出"); + jntyzxDingTalkFactory.sendMsg(name + ":zlb订单接口下单返回失败❌❌❌场地号:" + placeName + "已被售出"); return true; } } -- 2.49.1 From 6902a16cfa4062881d53c4ea82014abe01bfa687 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 15:24:44 +0800 Subject: [PATCH 30/32] =?UTF-8?q?feat:=E6=97=A5=E5=BF=97=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/xiang/common/utils/HttpService.java | 4 ++-- src/main/resources/logback-spring.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/xiang/common/utils/HttpService.java b/src/main/java/com/xiang/common/utils/HttpService.java index 8615497..afe8f8e 100644 --- a/src/main/java/com/xiang/common/utils/HttpService.java +++ b/src/main/java/com/xiang/common/utils/HttpService.java @@ -73,7 +73,7 @@ public class HttpService { CloseableHttpResponse response = null; String result = ""; try { - log.info("HTTP请求,请求地址===>{}, 请求头===>{}, 请求参数===>{}", url, JSON.toJSONString(header), jsonParams); + log.debug("HTTP请求,请求地址===>{}, 请求头===>{}, 请求参数===>{}", url, JSON.toJSONString(header), jsonParams); HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-Type", "application/json"); // 创建请求内容 @@ -88,7 +88,7 @@ public class HttpService { } response = httpClient.execute(httpPost); result = EntityUtils.toString(response.getEntity(), "utf-8"); - log.info("【POST请求】 请求地址===>{}, 响应结果==={}", url, result); + log.debug("【POST请求】 请求地址===>{}, 响应结果==={}", url, result); } catch (Exception e) { log.error("doPost异常", e); } finally { diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 3b51ac3..f7a5de3 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -2,7 +2,7 @@ - + ${APP_NAME} -- 2.49.1 From b4ed93171cf9ec722b4091953cf14e5ec53d4eac Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 15:42:21 +0800 Subject: [PATCH 31/32] =?UTF-8?q?feat:=E6=97=A5=E5=BF=97=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/xiang/common/utils/HttpService.java | 4 ++-- src/main/resources/logback-spring.xml | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/xiang/common/utils/HttpService.java b/src/main/java/com/xiang/common/utils/HttpService.java index afe8f8e..1edfa97 100644 --- a/src/main/java/com/xiang/common/utils/HttpService.java +++ b/src/main/java/com/xiang/common/utils/HttpService.java @@ -117,10 +117,10 @@ public class HttpService { httpGet.setHeader(entry.getKey(), entry.getValue()); } } - log.info("doGet请求:请求头:{},请求地址:{}", header, url + request); + log.debug("doGet请求:请求头:{},请求地址:{}", header, url + request); response = httpClient.execute(httpGet); result = EntityUtils.toString(response.getEntity(), "utf-8"); - log.info("【GET请求】, 请求地址===>{}, 响应结果===>{}", url + request, result); + log.debug("【GET请求】, 请求地址===>{}, 响应结果===>{}", url + request, result); } catch (Exception e) { log.error("doGet异常:", e); } finally { diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index f7a5de3..6ed7596 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -65,6 +65,23 @@ + + + ${LOG_HOME}/warn-%d{yyyy-MM-dd}.log + ${LOG_MAX_HISTORY} + + + %contextName: %d{yyyy-MM-dd HH:mm:ss.SSS} [%c][%t][%L][%p] [traceId:%X{traceId:-},spanId:%X{spanId:-},localIp:%X{localIp:-}] - %msg%n + UTF-8 + + + warn + ACCEPT + DENY + + + + @@ -90,12 +107,12 @@ + - - + -- 2.49.1 From 0f7af3078970ab106b9c22232edc6f0ca5fd8392 Mon Sep 17 00:00:00 2001 From: Xiang Date: Sat, 9 May 2026 16:43:55 +0800 Subject: [PATCH 32/32] =?UTF-8?q?feat:zlb=E5=9C=BA=E5=9C=B0=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jntyzx/miniapp/utils/VenueInfoUtils.java | 19 +++++++++++++++++++ .../zlb/schedule/ZlbUserConfigTask.java | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java index 8d73cf8..f3310b8 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java +++ b/src/main/java/com/xiang/service/module/jntyzx/miniapp/utils/VenueInfoUtils.java @@ -56,6 +56,25 @@ public class VenueInfoUtils { return 5; } + public static int sortVenueInfo4Zlb(String placeName) { + if (placeName.contains("10号")) { + return 0; + } + if (placeName.contains("9号")) { + return 1; + } + if (placeName.contains("2号")) { + return 2; + } + if (placeName.contains("8号")) { + return 3; + } + if (placeName.contains("7号")) { + return 4; + } + return 5; + } + public static List filterVenueList(String siteTimeName, List venueInfoDOS) { if (StringUtils.equals(siteTimeName, "18:00")) { return venueInfoDOS.stream().filter(VenueInfoUtils::get628VenueInfo).collect(Collectors.toList()); diff --git a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java index 7683bcf..7bfce4c 100644 --- a/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java +++ b/src/main/java/com/xiang/service/module/jntyzx/zlb/schedule/ZlbUserConfigTask.java @@ -145,6 +145,6 @@ public class ZlbUserConfigTask extends BaseScheduleTaskTemplate { if (CollectionUtils.isEmpty(siteInfoList)) { return Lists.newArrayList(); } - return siteInfoList.stream().sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo(item.getPlaceName()))).collect(Collectors.toList()); + return siteInfoList.stream().sorted(Comparator.comparing(item -> VenueInfoUtils.sortVenueInfo4Zlb(item.getPlaceName()))).collect(Collectors.toList()); } } -- 2.49.1