瀏覽代碼

Merge branch 'develop5.0-tmd' of http://106.12.23.251:10000/TEAMMODEL/TEAMModelOS into develop5.0-tmd

OnePsycho 3 年之前
父節點
當前提交
e1fe1b9cf3

+ 54 - 0
TEAMModelOS.SDK/Models/Cosmos/School/AreaSetting.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models
+{
+    public  class AreaSetting:CosmosEntity
+    {
+        public AreaSetting() {
+            pk = "AreaSetting";
+            code = "AreaSetting";
+        }
+        /// <summary>
+        /// 总学时
+        /// </summary>
+        public int allTime { get; set; }
+        /// <summary>
+        /// 线上研修总学时
+        /// </summary>
+        public int onlineTime { get; set; }
+        /// <summary>
+        /// 线下研修总学时
+        /// </summary>
+        public int offlineTime { get; set; }
+        /// <summary>
+        /// 应用考核总学时
+        /// </summary>
+        public int submitTime { get; set; }
+        /// <summary>
+        /// 课堂实录总学时
+        /// </summary>
+        public int classTime { get; set; }
+        /// <summary>
+        /// 文档类型
+        /// </summary>
+        public List<string> doc { get; set; } = new List<string>();
+        /// <summary>
+        /// 视频类型
+        /// </summary>
+        public List<string> video { get; set; } = new List<string>();
+        /// <summary>
+        /// 音频类型
+        /// </summary>
+        public List<string> audio { get; set; } = new List<string>();
+        /// <summary>
+        /// 图片类型
+        /// </summary>
+        public List<string> image { get; set; } = new List<string>();
+        /// <summary>
+        /// 压缩包类型
+        /// </summary>
+        public List<string> gzip { get; set; } = new List<string>();
+    }
+}

+ 11 - 21
TEAMModelOS/ClientApp/src/router/routes.js

@@ -1151,6 +1151,7 @@ export const routes = [{
 				},
 				children: []
 			},
+			// 区级数据
 			{
 				path: 'areaSetting',
 				name: 'areaSetting',
@@ -1163,11 +1164,20 @@ export const routes = [{
 			{
 				path: 'areaMgmt',
 				name: 'areaMgmt',
-				component: resolve => require(['@/view/areaMgmt/Index.vue'], resolve),
+				component: resolve => require(['@/view/areaMgmt/AreaData.vue'], resolve),
 				meta: {
 					activeName: 'areaMgmt'
 				}
 			},
+			// 学校总览
+			{
+				path: 'schoolMgmt',
+				name: 'schoolMgmt',
+				component: resolve => require(['@/view/areaMgmt/SchoolIndex.vue'], resolve),
+				meta: {
+					activeName: 'schoolMgmt'
+				}
+			},
 			{
 				path: 'spotCheck',
 				name: 'spotCheck',
@@ -1243,26 +1253,6 @@ export const routes = [{
 				},
 				children: []
 			},
-			// // 政策文件管理
-			// {
-			// 	path: 'policyMgt',
-			// 	name: 'policyMgt',
-			// 	component: resolve => require(['@/view/areaMgmt/PolicyMgt.vue'], resolve),
-			// 	meta: {
-			// 		activeName: 'policyMgt'
-			// 	},
-			// 	children: []
-			// },
-			// // 资源内容管理
-			// {
-			// 	path: 'resourceMgt',
-			// 	name: 'resourceMgt',
-			// 	component: resolve => require(['@/view/areaMgmt/ResourceMgt.vue'], resolve),
-			// 	meta: {
-			// 		activeName: 'resourceMgt'
-			// 	},
-			// 	children: []
-			// },
 			// 资源中心
 			{
 				path: 'resource',

+ 582 - 0
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue

@@ -0,0 +1,582 @@
+<template>
+    <div class="area-data-container">
+        <Loading v-show="isLoading"></Loading>
+        <vuescroll>
+            <!-- 头部统计 -->
+            <div class="top-block-wrap">
+                <div class="content-con-item border-style" v-for="(item,index) in topData" :key="index">
+                    <div class="left-area" :style="{background: item.color, width: '36%'}">
+                        <Icon :type="item.icon" class="icon" style="font-size: 40px; color: rgb(255, 255, 255);"></Icon>
+                    </div>
+                    <div class="right-area" style="width: 64%;">
+                        <div>
+                            <div class="count-to-wrapper">
+                                <p class="content-outer">
+                                    <CountTo class="count-to-count-text count-style" :endVal="item.number" :duration="600"></CountTo>
+                                    <span style="font-size: 28px;" v-if="item.type === 'rate'">%</span>
+                                </p>
+                            </div>
+                            <p>{{item.text}}</p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!-- 网络研修数据统计 -->
+            <div class="online-train-wrap">
+                <h4 class="block-title">网络研修统计</h4>
+                <div class="online-data-wrap">
+                    <div class="percent-wrap border-style">
+                        <Percent :count="dimension"></Percent>
+                    </div>
+                    <div class="point-wrap border-style">
+                        <AbilityPoint :count="abilityPoint"></AbilityPoint>
+                    </div>
+                </div>
+            </div>
+            <!-- 校本研修数据统计 -->
+            <div class="online-train-wrap">
+                <h4 class="block-title">校本研修统计</h4>
+                <div class="offline-data-wrap">
+                    <div class="offline-data-item border-style" v-for="(item,index) in trainType" :key="index">
+                        <img class="offline-img" :src="trainImgs[index]">
+                        <div style="margin-left:20px;height:fit-content;">
+                            <p class="offline-label" :title="item.label">{{item.label}}</p>
+                            <p class="offline-value"><b>{{item.count || 0}}</b></p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!-- 学时数据统计 -->
+            <div class="online-train-wrap" style="margin-bottom:40px">
+
+                <h4 class="block-title">学时统计</h4>
+                <div class="hour-data-wrap">
+                    <div class="hour-data-item border-style" v-for="(item,index) in hourData" :key="index">
+                        <p class="hour-data-title">
+                            {{item.name}}
+                            <span class="hour-value">({{item.total}}学时)</span>
+                        </p>
+                        <div class="hour-chart">
+                            <HourProg :count="[item.complete,item.going,item.uncomplete]"></HourProg>
+                            <p class="legend-item">
+                                <span>
+                                    未开始:{{item.uncomplete}}人
+                                </span>
+                            </p>
+                            <p class="legend-item">
+                                <span>
+                                    进行中:{{item.going}}人
+                                </span>
+                            </p>
+                            <p class="legend-item">
+                                <span>
+                                    已完成:{{item.complete}}人
+                                </span>
+                            </p>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!-- 学校概览 -->
+            <div class="online-train-wrap" style="margin-bottom:40px">
+                <div>
+                    <h4 class="block-title">学校总览</h4>
+                    <Button class="export-data-wrap" type="primary" icon="md-cloud-download">
+                        导出数据
+                    </Button>
+                    <Input search placeholder="搜索学校" class="school-search" />
+                </div>
+
+                <div class="school-data-wrap">
+                    <div class="school-data-item border-style" v-for="(item,index) in schoolList" :key="index">
+                        <img class="school-img" :src="item.picture">
+                        <div style="margin-left:20px;height:fit-content;">
+                            <p class="school-name" :title="item.label">{{item.name}}</p>
+                            <p class="school-value">
+                                <span>参训人数:</span>
+                                <span>68人</span>
+                            </p>
+                            <p class="school-value">
+                                <span>完成人数:</span>
+                                <span>48人</span>
+                            </p>
+                        </div>
+                        <span class="to-school-detail">
+                            更多>>>
+                        </span>
+                    </div>
+                </div>
+            </div>
+        </vuescroll>
+    </div>
+</template>
+<script>
+import Percent from '@/view/statistics/Percent.vue'
+import AbilityPoint from '@/view/statistics/AbilityPoint.vue'
+import HourProg from './HourProg.vue'
+import CountTo from 'vue-count-to'
+export default {
+    components: {
+        Percent, AbilityPoint, HourProg, CountTo
+    },
+    data() {
+        return {
+            schoolList: [],
+            topData: [
+                {
+                    icon: 'ios-people',
+                    color: '#2d8cf0',
+                    number: 281,
+                    text: '学校数量',
+                    type: 'num'
+                },
+                {
+                    icon: 'md-cube',
+                    color: '#2db7f5',
+                    number: 3021,
+                    text: '教师人数',
+                    type: 'num'
+                },
+                {
+                    icon: 'md-bookmark',
+                    color: '#5cadff',
+                    number: 2890,
+                    text: '参训人数',
+                    type: 'num'
+                },
+                {
+                    icon: 'md-thumbs-up',
+                    color: '#ff9900',
+                    number: 83,
+                    text: '参训率',
+                    type: 'rate'
+                },
+                {
+                    icon: 'md-checkmark-circle',
+                    color: '#19be6b',
+                    number: 13,
+                    text: '完成率',
+                    type: 'rate'
+                }
+
+            ],
+            schoolInfo: undefined,
+            isLoading: false,
+            dimension: [],
+            abilityPoint: [],
+            offlineData: [
+                {
+                    label: '教研组',
+                    value: 0
+                }
+            ],
+            trainType: [],
+            trainImgs: [
+                'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E4%BF%A1%E6%81%AF%E5%8C%96%E6%95%99%E5%AD%A6%E6%A1%88%E4%BE%8B.png',
+                'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E4%B8%93%E5%AE%B6%E4%B8%93%E9%A2%98%E5%9F%B9%E8%AE%AD.png',
+                'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E5%90%8C%E8%AF%BE%E5%BC%82%E6%9E%84.png',
+                'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E5%90%8C%E8%AF%BE%E5%90%8C%E6%9E%84.png',
+                'https://teammodelstorage.blob.core.chinacloudapi.cn/0-public/image/%E6%A0%A1%E6%9C%AC2.0%E5%9F%B9%E8%AE%AD.png'
+            ],
+            hourData: [
+                {
+                    name: '线上研修',
+                    total: 20,
+                    complete: 0,
+                    going: 0,
+                    uncomplete: 0
+                },
+                {
+                    name: '校本研修',
+                    total: 10,
+                    complete: 0,
+                    going: 0,
+                    uncomplete: 0
+                },
+                {
+                    name: '应用考核',
+                    total: 15,
+                    complete: 0,
+                    going: 0,
+                    uncomplete: 0
+                },
+                {
+                    name: '课堂实录',
+                    total: 5,
+                    complete: 0,
+                    going: 0,
+                    uncomplete: 0
+                }
+            ]
+        }
+    },
+    methods: {
+        returnList() {
+            this.$router.push({
+                name: 'areaMgmt'
+            })
+        },
+        findTotalData() {
+            this.isLoading = true
+            let params = {
+                school: this.schoolInfo.id
+            }
+            this.$api.ability.FindTotalData(params).then(
+                res => {
+                    if (!res.error) {
+                        let deData = this.$GLOBAL.DIMENSIONS()
+                        // 能力点统计
+                        res.abilityCount.forEach(item => {
+                            item.percent = (item.count * 100 / res.subCount).toFixed(1) + '%'
+                            let d = deData.find(i => {
+                                return i.code == item.dimension
+                            })
+                            if (d) {
+                                item.dimensionName = d.val
+                            }
+                        })
+                        this.abilityPoint = res.abilityCount
+
+                        // 能力维度统计
+                        res.dimensionCount.forEach(item => {
+                            let d = deData.find(i => {
+                                return i.code == item.dimension
+                            })
+                            if (d) {
+                                item.name = d.val
+                            }
+                        })
+                        this.dimension = res.dimensionCount
+
+                        //学时数据统计
+                        res.groupMembers.forEach(item => {
+                            // 线上研修
+                            if (item.onlineTime == 0) {
+                                this.hourData[0].uncomplete++
+                            } else if (item.onlineTime >= 20) {
+                                this.hourData[0].complete++
+                            } else {
+                                this.hourData[0].going++
+                            }
+                            // 校本研修
+                            if (item.offlinelTime == 0) {
+                                this.hourData[1].uncomplete++
+                            } else if (item.onlineTime >= 10) {
+                                this.hourData[1].complete++
+                            } else {
+                                this.hourData[1].going++
+                            }
+                            // 应用考核
+                            if (item.schoolScoreTime == 0) {
+                                this.hourData[2].uncomplete++
+                            } else if (item.onlineTime >= 15) {
+                                this.hourData[2].complete++
+                            } else {
+                                this.hourData[2].going++
+                            }
+                            // 课堂实录
+                            if (item.classVideoTime == 0) {
+                                this.hourData[3].uncomplete++
+                            } else if (item.onlineTime >= 5) {
+                                this.hourData[3].complete++
+                            } else {
+                                this.hourData[3].going++
+                            }
+                        })
+                    }
+                },
+                err => {
+
+                }
+            ).finally(() => {
+                this.isLoading = false
+            })
+        },
+        findTrainList() {
+            let params = {
+                code: this.schoolInfo.id
+            }
+            this.$api.train.findTrainList(params).then(
+                res => {
+                    console.log(res)
+                    this.trainType = this.$GLOBAL.TRAIN_TYPE()
+                    res.studies.forEach(item => {
+                        this.trainType[item.type - 1].count = this.trainType[item.type - 1].count || 0
+                        this.trainType[item.type - 1].count++
+                    })
+                    this.trainType.splice(this.trainType.length - 1, 1)
+                },
+                err => {
+                }
+            )
+        }
+    },
+    created() {
+        this.schoolInfo = {}
+        this.schoolInfo.id = 'hbcn'
+        this.findTotalData()
+        this.findTrainList()
+        this.schoolList = this.$store.state.user.userProfile.schools
+    },
+    watch: {
+
+    }
+}
+</script>
+<style lang="less" scoped>
+.export-data-wrap {
+    float: right;
+    // margin-top: 8px;
+    cursor: pointer;
+}
+.school-search {
+    float: right;
+    width: 240px;
+    border-radius: 20px;
+    margin-right: 20px;
+}
+.border-style {
+    box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 4px;
+}
+.hour-chart {
+    // height: 200px;
+    text-align: center;
+    .legend-item {
+        font-size: 12px;
+        margin-top: 5px;
+        &::before {
+            content: " ";
+            display: inline-block;
+            width: 15px;
+            height: 8px;
+            // background: red;
+            border-radius: 2px;
+            margin-right: 10px;
+            margin-left: 15px;
+        }
+        &:nth-child(2)::before {
+            background: #ed4014;
+        }
+        &:nth-child(3)::before {
+            background: #ff9900;
+        }
+        &:nth-child(4)::before {
+            background: #19be6b;
+        }
+    }
+}
+.hour-data-item {
+    width: 23%;
+    background: white;
+    padding-bottom: 20px;
+}
+.hour-data-wrap {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 20px;
+    font-size: 16px;
+    .hour-value {
+        font-weight: bold;
+        color: #1e1f24;
+        font-size: 16px;
+    }
+    .hour-data-title {
+        text-align: center;
+        // background: #f9f9f9;
+        padding: 10px 0px;
+    }
+}
+.offline-data-wrap {
+    margin-bottom: 20px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 20px;
+}
+.offline-data-item {
+    // margin: 10px;
+    display: flex;
+    align-items: center;
+    background: white;
+    padding: 15px 30px;
+    border-radius: 5px;
+    .offline-label {
+        width: 120px;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: nowrap;
+        color: #1e1f24;
+        cursor: pointer;
+    }
+    .offline-value {
+        margin-top: 5px;
+        font-size: 30px;
+        font-weight: bold;
+        color: #1e1f24;
+    }
+}
+.school-data-wrap {
+    margin-bottom: 20px;
+    display: flex;
+    // justify-content: space-between;
+    align-items: center;
+    margin-top: 20px;
+}
+.school-data-item {
+    position: relative;
+    display: flex;
+    align-items: center;
+    background: white;
+    padding: 20px 40px 20px 30px;
+    margin-right: 30px;
+    border-radius: 5px;
+    transition: all 0.2s ease 0s;
+    &:hover {
+        box-shadow: 0 26px 40px -24px #aaa;
+        transform: translateY(-4px);
+    }
+    .school-name {
+        width: 120px;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: nowrap;
+        color: #17233d;
+        cursor: pointer;
+        font-weight: 600;
+        font-size: 15px;
+    }
+    .school-value {
+        margin-top: 5px;
+        font-size: 12px;
+        font-weight: bold;
+        color: #808695;
+    }
+    .to-school-detail {
+        position: absolute;
+        right: 5px;
+        bottom: 5px;
+        color: #2d8cf0;
+        font-size: 12px;
+        cursor: pointer;
+    }
+}
+.offline-img {
+    width: 80px;
+    height: 80px;
+    border-radius: 10px;
+}
+.school-img {
+    width: 80px;
+    height: 80px;
+    border-radius: 10px;
+}
+.area-data-container {
+    width: 100%;
+    height: 100%;
+    padding: 15px;
+    background: #f7f7f7;
+}
+.school-base-info {
+    border-radius: 5px;
+    width: 100%;
+    background: white;
+    padding: 25px 15px;
+    // box-shadow: 0px 4px 4px 1px #f0f0f0;
+    display: flex;
+    position: relative;
+    align-items: center;
+}
+.return-list {
+    position: absolute;
+    right: 80px;
+    top: 35px;
+    color: #1cc0f3;
+    font-weight: bold;
+    cursor: pointer;
+}
+.school-info-item {
+    margin-top: 10px;
+    .info-label {
+        color: #a0a0a0;
+    }
+    .info-value {
+        color: #5cadff;
+        font-weight: 600;
+    }
+}
+.online-train-wrap {
+    border-radius: 5px;
+    width: 100%;
+    // background: white;
+    padding: 0px 20px;
+    // box-shadow: 0px 4px 4px 1px #f0f0f0;
+    margin-top: 50px;
+}
+.block-title {
+    border-left: 6px solid #1cc0f3;
+    padding-left: 15px;
+    font-size: 18px;
+    line-height: 16px;
+    color: #414749;
+    margin-bottom: 0px;
+}
+.online-data-wrap {
+    display: flex;
+    margin-top: 15px;
+}
+.percent-wrap {
+    width: fit-content;
+    padding: 30px 0px;
+    background: white;
+    // margin-left: 30px;
+}
+.point-wrap {
+    width: fit-content;
+    padding: 30px 0px;
+    background: white;
+    margin-left: 20px;
+    flex: 1;
+}
+.top-block-wrap {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    margin-top: 10px;
+    padding: 0px 20px 0px 20px;
+    // background: white;
+    // border-radius: 5px;
+}
+.content-con-item {
+    width: 18%;
+    height: 110px;
+    position: relative;
+    border-radius: 2px;
+    overflow: hidden;
+    .left-area {
+        float: left;
+        height: 100%;
+        display: table;
+        text-align: center;
+        .icon {
+            display: table-cell;
+            vertical-align: middle;
+        }
+    }
+    .right-area {
+        background: white;
+        float: left;
+        height: 100%;
+        display: table;
+        text-align: center;
+        .count-style {
+            font-size: 50px;
+        }
+    }
+}
+</style>
+<style lang="less">
+.school-search .ivu-input {
+    // border-radius: 20px;
+}
+</style>

+ 10 - 0
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaLayout.vue

@@ -131,6 +131,16 @@ export default {
                     menuName: 'areaMgmt',
                     child: []
                 },
+                // {
+                //     icon: 'iconfont icon-shujufenxi',
+                //     name: '学校总览',
+                //     router: '/area/schoolMgmt',
+                //     tag: '',
+                //     role: '',
+                //     permission: '',
+                //     menuName: 'schoolMgmt',
+                //     child: []
+                // },
                 {
                     icon: 'iconfont icon-basic-setting',
                     name: '研修总览',

TEAMModelOS/ClientApp/src/view/areaMgmt/Index.less → TEAMModelOS/ClientApp/src/view/areaMgmt/SchoolIndex.less


+ 1 - 1
TEAMModelOS/ClientApp/src/view/areaMgmt/Index.vue

@@ -65,4 +65,4 @@ export default {
 }
 </script>
 
-<style lang="less" src="./Index.less" scoped></style>
+<style lang="less" src="./SchoolIndex.less" scoped></style>

+ 232 - 2
TEAMModelOS/Controllers/Analysis/AchievementController.cs

@@ -27,7 +27,7 @@ namespace TEAMModelOS.Controllers.Analysis
 {
     [Route("analysis")]
     [ApiController]
-    public class AchievementController : Controller
+    public class AchievementController : ControllerBase
     {
 
         private readonly AzureCosmosFactory _azureCosmos;
@@ -4253,7 +4253,7 @@ namespace TEAMModelOS.Controllers.Analysis
                             tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(ex, ex.id, new PartitionKey($"{ex.code}")));
                         }
                     }
-                }                
+                }
                 await Task.WhenAll(tasks);
                 return Ok(new { examClassResults });
             }
@@ -4316,7 +4316,237 @@ namespace TEAMModelOS.Controllers.Analysis
                 return BadRequest();
             }
         }
+
+
+        [ProducesDefaultResponseType]
+        [HttpPost("upsert-record")]
+        public async Task<IActionResult> upsertRecord(JsonElement request)
+        {
+
+            if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+            if (!request.TryGetProperty("students", out JsonElement students)) return BadRequest();
+            if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
+            //if (!request.TryGetProperty("multipleRule", out JsonElement multipleRule)) return BadRequest();
+            //if (!request.TryGetProperty("paperId", out JsonElement paperId)) return BadRequest();
+            //根据不同评测的类型返回对应的编码
+            if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+            //if (!request.TryGetProperty("scode", out JsonElement scode)) return BadRequest();
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                //List<string> ids = students.ToObject<List<string>>();
+                List<(string stuId, List<List<string>> answer)> stus = new List<(string stuId, List<List<string>> answer)>();
+                ExamInfo info = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(id.GetString(), new PartitionKey($"Exam-{code}"));
+
+                List<ExamClassResult> examClassResults = new List<ExamClassResult>();
+                int n = 0;
+                foreach (ExamSubject subject in info.subjects)
+                {
+                    if (!subject.id.Equals(subjectId))
+                    {
+                        n++;
+                    }
+                }
+                //获取试卷信息
+                PaperSimple standerAnswers = new PaperSimple();
+                List<List<string>> standard = new List<List<string>>();
+                List<double> points = new List<double>();
+                standerAnswers = info.papers[n];
+                standard = standerAnswers.answers;
+                points = standerAnswers.point;
+                int rule = standerAnswers.multipleRule;
+                //List<Task<ItemResponse<ExamClassResult>>> tasks = new List<Task<ItemResponse<ExamClassResult>>>();
+                string classCode = "";
+                classCode = info.scope.Equals("school") ? info.school : info.creatorId;
+                stus.ForEach(async s =>
+                {
+                    bool isExist = false;
+                    foreach (ExamClassResult result in examClassResults)
+                    {
+                        if (result.studentIds.Contains(s.stuId))
+                        {
+                            isExist = true;
+                        }
+                    }
+                    if (!isExist)
+                    {
+                        examClassResults = new List<ExamClassResult>();
+                        await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryIterator<ExamClassResult>(
+                           queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}' and array_contains(c.studentIds,'{s.stuId}') ",
+                           requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{classCode}") }))
+                        {
+                            examClassResults.Add(item);
+                        }
+                    }
+                    List<List<string>> ans = s.answer;
+                    foreach (ExamClassResult result in examClassResults)
+                    {
+                        int newIndex = result.studentIds.IndexOf(id.ToString());
+                        for (int i = 0; i < ans.Count; i++)
+                        {
+                            var ac = ans[i].Count;
+                            var sc = standard[i].Count;
+                            //记录次数
+                            int n = 0;
+                            //算分处理
+                            if (sc > 0)
+                            {
+                                result.ans[newIndex][i] = ans[i];
+                                if (ac == sc && sc == 1)
+                                {
+                                    foreach (string right in ans[i])
+                                    {
+                                        if (standard[i].Contains(right))
+                                        {
+                                            result.studentScores[newIndex][i] = points[i];
+                                        }
+                                        else
+                                        {
+                                            result.studentScores[newIndex][i] = 0;
+                                        }
+                                    }
+
+                                }
+                                else
+                                {
+                                    if (rule > 0)
+                                    {
+                                        int falseCount = 0;
+                                        if (ac > 0)
+                                        {
+                                            foreach (string obj in ans[i])
+                                            {
+                                                if (!standard[i].Contains(obj))
+                                                {
+                                                    falseCount++;
+                                                }
+                                            }
+                                            switch (rule)
+                                            {
+                                                case 1:
+                                                    if (ac == sc)
+                                                    {
+                                                        if (falseCount == 0)
+                                                        {
+                                                            result.studentScores[newIndex][i] = points[i];
+                                                        }
+                                                        else
+                                                        {
+                                                            result.studentScores[newIndex][i] = 0;
+                                                        }
+                                                    }
+                                                    else
+                                                    {
+                                                        result.studentScores[newIndex][i] = 0;
+                                                    }
+                                                    break;
+                                                case 2:
+                                                    if (falseCount > 0)
+                                                    {
+                                                        result.studentScores[newIndex][i] = 0;
+                                                    }
+                                                    else
+                                                    {
+                                                        if (ac == sc)
+                                                        {
+                                                            result.studentScores[newIndex][i] = points[i];
+                                                        }
+                                                        else
+                                                        {
+                                                            result.studentScores[newIndex][i] = points[i] / 2;
+                                                        }
+
+                                                    }
+                                                    break;
+                                                case 3:
+                                                    if (falseCount > 0)
+                                                    {
+                                                        result.studentScores[newIndex][i] = 0;
+                                                    }
+                                                    else
+                                                    {
+                                                        if (ac == sc)
+                                                        {
+                                                            result.studentScores[newIndex][i] = points[i];
+                                                        }
+                                                        else
+                                                        {
+                                                            result.studentScores[newIndex][i] = System.Math.Round((double)ac / sc * points[i], 1);
+                                                        }
+
+                                                    }
+                                                    break;
+                                                case 4:
+                                                    if (ac == sc)
+                                                    {
+                                                        result.studentScores[newIndex][i] = points[i];
+                                                    }
+                                                    else
+                                                    {
+                                                        double persent = (double)(sc - 2 * falseCount) / sc;
+                                                        if (persent <= 0)
+                                                        {
+                                                            result.studentScores[newIndex][i] = 0;
+                                                        }
+                                                        else
+                                                        {
+                                                            result.studentScores[newIndex][i] = System.Math.Round(persent * points[i], 1);
+                                                        }
+                                                    }
+                                                    break;
+                                            }
+                                        }
+                                        else
+                                        {
+                                            result.studentScores[newIndex][i] = 0;
+                                        }
+
+                                    }
+                                }
+                            }
+                        }
+                        bool flag = true;
+                        foreach (List<double> scores in result.studentScores)
+                        {
+                            foreach (double score in scores)
+                            {
+                                if (score == -1)
+                                {
+                                    flag = false;
+                                    break;
+                                }
+                            }
+                        }
+                        if (flag)
+                        {
+                            result.progress = true;
+                            info.subjects.ForEach(s =>
+                            {
+                                if (s.id.Equals(subjectId.ToString()))
+                                {
+                                    s.classCount += 1;
+                                }
+                            });
+                            await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(info, id.ToString(), new PartitionKey($"Exam-{code}"));
+                        }
+                        result.sum[newIndex] = result.studentScores[newIndex].Sum();
+                        await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(result, result.id, new PartitionKey($"{result.code}"));
+                    }
+                });
+                //await Task.WhenAll(tasks);
+                return Ok(new { code = 200 });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/upsert-record()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+
+        }
     }
+
+
+
 }
 //private List<Dictionary<string, dynamic>> getKnowledgePoint(List<ExamResult> examResults, ExamInfo info)
 //{

+ 2 - 0
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -529,6 +529,7 @@ namespace TEAMModelOS.Controllers.Core
                 }
                 examRcds = new ExamData
                 {
+                    code = exam.code.Replace("Exam-", ""),
                     id = exam.id,
                     name = exam.name,
                     startTime = exam.startTime,
@@ -569,6 +570,7 @@ namespace TEAMModelOS.Controllers.Core
     }
     public record ExamData
     {
+        public string code { get; set; }
         public SheetConfig sheet { get; set; }
         public string id { get; set; }
         public string name { get; set; }

+ 97 - 0
TEAMModelOS/Controllers/Common/AreaSettingController.cs

@@ -0,0 +1,97 @@
+using Azure.Cosmos;
+using Azure.Storage.Sas;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.Filter;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Service;
+
+namespace TEAMModelOS.Controllers.Common
+{
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    [Route("school/area-setting")]
+    [ApiController]
+    public class AreaSettingController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly SnowflakeId _snowflakeId;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+        public IConfiguration _configuration { get; set; }
+        public AreaSettingController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing,
+            IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, IConfiguration configuration)
+        {
+            _azureCosmos = azureCosmos;
+            _serviceBus = serviceBus;
+            _snowflakeId = snowflakeId;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
+            _configuration = configuration;
+        }
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "Teacher")]
+        [HttpPost("find-id")]
+        [AuthToken(Roles = "teacher,student,admin,area")] 
+        public async Task<IActionResult> FindId(JsonElement request) {
+            var client = _azureCosmos.GetCosmosClient();
+            if (!request.TryGetProperty("areaId", out JsonElement _areaId)) return BadRequest();
+            AreaSetting setting = null;
+            try
+            {
+                setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>($"{_areaId}", partitionKey: new Azure.Cosmos.PartitionKey("AreaSetting"));
+                return Ok(new { setting });
+            }
+            catch (CosmosException ex)
+            {
+                return Ok(new { setting, error = ex.Status });
+            }
+        }
+        /// <summary>
+        /// 保存 
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("upsert")]
+        [AuthToken(Roles = "teacher,student,admin,area")]
+        public async Task<IActionResult> upsert(AreaSetting setting)
+        {
+            var client = _azureCosmos.GetCosmosClient();
+            try
+            {
+                setting.pk = "AreaSetting";
+                setting.code = "AreaSetting";
+                setting.ttl = -1;
+                setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").UpsertItemAsync<AreaSetting>(setting, partitionKey: new Azure.Cosmos.PartitionKey("AreaSetting"));
+                return Ok(new { setting, });
+            }
+            catch (CosmosException ex)
+            {
+                return Ok(new { error = ex.Status });
+            }
+        }
+    }
+}

+ 4 - 2
TEAMModelOS/Controllers/Teacher/InitController.cs

@@ -538,12 +538,14 @@ namespace TEAMModelOS.Controllers
             }
             catch (CosmosException ex)
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/init/get-school-info()\n{ex.Message}{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                HttpContext.Request.Headers.TryGetValue("referer", out var referer);
+                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/init/get-school-info()\n{ex.Message}{ex.StackTrace}{request.ToJsonString()}\n{referer}", GroupNames.醍摩豆服務運維群組);
                 return Ok(new { status = ex.Status });
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/init/get-school-info()\n{ex.Message}{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                HttpContext.Request.Headers.TryGetValue("referer", out var referer);
+                await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/init/get-school-info()\n{ex.Message}{ex.StackTrace}{request.ToJsonString()}\n{referer}", GroupNames.醍摩豆服務運維群組);
                 return Ok(new { status = 500 });
             }
         }

+ 12 - 8
TEAMModelOS/Controllers/XTest/FixDataController.cs

@@ -89,17 +89,21 @@ namespace TEAMModelOS.Controllers
           
             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<School>(queryText: "SELECT  value(c) FROM c", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") }))
             {
-                if (arr.Contains(item.id))
-                {
-                    item.areaId = "02944f32-f534-3397-ea56-e6f1fc6c3714";
+                if (string.IsNullOrEmpty(item.areaId)) {
                     item.standard = "standard2";
                     await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey("Base"));
                 }
-                else if (item.standard.Equals("standard2")) {
-                    item.areaId = null;
-                    item.standard = null;
-                    await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey("Base"));
-                }
+                //if (arr.Contains(item.id))
+                //{
+                //    item.areaId = "02944f32-f534-3397-ea56-e6f1fc6c3714";
+                //    item.standard = "standard2";
+                //    await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey("Base"));
+                //}
+                //else if (item.standard.Equals("standard2")) {
+                //    item.areaId = null;
+                //    item.standard = null;
+                //    await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey("Base"));
+                //}
             }
             return Ok();
         }

+ 7 - 3
TEAMModelOS/TEAMModelOS.csproj

@@ -37,12 +37,16 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2109.10</Version>
-    <AssemblyVersion>5.2109.10.1</AssemblyVersion>
-    <FileVersion>5.2109.10.1</FileVersion>
+    <Version>5.2109.16</Version>
+    <AssemblyVersion>5.2109.16.1</AssemblyVersion>
+    <FileVersion>5.2109.16.1</FileVersion>
     <Description>TEAMModelOS(IES5)</Description>
     <PackageReleaseNotes>版本说明</PackageReleaseNotes>
   </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
   <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build">
     <!-- Build Target:  Ensure Node.js is installed -->
     <Exec Command="node --version" ContinueOnError="true">