Commit 1cd609fa authored by wangfugui's avatar wangfugui

卡组自动同步功能api修改

parent 8b4221a8
package cn.garymb.ygomobile.deck_square;
import static cn.garymb.ygomobile.deck_square.DeckSquareFileUtil.convertToUnixTimestamp;
import android.widget.Toast;
import com.google.gson.Gson;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -29,6 +33,8 @@ import cn.garymb.ygomobile.deck_square.api_response.PushMultiDeck;
import cn.garymb.ygomobile.deck_square.api_response.PushSingleDeck;
import cn.garymb.ygomobile.deck_square.api_response.SquareDeckResponse;
import cn.garymb.ygomobile.deck_square.api_response.SyncDecksResponse;
import cn.garymb.ygomobile.deck_square.bo.MyDeckItem;
import cn.garymb.ygomobile.deck_square.bo.SyncMutliDeckResult;
import cn.garymb.ygomobile.ui.plus.VUiKit;
import cn.garymb.ygomobile.utils.LogUtil;
import cn.garymb.ygomobile.utils.OkhttpUtil;
......@@ -256,6 +262,8 @@ public class DeckSquareApiUtil {
/**
* 批量同步卡组到云,入参为DeckData
*
* @param deckDataList
* @param loginToken
* @return
......@@ -283,6 +291,51 @@ public class DeckSquareApiUtil {
return result;
}
/**
* 批量同步卡组到云,入参为MyDeckItem
*
* @param deckDataList
* @param loginToken
* @return
* @throws IOException
*/
public static SyncDecksResponse syncMyDecks(List<MyDeckItem> deckDataList, LoginToken loginToken) throws IOException {
SyncDecksResponse result = null;
String url = "http://rarnu.xyz:38383/api/mdpro3/sync/multi";
Map<String, String> headers = new HashMap<>();
headers.put("ReqSource", "MDPro3");
headers.put("token", loginToken.getServerToken());
/* 构造json */
List<PushMultiDeck.DeckData> dataList = new ArrayList<>();
for (MyDeckItem item : deckDataList) {
PushMultiDeck.DeckData data = new PushMultiDeck.DeckData();
data.setDeckId(item.getDeckId());
data.setDeckName(item.getDeckName());
data.setDeckCoverCard1(item.getDeckCoverCard1());
String deckContent = DeckSquareFileUtil.setDeckId(item.getDeckPath(), loginToken.getUserId(), item.getDeckId());
data.setDeckYdk(deckContent);
dataList.add(data);
}
Gson gson = new Gson();
PushMultiDeck pushMultiDeck = new PushMultiDeck();
pushMultiDeck.setDeckContributor(loginToken.getUserId().toString());
pushMultiDeck.setUserId(loginToken.getUserId());
pushMultiDeck.setDecks(dataList);
String json = gson.toJson(pushMultiDeck);
Response response = OkhttpUtil.postJson(url, json, headers, 1000);
String responseBodyString = response.body().string();
result = gson.fromJson(responseBodyString, SyncDecksResponse.class);
LogUtil.i(TAG, "push deck response:" + responseBodyString);
return result;
}
/**
* 阻塞方法,给卡组点赞
*
......@@ -425,11 +478,14 @@ public class DeckSquareApiUtil {
}
public static boolean synchronizeDecksV2() throws IOException {
public static SyncMutliDeckResult synchronizeDecksV2() throws IOException, ParseException {
SyncMutliDeckResult result = new SyncMutliDeckResult();
// 检查用户是否登录
LoginToken loginToken = DeckSquareApiUtil.getLoginData();
if (loginToken == null) {
return false;
result.setFlag(false);
result.setInfo("need login");
return result;
}
// 获取本地卡组列表
......@@ -437,7 +493,9 @@ public class DeckSquareApiUtil {
// 获取在线卡组列表
MyDeckResponse onlineDecksResponse = DeckSquareApiUtil.getUserDecks(loginToken);
if (onlineDecksResponse == null || onlineDecksResponse.getData() == null) {
return false;
result.setFlag(false);
result.setInfo("no online decks");
return result;
}
List<MyOnlineDeckDetail> onlineDecks = onlineDecksResponse.getData();
......@@ -448,10 +506,11 @@ public class DeckSquareApiUtil {
onlineDeckProcessed.put(onlineDeck.getDeckName(), false);
}
List<MyDeckItem> toUploadDecks = new ArrayList<>();
// 遍历本地卡组,处理同名卡组的情况
for (MyDeckItem localDeck : localDecks) {
String localDeckName = localDeck.getDeckName();
localDeckName = localDeckName.replace(".ydk", "");
// localDeckName = localDeckName.replace(".ydk", "");
for (MyOnlineDeckDetail onlineDeck : onlineDecks) {
......@@ -460,28 +519,38 @@ public class DeckSquareApiUtil {
onlineDeckProcessed.put(onlineDeck.getDeckName(), true);
// 比对更新时间
String localUpdateDate = localDeck.getUpdateDate();
String onlineUpdateDate = String.valueOf(0);//todo 这里应该把2025-05-19T06:11:17转成毫秒,onlineDeck.getDeckUpdateDate();
if (onlineUpdateDate != null && (localUpdateDate == null || onlineUpdateDate.compareTo(localUpdateDate) > 0)) {
long localUpdateDate = localDeck.getUpdateTimestamp();
long onlineUpdateDate = convertToUnixTimestamp(onlineDeck.getDeckUpdateDate());//todo 这里应该把2025-05-19T06:11:17转成毫秒,onlineDeck.getDeckUpdateDate();
if (onlineUpdateDate > localUpdateDate){
// 在线卡组更新时间更晚,下载在线卡组覆盖本地卡组
downloadOnlineDeck(onlineDeck, localDeck.getDeckPath());
} else {
} else{
// 本地卡组更新时间更晚,上传本地卡组覆盖在线卡组
uploadLocalDeck(localDeck, onlineDeck.getDeckId(), loginToken);
// uploadLocalDeck(localDeck, onlineDeck.getDeckId(), loginToken);
localDeck.setDeckId(onlineDeck.getDeckId());
toUploadDecks.add(localDeck);
result.toUpload.add(localDeck);
}
break;
}
}
}
SyncDecksResponse response = syncMyDecks(toUploadDecks, loginToken);
result.pushResponse = response;
// 处理只存在于在线的卡组(即本地没有同名卡组)
for (MyOnlineDeckDetail onlineDeck : onlineDecks) {
result.download.add(onlineDeck);
if (!onlineDeckProcessed.get(onlineDeck.getDeckName())) {
downloadMissingDeckToLocal(onlineDeck);
SyncMutliDeckResult.DownloadResult downloadResult = downloadMissingDeckToLocal(onlineDeck);
result.downloadResponse.add(downloadResult);
}
}
return true;
return result;
}
......@@ -573,13 +642,13 @@ public class DeckSquareApiUtil {
List<MyOnlineDeckDetail> onlineDecks;
}
private static boolean downloadMissingDeckToLocal(MyOnlineDeckDetail onlineDeck) {
private static SyncMutliDeckResult.DownloadResult downloadMissingDeckToLocal(MyOnlineDeckDetail onlineDeck) {
try {
// 根据卡组ID查询在线卡组详情
DownloadDeckResponse deckResponse = DeckSquareApiUtil.getDeckById(onlineDeck.getDeckId());
if (deckResponse == null || deckResponse.getData() == null) {
LogUtil.e(TAG, "Failed to get deck details for: " + onlineDeck.getDeckName());
return false;
return new SyncMutliDeckResult.DownloadResult(false, onlineDeck.getDeckId(), "Failed to get deck details for: " + onlineDeck.getDeckName());
}
MyOnlineDeckDetail deckDetail = deckResponse.getData();
......@@ -592,7 +661,7 @@ public class DeckSquareApiUtil {
boolean created = dir.mkdirs();
if (!created) {
LogUtil.e(TAG, "Failed to create directory: " + deckDirectory);
return false;
return new SyncMutliDeckResult.DownloadResult(false, onlineDeck.getDeckId(), "Failed to create directory: " + deckDirectory);
}
}
......@@ -608,7 +677,7 @@ public class DeckSquareApiUtil {
boolean saved = DeckSquareFileUtil.saveFileToPath(filePath, onlineDeck.getDeckName(), deckContent);
if (!saved) {
LogUtil.e(TAG, "Failed to save deck file: " + filePath);
return false;
return new SyncMutliDeckResult.DownloadResult(false, onlineDeck.getDeckId(), "Failed to save deck file: " + filePath);
}
LogUtil.i(TAG, "Deck saved to: " + filePath);
......@@ -621,11 +690,11 @@ public class DeckSquareApiUtil {
newLocalDeck.setIdUploaded(2); // 已上传状态
newLocalDeck.setUpdateDate(onlineDeck.getDeckUpdateDate());
return true;
return new SyncMutliDeckResult.DownloadResult(true, onlineDeck.getDeckId());
} catch (Exception e) {
LogUtil.e(TAG, "Error downloading missing deck: " + e.getMessage());
e.printStackTrace();
return false;
return new SyncMutliDeckResult.DownloadResult(false, onlineDeck.getDeckId(), "Error downloading missing deck: " + e.getMessage());
}
}
......@@ -657,6 +726,14 @@ public class DeckSquareApiUtil {
}
}
/**
* 将MyDeckItem调用单卡组同步接口推送到服务器
*
* @param localDeck
* @param onlineDeckId
* @param loginToken
* @return
*/
private static boolean uploadLocalDeck(MyDeckItem localDeck, String onlineDeckId, LoginToken loginToken) {
try {
DeckFile deckFile = new DeckFile(localDeck.getDeckPath(), DeckType.ServerType.MY_SQUARE);
......
......@@ -7,12 +7,17 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import cn.garymb.ygomobile.AppsSettings;
import cn.garymb.ygomobile.Constants;
import cn.garymb.ygomobile.deck_square.bo.MyDeckItem;
import cn.garymb.ygomobile.utils.IOUtils;
import cn.garymb.ygomobile.utils.LogUtil;
import ocgcore.CardManager;
......@@ -97,18 +102,16 @@ public class DeckSquareFileUtil {
for (File file : files) {
String deckId = getId(file);
MyDeckItem item = new MyDeckItem();
item.deckName = file.getName();
item.setDeckName(file.getName());
item.setUpdateDate(String.valueOf(file.lastModified()));
item.setDeckSouce(0);
item.setUpdateTimestamp(file.lastModified());
item.setDeckPath(file.getPath());
if (deckId != null) {
item.deckId = deckId;
item.idUploaded = 2;
item.setDeckId(deckId);
item.setIdUploaded(2);
} else {
item.idUploaded = 0;
item.setIdUploaded(0);
}
result.add(item);
}
......@@ -168,20 +171,32 @@ public class DeckSquareFileUtil {
}
public static boolean saveFileToPath(String path, String fileName, String content) {
FileOutputStream fos = null;
try {
// Create file object
File file = new File(path, fileName);
// Create file output stream
FileOutputStream fos = new FileOutputStream(file);
fos = new FileOutputStream(file);
// Write content
fos.write(content.getBytes());
fos.flush();
fos.close();
} catch (IOException e) {
// Update timestamp (works on all Android versions)
if (!file.setLastModified(System.currentTimeMillis())) {
LogUtil.w(TAG, "Timestamp update failed for: " + file);
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
}
return true;
}
......@@ -210,4 +225,15 @@ public class DeckSquareFileUtil {
}
return cardList;
}
public static long convertToUnixTimestamp(String dateTimeStr) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
sdf.setLenient(false);
Date date = sdf.parse(dateTimeStr);
return date.getTime();
}
}
......@@ -8,6 +8,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
......@@ -123,9 +124,12 @@ public class DeckSquareMyDeckFragment extends Fragment {
return DeckSquareApiUtil.synchronizeDecksV2();
}).fail((e) -> {
LogUtil.i(TAG, "Like deck fail" + e.getMessage());
YGOUtil.showTextToast("Sync decks fail", Toast.LENGTH_LONG);
LogUtil.i(TAG, "Sync decks fail" + e.getMessage());
}).done((result) -> {
String info = "sync decks: upload " + result.toUpload.size() + ", download " + result.download.size();
YGOUtil.showTextToast(info, Toast.LENGTH_LONG);
});
return binding.getRoot();
......@@ -209,9 +213,11 @@ public class DeckSquareMyDeckFragment extends Fragment {
return DeckSquareApiUtil.synchronizeDecksV2();
}).fail((e) -> {
LogUtil.i(TAG, "Like deck fail" + e.getMessage());
YGOUtil.showTextToast("Sync decks fail", Toast.LENGTH_LONG);
LogUtil.i(TAG, "Sync decks fail" + e.getMessage());
}).done((result) -> {
String info = "sync decks: upload " + result.toUpload.size() + ", download " + result.download.size();
YGOUtil.showTextToast(info, Toast.LENGTH_LONG);
});
//DeckSquareApiUtil.synchronizeDecks();
......
......@@ -16,6 +16,7 @@ import cn.garymb.ygomobile.deck_square.api_response.LoginToken;
import cn.garymb.ygomobile.deck_square.api_response.MyDeckResponse;
import cn.garymb.ygomobile.deck_square.api_response.MyOnlineDeckDetail;
import cn.garymb.ygomobile.deck_square.api_response.PushDeckResponse;
import cn.garymb.ygomobile.deck_square.bo.MyDeckItem;
import cn.garymb.ygomobile.lite.R;
import cn.garymb.ygomobile.loader.ImageLoader;
import cn.garymb.ygomobile.ui.plus.DialogPlus;
......@@ -74,7 +75,6 @@ public class MyDeckListAdapter extends BaseQuickAdapter<MyDeckItem, BaseViewHold
for (MyOnlineDeckDetail detail : serverDecks) {
MyDeckItem item = new MyDeckItem();
item.setDeckName(detail.getDeckName());
item.setDeckSouce(1);
item.setDeckId(detail.getDeckId());
item.setUserId(detail.getUserId());
item.setDeckCoverCard1(detail.getDeckCoverCard1());
......
package cn.garymb.ygomobile.deck_square;
package cn.garymb.ygomobile.deck_square.bo;
import android.os.Parcel;
import android.os.Parcelable;
......
package cn.garymb.ygomobile.deck_square;
package cn.garymb.ygomobile.deck_square.bo;
public class MyDeckItem {
//0代表未推到服务器,3代表包含deckId,1代表服务器存在可下载到本地,2代表已同步
public int idUploaded;
private int idUploaded;
public int userId;
public String deckName;
private int userId;
private String deckName;
public String deckId;
private String deckId;
public String updateDate;
private String updateDate;
private long updateTimestamp;
public int deckSouce;//卡组来源,0代表来自本地,1代表来自服务器
private String deckPath;//本地卡组时,存储卡组路径
public String deckPath;//本地卡组时,存储卡组路径
private int deckCoverCard1;
public int deckCoverCard1;
public Boolean isPublic;
private Boolean isPublic;
public int getIdUploaded() {
return idUploaded;
......@@ -59,12 +58,12 @@ public class MyDeckItem {
this.updateDate = updateDate;
}
public int getDeckSouce() {
return deckSouce;
public long getUpdateTimestamp() {
return updateTimestamp;
}
public void setDeckSouce(int deckSouce) {
this.deckSouce = deckSouce;
public void setUpdateTimestamp(long updateTimestamp) {
this.updateTimestamp = updateTimestamp;
}
public int getDeckCoverCard1() {
......@@ -100,7 +99,7 @@ public class MyDeckItem {
", deckName='" + deckName + '\'' +
", deckId='" + deckId + '\'' +
", updateDate='" + updateDate + '\'' +
", deckSouce=" + deckSouce +
", updateTimestamp=" + updateTimestamp +
", deckPath='" + deckPath + '\'' +
", deckCoverCard1=" + deckCoverCard1 +
", isPublic=" + isPublic +
......
package cn.garymb.ygomobile.deck_square.bo;
import java.util.ArrayList;
import java.util.List;
import cn.garymb.ygomobile.deck_square.api_response.MyOnlineDeckDetail;
import cn.garymb.ygomobile.deck_square.api_response.SyncDecksResponse;
public class SyncMutliDeckResult {
boolean flag = false;
String info = null;
public SyncDecksResponse pushResponse;
public List<MyDeckItem> toUpload;//用于记录已推送的卡组
public List<MyOnlineDeckDetail> download;
public List<DownloadResult> downloadResponse;
public static class DownloadResult {
boolean flag;
String deckId;
String info;
public DownloadResult(boolean flag, String deckId) {
this.flag = flag;
this.deckId = deckId;
}
public DownloadResult(boolean flag, String deckId, String info) {
this.flag = flag;
this.deckId = deckId;
this.info = info;
}
}
public SyncMutliDeckResult() {
flag = true;
downloadResponse = new ArrayList<>();
download = new ArrayList<>();
toUpload = new ArrayList<>();
}
public SyncMutliDeckResult(boolean flag, String info) {
this.flag = flag;
this.info = info;
downloadResponse = new ArrayList<>();
download = new ArrayList<>();
toUpload = new ArrayList<>();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
package cn.garymb.ygomobile.deck_square;
package cn.garymb.ygomobile.deck_square.bo;
import java.util.List;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment