Merge pull request #61 from onerain88/leaderboard
Leaderboard
commit
45cb4a14c3
|
@ -123,6 +123,18 @@
|
||||||
<Compile Include="..\Storage\LCSMSClient.cs">
|
<Compile Include="..\Storage\LCSMSClient.cs">
|
||||||
<Link>LCSMSClient.cs</Link>
|
<Link>LCSMSClient.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="..\Storage\Leaderboard\LCLeaderboardArchive.cs">
|
||||||
|
<Link>Leaderboard\LCLeaderboardArchive.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Storage\Leaderboard\LCStatistic.cs">
|
||||||
|
<Link>Leaderboard\LCStatistic.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Storage\Leaderboard\LCLeaderboard.cs">
|
||||||
|
<Link>Leaderboard\LCLeaderboard.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="..\Storage\Leaderboard\LCRanking.cs">
|
||||||
|
<Link>Leaderboard\LCRanking.cs</Link>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
using NUnit.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
|
||||||
|
using static NUnit.Framework.TestContext;
|
||||||
|
|
||||||
|
namespace Storage.Test {
|
||||||
|
public class LeaderboardTest {
|
||||||
|
private string leaderboardName;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() {
|
||||||
|
LCLogger.LogDelegate += Utils.Print;
|
||||||
|
LCApplication.Initialize(Utils.AppId, Utils.AppKey, Utils.AppServer, Utils.MasterKey);
|
||||||
|
LCApplication.UseMasterKey = true;
|
||||||
|
leaderboardName = $"Leaderboard_{DateTimeOffset.Now.DayOfYear}";
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown() {
|
||||||
|
LCApplication.UseMasterKey = false;
|
||||||
|
LCLogger.LogDelegate -= Utils.Print;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(0)]
|
||||||
|
public async Task Create() {
|
||||||
|
try {
|
||||||
|
LCLeaderboard oldLeaderboard = LCLeaderboard.CreateWithoutData(leaderboardName);
|
||||||
|
await oldLeaderboard.Destroy();
|
||||||
|
} catch (Exception e) {
|
||||||
|
WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
LCLeaderboard leaderboard = await LCLeaderboard.CreateLeaderboard(leaderboardName);
|
||||||
|
Assert.AreEqual(leaderboard.StatisticName, leaderboardName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(1)]
|
||||||
|
public async Task Update() {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
int today = DateTimeOffset.Now.DayOfYear;
|
||||||
|
string username = $"{today}_{i}";
|
||||||
|
string password = "leancloud";
|
||||||
|
LCUser user;
|
||||||
|
try {
|
||||||
|
user = await LCUser.Login(username, password);
|
||||||
|
} catch (Exception) {
|
||||||
|
user = new LCUser {
|
||||||
|
Username = username,
|
||||||
|
Password = password
|
||||||
|
};
|
||||||
|
await user.SignUp();
|
||||||
|
}
|
||||||
|
await LCLeaderboard.UpdateStatistics(user, new Dictionary<string, double> {
|
||||||
|
{ leaderboardName, i * 10 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(2)]
|
||||||
|
public async Task GetStatistics() {
|
||||||
|
int today = DateTimeOffset.Now.DayOfYear;
|
||||||
|
string username = $"{today}_0";
|
||||||
|
string password = "leancloud";
|
||||||
|
LCUser user = await LCUser.Login(username, password);
|
||||||
|
ReadOnlyCollection<LCStatistic> statistics = await LCLeaderboard.GetStatistics(user);
|
||||||
|
foreach (LCStatistic statistic in statistics) {
|
||||||
|
WriteLine($"{statistic.Name} : {statistic.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(3)]
|
||||||
|
public async Task GetResults() {
|
||||||
|
LCLeaderboard leaderboard = LCLeaderboard.CreateWithoutData(leaderboardName);
|
||||||
|
ReadOnlyCollection<LCRanking> rankings = await leaderboard.GetResults();
|
||||||
|
foreach (LCRanking ranking in rankings) {
|
||||||
|
WriteLine($"{ranking.Rank} : {ranking.User.ObjectId}, {ranking.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(4)]
|
||||||
|
public async Task GetResultsOfMe() {
|
||||||
|
int today = DateTimeOffset.Now.DayOfYear;
|
||||||
|
string username = $"{today}_0";
|
||||||
|
string password = "leancloud";
|
||||||
|
await LCUser.Login(username, password);
|
||||||
|
LCLeaderboard leaderboard = LCLeaderboard.CreateWithoutData(leaderboardName);
|
||||||
|
ReadOnlyCollection<LCRanking> rankings = await leaderboard.GetResultsAroundUser(limit: 5);
|
||||||
|
foreach (LCRanking ranking in rankings) {
|
||||||
|
WriteLine($"{ranking.Rank} : {ranking.User.ObjectId}, {ranking.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Order(5)]
|
||||||
|
public async Task GetOtherStatistics() {
|
||||||
|
int today = DateTimeOffset.Now.DayOfYear;
|
||||||
|
string username = $"{today}_0";
|
||||||
|
string password = "leancloud";
|
||||||
|
LCUser user = await LCUser.Login(username, password);
|
||||||
|
await LCUser.Login($"{today}_1", password);
|
||||||
|
ReadOnlyCollection<LCStatistic> statistics = await LCLeaderboard.GetStatistics(user);
|
||||||
|
foreach (LCStatistic statistic in statistics) {
|
||||||
|
WriteLine($"{statistic.Name}, {statistic.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -334,7 +334,9 @@ namespace Storage.Test {
|
||||||
Assert.Greater(hellos.Count, 0);
|
Assert.Greater(hellos.Count, 0);
|
||||||
foreach (LCObject item in hellos) {
|
foreach (LCObject item in hellos) {
|
||||||
LCObject world = item["objectValue"] as LCObject;
|
LCObject world = item["objectValue"] as LCObject;
|
||||||
Assert.IsTrue(world == null || world["content"] != "7788");
|
Assert.IsTrue(world == null ||
|
||||||
|
world["content"] == null ||
|
||||||
|
world["content"] as string != "7788");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace Storage.Test {
|
||||||
public static class Utils {
|
public static class Utils {
|
||||||
internal const string AppId = "ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz";
|
internal const string AppId = "ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz";
|
||||||
internal const string AppKey = "NUKmuRbdAhg1vrb2wexYo1jo";
|
internal const string AppKey = "NUKmuRbdAhg1vrb2wexYo1jo";
|
||||||
|
internal const string MasterKey = "pyvbNSh5jXsuFQ3C8EgnIdhw";
|
||||||
internal const string AppServer = "https://ikggdre2.lc-cn-n1-shared.com";
|
internal const string AppServer = "https://ikggdre2.lc-cn-n1-shared.com";
|
||||||
|
|
||||||
internal static void Print(LCLogLevel level, string info) {
|
internal static void Print(LCLogLevel level, string info) {
|
||||||
|
|
|
@ -41,19 +41,19 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime DecodeDate(IDictionary dict) {
|
public static DateTime DecodeDate(IDictionary dict) {
|
||||||
string str = dict["iso"].ToString();
|
string str = dict["iso"].ToString();
|
||||||
DateTime dateTime = DateTime.Parse(str);
|
DateTime dateTime = DateTime.Parse(str);
|
||||||
return dateTime.ToLocalTime();
|
return dateTime.ToLocalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] DecodeBytes(IDictionary dict) {
|
public static byte[] DecodeBytes(IDictionary dict) {
|
||||||
string str = dict["base64"].ToString();
|
string str = dict["base64"].ToString();
|
||||||
byte[] bytes = Convert.FromBase64String(str);
|
byte[] bytes = Convert.FromBase64String(str);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LCObject DecodeObject(IDictionary dict) {
|
public static LCObject DecodeObject(IDictionary dict) {
|
||||||
string className = dict["className"].ToString();
|
string className = dict["className"].ToString();
|
||||||
LCObject obj = LCObject.Create(className);
|
LCObject obj = LCObject.Create(className);
|
||||||
LCObjectData objectData = LCObjectData.Decode(dict as Dictionary<string, object>);
|
LCObjectData objectData = LCObjectData.Decode(dict as Dictionary<string, object>);
|
||||||
|
@ -61,13 +61,13 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LCRelation<LCObject> DecodeRelation(IDictionary dict) {
|
public static LCRelation<LCObject> DecodeRelation(IDictionary dict) {
|
||||||
LCRelation<LCObject> relation = new LCRelation<LCObject>();
|
LCRelation<LCObject> relation = new LCRelation<LCObject>();
|
||||||
relation.TargetClass = dict["className"].ToString();
|
relation.TargetClass = dict["className"].ToString();
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LCGeoPoint DecodeGeoPoint(IDictionary data) {
|
public static LCGeoPoint DecodeGeoPoint(IDictionary data) {
|
||||||
double latitude = double.Parse(data["latitude"].ToString());
|
double latitude = double.Parse(data["latitude"].ToString());
|
||||||
double longitude = double.Parse(data["longitude"].ToString());
|
double longitude = double.Parse(data["longitude"].ToString());
|
||||||
LCGeoPoint geoPoint = new LCGeoPoint(latitude, longitude);
|
LCGeoPoint geoPoint = new LCGeoPoint(latitude, longitude);
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeDateTime(DateTime dateTime) {
|
public static object EncodeDateTime(DateTime dateTime) {
|
||||||
DateTime utc = dateTime.ToUniversalTime();
|
DateTime utc = dateTime.ToUniversalTime();
|
||||||
string str = utc.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
|
string str = utc.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
|
@ -40,7 +40,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeBytes(byte[] bytes) {
|
public static object EncodeBytes(byte[] bytes) {
|
||||||
string str = Convert.ToBase64String(bytes);
|
string str = Convert.ToBase64String(bytes);
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__type", "Bytes" },
|
{ "__type", "Bytes" },
|
||||||
|
@ -48,7 +48,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeList(IList list) {
|
public static object EncodeList(IList list) {
|
||||||
List<object> l = new List<object>();
|
List<object> l = new List<object>();
|
||||||
foreach (object obj in list) {
|
foreach (object obj in list) {
|
||||||
l.Add(Encode(obj));
|
l.Add(Encode(obj));
|
||||||
|
@ -56,7 +56,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeDictionary(IDictionary dict) {
|
public static object EncodeDictionary(IDictionary dict) {
|
||||||
Dictionary<string, object> d = new Dictionary<string, object>();
|
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||||
foreach (DictionaryEntry entry in dict) {
|
foreach (DictionaryEntry entry in dict) {
|
||||||
string key = entry.Key.ToString();
|
string key = entry.Key.ToString();
|
||||||
|
@ -66,7 +66,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeLCObject(LCObject obj) {
|
public static object EncodeLCObject(LCObject obj) {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__type", "Pointer" },
|
{ "__type", "Pointer" },
|
||||||
{ "className", obj.ClassName },
|
{ "className", obj.ClassName },
|
||||||
|
@ -78,11 +78,11 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return operation.Encode();
|
return operation.Encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeQueryCondition(ILCQueryCondition cond) {
|
public static object EncodeQueryCondition(ILCQueryCondition cond) {
|
||||||
return cond.Encode();
|
return cond.Encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeACL(LCACL acl) {
|
public static object EncodeACL(LCACL acl) {
|
||||||
HashSet<string> readers = acl.readers;
|
HashSet<string> readers = acl.readers;
|
||||||
HashSet<string> writers = acl.writers;
|
HashSet<string> writers = acl.writers;
|
||||||
HashSet<string> union = new HashSet<string>(readers);
|
HashSet<string> union = new HashSet<string>(readers);
|
||||||
|
@ -97,14 +97,14 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeRelation(LCRelation<LCObject> relation) {
|
public static object EncodeRelation(LCRelation<LCObject> relation) {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__type", "Relation" },
|
{ "__type", "Relation" },
|
||||||
{ "className", relation.TargetClass }
|
{ "className", relation.TargetClass }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeGeoPoint(LCGeoPoint geoPoint) {
|
public static object EncodeGeoPoint(LCGeoPoint geoPoint) {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__type", "GeoPoint" },
|
{ "__type", "GeoPoint" },
|
||||||
{ "latitude", geoPoint.Latitude },
|
{ "latitude", geoPoint.Latitude },
|
||||||
|
|
|
@ -69,7 +69,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
|
|
||||||
public async Task<T> Post<T>(string path,
|
public async Task<T> Post<T>(string path,
|
||||||
Dictionary<string, object> headers = null,
|
Dictionary<string, object> headers = null,
|
||||||
Dictionary<string, object> data = null,
|
object data = null,
|
||||||
Dictionary<string, object> queryParams = null) {
|
Dictionary<string, object> queryParams = null) {
|
||||||
string url = await BuildUrl(path, queryParams);
|
string url = await BuildUrl(path, queryParams);
|
||||||
HttpRequestMessage request = new HttpRequestMessage {
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
|
@ -102,7 +102,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
|
|
||||||
public async Task<T> Put<T>(string path,
|
public async Task<T> Put<T>(string path,
|
||||||
Dictionary<string, object> headers = null,
|
Dictionary<string, object> headers = null,
|
||||||
Dictionary<string, object> data = null,
|
object data = null,
|
||||||
Dictionary<string, object> queryParams = null) {
|
Dictionary<string, object> queryParams = null) {
|
||||||
string url = await BuildUrl(path, queryParams);
|
string url = await BuildUrl(path, queryParams);
|
||||||
HttpRequestMessage request = new HttpRequestMessage {
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
|
@ -188,12 +188,17 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
headers.Add(kv.Key, kv.Value.ToString());
|
headers.Add(kv.Key, kv.Value.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 签名
|
if (LCApplication.UseMasterKey && !string.IsNullOrEmpty(LCApplication.MasterKey)) {
|
||||||
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
// Master Key
|
||||||
string data = $"{timestamp}{appKey}";
|
headers.Add("X-LC-Key", $"{LCApplication.MasterKey},master");
|
||||||
string hash = GetMd5Hash(md5, data);
|
} else {
|
||||||
string sign = $"{hash},{timestamp}";
|
// 签名
|
||||||
headers.Add("X-LC-Sign", sign);
|
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
string data = $"{timestamp}{appKey}";
|
||||||
|
string hash = GetMd5Hash(md5, data);
|
||||||
|
string sign = $"{hash},{timestamp}";
|
||||||
|
headers.Add("X-LC-Sign", sign);
|
||||||
|
}
|
||||||
// 当前用户 Session Token
|
// 当前用户 Session Token
|
||||||
LCUser currentUser = await LCUser.GetCurrent();
|
LCUser currentUser = await LCUser.GetCurrent();
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
|
|
|
@ -22,6 +22,10 @@ namespace LeanCloud {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string MasterKey {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
public static bool UseProduction {
|
public static bool UseProduction {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +38,14 @@ namespace LeanCloud {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize(string appId, string appKey, string server = null) {
|
public static bool UseMasterKey {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Initialize(string appId,
|
||||||
|
string appKey,
|
||||||
|
string server = null,
|
||||||
|
string masterKey = null) {
|
||||||
if (string.IsNullOrEmpty(appId)) {
|
if (string.IsNullOrEmpty(appId)) {
|
||||||
throw new ArgumentException(nameof(appId));
|
throw new ArgumentException(nameof(appId));
|
||||||
}
|
}
|
||||||
|
@ -44,6 +55,7 @@ namespace LeanCloud {
|
||||||
|
|
||||||
AppId = appId;
|
AppId = appId;
|
||||||
AppKey = appKey;
|
AppKey = appKey;
|
||||||
|
MasterKey = masterKey;
|
||||||
|
|
||||||
// 注册 LeanCloud 内部子类化类型
|
// 注册 LeanCloud 内部子类化类型
|
||||||
LCObject.RegisterSubclass(LCUser.CLASS_NAME, () => new LCUser());
|
LCObject.RegisterSubclass(LCUser.CLASS_NAME, () => new LCUser());
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace LeanCloud.Storage {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LCUser(LCObjectData objectData) : this() {
|
internal LCUser(LCObjectData objectData) : this() {
|
||||||
Merge(objectData);
|
Merge(objectData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,454 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Storage.Internal.Codec;
|
||||||
|
|
||||||
|
namespace LeanCloud.Storage {
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜顺序
|
||||||
|
/// </summary>
|
||||||
|
public enum LCLeaderboardOrder {
|
||||||
|
/// <summary>
|
||||||
|
/// 升序
|
||||||
|
/// </summary>
|
||||||
|
Ascending,
|
||||||
|
/// <summary>
|
||||||
|
/// 降序
|
||||||
|
/// </summary>
|
||||||
|
Descending
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜更新策略
|
||||||
|
/// </summary>
|
||||||
|
public enum LCLeaderboardUpdateStrategy {
|
||||||
|
/// <summary>
|
||||||
|
/// 更好的
|
||||||
|
/// </summary>
|
||||||
|
Better,
|
||||||
|
/// <summary>
|
||||||
|
/// 最近的
|
||||||
|
/// </summary>
|
||||||
|
Last,
|
||||||
|
/// <summary>
|
||||||
|
/// 总和
|
||||||
|
/// </summary>
|
||||||
|
Sum
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜刷新频率
|
||||||
|
/// </summary>
|
||||||
|
public enum LCLeaderboardVersionChangeInterval {
|
||||||
|
/// <summary>
|
||||||
|
/// 从不
|
||||||
|
/// </summary>
|
||||||
|
Never,
|
||||||
|
/// <summary>
|
||||||
|
/// 每天
|
||||||
|
/// </summary>
|
||||||
|
Day,
|
||||||
|
/// <summary>
|
||||||
|
/// 每周
|
||||||
|
/// </summary>
|
||||||
|
Week,
|
||||||
|
/// <summary>
|
||||||
|
/// 每月
|
||||||
|
/// </summary>
|
||||||
|
Month
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜
|
||||||
|
/// </summary>
|
||||||
|
public class LCLeaderboard {
|
||||||
|
/// <summary>
|
||||||
|
/// 成绩名字
|
||||||
|
/// </summary>
|
||||||
|
public string StatisticName {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排名顺序
|
||||||
|
/// </summary>
|
||||||
|
public LCLeaderboardOrder Order {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排名更新策略
|
||||||
|
/// </summary>
|
||||||
|
public LCLeaderboardUpdateStrategy UpdateStrategy {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本更新频率
|
||||||
|
/// </summary>
|
||||||
|
public LCLeaderboardVersionChangeInterval VersionChangeInterval {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本号
|
||||||
|
/// </summary>
|
||||||
|
public int Version {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下次重置时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime NextResetAt {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime CreatedAt {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建排行榜
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statisticName"></param>
|
||||||
|
/// <param name="order"></param>
|
||||||
|
/// <param name="updateStrategy"></param>
|
||||||
|
/// <param name="versionChangeInterval"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<LCLeaderboard> CreateLeaderboard(string statisticName,
|
||||||
|
LCLeaderboardOrder order = LCLeaderboardOrder.Descending,
|
||||||
|
LCLeaderboardUpdateStrategy updateStrategy = LCLeaderboardUpdateStrategy.Better,
|
||||||
|
LCLeaderboardVersionChangeInterval versionChangeInterval = LCLeaderboardVersionChangeInterval.Week) {
|
||||||
|
if (string.IsNullOrEmpty(statisticName)) {
|
||||||
|
throw new ArgumentNullException(nameof(statisticName));
|
||||||
|
}
|
||||||
|
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||||
|
{ "statisticName", statisticName },
|
||||||
|
{ "order", order.ToString().ToLower() },
|
||||||
|
{ "versionChangeInterval", versionChangeInterval.ToString().ToLower() },
|
||||||
|
{ "updateStrategy", updateStrategy.ToString().ToLower() },
|
||||||
|
};
|
||||||
|
string path = "leaderboard/leaderboards";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Post<Dictionary<string, object>>(path,
|
||||||
|
data:data);
|
||||||
|
LCLeaderboard leaderboard = new LCLeaderboard();
|
||||||
|
leaderboard.Merge(result);
|
||||||
|
return leaderboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建只包含名称的排行榜对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statisticName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static LCLeaderboard CreateWithoutData(string statisticName) {
|
||||||
|
if (string.IsNullOrEmpty(statisticName)) {
|
||||||
|
throw new ArgumentNullException(nameof(statisticName));
|
||||||
|
}
|
||||||
|
return new LCLeaderboard {
|
||||||
|
StatisticName = statisticName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取排行榜
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="statisticName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<LCLeaderboard> GetLeaderboard(string statisticName) {
|
||||||
|
LCLeaderboard leaderboard = CreateWithoutData(statisticName);
|
||||||
|
return leaderboard.Fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新用户成绩
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="statistics"></param>
|
||||||
|
/// <param name="overwrite"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<ReadOnlyCollection<LCStatistic>> UpdateStatistics(LCUser user,
|
||||||
|
Dictionary<string, double> statistics,
|
||||||
|
bool overwrite = true) {
|
||||||
|
if (user == null) {
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
if (statistics == null || statistics.Count == 0) {
|
||||||
|
throw new ArgumentNullException(nameof(statistics));
|
||||||
|
}
|
||||||
|
List<Dictionary<string, object>> data = statistics.Select(statistic => new Dictionary<string, object> {
|
||||||
|
{ "statisticName", statistic.Key },
|
||||||
|
{ "statisticValue", statistic.Value },
|
||||||
|
}).ToList();
|
||||||
|
string path = $"leaderboard/users/{user.ObjectId}/statistics";
|
||||||
|
if (overwrite) {
|
||||||
|
path = $"{path}?overwrite=1";
|
||||||
|
}
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Post<Dictionary<string, object>>(path,
|
||||||
|
data: data);
|
||||||
|
if (result.TryGetValue("results", out object results) &&
|
||||||
|
results is List<object> list) {
|
||||||
|
List<LCStatistic> statisticList = new List<LCStatistic>();
|
||||||
|
foreach (object item in list) {
|
||||||
|
LCStatistic statistic = LCStatistic.Parse(item as IDictionary<string, object>);
|
||||||
|
statisticList.Add(statistic);
|
||||||
|
}
|
||||||
|
return statisticList.AsReadOnly();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得用户成绩
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="statisticNames"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<ReadOnlyCollection<LCStatistic>> GetStatistics(LCUser user,
|
||||||
|
IEnumerable<string> statisticNames = null) {
|
||||||
|
if (user == null) {
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
string path = $"leaderboard/users/{user.ObjectId}/statistics";
|
||||||
|
if (statisticNames != null && statisticNames.Count() > 0) {
|
||||||
|
string names = string.Join(",", statisticNames);
|
||||||
|
path = $"{path}?statistics={names}";
|
||||||
|
}
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Get<Dictionary<string, object>>(path);
|
||||||
|
if (result.TryGetValue("results", out object results) &&
|
||||||
|
results is List<object> list) {
|
||||||
|
List<LCStatistic> statistics = new List<LCStatistic>();
|
||||||
|
foreach (object item in list) {
|
||||||
|
LCStatistic statistic = LCStatistic.Parse(item as Dictionary<string, object>);
|
||||||
|
statistics.Add(statistic);
|
||||||
|
}
|
||||||
|
return statistics.AsReadOnly();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除用户成绩
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="statisticNames"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task DeleteStatistics(LCUser user,
|
||||||
|
IEnumerable<string> statisticNames) {
|
||||||
|
if (user == null) {
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
if (statisticNames == null || statisticNames.Count() == 0) {
|
||||||
|
throw new ArgumentNullException(nameof(statisticNames));
|
||||||
|
}
|
||||||
|
string names = string.Join(",", statisticNames);
|
||||||
|
string path = $"leaderboard/users/{user.ObjectId}/statistics?statistics={names}";
|
||||||
|
await LCApplication.HttpClient.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取排行榜历史数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<ReadOnlyCollection<LCLeaderboardArchive>> GetArchives(int skip = 0,
|
||||||
|
int limit = 10) {
|
||||||
|
if (skip < 0) {
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(skip));
|
||||||
|
}
|
||||||
|
if (limit <= 0) {
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(limit));
|
||||||
|
}
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}/archives?skip={skip}&limit={limit}";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Get<Dictionary<string, object>>(path);
|
||||||
|
if (result.TryGetValue("results", out object results) &&
|
||||||
|
results is List<object> list) {
|
||||||
|
List<LCLeaderboardArchive> archives = new List<LCLeaderboardArchive>();
|
||||||
|
foreach (object item in list) {
|
||||||
|
if (item is IDictionary<string, object> dict) {
|
||||||
|
LCLeaderboardArchive archive = LCLeaderboardArchive.Parse(dict);
|
||||||
|
archives.Add(archive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return archives.AsReadOnly();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取排行榜结果
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <param name="selectUserKeys"></param>
|
||||||
|
/// <param name="includeStatistics"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<ReadOnlyCollection<LCRanking>> GetResults(int version = -1,
|
||||||
|
int skip = 0,
|
||||||
|
int limit = 10,
|
||||||
|
IEnumerable<string> selectUserKeys = null,
|
||||||
|
IEnumerable<string> includeStatistics = null) {
|
||||||
|
return GetResults(null, version, skip, limit, selectUserKeys, includeStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取用户及附近的排名
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="version"></param>
|
||||||
|
/// <param name="skip"></param>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <param name="selectUserKeys"></param>
|
||||||
|
/// <param name="includeStatistics"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<ReadOnlyCollection<LCRanking>> GetResultsAroundUser(int version = -1,
|
||||||
|
int skip = 0,
|
||||||
|
int limit = 10,
|
||||||
|
IEnumerable<string> selectUserKeys = null,
|
||||||
|
IEnumerable<string> includeStatistics = null) {
|
||||||
|
LCUser user = await LCUser.GetCurrent();
|
||||||
|
return await GetResults(user, version, skip, limit, selectUserKeys, includeStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ReadOnlyCollection<LCRanking>> GetResults(LCUser user,
|
||||||
|
int version,
|
||||||
|
int skip,
|
||||||
|
int limit,
|
||||||
|
IEnumerable<string> selectUserKeys,
|
||||||
|
IEnumerable<string> includeStatistics) {
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}/ranks";
|
||||||
|
if (user != null) {
|
||||||
|
path = $"{path}/{user.ObjectId}";
|
||||||
|
}
|
||||||
|
path = $"{path}?skip={skip}&limit={limit}";
|
||||||
|
if (version != -1) {
|
||||||
|
path = $"{path}&version={version}";
|
||||||
|
}
|
||||||
|
if (selectUserKeys != null) {
|
||||||
|
string keys = string.Join(",", selectUserKeys);
|
||||||
|
path = $"{path}&includeUser={keys}";
|
||||||
|
}
|
||||||
|
if (includeStatistics != null) {
|
||||||
|
string statistics = string.Join(",", includeStatistics);
|
||||||
|
path = $"{path}&includeStatistics={statistics}";
|
||||||
|
}
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Get<Dictionary<string, object>>(path);
|
||||||
|
if (result.TryGetValue("results", out object results) &&
|
||||||
|
results is List<object> list) {
|
||||||
|
List<LCRanking> rankings = new List<LCRanking>();
|
||||||
|
foreach (object item in list) {
|
||||||
|
LCRanking ranking = LCRanking.Parse(item as IDictionary<string, object>);
|
||||||
|
rankings.Add(ranking);
|
||||||
|
}
|
||||||
|
return rankings.AsReadOnly();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置更新策略
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="updateStrategy"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<LCLeaderboard> UpdateUpdateStrategy(LCLeaderboardUpdateStrategy updateStrategy) {
|
||||||
|
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||||
|
{ "updateStrategy", updateStrategy.ToString().ToLower() }
|
||||||
|
};
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Put<Dictionary<string, object>>(path,
|
||||||
|
data: data);
|
||||||
|
if (result.TryGetValue("updateStrategy", out object strategy) &&
|
||||||
|
Enum.TryParse(strategy as string, true, out LCLeaderboardUpdateStrategy s)) {
|
||||||
|
UpdateStrategy = s;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置版本更新频率
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="versionChangeInterval"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<LCLeaderboard> UpdateVersionChangeInterval(LCLeaderboardVersionChangeInterval versionChangeInterval) {
|
||||||
|
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||||
|
{ "versionChangeInterval", versionChangeInterval.ToString().ToLower() }
|
||||||
|
};
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Put<Dictionary<string, object>>(path,
|
||||||
|
data: data);
|
||||||
|
if (result.TryGetValue("versionChangeInterval", out object interval) &&
|
||||||
|
Enum.TryParse(interval as string, true, out LCLeaderboardVersionChangeInterval i)) {
|
||||||
|
VersionChangeInterval = i;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 拉取排行榜数据
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<LCLeaderboard> Fetch() {
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Get<Dictionary<string, object>>(path);
|
||||||
|
Merge(result);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置排行榜
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<LCLeaderboard> Reset() {
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}/incrementVersion";
|
||||||
|
Dictionary<string, object> result = await LCApplication.HttpClient.Put<Dictionary<string, object>>(path);
|
||||||
|
Merge(result);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 销毁排行榜
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Destroy() {
|
||||||
|
string path = $"leaderboard/leaderboards/{StatisticName}";
|
||||||
|
await LCApplication.HttpClient.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Merge(Dictionary<string, object> data) {
|
||||||
|
if (data.TryGetValue("statisticName", out object statisticName)) {
|
||||||
|
StatisticName = statisticName as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("order", out object order) &&
|
||||||
|
Enum.TryParse(order as string, true, out LCLeaderboardOrder o)) {
|
||||||
|
Order = o;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("updateStrategy", out object strategy) &&
|
||||||
|
Enum.TryParse(strategy as string, true, out LCLeaderboardUpdateStrategy s)) {
|
||||||
|
UpdateStrategy = s;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("versionChangeInterval", out object interval) &&
|
||||||
|
Enum.TryParse(interval as string, true, out LCLeaderboardVersionChangeInterval i)) {
|
||||||
|
VersionChangeInterval = i;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("version", out object version)) {
|
||||||
|
Version = Convert.ToInt32(version);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("createdAt", out object createdAt) &&
|
||||||
|
createdAt is DateTime dt) {
|
||||||
|
CreatedAt = dt;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("expiredAt", out object expiredAt) &&
|
||||||
|
expiredAt is IDictionary dict) {
|
||||||
|
NextResetAt = LCDecoder.DecodeDate(dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Storage.Internal.Codec;
|
||||||
|
|
||||||
|
namespace LeanCloud.Storage {
|
||||||
|
/// <summary>
|
||||||
|
/// 归档的排行榜
|
||||||
|
/// </summary>
|
||||||
|
public class LCLeaderboardArchive {
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string StatisticName {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本号
|
||||||
|
/// </summary>
|
||||||
|
public int Version {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态
|
||||||
|
/// </summary>
|
||||||
|
public string Status {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载地址
|
||||||
|
/// </summary>
|
||||||
|
public string Url {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 激活时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime ActivatedAt {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 归档时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime DeactivatedAt {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LCLeaderboardArchive Parse(IDictionary<string, object> data) {
|
||||||
|
LCLeaderboardArchive archive = new LCLeaderboardArchive();
|
||||||
|
if (data.TryGetValue("statisticName", out object statisticName)) {
|
||||||
|
archive.StatisticName = statisticName as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("version", out object version)) {
|
||||||
|
archive.Version = Convert.ToInt32(version);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("status", out object status)) {
|
||||||
|
archive.Status = status as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("url", out object url)) {
|
||||||
|
archive.Url = url as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("activatedAt", out object activatedAt) &&
|
||||||
|
activatedAt is System.Collections.IDictionary actDt) {
|
||||||
|
archive.ActivatedAt = LCDecoder.DecodeDate(actDt);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("deactivatedAt", out object deactivatedAt) &&
|
||||||
|
deactivatedAt is System.Collections.IDictionary deactDt) {
|
||||||
|
archive.DeactivatedAt = LCDecoder.DecodeDate(deactDt);
|
||||||
|
}
|
||||||
|
return archive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using LeanCloud.Storage.Internal.Object;
|
||||||
|
|
||||||
|
namespace LeanCloud.Storage {
|
||||||
|
/// <summary>
|
||||||
|
/// 排名
|
||||||
|
/// </summary>
|
||||||
|
public class LCRanking {
|
||||||
|
/// <summary>
|
||||||
|
/// 名次
|
||||||
|
/// </summary>
|
||||||
|
public int Rank {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户
|
||||||
|
/// </summary>
|
||||||
|
public LCUser User {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成绩名称
|
||||||
|
/// </summary>
|
||||||
|
public string StatisticName {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分数
|
||||||
|
/// </summary>
|
||||||
|
public double Value {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成绩
|
||||||
|
/// </summary>
|
||||||
|
public ReadOnlyCollection<LCStatistic> IncludedStatistics {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LCRanking Parse(IDictionary<string, object> data) {
|
||||||
|
LCRanking ranking = new LCRanking();
|
||||||
|
if (data.TryGetValue("rank", out object rank)) {
|
||||||
|
ranking.Rank = Convert.ToInt32(rank);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("user", out object user)) {
|
||||||
|
LCObjectData objectData = LCObjectData.Decode(user as System.Collections.IDictionary);
|
||||||
|
ranking.User = new LCUser(objectData);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("statisticName", out object statisticName)) {
|
||||||
|
ranking.StatisticName = statisticName as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("statisticValue", out object value)) {
|
||||||
|
ranking.Value = Convert.ToDouble(value);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("statistics", out object statistics) &&
|
||||||
|
statistics is List<object> list) {
|
||||||
|
List<LCStatistic> statisticList = new List<LCStatistic>();
|
||||||
|
foreach (object item in list) {
|
||||||
|
LCStatistic statistic = LCStatistic.Parse(item as IDictionary<string, object>);
|
||||||
|
statisticList.Add(statistic);
|
||||||
|
}
|
||||||
|
ranking.IncludedStatistics = statisticList.AsReadOnly();
|
||||||
|
}
|
||||||
|
return ranking;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LeanCloud.Storage {
|
||||||
|
/// <summary>
|
||||||
|
/// 成绩
|
||||||
|
/// </summary>
|
||||||
|
public class LCStatistic {
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜名字
|
||||||
|
/// </summary>
|
||||||
|
public string Name {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成绩值
|
||||||
|
/// </summary>
|
||||||
|
public double Value {
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排行榜版本
|
||||||
|
/// </summary>
|
||||||
|
public int Version {
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LCStatistic Parse(IDictionary<string, object> data) {
|
||||||
|
LCStatistic statistic = new LCStatistic();
|
||||||
|
if (data.TryGetValue("statisticName", out object statisticName)) {
|
||||||
|
statistic.Name = statisticName as string;
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("statisticValue", out object value)) {
|
||||||
|
statistic.Value = Convert.ToDouble(value);
|
||||||
|
}
|
||||||
|
if (data.TryGetValue("version", out object version)) {
|
||||||
|
statistic.Version = Convert.ToInt32(version);
|
||||||
|
}
|
||||||
|
return statistic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
<Folder Include="Internal\Object\" />
|
<Folder Include="Internal\Object\" />
|
||||||
<Folder Include="Internal\Operation\" />
|
<Folder Include="Internal\Operation\" />
|
||||||
<Folder Include="Internal\Query\" />
|
<Folder Include="Internal\Query\" />
|
||||||
|
<Folder Include="Leaderboard\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Common\Common\Common.csproj" />
|
<ProjectReference Include="..\..\Common\Common\Common.csproj" />
|
||||||
|
|
Loading…
Reference in New Issue