add apple inventory monitor schedule

This commit is contained in:
JiyangTang 2023-10-13 17:31:17 +08:00
parent 8f6ae40287
commit 2919d50eb6
4 changed files with 710 additions and 0 deletions

View File

@ -0,0 +1,382 @@
package top.baogutang.admin.domain;
public class AppleInventoryPushDetails {
private String title;
private String body;
private String level;
private String badge;
private String autoCopy;
private String copy;
private String sound;
private String icon;
private String group;
private String isArchive;
private String category;
AppleInventoryPushDetails(String title, String body, String level, String badge, String autoCopy, String copy, String sound, String icon, String group, String isArchive, String category) {
this.title = title;
this.body = body;
this.level = level;
this.badge = badge;
this.autoCopy = autoCopy;
this.copy = copy;
this.sound = sound;
this.icon = icon;
this.group = group;
this.isArchive = isArchive;
this.category = category;
}
public static PushDetailsBuilder builder() {
return new PushDetailsBuilder();
}
public String getTitle() {
return this.title;
}
public String getBody() {
return this.body;
}
public String getLevel() {
return this.level;
}
public String getBadge() {
return this.badge;
}
public String getAutoCopy() {
return this.autoCopy;
}
public String getCopy() {
return this.copy;
}
public String getSound() {
return this.sound;
}
public String getIcon() {
return this.icon;
}
public String getGroup() {
return this.group;
}
public String getIsArchive() {
return this.isArchive;
}
public String getCategory() {
return this.category;
}
public void setTitle(String title) {
this.title = title;
}
public void setBody(String body) {
this.body = body;
}
public void setLevel(String level) {
this.level = level;
}
public void setBadge(String badge) {
this.badge = badge;
}
public void setAutoCopy(String autoCopy) {
this.autoCopy = autoCopy;
}
public void setCopy(String copy) {
this.copy = copy;
}
public void setSound(String sound) {
this.sound = sound;
}
public void setIcon(String icon) {
this.icon = icon;
}
public void setGroup(String group) {
this.group = group;
}
public void setIsArchive(String isArchive) {
this.isArchive = isArchive;
}
public void setCategory(String category) {
this.category = category;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof AppleInventoryPushDetails)) {
return false;
} else {
AppleInventoryPushDetails other = (AppleInventoryPushDetails)o;
if (!other.canEqual(this)) {
return false;
} else {
label143: {
Object this$title = this.getTitle();
Object other$title = other.getTitle();
if (this$title == null) {
if (other$title == null) {
break label143;
}
} else if (this$title.equals(other$title)) {
break label143;
}
return false;
}
Object this$body = this.getBody();
Object other$body = other.getBody();
if (this$body == null) {
if (other$body != null) {
return false;
}
} else if (!this$body.equals(other$body)) {
return false;
}
Object this$level = this.getLevel();
Object other$level = other.getLevel();
if (this$level == null) {
if (other$level != null) {
return false;
}
} else if (!this$level.equals(other$level)) {
return false;
}
label122: {
Object this$badge = this.getBadge();
Object other$badge = other.getBadge();
if (this$badge == null) {
if (other$badge == null) {
break label122;
}
} else if (this$badge.equals(other$badge)) {
break label122;
}
return false;
}
label115: {
Object this$autoCopy = this.getAutoCopy();
Object other$autoCopy = other.getAutoCopy();
if (this$autoCopy == null) {
if (other$autoCopy == null) {
break label115;
}
} else if (this$autoCopy.equals(other$autoCopy)) {
break label115;
}
return false;
}
Object this$copy = this.getCopy();
Object other$copy = other.getCopy();
if (this$copy == null) {
if (other$copy != null) {
return false;
}
} else if (!this$copy.equals(other$copy)) {
return false;
}
Object this$sound = this.getSound();
Object other$sound = other.getSound();
if (this$sound == null) {
if (other$sound != null) {
return false;
}
} else if (!this$sound.equals(other$sound)) {
return false;
}
label94: {
Object this$icon = this.getIcon();
Object other$icon = other.getIcon();
if (this$icon == null) {
if (other$icon == null) {
break label94;
}
} else if (this$icon.equals(other$icon)) {
break label94;
}
return false;
}
label87: {
Object this$group = this.getGroup();
Object other$group = other.getGroup();
if (this$group == null) {
if (other$group == null) {
break label87;
}
} else if (this$group.equals(other$group)) {
break label87;
}
return false;
}
Object this$isArchive = this.getIsArchive();
Object other$isArchive = other.getIsArchive();
if (this$isArchive == null) {
if (other$isArchive != null) {
return false;
}
} else if (!this$isArchive.equals(other$isArchive)) {
return false;
}
Object this$category = this.getCategory();
Object other$category = other.getCategory();
if (this$category == null) {
if (other$category != null) {
return false;
}
} else if (!this$category.equals(other$category)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof AppleInventoryPushDetails;
}
public int hashCode() {
int result = 1;
Object $title = this.getTitle();
result = result * 59 + ($title == null ? 43 : $title.hashCode());
Object $body = this.getBody();
result = result * 59 + ($body == null ? 43 : $body.hashCode());
Object $level = this.getLevel();
result = result * 59 + ($level == null ? 43 : $level.hashCode());
Object $badge = this.getBadge();
result = result * 59 + ($badge == null ? 43 : $badge.hashCode());
Object $autoCopy = this.getAutoCopy();
result = result * 59 + ($autoCopy == null ? 43 : $autoCopy.hashCode());
Object $copy = this.getCopy();
result = result * 59 + ($copy == null ? 43 : $copy.hashCode());
Object $sound = this.getSound();
result = result * 59 + ($sound == null ? 43 : $sound.hashCode());
Object $icon = this.getIcon();
result = result * 59 + ($icon == null ? 43 : $icon.hashCode());
Object $group = this.getGroup();
result = result * 59 + ($group == null ? 43 : $group.hashCode());
Object $isArchive = this.getIsArchive();
result = result * 59 + ($isArchive == null ? 43 : $isArchive.hashCode());
Object $category = this.getCategory();
result = result * 59 + ($category == null ? 43 : $category.hashCode());
return result;
}
public String toString() {
String var10000 = this.getTitle();
return "PushDetails(title=" + var10000 + ", body=" + this.getBody() + ", level=" + this.getLevel() + ", badge=" + this.getBadge() + ", autoCopy=" + this.getAutoCopy() + ", copy=" + this.getCopy() + ", sound=" + this.getSound() + ", icon=" + this.getIcon() + ", group=" + this.getGroup() + ", isArchive=" + this.getIsArchive() + ", category=" + this.getCategory() + ")";
}
public static class PushDetailsBuilder {
private String title;
private String body;
private String level;
private String badge;
private String autoCopy;
private String copy;
private String sound;
private String icon;
private String group;
private String isArchive;
private String category;
PushDetailsBuilder() {
}
public PushDetailsBuilder title(String title) {
this.title = title;
return this;
}
public PushDetailsBuilder body(String body) {
this.body = body;
return this;
}
public PushDetailsBuilder level(String level) {
this.level = level;
return this;
}
public PushDetailsBuilder badge(String badge) {
this.badge = badge;
return this;
}
public PushDetailsBuilder autoCopy(String autoCopy) {
this.autoCopy = autoCopy;
return this;
}
public PushDetailsBuilder copy(String copy) {
this.copy = copy;
return this;
}
public PushDetailsBuilder sound(String sound) {
this.sound = sound;
return this;
}
public PushDetailsBuilder icon(String icon) {
this.icon = icon;
return this;
}
public PushDetailsBuilder group(String group) {
this.group = group;
return this;
}
public PushDetailsBuilder isArchive(String isArchive) {
this.isArchive = isArchive;
return this;
}
public PushDetailsBuilder category(String category) {
this.category = category;
return this;
}
public AppleInventoryPushDetails build() {
return new AppleInventoryPushDetails(this.title, this.body, this.level, this.badge, this.autoCopy, this.copy, this.sound, this.icon, this.group, this.isArchive, this.category);
}
public String toString() {
return "PushDetails.PushDetailsBuilder(title=" + this.title + ", body=" + this.body + ", level=" + this.level + ", badge=" + this.badge + ", autoCopy=" + this.autoCopy + ", copy=" + this.copy + ", sound=" + this.sound + ", icon=" + this.icon + ", group=" + this.group + ", isArchive=" + this.isArchive + ", category=" + this.category + ")";
}
}
}

View File

@ -0,0 +1,37 @@
package top.baogutang.admin.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @description:
* @author: nikooh
* @date: 2023/10/13 : 15:45
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class IphoneProductDto {
private String type;
private String model;
private String color;
private String capacity;
private String colorDisplay;
private double price;
private String priceDisplay;
private String priceCurrency;
private String carrierModel;
}

View File

@ -0,0 +1,193 @@
package top.baogutang.admin.schedule;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import top.baogutang.admin.domain.IphoneProductDto;
import top.baogutang.admin.utils.DingTalkMsgPushUtils;
import top.baogutang.admin.utils.IphoneProductParserUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @description: 苹果库存监控
* @author: nikooh
* @date: 2023/10/13 : 15:12
*/
@Slf4j
@Component
@RefreshScope
public class AppleInventorySchedule {
@Value("${baogutang.apple.country_code:cn}")
private String countryCode;
@Value("${baogutang.apple.device_code:15-pro}")
private String deviceCode;
@Value("${baogutang.apple.location:'上海 上海 闵行区'}")
private String location;
// @Value("${baogutang.apple.storeList}")
private List<String> storeList = new ArrayList<>();
@Resource
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
@Scheduled(cron = "0 0/5 * * * ? ")
public void appleInventoryMonitor() {
// 获取设备信息
List<IphoneProductDto> products = IphoneProductParserUtils.getProducts(deviceCode, countryCode);
//监视机型型号
products.forEach(product -> {
this.doMonitor(product);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(">>>>>>>>>>apple inventory monitor error:{}<<<<<<<<<<", e.getMessage(), e);
}
});
}
private void doMonitor(IphoneProductDto product) {
Map<String, Object> queryMap = new HashMap<>(5);
queryMap.put("pl", "true");
queryMap.put("mts.0", "regular");
queryMap.put("parts.0", product.getModel());
queryMap.put("location", location);
String baseUrl = String.format("https://www.apple.com.%s", countryCode);
Map<String, List<String>> headers = buildHeaders(baseUrl, deviceCode, product.getModel());
String url = baseUrl + "/shop/fulfillment-messages?" + URLUtil.buildQuery(queryMap, CharsetUtil.CHARSET_UTF_8);
try {
HttpResponse httpResponse = HttpRequest.get(url)
.header(headers)
.execute();
if (!httpResponse.isOk()) {
log.warn(">>>>>>>>>>请求可能过于频繁,请稍后再试~<<<<<<<<<<");
return;
}
JSONObject responseJsonObject = JSON.parseObject(httpResponse.body());
JSONObject pickupMessage = responseJsonObject.getJSONObject("body").getJSONObject("content").getJSONObject("pickupMessage");
JSONArray stores = pickupMessage.getJSONArray("stores");
if (stores == null) {
log.debug(pickupMessage.toString());
return;
}
if (stores.isEmpty()) {
log.info("您所在的 {} 附近没有Apple直营店请检查您的地址是否正确", location);
return;
}
StringBuilder pushContentBuilder = new StringBuilder();
List<String> availableStoreNameList = stores.stream()
.filter(store -> filterStore((JSONObject) store))
.filter(k ->
judgingStoreInventory((JSONObject) k, product.getModel()))
.map(store -> {
JSONObject storeJson = (JSONObject) store;
return storeJson.getString("storeName").trim();
})
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(availableStoreNameList)) {
return;
}
JSONObject storeJson = (JSONObject) stores.get(0);
JSONObject partsAvailability = storeJson.getJSONObject("partsAvailability");
String deviceName = partsAvailability.getJSONObject(product.getModel()).getJSONObject("messageTypes").getJSONObject("regular").getString("storePickupProductTitle");
pushContentBuilder.append("**")
.append(deviceName)
.append("**")
.append("今天可取货:")
.append("<br />");
availableStoreNameList.forEach(availableStoreName ->
pushContentBuilder.append("**")
.append(availableStoreName)
.append("** <br />"));
dingTalkMsgPushUtils.robotMarkdownMsgPush("苹果商店监控", pushContentBuilder.toString());
} catch (Exception e) {
log.error("AppleMonitor error", e);
}
}
/**
* build request headers
*
* @param baseUrl base country url
* @param productCode product code
* @return headers
*/
private Map<String, List<String>> buildHeaders(String baseUrl, String deviceCode, String productCode) {
ArrayList<String> referer = new ArrayList<>();
referer.add(baseUrl + "/shop/buy-iphone/" + deviceCode + "/" + productCode);
Map<String, List<String>> headers = new HashMap<>(10);
headers.put(Header.REFERER.getValue(), referer);
return headers;
}
private boolean filterStore(JSONObject storeInfo) {
if (CollectionUtils.isEmpty(storeList)) {
return Boolean.TRUE;
}
String storeName = storeInfo.getString("storeName");
return storeList.stream().anyMatch(k -> storeName.contains(k) || k.contains(storeName));
}
/**
* check store inventory
*
* @param storeJson store json
* @param productCode product code
* @return boolean
*/
private boolean judgingStoreInventory(JSONObject storeJson, String productCode) {
JSONObject partsAvailability = storeJson.getJSONObject("partsAvailability");
String status = partsAvailability.getJSONObject(productCode).getString("pickupDisplay");
return "available".equals(status);
}
/**
* build pickup information
*
* @param retailStore retailStore
* @return pickup message
*/
private String buildPickupInformation(JSONObject retailStore) {
String distanceWithUnit = retailStore.getString("distanceWithUnit");
String twoLineAddress = retailStore.getJSONObject("address").getString("twoLineAddress");
String daytimePhone = retailStore.getJSONObject("address").getString("daytimePhone");
String messageTemplate = "\n取货地址:{},电话:{},距离{}:{}";
return CharSequenceUtil.format(messageTemplate, twoLineAddress.replace("\n", " "), daytimePhone, location, distanceWithUnit);
}
}

View File

@ -0,0 +1,98 @@
package top.baogutang.admin.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import top.baogutang.admin.domain.IphoneProductDto;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* @description:
* @author: nikooh
* @date: 2023/10/13 : 15:47
*/
public class IphoneProductParserUtils {
public static List<IphoneProductDto> getProducts(String code, String country) {
String url = "https://www.apple.com/" + country + "/shop/buy-iphone/iphone-" + code;
String content = sendHttpRequest(url);
assert content.contains("productSelectionData");
return parseProducts(content);
}
private static String sendHttpRequest(String url) {
try {
URL urlObj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/117.0");
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static List<IphoneProductDto> parseProducts(String content) {
String selectText = null;
String[] parts = content.split("window.PRODUCT_SELECTION_BOOTSTRAP = ");
if (parts.length > 1) {
selectText = parts[1].split("</script>")[0].replace("productSelectionData", "\"productSelectionData\"");
}
if (selectText != null) {
JSONObject selectData = JSON.parseObject(selectText);
JSONObject productSelectionData = selectData.getJSONObject("productSelectionData");
JSONArray productsArray = productSelectionData.getJSONArray("products");
JSONObject displayValues = productSelectionData.getJSONObject("displayValues");
JSONObject prices = displayValues.getJSONObject("prices");
JSONObject colors = displayValues.getJSONObject("dimensionColor");
List<IphoneProductDto> products = new ArrayList<>();
for (int i = 0; i < productsArray.size(); i++) {
JSONObject productObject = productsArray.getJSONObject(i);
String priceTag = productObject.getString("fullPrice");
JSONObject priceData = prices.getJSONObject(priceTag);
IphoneProductDto product = new IphoneProductDto(
productObject.getString("familyType"),
productObject.getString("partNumber"),
productObject.getString("dimensionColor"),
productObject.getString("dimensionCapacity"),
colors.getJSONObject(productObject.getString("dimensionColor")).getString("value"),
priceData.getJSONObject("currentPrice").getDouble("raw_amount"),
priceData.getJSONObject("currentPrice").getString("amount"),
priceData.getString("priceCurrency"),
productObject.containsKey("carrierModel") ? productObject.getString("carrierModel") : ""
);
products.add(product);
}
return products;
}
return new ArrayList<>();
}
}