GameController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. using Common.Extension;
  2. using Common.Http.Apis.Request;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System.Text.Json;
  5. namespace Kitty_Fall.Website.Controllers
  6. {
  7. public class GameController : Controller
  8. {
  9. private const string CompletedGameTransIdSessionKey = "completedGameTransId";
  10. private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(GameController));
  11. private readonly IConfiguration _configuration;
  12. public GameController(IConfiguration configuration)
  13. {
  14. _configuration = configuration;
  15. }
  16. [HttpPost]
  17. public async Task<IActionResult> StartSession([FromBody] UserGameLevelResultReq request)
  18. {
  19. try
  20. {
  21. var completedTransId = HttpContext.Session.GetString(CompletedGameTransIdSessionKey);
  22. if (!string.IsNullOrWhiteSpace(request.transId)
  23. && string.Equals(request.transId, completedTransId, StringComparison.Ordinal))
  24. {
  25. request.transId = Guid.NewGuid().ToString("N");
  26. }
  27. var apiResult = await SendGameApiAsync(request, UrlConstant.GameStartSessionUrl);
  28. if (apiResult.errorCode != "0"
  29. && string.Equals(apiResult.data?.reason, "SESSION_CLOSED", StringComparison.OrdinalIgnoreCase))
  30. {
  31. // The browser can still hold the completed run's ID (for example
  32. // after iframe navigation/cache). Start a genuinely new run once;
  33. // never reuse or reopen the closed API session.
  34. request.transId = Guid.NewGuid().ToString("N");
  35. apiResult = await SendGameApiAsync(request, UrlConstant.GameStartSessionUrl);
  36. }
  37. if (apiResult.errorCode == "0" && apiResult.data != null)
  38. {
  39. var maxLevel = await GetTodayMaxLevelAsync(request);
  40. HttpContext.Session.SetComplexData("totalTurn", (long)apiResult.data.totalTurn);
  41. return Json(new
  42. {
  43. success = true,
  44. point = apiResult.data.point,
  45. data = apiResult.data.data,
  46. score = apiResult.data.score,
  47. transId = apiResult.data.transId,
  48. totalTurn = apiResult.data.totalTurn,
  49. max_level = maxLevel
  50. });
  51. }
  52. return Json(new
  53. {
  54. success = false,
  55. reason = apiResult.data?.reason ?? "",
  56. message = apiResult.message ?? "Start game failed."
  57. });
  58. }
  59. catch (Exception ex)
  60. {
  61. log.Error("StartSession error: ", ex);
  62. return Json(new { success = false, message = ex.Message });
  63. }
  64. }
  65. [HttpPost]
  66. public async Task<IActionResult> SaveLevelResult([FromBody] UserGameLevelResultReq request)
  67. {
  68. try
  69. {
  70. var apiResult = await SendGameApiAsync(request, UrlConstant.GameSaveLevelResultUrl);
  71. if (apiResult.data != null)
  72. {
  73. if (request.gameOver != 0 && apiResult.errorCode == "0")
  74. {
  75. HttpContext.Session.SetString(CompletedGameTransIdSessionKey, request.transId ?? "");
  76. }
  77. return Json(new
  78. {
  79. point = apiResult.data.point,
  80. data = apiResult.data.data,
  81. score = apiResult.data.score,
  82. transId = apiResult.data.transId,
  83. rewardType = apiResult.data.rewardType,
  84. rewardValue = apiResult.data.rewardValue,
  85. rewardUnit = apiResult.data.rewardUnit,
  86. success = apiResult.errorCode == "0"
  87. });
  88. }
  89. return Json(new
  90. {
  91. point = "0",
  92. data = "",
  93. // Khong tra lai score cu cua level truoc khi API ghi level loi.
  94. score = "0",
  95. transId = request.transId ?? "",
  96. rewardType = "",
  97. rewardValue = 0,
  98. rewardUnit = "",
  99. success = false,
  100. message = apiResult.message ?? "Save level result failed."
  101. });
  102. }
  103. catch (Exception ex)
  104. {
  105. log.Error("SaveLevelResult error: ", ex);
  106. return Json(new { success = false, message = ex.Message });
  107. }
  108. }
  109. [HttpPost]
  110. public async Task<IActionResult> ExitGame(UserGameLevelResultReq request)
  111. {
  112. try
  113. {
  114. log.Info($"ExitGame msisdn={request.msisdn}, transId={request.transId}, level={request.level}, score={request.score}, gameType={request.gameType}, gameOver={request.gameOver}");
  115. request.gameOver = request.gameOver == 0 ? 1 : request.gameOver;
  116. var apiResult = await SendGameApiAsync(request, UrlConstant.GameSaveLevelResultUrl);
  117. if (apiResult.errorCode == "0" && apiResult.data != null)
  118. {
  119. HttpContext.Session.SetComplexData("totalTurn", (long)apiResult.data.totalTurn);
  120. HttpContext.Session.SetString(CompletedGameTransIdSessionKey, request.transId ?? "");
  121. }
  122. var payload = JsonSerializer.Serialize(new
  123. {
  124. type = "EXIT_GAME_RESULT",
  125. success = apiResult.errorCode == "0",
  126. score = apiResult.data?.score ?? request.score.ToString(),
  127. data = apiResult.data?.totalDataMb ?? 0,
  128. loyalty = apiResult.data?.totalLoyalty ?? 0,
  129. totalTurn = apiResult.data?.totalTurn,
  130. level = request.level,
  131. message = apiResult.message ?? ""
  132. });
  133. var callbackHtml = $"""
  134. <!doctype html>
  135. <html><head><meta charset="utf-8"></head><body>
  136. <script>window.parent.postMessage({payload}, "*");</script>
  137. </body></html>
  138. """;
  139. return Content(callbackHtml, "text/html; charset=utf-8");
  140. }
  141. catch (Exception ex)
  142. {
  143. log.Error("ExitGame error: ", ex);
  144. return Json(new { success = false, message = ex.Message });
  145. }
  146. }
  147. private async Task<UserGameLevelResultRes> SendGameApiAsync(UserGameLevelResultReq request, string apiPath)
  148. {
  149. var msisdn = HttpContext.Session.GetComplexData<string>("msisdn") ?? request.msisdn ?? "";
  150. var apiToken = HttpContext.Session.GetString("apiToken") ?? request.token ?? "";
  151. var language = GetCurrentLanguage();
  152. request.msisdn = msisdn;
  153. request.token = apiToken;
  154. request.language = language;
  155. var baseUrl = GetParameter("url");
  156. if (string.IsNullOrWhiteSpace(baseUrl) || string.IsNullOrWhiteSpace(apiToken))
  157. {
  158. return new UserGameLevelResultRes("{\"errorCode\":1,\"message\":\"Please login again.\",\"status\":false,\"data\":{}}");
  159. }
  160. var postTask = DotnetLib.Rest.RestHandler.SendPostWithAuthen(
  161. log,
  162. baseUrl + apiPath,
  163. request,
  164. apiToken,
  165. "",
  166. "",
  167. language);
  168. if (postTask == null)
  169. {
  170. return new UserGameLevelResultRes("{\"errorCode\":1,\"message\":\"Game API returned empty response.\",\"status\":false,\"data\":{}}");
  171. }
  172. var response = await postTask;
  173. if (response?.data == null)
  174. {
  175. return new UserGameLevelResultRes("{\"errorCode\":1,\"message\":\"Game API returned empty response.\",\"status\":false,\"data\":{}}");
  176. }
  177. return new UserGameLevelResultRes(response.data);
  178. }
  179. private async Task<int> GetTodayMaxLevelAsync(UserGameLevelResultReq request)
  180. {
  181. const int defaultLevel = 1;
  182. try
  183. {
  184. var baseUrl = GetParameter("url");
  185. var apiToken = HttpContext.Session.GetString("apiToken") ?? request.token ?? "";
  186. var msisdn = HttpContext.Session.GetComplexData<string>("msisdn") ?? request.msisdn ?? "";
  187. var language = GetCurrentLanguage();
  188. if (string.IsNullOrWhiteSpace(baseUrl)
  189. || string.IsNullOrWhiteSpace(apiToken)
  190. || string.IsNullOrWhiteSpace(msisdn)
  191. || string.IsNullOrWhiteSpace(request.gameType))
  192. {
  193. return defaultLevel;
  194. }
  195. var today = DateTime.Today.ToString("yyyy-MM-dd");
  196. var historyRequest = new UserGameActivityHistoryReq
  197. {
  198. msisdn = msisdn,
  199. language = language,
  200. fromDate = today,
  201. toDate = today,
  202. pageNumber = 1,
  203. pageSize = 1000
  204. };
  205. var postTask = DotnetLib.Rest.RestHandler.SendPostWithAuthen(
  206. log,
  207. baseUrl + UrlConstant.GameActivityHistoryUrl,
  208. historyRequest,
  209. apiToken,
  210. "",
  211. "",
  212. language);
  213. if (postTask == null)
  214. return defaultLevel;
  215. var response = await postTask;
  216. if (response?.data == null)
  217. return defaultLevel;
  218. var history = new UserGameActivityHistoryRes(response.data).data;
  219. if (history?.items == null)
  220. return defaultLevel;
  221. var maxLevel = history.items
  222. .Where(item =>
  223. string.Equals(item.modeCode, request.gameType, StringComparison.OrdinalIgnoreCase)
  224. || string.Equals(item.modeName, request.gameType, StringComparison.OrdinalIgnoreCase))
  225. .Select(item => item.levelNo)
  226. .DefaultIfEmpty(defaultLevel)
  227. .Max();
  228. return Math.Clamp(maxLevel, defaultLevel, 50);
  229. }
  230. catch (Exception ex)
  231. {
  232. log.Warn("GetTodayMaxLevel failed; using level 1.", ex);
  233. return defaultLevel;
  234. }
  235. }
  236. private string GetParameter(string key)
  237. {
  238. return _configuration.GetSection(key).Value
  239. ?? _configuration.GetSection("Url").Value
  240. ?? "";
  241. }
  242. private string GetCurrentLanguage()
  243. {
  244. var sessionLang = HttpContext.Session.GetComplexData<string>("culture");
  245. if (!string.IsNullOrWhiteSpace(sessionLang)) return sessionLang;
  246. return System.Globalization.CultureInfo.CurrentUICulture.Name.StartsWith("vi", StringComparison.OrdinalIgnoreCase) ? "vi" : "en";
  247. }
  248. }
  249. }