소스 검색

Merge branch 'develop' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop

zhouj1203@hotmail.com 1 년 전
부모
커밋
5f9daa8008
24개의 변경된 파일2953개의 추가작업 그리고 939개의 파일을 삭제
  1. 4 0
      TEAMModelBI/ClientApp/src/api/index.js
  2. 5 3
      TEAMModelBI/ClientApp/src/until/http.js
  3. 1193 0
      TEAMModelBI/ClientApp/src/view/userInquire/idIncrement.vue
  4. 9 4
      TEAMModelBI/ClientApp/src/view/userInquire/index.vue
  5. 3 2
      TEAMModelBI/Controllers/BIProductAnalysis/ProductAnalysisController.cs
  6. 1 3
      TEAMModelBI/Controllers/BITmid/TmidController.cs
  7. 21 10
      TEAMModelBI/Filter/RequestAuditFilter.cs
  8. 2 2
      TEAMModelOS.FunctionV4/HttpTrigger/IESHttpTrigger.cs
  9. 2 10
      TEAMModelOS.SDK/Helper/Common/DateTimeHelper/DateTimeHelper.cs
  10. 5 0
      TEAMModelOS/ClientApp/src/static/Global.js
  11. 24 4
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  12. 14 8
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Buzr.vue
  13. 107 0
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Cowork.vue
  14. 14 8
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Pick.vue
  15. 47 27
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/PopQues.vue
  16. 4 2
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Push.vue
  17. 55 41
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Receive.vue
  18. 38 33
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/SmartRating.vue
  19. 135 0
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/SubjectiveAns.vue
  20. 53 40
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/WrkCmp.vue
  21. 1150 0
      TEAMModelOS/Controllers/System/BillController.cs
  22. 0 716
      TEAMModelOS/Controllers/XTest/BillController.cs
  23. 66 25
      TEAMModelOS/Filter/RequestAuditFilter.cs
  24. 1 1
      TEAMModelOS/Startup.cs

+ 4 - 0
TEAMModelBI/ClientApp/src/api/index.js

@@ -559,6 +559,10 @@ export default {
     getfilterSchool(data) {
         return post('/prodanalysis/get-school', data)
     },
+    //ID歸戶增量統計
+    getTmidUseprod(data) {
+        return post('/tmid/get-tmid-useprod', data)
+    },
     /*产品使用分析end*/
 
     //获取地址location

+ 5 - 3
TEAMModelBI/ClientApp/src/until/http.js

@@ -21,8 +21,7 @@ axios.interceptors.request.use(
             config.url.indexOf('bizconfig') != -1 ||
             config.url.indexOf('paper') != -1 ||
             config.url.indexOf('notice') != -1 ||
-            config.url.indexOf('bizuser') != -1 ||
-            config.url.indexOf('prodanalysis') != -1 
+            config.url.indexOf('bizuser') != -1
         ) {
             config.headers = {
                 'Content-Type': 'application/json',
@@ -33,7 +32,10 @@ axios.interceptors.request.use(
                 'Content-Type': 'application/json',
                 // 'site': 'china'
             }
-        } else if (config.url.indexOf('ies5') != -1 || config.url.indexOf('tmidstics') != -1 || config.url.indexOf('/service/PushNotify') != -1) {
+        } else if (config.url.indexOf('ies5') != -1 ||
+            config.url.indexOf('tmid') != -1 ||
+            config.url.indexOf('/service/PushNotify') != -1 ||
+            config.url.indexOf('prodanalysis') != -1) {
             config.headers = {
                 'Content-Type': 'application/json',
                 'authorization': 'Bearer ' + JSON.parse(localStorage.access_token)

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1193 - 0
TEAMModelBI/ClientApp/src/view/userInquire/idIncrement.vue


+ 9 - 4
TEAMModelBI/ClientApp/src/view/userInquire/index.vue

@@ -3,8 +3,8 @@
         <div class="inquire-title">
             <p>TEAM Model 智慧教育</p>
         </div>
-        <el-tabs v-if="pageShow ==='default'" style="width: 100%;height:100%;" type="card" class="demo-tabs">
-            <el-tab-pane label="用户查询" style="padding: 1%;display: flex;justify-content: center;">
+        <el-tabs v-if="pageShow ==='default'" style="width: 100%;height:100%;" type="card" class="demo-tabs" v-model="activeTab">
+            <el-tab-pane label="用户查询" style="padding: 1%;display: flex;justify-content: center;" name="tab1">
                 <div class="searchbox" v-loading="searchLoading" element-loading-text="数据搜索中...">
                     <div class="searchbox-title">
                         <p>用户查询</p>
@@ -39,9 +39,12 @@
                     </div>
                 </div>
             </el-tab-pane>
-            <el-tab-pane label="弱歸戶" style="padding: 1%">
+            <el-tab-pane label="弱歸戶" style="padding: 1%" name="tab2">
                 <UpdCodeW />
             </el-tab-pane>
+            <el-tab-pane label="ID歸戶增量統計"  name="tab3">
+                <IdIncrement />
+            </el-tab-pane>
         </el-tabs>
         <div class="inquirebox-details" v-else-if="pageShow ==='details'">
             <Detailsbox :searchdata="searchResult" :defDate="defDate" @parentClick="backClicks"></Detailsbox>
@@ -49,11 +52,12 @@
     </div>
 </template>
 <script setup>
-import { ref, getCurrentInstance, watch, h, nextTick,provide } from 'vue'
+import { ref, reactive, getCurrentInstance, watch, h, nextTick,provide } from 'vue'
 import { ElMessage, ElLoading } from 'element-plus'
 import { Search,Delete } from '@element-plus/icons'
 import Detailsbox from './details.vue'
 import UpdCodeW from './updCodeW.vue'
+import IdIncrement from './idIncrement.vue'
 import {useRoute} from "vue-router"
 import { useStore } from 'vuex'
 let { proxy } = getCurrentInstance()
@@ -75,6 +79,7 @@ let selecttypes = ref('precise')
 let searchResult=ref()
 let defDate = ref()
 let searchLoading=ref(false)
+let activeTab = reactive('tab1')
 const searchRecordsArr = ref(localStorage.getItem('searchRecords') ?  JSON.parse(localStorage.getItem('searchRecords')):[]);
 const backClicks=()=>{pageShow.value='default'}
 console.log(searchRecordsArr.value,'搜索记录')

+ 3 - 2
TEAMModelBI/Controllers/BIProductAnalysis/ProductAnalysisController.cs

@@ -1,5 +1,6 @@
 using Azure.Cosmos;
 using DingTalk.Api.Request;
+using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Configuration;
@@ -55,7 +56,7 @@ namespace TEAMModelBI.Controllers.ProductAnalysis
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "admin")]
+        [Authorize(Roles = "IES")]
         [HttpPost("get-iotstics")]
         public async Task<IActionResult> GetIotStics(JsonElement jsonElement)
         {
@@ -161,7 +162,7 @@ namespace TEAMModelBI.Controllers.ProductAnalysis
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "admin")]
+        [Authorize(Roles = "IES")]
         [HttpPost("get-school")]
         public async Task<IActionResult> GetSchoolInfo(JsonElement jsonElement)
         {

+ 1 - 3
TEAMModelBI/Controllers/BITmid/TmidController.cs

@@ -50,7 +50,6 @@ namespace TEAMModelBI.Controllers.BITmid
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        //[AuthToken(Roles = "admin")]
         [Authorize(Roles = "IES")]
         [HttpPost("get-tmidstics")]
         public async Task<IActionResult> GetTmidStics(JsonElement jsonElement)
@@ -538,7 +537,7 @@ namespace TEAMModelBI.Controllers.BITmid
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        //[AuthToken(Roles = "admin")]
+        [Authorize(Roles = "IES")]
         [HttpPost("get-tmid-iot")]
         public async Task<IActionResult> GetTmidIot(JsonElement jsonElement)
         {
@@ -642,7 +641,6 @@ namespace TEAMModelBI.Controllers.BITmid
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        //[AuthToken(Roles = "admin")]
         [Authorize(Roles = "IES")]
         [HttpPost("get-tmid-useprod")]
         public async Task<IActionResult> GetTmidUseProd(JsonElement jsonElement)

+ 21 - 10
TEAMModelBI/Filter/RequestAuditFilter.cs

@@ -49,7 +49,7 @@ namespace TEAMModelOS.Filter
 
             
             // 获取来源 Url 地址
-           //var refererUrl = httpRequest.GetRefererUrlAddress();
+           var refererUrl = httpRequest.GetRefererUrlAddress();
 
             // 获取请求参数(写入日志,需序列化成字符串后存储)
             var parameters = context.ActionArguments;
@@ -67,12 +67,18 @@ namespace TEAMModelOS.Filter
                     client = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("roles"))?.Value;
                 } catch (Exception ex ) { }
             }
-            string id = string.Empty, name = string.Empty, picture = string.Empty, school = string.Empty;
+            string id = string.Empty, name = string.Empty, picture = string.Empty, school = string.Empty, scope = string.Empty;
             if (!string.IsNullOrWhiteSpace(authtoken)) {
                 var jwt = new JwtSecurityTokenHandler().ReadJwtToken(authtoken);
                 id = jwt.Payload.Sub;
                 school = jwt.Payload.Azp;
                 name = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("name"))?.Value;
+                scope = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("scope"))?.Value;
+            }
+            string secChUaPlatform = string.Empty;
+            if (httpContext.Request.Headers.TryGetValue("Sec-Ch-Ua-Platform", out var values))
+            {
+                secChUaPlatform = values.FirstOrDefault();
             }
             // 请求时间
             var requestedTime = DateTimeOffset.Now.GetGMTTime(8).ToUnixTimeMilliseconds();
@@ -88,19 +94,24 @@ namespace TEAMModelOS.Filter
             // var stackTrace = EnhancedStackTrace.Current();
             // string region = await _searcher.SearchIpAsync(remoteIPv4);
             //同一个账号,同一IP,同一接口,UA标识(UA标识随意切换则表示可能会存在DDOS),时间段
-             //_logger.LogInformation(new{ ua=httpContext.GetUserAgent(), ip=remoteIPv4,time=requestedTime,path =$"{httpRequest.PathBase}{httpRequest.Path}",host= $"{httpRequest.Host}", param=parameters,id ,name ,school,succeed =isRequestSucceed }.ToJsonString());
-            var data = new {
-                //ua = httpContext.GetUserAgent(),
+            //_logger.LogInformation(new{ ua=httpContext.GetUserAgent(), ip=remoteIPv4,time=requestedTime,path =$"{httpRequest.PathBase}{httpRequest.Path}",host= $"{httpRequest.Host}", param=parameters,id ,name ,school,succeed =isRequestSucceed }.ToJsonString());
+            var data = new
+            {
+                ua = httpContext.GetUserAgent(),
                 ip = remoteIPv4,
                 time = requestedTime,
                 path = $"{httpRequest.PathBase}{httpRequest.Path}",
                 host = $"{httpRequest.Host}",
                 param = parameters,
-                id,
-                name,
-                school,
-                client,tid= tokenSha,
-                p="bi",
+                id = id,
+                name = name,
+                school = school,
+                client = client,
+                tid = tokenSha,
+                scope = scope,
+                referer = refererUrl,
+                platform = secChUaPlatform,
+                p = "bi",
             };
             _= _httpTrigger.RequestHttpTrigger(data, "China", "http-log");
             

+ 2 - 2
TEAMModelOS.FunctionV4/HttpTrigger/IESHttpTrigger.cs

@@ -83,8 +83,8 @@ namespace TEAMModelOS.FunctionV4
         public async Task<HttpResponseData> HttpLog([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
             string data = await new StreamReader(req.Body).ReadToEndAsync();
             var response = req.CreateResponse(HttpStatusCode.OK);
-            
-            var appendBlob = _azureStorage.GetBlobContainerClient("0-service-log").GetAppendBlobClient($"http-log/{DateTimeOffset.UtcNow.GetGMTTime(8):yyyy-MM-dd}.log");
+            var gmt8Time =  DateTimeOffset.UtcNow.GetGMTTime(8);
+            var appendBlob = _azureStorage.GetBlobContainerClient("0-service-log").GetAppendBlobClient($"http-log/{gmt8Time:yyyy-MM-dd}/{gmt8Time:HH}.log");
             if (!appendBlob.Exists())
             {
                 appendBlob.Create();

+ 2 - 10
TEAMModelOS.SDK/Helper/Common/DateTimeHelper/DateTimeHelper.cs

@@ -36,11 +36,7 @@ namespace TEAMModelOS.SDK
             //处理UTC时差
             TimeZoneInfo localTimezone = TimeZoneInfo.Local;
             var Hours = localTimezone.BaseUtcOffset.Hours;
-            if (Hours!=0)
-            {
-                //有时差
-                dateTime = dateTime.AddHours(GMT-Hours);
-            }
+            dateTime = dateTime.AddHours(GMT-Hours);
             return dateTime;
         }
         /// <summary>
@@ -54,11 +50,7 @@ namespace TEAMModelOS.SDK
             //处理UTC时差
             TimeZoneInfo localTimezone = TimeZoneInfo.Local;
             var Hours = localTimezone.BaseUtcOffset.Hours;
-            if (Hours!=0)
-            {
-                //有时差
-                dateTime = dateTime.AddHours(GMT-Hours);
-            }
+            dateTime = dateTime.AddHours(GMT-Hours);
             return dateTime;
         }
 

+ 5 - 0
TEAMModelOS/ClientApp/src/static/Global.js

@@ -569,6 +569,11 @@ const HI_TEACH_EVENT = () => {
 			text: '智慧评分',
 			type: 'fn',
 			relation: 'smart'
+		},
+		CoworkLoad: {
+			text: '协作',
+			type: 'fn',
+			relation: 'cowork'
 		}
 	}
 }

+ 24 - 4
TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue

@@ -149,7 +149,7 @@
                     <!-- 互动数据 -->
                     <div v-for="event in item.pageData" :key="event.Time">
                       <!-- 即问即答 -->
-                      <PopQues class="event-item" v-if="event.Event === 'PopQuesLoad' || event.Event === 'ReAtmpAnsStrt'" :evtType="event.Event" :irsData="event.data" :students="baseData.student"></PopQues>
+                      <PopQues class="event-item" v-if="event.Event === 'PopQuesLoad' || event.Event === 'ReAtmpAnsStrt'" :evtType="event.Event" :irsData="event.data" :students="baseData.student" :recordInfo="recordInfo" :blobInfo="blobInfo"></PopQues>
                       <!-- 抢权 -->
                       <Buzr class="event-item student-event" v-else-if="event.Event === 'BuzrAns'" :buzrData="event.data" :students="baseData.student"></Buzr>
                       <!-- 推送 -->
@@ -164,6 +164,8 @@
                       <Exam class="event-item" :examInfo="event.data" :recordInfo="recordInfo" v-else-if="event.Event === 'SPQStrt'"></Exam>
                       <!-- 智慧评分 -->
                       <SmartRating class="event-item student-event" :recordInfo="recordInfo" :smartRate="event.data" :students="baseData.student" :vote="event.vote" :blobInfo="blobInfo" v-else-if="event.Event === 'RatingStart'"></SmartRating>
+                      <!-- 协作 -->
+                      <Cowork class="event-item student-event" :recordInfo="recordInfo" :blobInfo="blobInfo" :cowork="event.data" :students="baseData.student" v-else-if="event.Event === 'CoworkLoad'"></Cowork>
                     </div>
                   </div>
                 </div>
@@ -262,13 +264,14 @@ import Exam from './eventchart/Exam.vue'
 import Receive from './eventchart/Receive.vue'
 import DataCount from './eventchart/DataCount.vue'
 import SmartRating from './eventchart/SmartRating.vue';
+import Cowork from './eventchart/Cowork.vue';
 import CountTo from 'vue-count-to'
 import BlobTool from '@/utils/blobTool.js'
 import FileSaver from "file-saver";
 import JSZip from "jszip";
 export default {
   components: {
-    PopQues, Pick, Push, Receive, DataCount, CountTo, Buzr, Exam, WrkCmp, BaseReportPie, BaseReportRadar, SmartRating
+    PopQues, Pick, Push, Receive, DataCount, CountTo, Buzr, Exam, WrkCmp, BaseReportPie, BaseReportRadar, SmartRating, Cowork
   },
   data() {
     return {
@@ -308,7 +311,13 @@ export default {
           text: this.$t('cusMgt.rcd.filter6'),
           events: ['RatingStart'],
           count: 0
-        }
+        },
+        {
+          value: 'CoworkLoad',
+          text: this.$t('cusMgt.rcd.filter7'),
+          events: ['CoworkLoad'],
+          count: 0
+        },
       ],
       split1: 0.4,
       backPage: undefined,
@@ -317,6 +326,7 @@ export default {
       irsData: [],//irs.json
       taskData: [],//task.json
       smartData: [],//smartRating.json
+      coworkData: [],//Cowork.json
       fnEvents: [],//功能事件
       events: [],//事件ID
       hiTeachEvent: [],//需要解析的事件信息
@@ -525,7 +535,7 @@ export default {
         return
       }
       // 时间轴数据正常
-      //获取Push.json、IRS.json、Task.json、Base.json数据  新增SmartRating.json(智慧评分)
+      //获取Push.json、IRS.json、Task.json、Base.json数据  新增SmartRating.json(智慧评分)  新增Cowork.json(协作)
       try {
         let pushUrl = `${this.blobInfo.blob_uri}/records/${this.recordInfo.id}/IES/Push.json${this.blobInfo.blob_sas}`
         this.pushData = JSON.parse(await this.$tools.getFile(pushUrl) || '[]')
@@ -564,6 +574,12 @@ export default {
       } catch (e) {
         this.smartData = []
       }
+      try {
+        let coworkUrl = `${this.blobInfo.blob_uri}/records/${this.recordInfo.id}/IES/Cowork.json${this.blobInfo.blob_sas}`
+        this.coworkData = JSON.parse(await this.$tools.getFile(coworkUrl) || '[]')
+      } catch (e) {
+        this.coworkData = []
+      }
       let pgids = this.pageIds
       //这里需要判断录制开始的pageid
       // let startInfo = this.pageEvents?.findLast(item => item.Event === 'EzsStartRecord')
@@ -632,6 +648,10 @@ export default {
               }
               this.filterInte[5].count++
               break
+            case 'cowork':
+              e.data = this.coworkData.find(t => t.pageID == e.Pgid)
+              this.filterInte[6].count++
+              break
             default:
               break
           }

+ 14 - 8
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Buzr.vue

@@ -1,11 +1,13 @@
 <template>
     <div class="buzz-wrap">
-        <p class="event-type">
-            {{$t('cusMgt.rcd.evt1')}}
-        </p>
-        <div class="buzz-box" v-for="(item,index) in buzzClients" :key="index">
-            <!-- <Icon custom="iconfont icon-buzz" size="24" /> -->
-            <span>{{getChinese(item.name)}}</span>
+        <div>
+            <p class="event-type">
+                {{$t('cusMgt.rcd.evt1')}}
+            </p>
+            <div class="buzz-box" v-for="(item,index) in buzzClients" :key="index">
+                <!-- <Icon custom="iconfont icon-buzz" size="24" /> -->
+                <span>{{getChinese(item.name)}}</span>
+            </div>
         </div>
         <StudentClient></StudentClient>
     </div>
@@ -106,8 +108,12 @@ export default {
     }
 }
 .event-type {
-    margin-right: 20px;
+    /* margin-right: 20px;
     font-size: 15px;
-    font-weight: 600;
+    font-weight: 600; */
+    
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
 }
 </style>

+ 107 - 0
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Cowork.vue

@@ -0,0 +1,107 @@
+<template>
+    <div>
+        <div>
+            <p class="action-type">{{ cowork.coworkType === 'All' ? '全体协作' : (cowork.coworkType === 'Group' ? '分组协作' : '差异化协作') }}</p>
+            <div class="cowork-box">
+                <div v-for="(item, index) in coworkData" :key="index" style="">
+                    <img :src="item.snapshotUrl" class="receive-img" alt="" @click="viewImage(item.snapshotUrl)">
+                    <p>
+                        {{ item.title }}
+                        <Tooltip placement="bottom" v-show="cowork.coworkType != 'All'">
+                            <Icon type="ios-contacts" size="20" color="#2D8CF0" />
+                            <template #content>
+                                <span v-for="(stu, sIndex) in item.stuList" :key="sIndex">
+                                    <span v-if="stu">
+                                        {{ stu.name }}
+                                        <span v-show="sIndex != (item.stuList.length - 1)">、</span>
+                                    </span>
+                                </span>
+                            </template>
+                        </Tooltip>
+                    </p>
+                </div>
+            </div>
+        </div>
+        <StudentClient class="receive-student"></StudentClient>
+    </div>
+</template>
+
+<script>
+import StudentClient from './StudentClient.vue'
+export default {
+    components: {
+        StudentClient
+    },
+    props: {
+        recordInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        cowork: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        blobInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        students: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+    },
+    computed: {
+        coworkData() {
+            let data = []
+            if(this.cowork.coworkGroupInfoList.length) {
+                data = this._.cloneDeep(this.cowork.coworkGroupInfoList)
+                data.forEach(item => {
+                    item.snapshotUrl = `${this.blobInfo.blob_uri}/records/${this.recordInfo.id}${item.snapshot}${this.blobInfo.blob_sas}`
+                    item.stuList = item.members.map(members => {
+                        return this.students.find(stu => stu.seatID == members)
+                    })
+                })
+            }
+            return data
+        }
+    },
+    methods: {
+        viewImage(url) {
+            this.$hevueImgPreview(url)
+        },
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.action-type {
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
+}
+.cowork-box {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-end;
+
+    &>div {
+        margin: 0 10px 15px;
+        text-align: center;
+    }
+
+    .receive-img {
+        max-width: 150px;
+        max-height: 150px;
+        border: 1px solid #eeeeee;
+        cursor: pointer;
+    }
+}
+</style>

+ 14 - 8
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Pick.vue

@@ -1,12 +1,14 @@
 <template>
     <!-- 随机挑人 -->
     <div class="pick-wrap">
-        <p class="event-type">
-            {{$t('cusMgt.rcd.evt3')}}
-        </p>
-        <div class="pick-item" v-for="(item,index) in pickRes" :key="index">
-            <p class="student-no">{{item.seatNo}}</p>
-            <p class="student-name">{{item.name}}</p>
+        <div>
+            <p class="event-type">
+                {{$t('cusMgt.rcd.evt3')}}
+            </p>
+            <div class="pick-item" v-for="(item,index) in pickRes" :key="index">
+                <p class="student-no">{{item.seatNo}}</p>
+                <p class="student-name">{{item.name}}</p>
+            </div>
         </div>
         <StudentClient></StudentClient>
     </div>
@@ -109,8 +111,12 @@ export default {
     }
 }
 .event-type {
-    margin-right: 20px;
+    /* margin-right: 20px;
     font-size: 15px;
-    font-weight: 600;
+    font-weight: 600; */
+    
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
 }
 </style>

+ 47 - 27
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/PopQues.vue

@@ -1,30 +1,34 @@
 <template>
     <div class="pop-ques-wrap">
         <TeacherClient></TeacherClient>
-        <p class="event-type">
-            {{evtType == 'PopQuesLoad' ? $t('studentWeb.hiteachNote.qA') : $t('studentWeb.hiteachNote.qaAgain')}}
-            <Tooltip :content="$t('talMgmt.text47')">
-            <v-icon class="qu-flip-icon" :style="{'color':isOverview ? '':'#2d8cf0'}" :iconClass="imgSrc" v-show="quType === 'single' || quType === 'multiple' || quType === 'judge'" @click.native="isOverview = !isOverview" />
-            </Tooltip>
-        </p>
-        <!-- 统计数据 -->
-        <template v-if="isOverview">
-            <!-- 单选、多选、判断选项分布 -->
-            <OptionCount v-if="quType === 'single' || quType === 'multiple' || quType === 'judge'" :optionCount="optionData" :answer="answer"></OptionCount>
-            <!-- 填空题(文字题) -->
-            <CompleteAns v-else-if="quType === 'complete'" :answer="answerData" :students="students"></CompleteAns>
-            <!-- 如果有设置正确答案的正确率统计 -->
-            <CorrectRate :correctData="correctData" v-if="hasAnswer"></CorrectRate>
-        </template>
-        <div v-else>
-            <p v-for="key in Object.keys(optionData)" :key="key">
-                <Tag :color="key == 'noAns' ? 'warning' : 'primary'">
-                    {{key == 'noAns' ? $t('cusMgt.rcd.noAns') : key}}
-                </Tag>
-                <Tag color="default" v-for="s in optionData[key]" :key="s">
-                    {{s}}
-                </Tag>
+        <div>
+            <p class="event-type">
+                {{evtType == 'PopQuesLoad' ? $t('studentWeb.hiteachNote.qA') : $t('studentWeb.hiteachNote.qaAgain')}}
+                <Tooltip :content="$t('talMgmt.text47')">
+                <v-icon class="qu-flip-icon" :style="{'color':isOverview ? '':'#2d8cf0'}" :iconClass="imgSrc" v-show="quType === 'single' || quType === 'multiple' || quType === 'judge'" @click.native="isOverview = !isOverview" />
+                </Tooltip>
             </p>
+            <!-- 统计数据 -->
+            <div v-if="isOverview">
+                <!-- 单选、多选、判断选项分布 -->
+                <OptionCount v-if="quType === 'single' || quType === 'multiple' || quType === 'judge'" :optionCount="optionData" :answer="answer"></OptionCount>
+                <!-- 填空题(文字题) -->
+                <CompleteAns v-else-if="quType === 'complete'" :answer="answerData" :students="students"></CompleteAns>
+                <!-- 如果有设置正确答案的正确率统计 -->
+                <CorrectRate :correctData="correctData" v-if="hasAnswer"></CorrectRate>
+                <!-- 问答题(文字、图片、音频) -->
+                <SubjectiveAns v-else-if="quType === 'subjective'" :answer="answerData" :students="students" :recordInfo="recordInfo" :blobInfo="blobInfo" :answerType="irsData.question?.exercise?.answerType" />
+            </div>
+            <div v-else>
+                <p v-for="key in Object.keys(optionData)" :key="key">
+                    <Tag :color="key == 'noAns' ? 'warning' : 'primary'">
+                        {{key == 'noAns' ? $t('cusMgt.rcd.noAns') : key}}
+                    </Tag>
+                    <Tag color="default" v-for="s in optionData[key]" :key="s">
+                        {{s}}
+                    </Tag>
+                </p>
+            </div>
         </div>
     </div>
 </template>
@@ -33,8 +37,15 @@ import TeacherClient from './TeacherClient.vue'
 import CorrectRate from './CorrectRate.vue'
 import OptionCount from './OptionCount.vue'
 import CompleteAns from './CompleteAns.vue'
+import SubjectiveAns from './SubjectiveAns.vue'
 export default {
     props: {
+        recordInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
         irsData: {
             type: Object,
             default: () => {
@@ -50,7 +61,13 @@ export default {
             default: () => {
                 return []
             }
-        }
+        },
+        blobInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
     },
     data() {
         require('@/icons/svg/flop.svg')
@@ -78,7 +95,7 @@ export default {
         }
     },
     components: {
-        OptionCount, CorrectRate, TeacherClient, CompleteAns
+        OptionCount, CorrectRate, TeacherClient, CompleteAns, SubjectiveAns
     },
     watch: {
         irsData: {
@@ -162,8 +179,11 @@ export default {
     display: flex;
 }
 .event-type {
-    margin-right: 20px;
+    /* margin-right: 20px;
     font-size: 15px;
-    font-weight: 600;
+    font-weight: 600; */
+    
+    font-weight: bold;
+    margin-bottom: 5px;
 }
 </style>

+ 4 - 2
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Push.vue

@@ -37,8 +37,10 @@ export default {
 </script>
 <style lang="less" scoped>
 .text-label{
-    display: inline-block;
-    vertical-align: top;
+    // display: inline-block;
+    // vertical-align: top;
+    font-weight: bold;
+    margin-bottom: 5px;
 }
 .tea-push-img{
     max-width: 120px;

+ 55 - 41
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Receive.vue

@@ -1,47 +1,51 @@
 <template>
     <!-- 作品收集 -->
     <div class="receive-wrap">
-        <p class="clt-type">
-            <!-- {{cltTypeMap[collateType].text}}收集:{{collateType}} -->
-            {{$t('cusMgt.rcd.evt4')}}
-        </p>
-        <div v-for="(item) in receiveData" :key="item.seatID+item.groupID" class="receive-item">
-            <div v-if="collateType == 0">
-                {{$t('cusMgt.rcd.evt5')}}
-            </div>
-            <!-- 图片类型作品 -->
-            <div v-else-if="collateType == 'Image'">
-                <img v-for="(blob,index) in item.blobFiles" :key="index" class="receive-img" :src="blob" alt="" @click="viewImage(blob)">
-            </div>
-            <!-- HTEX作品类型 -->
-            <div v-else-if="collateType == 'Htex'">
-                <!-- 收集的HTEX作品 -->
-            </div>
-            <!-- 音频 -->
-            <div v-else-if="collateType == 'Audio'">
-                <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(blob)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-audio-outline" />
-                </div>
-            </div>
-            <!-- 视频 -->
-            <div v-else-if="collateType == 'Video'">
-                <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(blob)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-video-outline" />
-                </div>
-            </div>
-            <!-- 文字 -->
-            <div v-else-if="collateType == 'Text'">
-                收集的文字
-            </div>
-            <!-- 附件 -->
-            <div v-else-if="collateType == 'File'">
-                <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="downloadFile(blob,item)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-file" />
+        <div>
+            <p class="clt-type">
+                <!-- {{cltTypeMap[collateType].text}}收集:{{collateType}} -->
+                {{$t('cusMgt.rcd.evt4')}}
+            </p>
+            <div class="receive-box">
+                <div v-for="(item) in receiveData" :key="item.seatID+item.groupID" class="receive-item">
+                    <div v-if="collateType == 0">
+                        {{$t('cusMgt.rcd.evt5')}}
+                    </div>
+                    <!-- 图片类型作品 -->
+                    <div v-else-if="collateType == 'Image'">
+                        <img v-for="(blob,index) in item.blobFiles" :key="index" class="receive-img" :src="blob" alt="" @click="viewImage(blob)">
+                    </div>
+                    <!-- HTEX作品类型 -->
+                    <div v-else-if="collateType == 'Htex'">
+                        <!-- 收集的HTEX作品 -->
+                    </div>
+                    <!-- 音频 -->
+                    <div v-else-if="collateType == 'Audio'">
+                        <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(blob)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-audio-outline" />
+                        </div>
+                    </div>
+                    <!-- 视频 -->
+                    <div v-else-if="collateType == 'Video'">
+                        <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(blob)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-video-outline" />
+                        </div>
+                    </div>
+                    <!-- 文字 -->
+                    <div v-else-if="collateType == 'Text'">
+                        收集的文字
+                    </div>
+                    <!-- 附件 -->
+                    <div v-else-if="collateType == 'File'">
+                        <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="downloadFile(blob,item)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-file" />
+                        </div>
+                    </div>
+                    <p class="name-text">
+                        {{ item.isGroupItem ? item.groupID : item.sName}}
+                    </p>
                 </div>
             </div>
-            <p class="name-text">
-                {{ item.isGroupItem ? item.groupID : item.sName}}
-            </p>
         </div>
         <StudentClient class="receive-student"></StudentClient>
         <!--文件预览-->
@@ -230,8 +234,14 @@ export default {
     right: 0px;
     top: 5px;
 }
+.receive-box {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-end;
+}
 .receive-item {
     margin-right: 20px;
+    text-align: center;
 }
 .receive-img {
     max-width: 150px;
@@ -260,8 +270,12 @@ export default {
     font-size: 30px;
 }
 .clt-type {
-    margin-right: 10px;
+    /* margin-right: 10px;
     font-size: 15px;
-    font-weight: 600;
+    font-weight: 600; */
+    
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
 }
 </style>

+ 38 - 33
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/SmartRating.vue

@@ -6,38 +6,39 @@
             mutualSummary:互评
             mutualDetailSummary:互评数据
          -->
-        <div class="smart-wrap">
+        <div>
             <p class="clt-type">
                 {{ smartType.name }}
                 <Icon type="md-eye-off" :title="$t('cusMgt.rcd.anonymousSub')" v-if="smartRate.smartRateSummary.rateInfo.AnonyCandi" />
-                :
             </p>
-            <template v-if="smartType.value === 'vote'">
-                <!-- <div v-for="(item, index) in scoreListNew" :key="index" style="margin-bottom: 10px;"> -->
-                    {{ $t('answerSheet.tip2') }}{{ vote.round }}{{ $t('cusMgt.rcd.wheel') }}({{ vote.votes }}{{ $t('studentWeb.vote.tickets') }})
-                    <Icon :title="$t('cusMgt.rcd.viewCom')" type="md-chatbubbles" size="17" color="#2EC7C9" @click="openComment('vote')" style="cursor: pointer; margin-top: 3px; margin-right: 5px;" />
-                    <SmartVote :smartData="scoreListNew"></SmartVote>
-                <!-- </div> -->
-            </template>
-            <template v-else-if="smartType.value === 'score'">
-                <SmartScore :smartData="scoreListNew"></SmartScore>
-            </template>
-            <template v-else>
-                <div v-for="(item, index) in scoreListNew" :key="index" class="smart-list">
-                    <p style="height: 21px;">
-                        <Icon type="md-trophy" v-if="item.king" color="#ff880d" />
-                    </p>
-                    <p>
-                        <span style="color: #2d8cf0;">{{ item.name }}
-                            <span v-show="!item.isGeneral">({{ item.id }})</span>
-                        </span>
-                        <Icon type="md-chatbubbles" color="#2EC7C9" style="cursor: pointer;" @click="openComment('mutal', index, item)" />
-                    </p>
-                    <p>{{ $t('cusMgt.rcd.avgScore') }}:{{ item.result }}</p>
-                    <img v-if="smartRate.smartRateSummary.rateInfo.RatingSource === 'StudentWork'" :src="item.material" @click="$hevueImgPreview(item.material)" />
-                    <p v-if="smartRate.smartRateSummary.rateInfo.RatingSource === 'IRS'" v-html="item.material" class="smart-material"></p>
-                </div>
-            </template>
+            <div class="smart-wrap">
+                <template v-if="smartType.value === 'vote'">
+                    <!-- <div v-for="(item, index) in scoreListNew" :key="index" style="margin-bottom: 10px;"> -->
+                        {{ $t('answerSheet.tip2') }}{{ vote.round }}{{ $t('cusMgt.rcd.wheel') }}({{ vote.votes }}{{ $t('studentWeb.vote.tickets') }})
+                        <Icon :title="$t('cusMgt.rcd.viewCom')" type="md-chatbubbles" size="17" color="#2EC7C9" @click="openComment('vote')" style="cursor: pointer; margin-top: 3px; margin-right: 5px;" />
+                        <SmartVote :smartData="scoreListNew"></SmartVote>
+                    <!-- </div> -->
+                </template>
+                <template v-else-if="smartType.value === 'score'">
+                    <SmartScore :smartData="scoreListNew"></SmartScore>
+                </template>
+                <template v-else>
+                    <div v-for="(item, index) in scoreListNew" :key="index" class="smart-list">
+                        <p style="height: 21px;">
+                            <Icon type="md-trophy" v-if="item.king" color="#ff880d" />
+                        </p>
+                        <p>
+                            <span style="color: #2d8cf0;">{{ item.name }}
+                                <span v-show="!item.isGeneral">({{ item.id }})</span>
+                            </span>
+                            <Icon type="md-chatbubbles" color="#2EC7C9" style="cursor: pointer;" @click="openComment('mutal', index, item)" />
+                        </p>
+                        <p>{{ $t('cusMgt.rcd.avgScore') }}:{{ item.result }}</p>
+                        <img v-if="smartRate.smartRateSummary.rateInfo.RatingSource === 'StudentWork'" :src="item.material" @click="$hevueImgPreview(item.material)" />
+                        <p v-if="smartRate.smartRateSummary.rateInfo.RatingSource === 'IRS'" v-html="item.material" class="smart-material"></p>
+                    </div>
+                </template>
+            </div>
         </div>
         <StudentClient></StudentClient>
         <Modal v-model="isComment" :title="$t('homework.table.comment')" :footer-hide="true">
@@ -257,17 +258,21 @@ export default {
 </script>
 
 <style lang="less" scoped>
+.clt-type {
+    /* margin-right: 10px;
+    font-size: 15px;
+    font-weight: 600; */
+    
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
+}
 .smart-wrap {
     display: flex;
     flex-wrap: wrap;
     justify-content: end;
     position: relative;
 
-    .clt-type {
-        margin-right: 10px;
-        font-size: 15px;
-        font-weight: 600;
-    }
 
     .smart-vote {
         width: 200px;

+ 135 - 0
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/SubjectiveAns.vue

@@ -0,0 +1,135 @@
+<template>
+    <div class="text-answer-item">
+        <div v-for="(student) in fullData" :key="student.seatID" @click="handleShowContent(student)">
+            <span class="student-name">{{ student.name }}:</span>
+            <div v-for="(item, index) in student.answer" :key="index">
+                <!-- 文本、画记 -->
+                <template v-if="answerType === 'text' || answerType === 'text_Image'">
+                    <div v-html="item" class="popques-svg"></div>
+                </template>
+                <template v-else>
+                    <div v-if="item.type === 'image'" class="popques-img">
+                        <img :src="item.url" alt="" @click="viewImage(item.url)">
+                    </div>
+                    <div v-if="item.type === 'audio'">
+                        <audio controls>
+                            <source :src="item.url">
+                            {{$t('teachContent.notAudio')}}
+                        </audio>
+                    </div>
+                    <div v-if="item.type === 'video'">
+                        <video :src="item.url" width="870" controls="controls" style="max-height: 800px;"></video>
+                    </div>
+                </template>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import Video from '../../video/Video.vue'
+export default {
+    components: { Video },
+    props: {
+        recordInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        answer: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        students: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        answerType: {
+            type: String,
+            default: '',
+        },
+        blobInfo: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+    },
+    computed: {
+        fullData() {
+            if (this.answer && this.answer.length) {
+                if (this.students && this.students.length) {
+                    let students = this._.cloneDeep(this.students)
+                    students.forEach((student, index) => {
+                        if(this.answerType != 'text' && this.answerType != 'text_Image') {
+                            student.answer = this.answer[index].map(item => {
+                                // 示例: "/Clients/202106001/Ans/1-1709794642933001.png"
+                                let url = {
+                                    type: '',
+                                    url: `${this.blobInfo.blob_uri}/records/${this.recordInfo.id}${item}${this.blobInfo.blob_sas}`
+                                }
+                                if(this.answerType === 'file') {
+                                    let suffix = item.substr(item.lastIndexOf(".") + 1)
+                                    let isImg = ['jpg', 'png', 'gif'].includes(suffix)
+                                    let isDoc = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(suffix)
+                                    let isPdf = suffix === 'pdf'
+                                    let isVideo = suffix === 'mp4' || suffix === 'webm'
+                                    let isAudio = ['mp3', 'wav'].includes(suffix)
+                                    url.type = isImg ? 'image' : isVideo ? 'video' : isAudio ? 'audio' : (isDoc ? 'doc' : 'other')
+                                } else {
+                                    url.type = this.answerType
+                                }
+                                return url
+                            })
+                        } else {
+                            student.answer = this.answer[index]
+                        }
+                    });
+                    students = students.filter(s => !!s.answer?.length)
+                    console.log('完整数据', students)
+                    return students
+                }
+            } else {
+                return []
+            }
+        }
+    },
+    methods: {
+        viewImage(url) {
+            this.$hevueImgPreview(url)
+        },
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.text-answer-item {
+    display: flex;
+    flex-wrap: wrap;
+
+    &>div {
+        margin-right: 20px;
+        display: flex;
+
+        .student-name {
+            margin-right: 5px;
+        }
+    }
+}
+.popques-img img{
+    cursor: pointer;
+    width: 100px !important;
+    height: 100px !important;
+}
+</style>
+<style lang="less">
+.popques-svg svg{
+    width: 200px;
+    height: auto;
+}
+</style>

+ 53 - 40
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/WrkCmp.vue

@@ -1,46 +1,50 @@
 <template>
     <!-- 作品收集 -->
     <div class="receive-wrap">
-        <p class="clt-type">
-            {{$t('cusMgt.rcd.wrkCmp')}}:
-        </p>
-        <div v-for="(item) in cmpDataList" :key="item.url" class="receive-item">
-            <div v-if="collateType == 0">
-                {{$t('cusMgt.rcd.evt5')}}
-            </div>
-            <!-- 图片类型作品 -->
-            <div v-else-if="collateType == 1">
-                <img class="receive-img" :src="item.url" alt="" @click="viewImage(item.url)">
-            </div>
-            <!-- HTEX作品类型 -->
-            <div v-else-if="collateType == 2">
-                <!-- 收集的HTEX作品 -->
-            </div>
-            <!-- 音频 -->
-            <div v-else-if="collateType == 3">
-                <div class="audio-box" @click="viewAudio(item.url)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-audio-outline" />
-                </div>
-            </div>
-            <!-- 视频 -->
-            <div v-else-if="collateType == 4">
-                <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(item.url)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-video-outline" />
-                </div>
-            </div>
-            <!-- 文字 -->
-            <div v-else-if="collateType == 5">
-                收集的文字
-            </div>
-            <!-- 附件 -->
-            <div v-else-if="collateType == 6">
-                <div class="audio-box" @click="downloadFile(item.url,item)">
-                    <Icon class="collate-type-icon" custom="iconfont icon-file" />
+        <div>
+            <p class="clt-type">
+                {{$t('cusMgt.rcd.wrkCmp')}}:
+            </p>
+            <div class="receive-box">
+                <div v-for="(item) in cmpDataList" :key="item.url" class="receive-item">
+                    <div v-if="collateType == 0">
+                        {{$t('cusMgt.rcd.evt5')}}
+                    </div>
+                    <!-- 图片类型作品 -->
+                    <div v-else-if="collateType == 1">
+                        <img class="receive-img" :src="item.url" alt="" @click="viewImage(item.url)">
+                    </div>
+                    <!-- HTEX作品类型 -->
+                    <div v-else-if="collateType == 2">
+                        <!-- 收集的HTEX作品 -->
+                    </div>
+                    <!-- 音频 -->
+                    <div v-else-if="collateType == 3">
+                        <div class="audio-box" @click="viewAudio(item.url)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-audio-outline" />
+                        </div>
+                    </div>
+                    <!-- 视频 -->
+                    <div v-else-if="collateType == 4">
+                        <div v-for="(blob,index) in item.blobFiles" :key="index" class="audio-box" @click="viewAudio(item.url)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-video-outline" />
+                        </div>
+                    </div>
+                    <!-- 文字 -->
+                    <div v-else-if="collateType == 5">
+                        收集的文字
+                    </div>
+                    <!-- 附件 -->
+                    <div v-else-if="collateType == 6">
+                        <div class="audio-box" @click="downloadFile(item.url,item)">
+                            <Icon class="collate-type-icon" custom="iconfont icon-file" />
+                        </div>
+                    </div>
+                    <p class="name-text">
+                        {{ item.isGroupItem ? item.groupID : item.sName}}
+                    </p>
                 </div>
             </div>
-            <p class="name-text">
-                {{ item.isGroupItem ? item.groupID : item.sName}}
-            </p>
         </div>
         <StudentClient></StudentClient>
         <!--文件预览-->
@@ -223,6 +227,11 @@ export default {
 .receive-wrap {
     display: flex;
 }
+.receive-box {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: flex-end;
+}
 .receive-item {
     margin-right: 20px;
 }
@@ -253,8 +262,12 @@ export default {
     font-size: 30px;
 }
 .clt-type {
-    margin-right: 10px;
+    /* margin-right: 10px;
     font-size: 15px;
-    font-weight: 600;
+    font-weight: 600; */
+    
+    text-align: right;
+    font-weight: bold;
+    margin: 0 10px 5px 0;
 }
 </style>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1150 - 0
TEAMModelOS/Controllers/System/BillController.cs


+ 0 - 716
TEAMModelOS/Controllers/XTest/BillController.cs

@@ -1,716 +0,0 @@
-using Azure.Storage.Blobs.Models;
-using DocumentFormat.OpenXml.Spreadsheet;
-using HTEXLib.COMM.Helpers;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Cors;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Azure.Amqp.Framing;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Options;
-using NUnit.Framework;
-using OfficeOpenXml;
-using OpenXmlPowerTools;
-using StackExchange.Redis;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.IdentityModel.Tokens.Jwt;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Runtime.Intrinsics.Arm;
-using System.Text;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using System.Web;
-using TEAMModelOS.Filter;
-using TEAMModelOS.Models;
-using TEAMModelOS.SDK;
-using TEAMModelOS.SDK.DI;
-using TEAMModelOS.SDK.Extension;
-using TEAMModelOS.SDK.Helper.Security.ShaHash;
-using TEAMModelOS.SDK.Models;
-using TEAMModelOS.SDK.Models.Cosmos.School;
-using TEAMModelOS.SDK.Models.Service.BI;
-
-namespace TEAMModelOS.Controllers.XTest
-{
-    [ProducesResponseType(StatusCodes.Status200OK)]
-    [ProducesResponseType(StatusCodes.Status400BadRequest)]
-
-    [Route("bill")]
-    [ApiController]
-    public class BillController : ControllerBase
-    {
-        private readonly DingDing _dingDing;
-        private readonly IHttpClientFactory _httpClient;
-        private readonly IConfiguration _configuration;
-        private readonly AzureStorageFactory _azureStorage;
-        private readonly IPSearcher _ipSearcher;
-        private readonly Option _option;
-        public BillController(IHttpClientFactory httpClient, IConfiguration configuration, AzureStorageFactory azureStorage,    IPSearcher searcher, DingDing  dingDing, IOptionsSnapshot<Option> option) 
-        {
-            _httpClient = httpClient;
-            _configuration = configuration;
-            _azureStorage = azureStorage;
-            _ipSearcher = searcher;
-            _dingDing = dingDing;
-            _option = option.Value;
-        }
-        
-        [HttpPost("report")]
-        [EnableCors("AllowSpecificOrigin")]
-        [AllowAnonymous]
-        [RequestSizeLimit(102_400_000_00)] //最大10000m左右
-        public async Task<IActionResult> Report(JsonElement json)
-        {
-            try {
-
-                List<KeyBillDetail> monthData = new List<KeyBillDetail>();
-                List<DayBillDetail> dayData = new List<DayBillDetail>();
-                List<DayBillDetail> resourceGroupDataGroup = new List<DayBillDetail>();
-                List<DayBillDetail> cloudServiceDataGroup = new List<DayBillDetail>();
-                List<DayBillDetail> meterCategoryDataGroup = new List<DayBillDetail>();
-                List<DayBillDetail> consumedServiceDataGroup = new List<DayBillDetail>();
-                string BillToken = _configuration.GetValue<string>("Azure:Bill:Token");
-                List<string> times = json.GetProperty("times").ToObject<List<string>>();
-
-                List<string> ResourceGroup = new List<string>();
-                List<string> CloudService = new List<string>();
-                List<string> Product = new List<string>();
-                List<string> MeterCategory = new List<string>();
-                List<string> MeterSubCategory = new List<string>();
-                List<string> MeterName = new List<string>();
-                List<string> ConsumedService = new List<string>();
-                
-                if (json.TryGetProperty("CloudService", out JsonElement _CloudService)) {
-                    CloudService= _CloudService.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("ResourceGroup", out JsonElement _ResourceGroup))
-                {
-                    ResourceGroup= _ResourceGroup.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("Product", out JsonElement _Product))
-                {
-                    Product= _Product.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("MeterCategory", out JsonElement _MeterCategory))
-                {
-                    MeterCategory= _MeterCategory.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("MeterSubCategory", out JsonElement _MeterSubCategory))
-                {
-                    MeterSubCategory= _MeterSubCategory.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("MeterName", out JsonElement _MeterName))
-                {
-                    MeterName= _MeterName.ToObject<List<string>>();
-                }
-                if (json.TryGetProperty("ConsumedService", out JsonElement _ConsumedService))
-                {
-                    ConsumedService= _ConsumedService.ToObject<List<string>>();
-                }
-                var httpClient = _httpClient.CreateClient();
-                httpClient.DefaultRequestHeaders.Remove("Authorization");
-                httpClient.DefaultRequestHeaders.Remove("api-version");
-                httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {BillToken}");
-                httpClient.DefaultRequestHeaders.Add("api-version", "2014-09-02");
-                var jwt = new JwtSecurityToken(BillToken);
-                jwt.Payload.TryGetValue("EnrollmentNumber", out object EnrollmentNumber);
-                int? exp = jwt.Payload.Exp;
-                long now = DateTimeOffset.Now.ToUnixTimeSeconds();
-                if (exp.HasValue   && now>=exp.Value)
-                {
-                    return Ok(new { code = 401 });
-                }
-
-                Dictionary<string, HashSet<string>> dict = new Dictionary<string, HashSet<string>>();
-                Dictionary<string, HashSet<string>> dictCond = new Dictionary<string, HashSet<string>>();
-             
-                var BlobClientDict = _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/dict.json");
-                if (BlobClientDict.Exists())
-                {
-                    BlobDownloadResult resultDict = await _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/dict.json").DownloadContentAsync();
-                    var dictBlob = resultDict.Content.ToObjectFromJson<Dictionary<string, HashSet<string>>>();
-                    if (dictBlob!=null  && dictBlob.Count>0)
-                    {
-                        dict=dictBlob;
-                    }
-                }
-                bool change = false;
-                foreach (var time in times) {
-                    IEnumerable<BillDetail> billDetails = new List<BillDetail>();
-                    string force = json.GetProperty("force").GetString();
-                    var BlobClient = _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/{time}.json");
-                    string nowMonth = DateTimeOffset.Now.ToString("yyyy-MM");
-                    if (BlobClient.Exists()&& !force.Equals("1")  &&  !nowMonth.Equals(time))
-                    {
-                        BlobDownloadResult result = await _azureStorage.GetBlobContainerClient("teammodelos").GetBlobClient($"bill/{time}.json").DownloadContentAsync();
-                        var bill = result.Content.ToObjectFromJson<List<BillDetail>>();
-                        billDetails=bill;
-                    }
-                    else
-                    {
-                        string url = $"https://ea.azure.cn/rest/{EnrollmentNumber}/usage-report/paginated?month={time}&fmt=JSON&pageindex=0";
-                        var response = await httpClient.GetAsync(url);
-                        if (response.IsSuccessStatusCode)
-                        {
-                            var content = await response.Content.ReadAsStringAsync(); 
-                            var bill = content.ToObject<List<BillDetail>>();
-                            //移除金额0的项目
-                            bill=  bill.FindAll(x => x.ExtendedCost>0);
-                            bill.ForEach(x => { 
-                                x.ResourceGroup= x.ResourceGroup.ToLower();
-                                x.MeterCategory=x.MeterCategory.ToLower();
-                                x.ConsumedService=x.ConsumedService.ToLower();
-                                if (string.IsNullOrWhiteSpace(x.ConsumedService)) {
-                                    x.ConsumedService="default";
-                                }
-                                if (string.IsNullOrWhiteSpace(x.ResourceGroup))
-                                {
-                                    x.ResourceGroup="default";
-                                }
-                                if (string.IsNullOrWhiteSpace(x.MeterCategory))
-                                {
-                                    x.MeterCategory="default";
-                                }
-                                if (x.ResourceGroup.Equals("teammodelchengdu")) {
-                                    x.CloudService="ies5"; 
-                                }
-                                else if (x.ResourceGroup.Equals("coreservicerg"))
-                                {
-                                    x.CloudService="core";
-                                }
-                                else if (x.ResourceGroup.Equals("mc_iesresourcegroup_sokcluster_chinaeast2")||x.ResourceGroup.Equals("iesresourcegroup"))
-                                {
-                                    x.CloudService="sokrates";
-                                }
-                                else if (x.ResourceGroup.Equals("coreserviceresourcegroupcn"))
-                                {
-                                    x.CloudService="ies3";
-                                }
-                                else 
-                                {
-                                    x.CloudService="other";
-                                }
-                            });
-                            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()});
-                            List<BillDetail> details = new List<BillDetail>();
-                            foreach ( var item in group )
-                            {
-                                BillDetail billDetail = item.list.First();
-                                billDetail.ExtendedCost=item.list.Sum(x=>x.ExtendedCost);
-                                details.Add(billDetail);
-                            }
-                            if (!nowMonth.Equals(time))
-                            {
-                                await _azureStorage.GetBlobContainerClient("teammodelos").UploadFileByContainer(details.ToJsonString(), "bill", $"{time}.json", true);
-                            }
-                            billDetails=details;
-                            //MeterCategory 服务
-                            {
-                                var newKey = billDetails.Select(x => x.MeterCategory).ToHashSet();
-                                if (dict.ContainsKey("MeterCategory"))
-                                {
-                                    var moreKey = newKey.Except(dict["MeterCategory"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["MeterCategory"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("MeterCategory", newKey);
-                                }
-                            }
-                            //MeterSubCategory 服务类型
-                            {
-                                var newKey = billDetails.Select(x => x.MeterSubCategory).ToHashSet();
-                                if (dict.ContainsKey("MeterSubCategory"))
-                                {
-                                    var moreKey = newKey.Except(dict["MeterSubCategory"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["MeterSubCategory"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("MeterSubCategory", newKey);
-                                }
-                            }
-                            //ConsumedService 服务信息
-                            {
-                                var newKey = billDetails.Select(x => x.ConsumedService).ToHashSet();
-                                if (dict.ContainsKey("ConsumedService"))
-                                {
-                                    var moreKey = newKey.Except(dict["ConsumedService"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["ConsumedService"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("ConsumedService", newKey);
-                                }
-                            }
-                            //CloudService 资源组
-                            {
-                                var newKey = billDetails.Select(x => x.CloudService.ToLower()).ToHashSet();
-                                if (dict.ContainsKey("CloudService"))
-                                {
-                                    var moreKey = newKey.Except(dict["CloudService"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["CloudService"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("CloudService", newKey);
-                                }
-                            }
-                            //ResourceGroup 资源组
-                            {
-                                var newKey = billDetails.Select(x => x.ResourceGroup.ToLower()).ToHashSet();
-                                if (dict.ContainsKey("ResourceGroup"))
-                                {
-                                    var moreKey = newKey.Except(dict["ResourceGroup"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["ResourceGroup"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("ResourceGroup", newKey);
-                                }
-                            }
-                            //MeterName 服务资源
-                            {
-                                var newKey = billDetails.Select(x => x.MeterName).ToHashSet();
-                                if (dict.ContainsKey("MeterName"))
-                                {
-                                    var moreKey = newKey.Except(dict["MeterName"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["MeterName"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("MeterName", newKey);
-                                }
-                            }
-                            //Product  产品
-                            {
-                                var newKey = billDetails.Select(x => x.Product).ToHashSet();
-                                if (dict.ContainsKey("Product"))
-                                {
-                                    var moreKey = newKey.Except(dict["Product"]);
-                                    foreach (var key in moreKey)
-                                    {
-                                        change=true;
-                                        dict["Product"].Add(key);
-                                    }
-                                }
-                                else
-                                {
-                                    change=true;
-                                    dict.Add("Product", newKey);
-                                }
-                            }
-                        }
-                    }
-                    if (CloudService.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => CloudService.Contains(x.CloudService));
-                    }
-                    if (ResourceGroup.IsNotEmpty()) {
-                        billDetails=  billDetails.Where(x => ResourceGroup.Contains(x.ResourceGroup));
-                    }
-                    if (Product.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => Product.Contains(x.Product));
-                    }
-                    if (MeterCategory.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => MeterCategory.Contains(x.MeterCategory));
-                    }
-                    if (MeterSubCategory.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => MeterSubCategory.Contains(x.MeterSubCategory));
-                    }
-                    if (MeterName.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => MeterName.Contains(x.MeterName));
-                    }
-                    if (ConsumedService.IsNotEmpty())
-                    {
-                        billDetails=  billDetails.Where(x => ConsumedService.Contains(x.ConsumedService));
-                    }
-                    {
-                        //MeterCategory 服务
-                        {
-                            var newKey = billDetails.Select(x => x.MeterCategory).ToHashSet();
-                            if (dictCond.ContainsKey("MeterCategory"))
-                            {
-                                var moreKey = newKey.Except(dictCond["MeterCategory"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["MeterCategory"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("MeterCategory", newKey);
-                            }
-                        }
-                        //MeterSubCategory 服务类型
-                        {
-                            var newKey = billDetails.Select(x => x.MeterSubCategory).ToHashSet();
-                            if (dictCond.ContainsKey("MeterSubCategory"))
-                            {
-                                var moreKey = newKey.Except(dictCond["MeterSubCategory"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["MeterSubCategory"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("MeterSubCategory", newKey);
-                            }
-                        }
-                        //ConsumedService 服务信息
-                        {
-                            var newKey = billDetails.Select(x => x.ConsumedService).ToHashSet();
-                            if (dictCond.ContainsKey("ConsumedService"))
-                            {
-                                var moreKey = newKey.Except(dictCond["ConsumedService"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["ConsumedService"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("ConsumedService", newKey);
-                            }
-                        }
-                        //CloudService 资源组
-                        {
-                            var newKey = billDetails.Select(x => x.CloudService.ToLower()).ToHashSet();
-                            if (dictCond.ContainsKey("CloudService"))
-                            {
-                                var moreKey = newKey.Except(dictCond["CloudService"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["CloudService"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("CloudService", newKey);
-                            }
-                        }
-
-                        //ResourceGroup 资源组
-                        {
-                            var newKey = billDetails.Select(x => x.ResourceGroup.ToLower()).ToHashSet();
-                            if (dictCond.ContainsKey("ResourceGroup"))
-                            {
-                                var moreKey = newKey.Except(dictCond["ResourceGroup"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["ResourceGroup"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("ResourceGroup", newKey);
-                            }
-                        }
-                        //MeterName 服务资源
-                        {
-                            var newKey = billDetails.Select(x => x.MeterName).ToHashSet();
-                            if (dictCond.ContainsKey("MeterName"))
-                            {
-                                var moreKey = newKey.Except(dictCond["MeterName"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["MeterName"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("MeterName", newKey);
-                            }
-                        }
-                        //Product  产品
-                        {
-                            var newKey = billDetails.Select(x => x.Product).ToHashSet();
-                            if (dictCond.ContainsKey("Product"))
-                            {
-                                var moreKey = newKey.Except(dictCond["Product"]);
-                                foreach (var key in moreKey)
-                                {
-                                    change=true;
-                                    dictCond["Product"].Add(key);
-                                }
-                            }
-                            else
-                            {
-                                change=true;
-                                dictCond.Add("Product", newKey);
-                            }
-                        }
-                    }
-                    monthData.Add(new KeyBillDetail { key=time, 
-                        cost= billDetails.Sum(x=>x.ExtendedCost)
-                    
-                    });
-
-                    var ResourceGroupData = billDetails.GroupBy(x => x.ResourceGroup);
-                    DayBillDetail ResourceGroupDataBillDetail = new DayBillDetail() { month= time };
-                    foreach (var group in ResourceGroupData)
-                    {
-                        ResourceGroupDataBillDetail.bills.Add(new KeyBillDetail() { key= group.Key, cost= group.Select(x => x.ExtendedCost).Sum() });
-                    }
-                    ResourceGroupDataBillDetail.bills= ResourceGroupDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
-                    resourceGroupDataGroup.Add(ResourceGroupDataBillDetail);
-
-
-
-                    var CloudServiceData = billDetails.GroupBy(x => x.CloudService);
-                    DayBillDetail CloudServiceDataBillDetail = new DayBillDetail() { month= time };
-                    foreach (var group in CloudServiceData)
-                    {
-                        CloudServiceDataBillDetail.bills.Add(new KeyBillDetail() { key= group.Key, cost= group.Select(x => x.ExtendedCost).Sum() });
-                    }
-                    CloudServiceDataBillDetail.bills= CloudServiceDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
-                    cloudServiceDataGroup.Add(CloudServiceDataBillDetail);
-
-                    var MeterCategoryData = billDetails.GroupBy(x => x.MeterCategory);
-                    DayBillDetail MeterCategoryDataBillDetail = new DayBillDetail() { month= time };
-                    foreach (var group in MeterCategoryData) {
-
-                        MeterCategoryDataBillDetail.bills.Add(new KeyBillDetail() { key= group.Key, cost= group.Select(x => x.ExtendedCost).Sum() });
-                    }
-                    MeterCategoryDataBillDetail.bills= MeterCategoryDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
-                    meterCategoryDataGroup.Add(MeterCategoryDataBillDetail);
-
-
-                    DayBillDetail ConsumedServiceDataBillDetail = new DayBillDetail() { month= time };
-                    var ConsumedServiceData = billDetails.GroupBy(x => x.ConsumedService);
-                    foreach (var group in ConsumedServiceData) {
-                        ConsumedServiceDataBillDetail.bills.Add(new KeyBillDetail() { key= group.Key, cost= group.Select(x => x.ExtendedCost).Sum() });
-                    }
-                    ConsumedServiceDataBillDetail.bills= ConsumedServiceDataBillDetail.bills.OrderByDescending(x => x.cost).ToList();
-                    consumedServiceDataGroup.Add(ConsumedServiceDataBillDetail);
-
-
-                    DayBillDetail dayBillDetail = new DayBillDetail() { month= time};
-                    for (int i = 1; i<=31; i++)
-                    {
-                        var day = billDetails.Where(x => x.Day==i);
-                        if (day!=null  && day.Count()>0)
-                        {
-                            dayBillDetail.bills.Add(new KeyBillDetail
-                            {
-                                key=$"{i}",
-                                cost= day.Sum(x =>x.ExtendedCost)
-                            });
-                        }
-                        else
-                        {
-                            dayBillDetail.bills.Add(new KeyBillDetail { key=$"{i}",cost=0 });
-                        }
-                    }
-                    dayData.Add(dayBillDetail);
-                }
-                if (change)
-                {
-                    await _azureStorage.GetBlobContainerClient("teammodelos").UploadFileByContainer(dict.ToJsonString(), "bill", $"dict.json", true);
-                }
-                return Ok(new {   monthData, dayData, dict, dictCond, code=200, consumedServiceDataGroup ,meterCategoryDataGroup, cloudServiceDataGroup, resourceGroupDataGroup});
-
-
-            } catch (Exception ex) {
-                return Ok(new { code = 500 ,msg= $"{ex.Message}{ex.StackTrace}"});
-            }
-           
-        }
-
-        public class KeyBillDetail {
-            public string key { get; set; }
-            public double cost { get; set; }
-        }
-        public class DayBillDetail
-        {
-            public string month { get; set; }
-            /// <summary>
-            /// 每天的数据
-            /// </summary>
-            public List<KeyBillDetail> bills { get; set; } = new List<KeyBillDetail>();
-        }
-        public class BillDetail
-        {
-            /// <summary>
-            /// 
-            /// </summary>
-            // public string AccountOwnerId { get; set; }
-            /// <summary>
-            /// 云米
-            /// </summary>
-            //public string AccountName { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            //public string ServiceAdministratorId { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            //public long SubscriptionId { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string SubscriptionGuid { get; set; }
-            /// <summary>
-            /// 标准预付费服务(Converted to EA)
-            /// </summary>
-            //public string SubscriptionName { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string Date { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public int Month { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public int Day { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public int Year { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string Product { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            //public string MeterId { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string MeterCategory { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string MeterSubCategory { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string MeterRegion { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string MeterName { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public double ConsumedQuantity { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public double ResourceRate { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public double ExtendedCost { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string ResourceLocation { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string ConsumedService { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string InstanceId { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string ServiceInfo1 { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string ServiceInfo2 { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string AdditionalInfo { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-          //  public string Tags { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-         //   public string StoreServiceIdentifier { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-          //  public string DepartmentName { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-           // public string CostCenter { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            //public string UnitOfMeasure { get; set; }
-            /// <summary>
-            /// 
-            /// </summary>
-            public string ResourceGroup { get; set; }
-            public string CloudService { get; set; }
-            public string CloudServiceName { get; set; }
-        }
-
-    }
-}

+ 66 - 25
TEAMModelOS/Filter/RequestAuditFilter.cs

@@ -24,11 +24,13 @@ namespace TEAMModelOS.Filter
         private readonly HttpTrigger _httpTrigger;
         public RequestAuditFilter(/*ILoggerFactory loggerFactory*/HttpTrigger httpTrigger)
         {
-          //  _logger = loggerFactory.CreateLogger<RequestAuditFilter>();
+            //  _logger = loggerFactory.CreateLogger<RequestAuditFilter>();
             _httpTrigger = httpTrigger;
         }
         public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
         {
+            string id = string.Empty, name = string.Empty, picture = string.Empty, school = string.Empty, scope = string.Empty, roles = string.Empty;
+                 
             //============== 这里是执行方法之前获取数据 ====================
 
             // 获取控制器、路由信息
@@ -39,17 +41,18 @@ namespace TEAMModelOS.Filter
 
             // 获取 HttpContext 和 HttpRequest 对象
             var httpContext = context.HttpContext;
+           string ua = httpContext.GetUserAgent();
             var httpRequest = httpContext.Request;
 
             // 获取客户端 Ipv4 地址
             var remoteIPv4 = httpContext.GetRemoteIpAddressToIPv4();
 
             // 获取请求的 Url 地址
-           // var requestUrl = httpRequest.GetRequestUrlAddress();
+            // var requestUrl = httpRequest.GetRequestUrlAddress();
+
 
-            
             // 获取来源 Url 地址
-           //var refererUrl = httpRequest.GetRefererUrlAddress();
+            var refererUrl = httpRequest.GetRefererUrlAddress();
 
             // 获取请求参数(写入日志,需序列化成字符串后存储)
             var parameters = context.ActionArguments;
@@ -57,25 +60,56 @@ namespace TEAMModelOS.Filter
             // 获取操作人(必须授权访问才有值)"userId" 为你存储的 claims type,jwt 授权对应的是 payload 中存储的键名
             //var userId = httpContext.User?.FindFirstValue("userId");
             var authtoken = context.HttpContext.GetXAuth("AuthToken");
-            string tokenSha = string.Empty,client = string.Empty;
-            if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues Authorization)) 
+            string tokenSha = string.Empty, client = string.Empty;
+            if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues Authorization))
             {
-                try {
-                    string token = Authorization.ToString().Replace("Bearer ", "");
-                    var jwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
-                      tokenSha = ShaHashHelper.GetSHA1(token);
-                    client = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("roles"))?.Value;
-                } catch (Exception ex ) { }
+                var jwt = new JwtSecurityTokenHandler().ReadJwtToken(Authorization.ToString().Replace("Bearer ", ""));
+                client= roles = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("roles"))?.Value;
+                tokenSha= ShaHashHelper.GetSHA1(Authorization.ToString());
             }
-            string id = string.Empty, name = string.Empty, picture = string.Empty, school = string.Empty;
-            if (!string.IsNullOrWhiteSpace(authtoken)) {
+            if (context.HttpContext.Request.Headers.TryGetValue("X-Auth-IdToken", out StringValues XAuthIdToken))
+            {
+                var jwt = new JwtSecurityTokenHandler().ReadJwtToken(XAuthIdToken);
+                id = jwt.Payload.Sub;
+                name = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("name"))?.Value;
+                if (string.IsNullOrEmpty(tokenSha))
+                {
+                    tokenSha= ShaHashHelper.GetSHA1(XAuthIdToken.ToString());
+                }
+            }
+            if (context.HttpContext.Request.Headers.TryGetValue("X-Auth-School", out StringValues XAuthSchool))
+            {
+                try
+                {
+                    school = XAuthSchool.ToString();
+                }
+                catch (Exception ex) { }
+            }
+
+            if (!string.IsNullOrWhiteSpace(authtoken))
+            {
                 var jwt = new JwtSecurityTokenHandler().ReadJwtToken(authtoken);
                 id = jwt.Payload.Sub;
                 school = jwt.Payload.Azp;
                 name = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("name"))?.Value;
+                scope = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("scope"))?.Value;
+                if (string.IsNullOrEmpty(tokenSha))
+                {
+                    tokenSha= ShaHashHelper.GetSHA1(authtoken);
+                }
+            }
+            string secChUaPlatform = string.Empty;
+            if (httpContext.Request.Headers.TryGetValue("Sec-Ch-Ua-Platform", out var values))
+            {
+                secChUaPlatform = values.FirstOrDefault();
             }
-            // 请求时间
-            var requestedTime = DateTimeOffset.Now.GetGMTTime(8).ToUnixTimeMilliseconds();
+            if (string.IsNullOrEmpty(tokenSha)) 
+            
+            {
+                tokenSha= ShaHashHelper.GetSHA1($"{ua}{remoteIPv4}{httpRequest.Host}{secChUaPlatform}");
+            }
+                // 请求时间
+                var requestedTime = DateTimeOffset.Now.GetGMTTime(8).ToUnixTimeMilliseconds();
             //============== 这里是执行方法之后获取数据 ====================
             var actionContext = await next();
             // 获取返回的结果
@@ -88,22 +122,29 @@ namespace TEAMModelOS.Filter
             // var stackTrace = EnhancedStackTrace.Current();
             // string region = await _searcher.SearchIpAsync(remoteIPv4);
             //同一个账号,同一IP,同一接口,UA标识(UA标识随意切换则表示可能会存在DDOS),时间段
-             //_logger.LogInformation(new{ ua=httpContext.GetUserAgent(), ip=remoteIPv4,time=requestedTime,path =$"{httpRequest.PathBase}{httpRequest.Path}",host= $"{httpRequest.Host}", param=parameters,id ,name ,school,succeed =isRequestSucceed }.ToJsonString());
-            var data = new {
-                //ua = httpContext.GetUserAgent(),
+            //_logger.LogInformation(new{ ua=httpContext.GetUserAgent(), ip=remoteIPv4,time=requestedTime,path =$"{httpRequest.PathBase}{httpRequest.Path}",host= $"{httpRequest.Host}", param=parameters,id ,name ,school,succeed =isRequestSucceed }.ToJsonString());
+          
+            var data = new
+            {
+                ua =ua,
                 ip = remoteIPv4,
                 time = requestedTime,
                 path = $"{httpRequest.PathBase}{httpRequest.Path}",
                 host = $"{httpRequest.Host}",
                 param = parameters,
-                id,
-                name,
-                school,
-                client,tid= tokenSha,
-                p="os",
+                id = id,
+                name = name,
+                school = school,
+                client = client,
+                tid = tokenSha,
+                scope = scope,
+                referer = refererUrl,
+                platform = secChUaPlatform,
+                p = "os",
+                idToken=XAuthIdToken
             };
             _= _httpTrigger.RequestHttpTrigger(data, "China", "http-log");
-            
+
         }
     }
 }

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -113,7 +113,7 @@ namespace TEAMModelOS
 #if DEBUG
                     builder.AllowAnyOrigin() // 添加允许的源
                            .AllowAnyHeader() // 允许任何请求标头
-                           //.AllowCredentials()
+                         //  .AllowCredentials()
                            ; // 允许包含凭据
 #else
                     //builder.WithOrigins("https://teammodeltest.blob.core.chinacloudapi.cn",  "https://teammodelos.blob.core.chinacloudapi.cn") // 添加允许的源       .AllowAnyMethod() // 允许任何请求方法