BillController.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. using Azure.Storage.Blobs.Models;
  2. using HTEXLib.COMM.Helpers;
  3. using Microsoft.AspNetCore.Authorization;
  4. using Microsoft.AspNetCore.Cors;
  5. using Microsoft.AspNetCore.Http;
  6. using Microsoft.AspNetCore.Mvc;
  7. using Microsoft.Extensions.Configuration;
  8. using Microsoft.Extensions.Options;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.IdentityModel.Tokens.Jwt;
  12. using System.Linq;
  13. using System.Net.Http;
  14. using System.Text.Json;
  15. using System.Threading.Tasks;
  16. using TEAMModelOS.Models;
  17. using TEAMModelOS.SDK;
  18. using TEAMModelOS.SDK.DI;
  19. using TEAMModelOS.SDK.Extension;
  20. namespace TEAMModelOS.Controllers
  21. {
  22. [ProducesResponseType(StatusCodes.Status200OK)]
  23. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  24. [Route("bill")]
  25. [ApiController]
  26. public class BillController : ControllerBase
  27. {
  28. private readonly DingDing _dingDing;
  29. private readonly IHttpClientFactory _httpClient;
  30. private readonly IConfiguration _configuration;
  31. private readonly AzureStorageFactory _azureStorage;
  32. private readonly AzureRedisFactory _azureRedis;
  33. private readonly IPSearcher _ipSearcher;
  34. private readonly Option _option;
  35. private readonly Region2LongitudeLatitudeTranslator _longitudeLatitudeTranslator;
  36. public BillController(AzureRedisFactory azureRedis, Region2LongitudeLatitudeTranslator longitudeLatitudeTranslator, IHttpClientFactory httpClient, IConfiguration configuration, AzureStorageFactory azureStorage, IPSearcher searcher, DingDing dingDing, IOptionsSnapshot<Option> option)
  37. {
  38. _httpClient = httpClient;
  39. _configuration = configuration;
  40. _azureStorage = azureStorage;
  41. _ipSearcher = searcher;
  42. _dingDing = dingDing;
  43. _option = option.Value;
  44. _azureRedis=azureRedis;
  45. _longitudeLatitudeTranslator = longitudeLatitudeTranslator;
  46. }
  47. [HttpPost("report")]
  48. [EnableCors("AllowSpecificOrigin")]
  49. [AllowAnonymous]
  50. [RequestSizeLimit(102_400_000_00)] //最大10000m左右
  51. public async Task<IActionResult> Report(JsonElement json)
  52. {
  53. try
  54. {
  55. List<KeyBillDetail> monthData = new List<KeyBillDetail>();
  56. List<DayBillDetail> dayData = new List<DayBillDetail>();
  57. List<DayBillDetail> resourceGroupDataGroup = new List<DayBillDetail>();
  58. List<DayBillDetail> cloudServiceDataGroup = new List<DayBillDetail>();
  59. List<DayBillDetail> meterCategoryDataGroup = new List<DayBillDetail>();
  60. List<DayBillDetail> consumedServiceDataGroup = new List<DayBillDetail>();
  61. string BillToken = _configuration.GetValue<string>("Azure:Bill:Token");
  62. List<string> times = json.GetProperty("times").ToObject<List<string>>();
  63. List<string> ResourceGroup = new List<string>();
  64. List<string> CloudService = new List<string>();
  65. List<string> Product = new List<string>();
  66. List<string> MeterCategory = new List<string>();
  67. List<string> MeterSubCategory = new List<string>();
  68. List<string> MeterName = new List<string>();
  69. List<string> ConsumedService = new List<string>();
  70. if (json.TryGetProperty("CloudService", out JsonElement _CloudService))
  71. {
  72. CloudService = _CloudService.ToObject<List<string>>();
  73. }
  74. if (json.TryGetProperty("ResourceGroup", out JsonElement _ResourceGroup))
  75. {
  76. ResourceGroup = _ResourceGroup.ToObject<List<string>>();
  77. }
  78. if (json.TryGetProperty("Product", out JsonElement _Product))
  79. {
  80. Product = _Product.ToObject<List<string>>();
  81. }
  82. if (json.TryGetProperty("MeterCategory", out JsonElement _MeterCategory))
  83. {
  84. MeterCategory = _MeterCategory.ToObject<List<string>>();
  85. }
  86. if (json.TryGetProperty("MeterSubCategory", out JsonElement _MeterSubCategory))
  87. {
  88. MeterSubCategory = _MeterSubCategory.ToObject<List<string>>();
  89. }
  90. if (json.TryGetProperty("MeterName", out JsonElement _MeterName))
  91. {
  92. MeterName = _MeterName.ToObject<List<string>>();
  93. }
  94. if (json.TryGetProperty("ConsumedService", out JsonElement _ConsumedService))
  95. {
  96. ConsumedService = _ConsumedService.ToObject<List<string>>();
  97. }
  98. var httpClient = _httpClient.CreateClient();
  99. httpClient.DefaultRequestHeaders.Remove("Authorization");
  100. httpClient.DefaultRequestHeaders.Remove("api-version");
  101. httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {BillToken}");
  102. httpClient.DefaultRequestHeaders.Add("api-version", "2014-09-02");
  103. var jwt = new JwtSecurityToken(BillToken);
  104. jwt.Payload.TryGetValue("EnrollmentNumber", out object EnrollmentNumber);
  105. int? exp = jwt.Payload.Exp;
  106. long now = DateTimeOffset.Now.ToUnixTimeSeconds();
  107. if (exp.HasValue && now >= exp.Value)
  108. {
  109. return Ok(new { code = 401 });
  110. }
  111. Dictionary<string, HashSet<string>> dict = new Dictionary<string, HashSet<string>>();
  112. Dictionary<string, HashSet<string>> dictCond = new Dictionary<string, HashSet<string>>();
  113. var BlobClientDict = _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/dict.json");
  114. if (BlobClientDict.Exists())
  115. {
  116. BlobDownloadResult resultDict = await _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/dict.json").DownloadContentAsync();
  117. var dictBlob = resultDict.Content.ToObjectFromJson<Dictionary<string, HashSet<string>>>();
  118. if (dictBlob != null && dictBlob.Count > 0)
  119. {
  120. dict = dictBlob;
  121. }
  122. }
  123. bool change = false;
  124. foreach (var time in times)
  125. {
  126. IEnumerable<BillDetail> billDetails = new List<BillDetail>();
  127. string force = json.GetProperty("force").GetString();
  128. var BlobClient = _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/{time}.json");
  129. string nowMonth = DateTimeOffset.Now.ToString("yyyy-MM");
  130. //获取已经存在的月账单,且如果force!=1 并且不是当月的 ,如果force=1或者 是当月的账单,当月账单每天还在不断累加。 则是强制去刷新Azure远程账单
  131. if (BlobClient.Exists() && !force.Equals("1") && !nowMonth.Equals(time))
  132. {
  133. //不是当月的历史账单以现成的数据结构存储,直接获取。
  134. BlobDownloadResult result = await _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/{time}.json").DownloadContentAsync();
  135. var bill = result.Content.ToObjectFromJson<List<BillDetail>>();
  136. billDetails = bill;
  137. }
  138. else
  139. {
  140. string url = $"https://ea.azure.cn/rest/{EnrollmentNumber}/usage-report/paginated?month={time}&fmt=JSON&pageindex=0";
  141. var response = await httpClient.GetAsync(url);
  142. if (response.IsSuccessStatusCode)
  143. {
  144. var content = await response.Content.ReadAsStringAsync();
  145. var bill = content.ToObject<List<BillDetail>>();
  146. //移除金额0的项目
  147. bill = bill.FindAll(x => x.ExtendedCost > 0);
  148. bill.ForEach(x =>
  149. {
  150. x.ResourceGroup = x.ResourceGroup.ToLower();
  151. x.MeterCategory = x.MeterCategory.ToLower();
  152. x.ConsumedService = x.ConsumedService.ToLower();
  153. if (string.IsNullOrWhiteSpace(x.ConsumedService))
  154. {
  155. x.ConsumedService = "default";
  156. }
  157. if (string.IsNullOrWhiteSpace(x.ResourceGroup))
  158. {
  159. x.ResourceGroup = "default";
  160. }
  161. if (string.IsNullOrWhiteSpace(x.MeterCategory))
  162. {
  163. x.MeterCategory = "default";
  164. }
  165. if (x.ResourceGroup.Equals("teammodelchengdu"))
  166. {
  167. x.CloudService = "ies5";
  168. }
  169. else if (x.ResourceGroup.Equals("coreservicerg"))
  170. {
  171. x.CloudService = "core";
  172. }
  173. else if (x.ResourceGroup.Equals("mc_iesresourcegroup_sokcluster_chinaeast2") || x.ResourceGroup.Equals("iesresourcegroup"))
  174. {
  175. x.CloudService = "sokrates";
  176. }
  177. else if (x.ResourceGroup.Equals("coreserviceresourcegroupcn"))
  178. {
  179. x.CloudService = "ies3";
  180. }
  181. else
  182. {
  183. x.CloudService = "other";
  184. }
  185. });
  186. var group = bill.GroupBy(x => $"{x.Date}{x.ResourceGroup}{x.CloudService}{x.Product}{x.MeterCategory}{x.MeterSubCategory}{x.MeterName}{x.ConsumedService}").Select(x => new { x.Key, list = x.ToList() });
  187. List<BillDetail> details = new List<BillDetail>();
  188. foreach (var item in group)
  189. {
  190. BillDetail billDetail = item.list.First();
  191. billDetail.ExtendedCost = item.list.Sum(x => x.ExtendedCost);
  192. details.Add(billDetail);
  193. }
  194. if (!nowMonth.Equals(time))
  195. {
  196. await _azureStorage.GetBlobContainerClient("teammodelos").UploadFileByContainer(details.ToJsonString(), "bill", $"{time}.json", true);
  197. }
  198. billDetails = details;
  199. //MeterCategory 服务
  200. {
  201. var newKey = billDetails.Select(x => x.MeterCategory).ToHashSet();
  202. if (dict.ContainsKey("MeterCategory"))
  203. {
  204. var moreKey = newKey.Except(dict["MeterCategory"]);
  205. foreach (var key in moreKey)
  206. {
  207. change = true;
  208. dict["MeterCategory"].Add(key);
  209. }
  210. }
  211. else
  212. {
  213. change = true;
  214. dict.Add("MeterCategory", newKey);
  215. }
  216. }
  217. //MeterSubCategory 服务类型
  218. {
  219. var newKey = billDetails.Select(x => x.MeterSubCategory).ToHashSet();
  220. if (dict.ContainsKey("MeterSubCategory"))
  221. {
  222. var moreKey = newKey.Except(dict["MeterSubCategory"]);
  223. foreach (var key in moreKey)
  224. {
  225. change = true;
  226. dict["MeterSubCategory"].Add(key);
  227. }
  228. }
  229. else
  230. {
  231. change = true;
  232. dict.Add("MeterSubCategory", newKey);
  233. }
  234. }
  235. //ConsumedService 服务信息
  236. {
  237. var newKey = billDetails.Select(x => x.ConsumedService).ToHashSet();
  238. if (dict.ContainsKey("ConsumedService"))
  239. {
  240. var moreKey = newKey.Except(dict["ConsumedService"]);
  241. foreach (var key in moreKey)
  242. {
  243. change = true;
  244. dict["ConsumedService"].Add(key);
  245. }
  246. }
  247. else
  248. {
  249. change = true;
  250. dict.Add("ConsumedService", newKey);
  251. }
  252. }
  253. //CloudService 资源组
  254. {
  255. var newKey = billDetails.Select(x => x.CloudService.ToLower()).ToHashSet();
  256. if (dict.ContainsKey("CloudService"))
  257. {
  258. var moreKey = newKey.Except(dict["CloudService"]);
  259. foreach (var key in moreKey)
  260. {
  261. change = true;
  262. dict["CloudService"].Add(key);
  263. }
  264. }
  265. else
  266. {
  267. change = true;
  268. dict.Add("CloudService", newKey);
  269. }
  270. }
  271. //ResourceGroup 资源组
  272. {
  273. var newKey = billDetails.Select(x => x.ResourceGroup.ToLower()).ToHashSet();
  274. if (dict.ContainsKey("ResourceGroup"))
  275. {
  276. var moreKey = newKey.Except(dict["ResourceGroup"]);
  277. foreach (var key in moreKey)
  278. {
  279. change = true;
  280. dict["ResourceGroup"].Add(key);
  281. }
  282. }
  283. else
  284. {
  285. change = true;
  286. dict.Add("ResourceGroup", newKey);
  287. }
  288. }
  289. //MeterName 服务资源
  290. {
  291. var newKey = billDetails.Select(x => x.MeterName).ToHashSet();
  292. if (dict.ContainsKey("MeterName"))
  293. {
  294. var moreKey = newKey.Except(dict["MeterName"]);
  295. foreach (var key in moreKey)
  296. {
  297. change = true;
  298. dict["MeterName"].Add(key);
  299. }
  300. }
  301. else
  302. {
  303. change = true;
  304. dict.Add("MeterName", newKey);
  305. }
  306. }
  307. //Product 产品
  308. {
  309. var newKey = billDetails.Select(x => x.Product).ToHashSet();
  310. if (dict.ContainsKey("Product"))
  311. {
  312. var moreKey = newKey.Except(dict["Product"]);
  313. foreach (var key in moreKey)
  314. {
  315. change = true;
  316. dict["Product"].Add(key);
  317. }
  318. }
  319. else
  320. {
  321. change = true;
  322. dict.Add("Product", newKey);
  323. }
  324. }
  325. }
  326. }
  327. if (CloudService.IsNotEmpty())
  328. {
  329. billDetails = billDetails.Where(x => CloudService.Contains(x.CloudService));
  330. }
  331. if (ResourceGroup.IsNotEmpty())
  332. {
  333. billDetails = billDetails.Where(x => ResourceGroup.Contains(x.ResourceGroup));
  334. }
  335. if (Product.IsNotEmpty())
  336. {
  337. billDetails = billDetails.Where(x => Product.Contains(x.Product));
  338. }
  339. if (MeterCategory.IsNotEmpty())
  340. {
  341. billDetails = billDetails.Where(x => MeterCategory.Contains(x.MeterCategory));
  342. }
  343. if (MeterSubCategory.IsNotEmpty())
  344. {
  345. billDetails = billDetails.Where(x => MeterSubCategory.Contains(x.MeterSubCategory));
  346. }
  347. if (MeterName.IsNotEmpty())
  348. {
  349. billDetails = billDetails.Where(x => MeterName.Contains(x.MeterName));
  350. }
  351. if (ConsumedService.IsNotEmpty())
  352. {
  353. billDetails = billDetails.Where(x => ConsumedService.Contains(x.ConsumedService));
  354. }
  355. {
  356. //MeterCategory 服务
  357. {
  358. var newKey = billDetails.Select(x => x.MeterCategory).ToHashSet();
  359. if (dictCond.ContainsKey("MeterCategory"))
  360. {
  361. var moreKey = newKey.Except(dictCond["MeterCategory"]);
  362. foreach (var key in moreKey)
  363. {
  364. change = true;
  365. dictCond["MeterCategory"].Add(key);
  366. }
  367. }
  368. else
  369. {
  370. change = true;
  371. dictCond.Add("MeterCategory", newKey);
  372. }
  373. }
  374. //MeterSubCategory 服务类型
  375. {
  376. var newKey = billDetails.Select(x => x.MeterSubCategory).ToHashSet();
  377. if (dictCond.ContainsKey("MeterSubCategory"))
  378. {
  379. var moreKey = newKey.Except(dictCond["MeterSubCategory"]);
  380. foreach (var key in moreKey)
  381. {
  382. change = true;
  383. dictCond["MeterSubCategory"].Add(key);
  384. }
  385. }
  386. else
  387. {
  388. change = true;
  389. dictCond.Add("MeterSubCategory", newKey);
  390. }
  391. }
  392. //ConsumedService 服务信息
  393. {
  394. var newKey = billDetails.Select(x => x.ConsumedService).ToHashSet();
  395. if (dictCond.ContainsKey("ConsumedService"))
  396. {
  397. var moreKey = newKey.Except(dictCond["ConsumedService"]);
  398. foreach (var key in moreKey)
  399. {
  400. change = true;
  401. dictCond["ConsumedService"].Add(key);
  402. }
  403. }
  404. else
  405. {
  406. change = true;
  407. dictCond.Add("ConsumedService", newKey);
  408. }
  409. }
  410. //CloudService 资源组
  411. {
  412. var newKey = billDetails.Select(x => x.CloudService.ToLower()).ToHashSet();
  413. if (dictCond.ContainsKey("CloudService"))
  414. {
  415. var moreKey = newKey.Except(dictCond["CloudService"]);
  416. foreach (var key in moreKey)
  417. {
  418. change = true;
  419. dictCond["CloudService"].Add(key);
  420. }
  421. }
  422. else
  423. {
  424. change = true;
  425. dictCond.Add("CloudService", newKey);
  426. }
  427. }
  428. //ResourceGroup 资源组
  429. {
  430. var newKey = billDetails.Select(x => x.ResourceGroup.ToLower()).ToHashSet();
  431. if (dictCond.ContainsKey("ResourceGroup"))
  432. {
  433. var moreKey = newKey.Except(dictCond["ResourceGroup"]);
  434. foreach (var key in moreKey)
  435. {
  436. change = true;
  437. dictCond["ResourceGroup"].Add(key);
  438. }
  439. }
  440. else
  441. {
  442. change = true;
  443. dictCond.Add("ResourceGroup", newKey);
  444. }
  445. }
  446. //MeterName 服务资源
  447. {
  448. var newKey = billDetails.Select(x => x.MeterName).ToHashSet();
  449. if (dictCond.ContainsKey("MeterName"))
  450. {
  451. var moreKey = newKey.Except(dictCond["MeterName"]);
  452. foreach (var key in moreKey)
  453. {
  454. change = true;
  455. dictCond["MeterName"].Add(key);
  456. }
  457. }
  458. else
  459. {
  460. change = true;
  461. dictCond.Add("MeterName", newKey);
  462. }
  463. }
  464. //Product 产品
  465. {
  466. var newKey = billDetails.Select(x => x.Product).ToHashSet();
  467. if (dictCond.ContainsKey("Product"))
  468. {
  469. var moreKey = newKey.Except(dictCond["Product"]);
  470. foreach (var key in moreKey)
  471. {
  472. change = true;
  473. dictCond["Product"].Add(key);
  474. }
  475. }
  476. else
  477. {
  478. change = true;
  479. dictCond.Add("Product", newKey);
  480. }
  481. }
  482. }
  483. monthData.Add(new KeyBillDetail
  484. {
  485. key = time,
  486. cost = billDetails.Sum(x => x.ExtendedCost)
  487. });
  488. var ResourceGroupData = billDetails.GroupBy(x => x.ResourceGroup);
  489. DayBillDetail ResourceGroupDataBillDetail = new DayBillDetail() { month = time };
  490. foreach (var group in ResourceGroupData)
  491. {
  492. ResourceGroupDataBillDetail.bills.Add(new KeyBillDetail() { key = group.Key, cost = group.Select(x => x.ExtendedCost).Sum() });
  493. }
  494. ResourceGroupDataBillDetail.bills = ResourceGroupDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
  495. resourceGroupDataGroup.Add(ResourceGroupDataBillDetail);
  496. var CloudServiceData = billDetails.GroupBy(x => x.CloudService);
  497. DayBillDetail CloudServiceDataBillDetail = new DayBillDetail() { month = time };
  498. foreach (var group in CloudServiceData)
  499. {
  500. CloudServiceDataBillDetail.bills.Add(new KeyBillDetail() { key = group.Key, cost = group.Select(x => x.ExtendedCost).Sum() });
  501. }
  502. CloudServiceDataBillDetail.bills = CloudServiceDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
  503. cloudServiceDataGroup.Add(CloudServiceDataBillDetail);
  504. var MeterCategoryData = billDetails.GroupBy(x => x.MeterCategory);
  505. DayBillDetail MeterCategoryDataBillDetail = new DayBillDetail() { month = time };
  506. foreach (var group in MeterCategoryData)
  507. {
  508. MeterCategoryDataBillDetail.bills.Add(new KeyBillDetail() { key = group.Key, cost = group.Select(x => x.ExtendedCost).Sum() });
  509. }
  510. MeterCategoryDataBillDetail.bills = MeterCategoryDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
  511. meterCategoryDataGroup.Add(MeterCategoryDataBillDetail);
  512. DayBillDetail ConsumedServiceDataBillDetail = new DayBillDetail() { month = time };
  513. var ConsumedServiceData = billDetails.GroupBy(x => x.ConsumedService);
  514. foreach (var group in ConsumedServiceData)
  515. {
  516. ConsumedServiceDataBillDetail.bills.Add(new KeyBillDetail() { key = group.Key, cost = group.Select(x => x.ExtendedCost).Sum() });
  517. }
  518. ConsumedServiceDataBillDetail.bills = ConsumedServiceDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
  519. consumedServiceDataGroup.Add(ConsumedServiceDataBillDetail);
  520. DayBillDetail dayBillDetail = new DayBillDetail() { month = time };
  521. for (int i = 1; i <= 31; i++)
  522. {
  523. var day = billDetails.Where(x => x.Day == i);
  524. if (day != null && day.Count() > 0)
  525. {
  526. dayBillDetail.bills.Add(new KeyBillDetail
  527. {
  528. key = $"{i}",
  529. cost = day.Sum(x => x.ExtendedCost)
  530. });
  531. }
  532. else
  533. {
  534. dayBillDetail.bills.Add(new KeyBillDetail { key = $"{i}", cost = 0 });
  535. }
  536. }
  537. dayData.Add(dayBillDetail);
  538. }
  539. if (change)
  540. {
  541. await _azureStorage.GetBlobContainerClient("teammodelos").UploadFileByContainer(dict.ToJsonString(), "bill", $"dict.json", true);
  542. }
  543. return Ok(new { monthData, dayData, dict, dictCond, code = 200, consumedServiceDataGroup, meterCategoryDataGroup, cloudServiceDataGroup, resourceGroupDataGroup });
  544. }
  545. catch (Exception ex)
  546. {
  547. return Ok(new { code = 500, msg = $"{ex.Message}{ex.StackTrace}" });
  548. }
  549. }
  550. public class KeyBillDetail
  551. {
  552. public string key { get; set; }
  553. public double cost { get; set; }
  554. }
  555. public class DayBillDetail
  556. {
  557. public string month { get; set; }
  558. /// <summary>
  559. /// 每天的数据
  560. /// </summary>
  561. public List<KeyBillDetail> bills { get; set; } = new List<KeyBillDetail>();
  562. }
  563. public class BillDetail
  564. {
  565. /// <summary>
  566. ///
  567. /// </summary>
  568. // public string AccountOwnerId { get; set; }
  569. /// <summary>
  570. /// 云米
  571. /// </summary>
  572. //public string AccountName { get; set; }
  573. /// <summary>
  574. ///
  575. /// </summary>
  576. //public string ServiceAdministratorId { get; set; }
  577. /// <summary>
  578. ///
  579. /// </summary>
  580. //public long SubscriptionId { get; set; }
  581. /// <summary>
  582. ///
  583. /// </summary>
  584. public string SubscriptionGuid { get; set; }
  585. /// <summary>
  586. /// 标准预付费服务(Converted to EA)
  587. /// </summary>
  588. //public string SubscriptionName { get; set; }
  589. /// <summary>
  590. ///
  591. /// </summary>
  592. public string Date { get; set; }
  593. /// <summary>
  594. ///
  595. /// </summary>
  596. public int Month { get; set; }
  597. /// <summary>
  598. ///
  599. /// </summary>
  600. public int Day { get; set; }
  601. /// <summary>
  602. ///
  603. /// </summary>
  604. public int Year { get; set; }
  605. /// <summary>
  606. ///
  607. /// </summary>
  608. public string Product { get; set; }
  609. /// <summary>
  610. ///
  611. /// </summary>
  612. //public string MeterId { get; set; }
  613. /// <summary>
  614. ///
  615. /// </summary>
  616. public string MeterCategory { get; set; }
  617. /// <summary>
  618. ///
  619. /// </summary>
  620. public string MeterSubCategory { get; set; }
  621. /// <summary>
  622. ///
  623. /// </summary>
  624. // public string MeterRegion { get; set; }
  625. /// <summary>
  626. ///
  627. /// </summary>
  628. public string MeterName { get; set; }
  629. /// <summary>
  630. ///
  631. /// </summary>
  632. // public double ConsumedQuantity { get; set; }
  633. /// <summary>
  634. ///
  635. /// </summary>
  636. // public double ResourceRate { get; set; }
  637. /// <summary>
  638. ///
  639. /// </summary>
  640. public double ExtendedCost { get; set; }
  641. /// <summary>
  642. ///
  643. /// </summary>
  644. // public string ResourceLocation { get; set; }
  645. /// <summary>
  646. ///
  647. /// </summary>
  648. public string ConsumedService { get; set; }
  649. /// <summary>
  650. ///
  651. /// </summary>
  652. public string InstanceId { get; set; }
  653. /// <summary>
  654. ///
  655. /// </summary>
  656. // public string ServiceInfo1 { get; set; }
  657. /// <summary>
  658. ///
  659. /// </summary>
  660. // public string ServiceInfo2 { get; set; }
  661. /// <summary>
  662. ///
  663. /// </summary>
  664. // public string AdditionalInfo { get; set; }
  665. /// <summary>
  666. ///
  667. /// </summary>
  668. // public string Tags { get; set; }
  669. /// <summary>
  670. ///
  671. /// </summary>
  672. // public string StoreServiceIdentifier { get; set; }
  673. /// <summary>
  674. ///
  675. /// </summary>
  676. // public string DepartmentName { get; set; }
  677. /// <summary>
  678. ///
  679. /// </summary>
  680. // public string CostCenter { get; set; }
  681. /// <summary>
  682. ///
  683. /// </summary>
  684. //public string UnitOfMeasure { get; set; }
  685. /// <summary>
  686. ///
  687. /// </summary>
  688. public string ResourceGroup { get; set; }
  689. public string CloudService { get; set; }
  690. public string CloudServiceName { get; set; }
  691. }
  692. }
  693. }