|
@@ -0,0 +1,297 @@
|
|
|
+package com.sxtvs.open.api.odata.service;
|
|
|
+
|
|
|
+import cn.hutool.core.date.DateField;
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
+import cn.hutool.http.HttpUtil;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.sxtvs.open.api.odata.dao.ToutiaoAccountMapper;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.DouyinAccessTokenResponse;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.DouyinRefreshTokenResponse;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.DouyinUserInfoResponse;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.videolist.Data;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.videolist.DouyinVideoListResponse;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.videolist.ListItem;
|
|
|
+import com.sxtvs.open.api.odata.dto.douyin.videolist.Statistics;
|
|
|
+import com.sxtvs.open.api.odata.entity.PlatformData;
|
|
|
+import com.sxtvs.open.api.odata.entity.ToutiaoAccount;
|
|
|
+import com.sxtvs.open.core.conf.Constant;
|
|
|
+import lombok.SneakyThrows;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
+import org.springframework.retry.annotation.Backoff;
|
|
|
+import org.springframework.retry.annotation.Retryable;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.client.RestClientException;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class ToutiaoAccountService extends ServiceImpl<ToutiaoAccountMapper, ToutiaoAccount> {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ @Qualifier("douyinRestTemplate")
|
|
|
+ private RestTemplate restTemplate;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PlatformDataService platformDataService;
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private QrcodeLogServiceImpl qrcodeLogService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ObjectMapper objectMapper;
|
|
|
+
|
|
|
+ @SneakyThrows
|
|
|
+ private void renewRefreshToken(ToutiaoAccount toutiaoAccount) {
|
|
|
+
|
|
|
+ HashMap<String, Object> param = new HashMap<String, Object>() {{
|
|
|
+ this.put("client_key", Constant.douyinClientKey);
|
|
|
+ this.put("refresh_token", toutiaoAccount.getRefreshToken());
|
|
|
+ }};
|
|
|
+
|
|
|
+ String body = HttpUtil.get("https://open.snssdk.com/oauth/renew_refresh_token/", param);
|
|
|
+ String refreshToken;
|
|
|
+ try {
|
|
|
+ log.info("renew_refresh_token {}", body);
|
|
|
+ DouyinRefreshTokenResponse douyinRefreshTokenResponse = objectMapper.readValue(body, DouyinRefreshTokenResponse.class);
|
|
|
+ refreshToken = douyinRefreshTokenResponse.getData().getRefreshToken();
|
|
|
+ if (refreshToken == null) {
|
|
|
+ log.error("{}", toutiaoAccount.getNickName());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ DateTime now = new DateTime();
|
|
|
+ toutiaoAccount.setRefreshToken(refreshToken);
|
|
|
+ toutiaoAccount.setRtExpireTime(DateUtil.offsetDay(now, 30));
|
|
|
+ toutiaoAccount.setUpdateTime(now.toJdkDate());
|
|
|
+ this.updateById(toutiaoAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+ @SneakyThrows
|
|
|
+ private void renewAccessToken(ToutiaoAccount toutiaoAccount) {
|
|
|
+ HashMap<String, Object> param = new HashMap<String, Object>() {{
|
|
|
+ this.put("client_key", Constant.douyinClientKey);
|
|
|
+ this.put("grant_type", "refresh_token");
|
|
|
+ this.put("refresh_token", toutiaoAccount.getRefreshToken());
|
|
|
+ }};
|
|
|
+ String body = HttpUtil.get("https://open.snssdk.com/oauth/refresh_token/", param);
|
|
|
+ String accessToken;
|
|
|
+ try {
|
|
|
+ DouyinAccessTokenResponse tokenResponse = objectMapper.readValue(body, DouyinAccessTokenResponse.class);
|
|
|
+ accessToken = tokenResponse.getData().getAccessToken();
|
|
|
+ if (accessToken == null) {
|
|
|
+ log.error("{} {}", toutiaoAccount.getNickName(), body);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ DateTime now = new DateTime();
|
|
|
+ toutiaoAccount.setAccessToken(accessToken);
|
|
|
+ toutiaoAccount.setAtExpireTime(DateUtil.offsetDay(now, 15));
|
|
|
+ toutiaoAccount.setUpdateTime(now.toJdkDate());
|
|
|
+ this.updateById(toutiaoAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Retryable(value = RestClientException.class,
|
|
|
+ backoff = @Backoff(delay = 5000L, multiplier = 2))
|
|
|
+ public ToutiaoAccount upsert(String code) {
|
|
|
+ HashMap<String, String> param = new HashMap<String, String>() {{
|
|
|
+ this.put("client_key", Constant.douyinClientKey);
|
|
|
+ this.put("client_secret", Constant.douyinClientSecret);
|
|
|
+ this.put("code", code);
|
|
|
+ }};
|
|
|
+ DouyinAccessTokenResponse douyinAccessTokenResponse = restTemplate.getForObject(
|
|
|
+ "https://open.snssdk.com/oauth/access_token/?" +
|
|
|
+ "client_key={client_key}&client_secret={client_secret}&" +
|
|
|
+ "grant_type=authorization_code&code={code}",
|
|
|
+ DouyinAccessTokenResponse.class, param);
|
|
|
+ DouyinAccessTokenResponse.Data data = douyinAccessTokenResponse.getData();
|
|
|
+ String openId = data.getOpenId();
|
|
|
+ String accessToken = data.getAccessToken();
|
|
|
+ String refreshToken = data.getRefreshToken();
|
|
|
+
|
|
|
+ param.clear();
|
|
|
+ param.put("access_token", accessToken);
|
|
|
+ param.put("open_id", openId);
|
|
|
+ DouyinUserInfoResponse douyinUserInfoResponse = restTemplate.getForObject("https://open.snssdk.com/oauth/userinfo/?" +
|
|
|
+ "access_token={access_token}&open_id={open_id}",
|
|
|
+ DouyinUserInfoResponse.class, param);
|
|
|
+
|
|
|
+
|
|
|
+ ToutiaoAccount toutiaoAccount = this.getById(openId);
|
|
|
+
|
|
|
+ DateTime now = new DateTime();
|
|
|
+ log.info("getById toutiaoAccount {} ", toutiaoAccount);
|
|
|
+ String nickname = douyinUserInfoResponse.getData().getNickname();
|
|
|
+
|
|
|
+ qrcodeLogService.writeLog("头条", openId, nickname);
|
|
|
+ if (toutiaoAccount == null) {
|
|
|
+ toutiaoAccount = ToutiaoAccount.builder()
|
|
|
+ .openId(openId)
|
|
|
+ .atExpireTime(DateUtil.offsetDay(now, 15))
|
|
|
+ .rtExpireTime(DateUtil.offsetDay(now, 30))
|
|
|
+ .accessToken(accessToken)
|
|
|
+ .refreshToken(refreshToken)
|
|
|
+ .nickName(nickname)
|
|
|
+ .updateTime(now.toJdkDate())
|
|
|
+ .createTime(now.toJdkDate())
|
|
|
+ .build();
|
|
|
+ this.save(toutiaoAccount);
|
|
|
+ log.info("{} 新增成功", nickname);
|
|
|
+ } else {
|
|
|
+ toutiaoAccount.setAtExpireTime(DateUtil.offsetDay(now, 15));
|
|
|
+ toutiaoAccount.setRtExpireTime(DateUtil.offsetDay(now, 30));
|
|
|
+ toutiaoAccount.setAccessToken(accessToken);
|
|
|
+ toutiaoAccount.setRefreshToken(refreshToken);
|
|
|
+ toutiaoAccount.setNickName(nickname);
|
|
|
+ toutiaoAccount.setUpdateTime(now.toJdkDate());
|
|
|
+ this.updateById(toutiaoAccount);
|
|
|
+ log.info("toutiaoAccount {} 更新成功", toutiaoAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return toutiaoAccount;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Retryable(value = RestClientException.class,
|
|
|
+ backoff = @Backoff(delay = 5000L, multiplier = 2))
|
|
|
+ public Data videoList(String openId, String cursor) {
|
|
|
+ ToutiaoAccount toutiaoAccount = this.getById(openId);
|
|
|
+ HashMap<String, String> param = new HashMap<String, String>() {{
|
|
|
+ this.put("open_id", toutiaoAccount.getOpenId());
|
|
|
+ this.put("access_token", toutiaoAccount.getAccessToken());
|
|
|
+ this.put("cursor", cursor);
|
|
|
+ this.put("count", "10");
|
|
|
+ }};
|
|
|
+ DouyinVideoListResponse douyinVideoListResponse = restTemplate.getForObject(
|
|
|
+ "https://open.douyin.com/toutiao/video/list/?open_id={open_id}&access_token={access_token}&" +
|
|
|
+ "cursor={cursor}&count={count}",
|
|
|
+ DouyinVideoListResponse.class, param);
|
|
|
+ if (douyinVideoListResponse == null) {
|
|
|
+ throw new RuntimeException();
|
|
|
+ }
|
|
|
+
|
|
|
+ return douyinVideoListResponse.getData();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Retryable(value = RestClientException.class,
|
|
|
+ backoff = @Backoff(delay = 5000L, multiplier = 2))
|
|
|
+ public void refresh() {
|
|
|
+ Date rtExpireTime = new DateTime()
|
|
|
+ .offset(DateField.DAY_OF_YEAR, 7)
|
|
|
+ .toJdkDate();
|
|
|
+ Date expireTime = new DateTime()
|
|
|
+ .offset(DateField.DAY_OF_YEAR, 1)
|
|
|
+ .offset(DateField.HOUR, 12)
|
|
|
+ .toJdkDate();
|
|
|
+
|
|
|
+
|
|
|
+ //检测快过期的rt
|
|
|
+ for (ToutiaoAccount douyinAccount : this.lambdaQuery()
|
|
|
+ .le(ToutiaoAccount::getRtExpireTime, rtExpireTime)
|
|
|
+ .list()) {
|
|
|
+ renewRefreshToken(douyinAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+ //检测快过期的at
|
|
|
+ for (ToutiaoAccount douyinAccount : this.lambdaQuery()
|
|
|
+ .le(ToutiaoAccount::getAtExpireTime, expireTime)
|
|
|
+ .list()) {
|
|
|
+ renewAccessToken(douyinAccount);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void refreshTest() {
|
|
|
+ for (ToutiaoAccount toutiaoAccount : this.lambdaQuery()
|
|
|
+ .eq(ToutiaoAccount::getOpenId, "b1deadcf-7f0e-49dc-9a99-dd290e344070")
|
|
|
+ .list()) {
|
|
|
+ renewRefreshToken(toutiaoAccount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void saveAllAccountData() {
|
|
|
+ List<ToutiaoAccount> toutiaoAccounts = list();
|
|
|
+ String dt = new DateTime().toString("yyyyMMdd");
|
|
|
+
|
|
|
+ for (ToutiaoAccount toutiaoAccount : toutiaoAccounts) {
|
|
|
+ try {
|
|
|
+ saveDouyinData(dt, toutiaoAccount);
|
|
|
+ } catch (Exception e) {
|
|
|
+ String nickName = toutiaoAccount.getNickName();
|
|
|
+ log.error(nickName + " error", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void saveDouyinData(String dt, ToutiaoAccount toutiaoAccount) {
|
|
|
+ String openId = toutiaoAccount.getOpenId();
|
|
|
+ Data data = videoList(openId, "0");
|
|
|
+ if (data.getList() == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<ListItem> res = new ArrayList<>(data.getList());
|
|
|
+ while (data.getHasMore()) {
|
|
|
+ data = videoList(openId, String.valueOf(data.getCursor()));
|
|
|
+ if (data.getList() != null) {
|
|
|
+ res.addAll(data.getList());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<PlatformData> dataList = res.stream()
|
|
|
+ .map(item -> {
|
|
|
+ PlatformData platformData = new PlatformData();
|
|
|
+
|
|
|
+ platformData.setId(item.getItemId() + dt);
|
|
|
+ Date publishTime = new Date(item.getCreateTime() * 1000);
|
|
|
+ log.info("{} {}", publishTime, item.getCreateTime());
|
|
|
+ platformData.setPublishTime(publishTime);
|
|
|
+ platformData.setNickName(toutiaoAccount.getNickName());
|
|
|
+ platformData.setItemId(item.getItemId());
|
|
|
+ platformData.setDt(dt);
|
|
|
+ platformData.setTitle(item.getTitle());
|
|
|
+ platformData.setOpenId(openId);
|
|
|
+ Statistics statistics = item.getStatistics();
|
|
|
+ platformData.setCommentCount(statistics.getCommentCount());
|
|
|
+ platformData.setDiggCount(statistics.getDiggCount());
|
|
|
+ platformData.setDownloadCount(statistics.getDownloadCount());
|
|
|
+ platformData.setForwardCount(statistics.getForwardCount());
|
|
|
+ platformData.setPlayCount(statistics.getPlayCount());
|
|
|
+ platformData.setShareCount(statistics.getShareCount());
|
|
|
+ platformData.setPlatform("toutiao");
|
|
|
+ return platformData;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ platformDataService.removeByIds(dataList.stream().map(PlatformData::getId)
|
|
|
+ .collect(Collectors.toList()));
|
|
|
+
|
|
|
+ platformDataService.saveBatch(dataList);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ System.out.println(new Date(1618625930L * 1000));
|
|
|
+ }
|
|
|
+
|
|
|
+}
|