对接3方系统,开通新人特权

This commit is contained in:
2026-05-16 15:40:17 +08:00
parent 3e753ba8b2
commit 74ac62b1e0
4 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
package com.rj.dto.hxr;
/** hxrd 后台部分写接口通用 {@code code/msg} 响应。 */
public record HxrSimpleApiResponse(int code, String msg) {}

View File

@@ -0,0 +1,6 @@
package com.rj.dto.hxr;
/**
* hxrd 后台 {@code /app/admin/user/select} 列表项(仅声明更新所需字段,其余字段忽略)。
*/
public record HxrUserRow(Long id) {}

View File

@@ -0,0 +1,6 @@
package com.rj.dto.hxr;
import java.util.List;
/** hxrd 后台 {@code /app/admin/user/select} 顶层 JSON。 */
public record HxrUserSelectResponse(int code, String msg, int count, List<HxrUserRow> data) {}

View File

@@ -0,0 +1,201 @@
package com.rj.service;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.rj.config.HxrAdminProperties;
import com.rj.dto.hxr.HxrSimpleApiResponse;
import com.rj.dto.hxr.HxrUserRow;
import com.rj.dto.hxr.HxrUserSelectResponse;
import com.rj.util.IsoWorkdayUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
/**
* 调用 hxrd 后台用户查询、更新接口(与 {@link HxrAdminOrderSelectService} 共用 {@link HxrAdminProperties} 会话)。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class HxrAdminUserService {
private static final ObjectMapper JSON = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private static final DateTimeFormatter VIP_TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final HxrAdminProperties properties;
/**
* VIP 到期时间:以 {@code applyDate} 为起算日,若当天为周六、周日则先顺延到下一工作日,再顺延 2 个工作日;
* 时刻与调用时「当前时刻」的时分秒相同(仅调整日期)。
*/
public static LocalDateTime computeVipExpireTime(LocalDate applyDate) {
if (applyDate == null) {
throw new IllegalArgumentException("applyDate不能为空");
}
LocalDateTime now = LocalDateTime.now();
LocalDate d = applyDate;
while (IsoWorkdayUtils.isWeekend(d)) {
d = d.plusDays(1);
}
for (int i = 0; i < 2; i++) {
d = d.plusDays(1);
while (IsoWorkdayUtils.isWeekend(d)) {
d = d.plusDays(1);
}
}
return LocalDateTime.of(d, now.toLocalTime());
}
/**
* 调用第三方接口
* 按手机号查询用户列表,返回第一条。
*/
public Optional<HxrUserRow> fetchFirstUserByMobile(String mobile) throws Exception {
String cookieHeader = properties.resolveCookieHeader();
if (cookieHeader == null || cookieHeader.isBlank()) {
log.warn("hxr.admin 未配置 cookie 或 phpsid跳过 user/select");
return Optional.empty();
}
if (mobile == null || mobile.isBlank()) {
return Optional.empty();
}
String uri = UriComponentsBuilder.fromUriString(properties.getUserSelectUrl())
.replaceQueryParam("mobile", mobile.trim())
.replaceQueryParam("id", "")
.replaceQueryParam("pid", "")
.build()
.encode()
.toUriString();
HttpClient client = HttpClient.newBuilder()
.connectTimeout(java.time.Duration.ofSeconds(35))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.timeout(java.time.Duration.ofSeconds(120))
.header("Accept", "application/json, text/javascript, */*; q=0.01")
.header("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
.header("Cookie", cookieHeader)
.header("Priority", "u=1, i")
.header("Referer", "https://hxrdhoutai.hxrdsm.cn/app/admin/user/index")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0")
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
int status = response.statusCode();
if (status < 200 || status >= 300) {
log.warn("hxr admin user/select HTTP {} mobile={} bodyPrefix={}",
status, mobile, abbreviate(response.body(), 400));
return Optional.empty();
}
HxrUserSelectResponse body = JSON.readValue(response.body(), HxrUserSelectResponse.class);
if (body.code() != 0) {
log.warn("hxr admin user/select api code={} msg={} mobile={}", body.code(), body.msg(), mobile);
return Optional.empty();
}
List<HxrUserRow> data = body.data();
if (data == null || data.isEmpty()) {
return Optional.empty();
}
HxrUserRow first = data.get(0);
if (first.id() == null) {
return Optional.empty();
}
return Optional.of(first);
}
/**
* 更新用户 VIP 相关字段({@code application/x-www-form-urlencoded})。
*
* @return 接口 {@code code==0} 时为 true
*/
public boolean updateUserVipFields(long userId, int maxOrder, String viptimeFormatted) throws Exception {
String cookieHeader = properties.resolveCookieHeader();
if (cookieHeader == null || cookieHeader.isBlank()) {
log.warn("hxr.admin 未配置 cookie 或 phpsid跳过 user/update");
return false;
}
String form = buildUpdateFormBody(userId, maxOrder, viptimeFormatted);
HttpClient client = HttpClient.newBuilder()
.connectTimeout(java.time.Duration.ofSeconds(35))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(properties.getUserUpdateUrl()))
.timeout(java.time.Duration.ofSeconds(120))
.header("Accept", "application/json, text/javascript, */*; q=0.01")
.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
.header("Cookie", cookieHeader)
.header("Referer", "https://hxrdhoutai.hxrdsm.cn/app/admin/user/index")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0")
.POST(HttpRequest.BodyPublishers.ofString(form, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
int status = response.statusCode();
if (status < 200 || status >= 300) {
log.warn("hxr admin user/update HTTP {} id={} bodyPrefix={}",
status, userId, abbreviate(response.body(), 400));
return false;
}
try {
HxrSimpleApiResponse api = JSON.readValue(response.body(), HxrSimpleApiResponse.class);
if (api.code() != 0) {
log.warn("hxr admin user/update api code={} msg={} id={}", api.code(), api.msg(), userId);
return false;
}
return true;
} catch (Exception e) {
log.warn("hxr admin user/update parse body id={} err={} bodyPrefix={}",
userId, e.getMessage(), abbreviate(response.body(), 400));
return false;
}
}
public static String formatVipTime(LocalDateTime t) {
return t.format(VIP_TIME_FORMAT);
}
private static String buildUpdateFormBody(long userId, int maxOrder, String viptimeFormatted) {
return "id=" + userId
+ "&max_order=" + maxOrder
+ "&is_vip=1"
+ "&is_resell=1"
+ "&status=1"
+ "&viptime=" + URLEncoder.encode(viptimeFormatted, StandardCharsets.UTF_8);
}
private static String abbreviate(String s, int maxLen) {
if (s == null) {
return "";
}
return s.length() <= maxLen ? s : s.substring(0, maxLen) + "...";
}
}