Browse Source

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

upon 1 year ago
parent
commit
1fe702fa44

+ 16 - 5
TEAMModelOS.SDK/DI/DingDing/DingDing.cs

@@ -12,6 +12,7 @@ using System.Text.Json;
 using System.IO;
 using TEAMModelOS.SDK.Extension;
 using System.Diagnostics;
+using HTEXLib.COMM.Helpers;
 
 namespace TEAMModelOS.SDK.DI
 {
@@ -58,16 +59,27 @@ namespace TEAMModelOS.SDK.DI
         /// <param name="secret">加簽密鑰</param>
         /// <param name="msg">發送訊息</param>
         /// <returns></returns>
-        public async Task SendBotMsg(string msg, GroupNames groupkey)
+        public async Task SendBotMsg(string msg , GroupNames groupkey, List<string> mobiles = null)
         {
             // TODO 有空處理自動抓取方法名,代碼行數顯示
             //StackTrace st = new StackTrace(new StackFrame(1, true));
             //StackFrame sf = st.GetFrame(0);            
             //var f = $"Func:{sf.GetMethod().Name},Line : {sf.GetFileLineNumber()}";
-
-            var content = new { msgtype = "text", text = new { content = msg } };
+            List<string> atMobiles = new List<string>();
+            if (mobiles.IsNotEmpty()) {
+                atMobiles=mobiles;
+            }
+            var content = new { msgtype = "text", text = new { content = msg }, at=new { atMobiles  } };
 #if DEBUG
-            var keys = GroupNames.成都开发測試群組.GetDescriptionText().Split(',');
+            string[] keys = null; 
+            if (groupkey.Equals(GroupNames.醍摩豆小财神))
+            {
+                  keys = GroupNames.醍摩豆小财神.GetDescriptionText().Split(',');
+            }
+            else
+            {
+                  keys = GroupNames.成都开发測試群組.GetDescriptionText().Split(',');
+            }
 #else
             var keys = groupkey.GetDescriptionText().Split(',');
 #endif
@@ -78,7 +90,6 @@ namespace TEAMModelOS.SDK.DI
                 await _httpClient.PostAsJsonAsync($"{url}{keys[0]}&timestamp={timestamp}&sign={BotAddSign(keys[1], timestamp)}", content);
             }
         }
-
         // <summary>
         /// 發送需要加簽驗證的Bot訊息(msgtype為text)
         /// </summary>

+ 2 - 3
TEAMModelOS.SDK/DI/Mail/MailFactory.cs

@@ -66,16 +66,15 @@ namespace TEAMModelOS.SDK.DI.Mail
             });
             return services;
         }
-        public static string SendEmail(this SmtpClient smtpClient, AzureRedisFactory coreRedisFactory, string subject, string content, string toMail,string tmdid,string tmdname )
+        public static string SendEmail(this SmtpClient smtpClient, AzureRedisFactory coreRedisFactory, string subject, string content, string toMail,string tmdid,string tmdname ,string sender = "醍摩豆客服助手")
         {
             if (smtpClient!=null)
             {
                 NetworkCredential networkCredential = (NetworkCredential)smtpClient.Credentials!;
-                MailMessage mailMessage = new MailMessage(new MailAddress(networkCredential.UserName, "醍摩豆发票助手"), new MailAddress(toMail, "醍摩豆用户"));
+                MailMessage mailMessage = new MailMessage(new MailAddress(networkCredential.UserName, sender), new MailAddress(toMail, $"{tmdname}({tmdid})"));
                 mailMessage.Subject = subject;
                 mailMessage.Body = content;
                 mailMessage.IsBodyHtml = true;
-
                 smtpClient.SendCompleted+=async (sender, e) =>
                 {
                     int result = 0;

+ 29 - 20
TEAMModelOS.SDK/Models/Service/LessonService.cs

@@ -177,11 +177,11 @@ namespace TEAMModelOS.SDK.Models.Service
             {
                 int year = DateTimeOffset.UtcNow.Year;
                 string hs = lessonBase?.report?.clientSummaryList.ToJsonString();
-                var clientSummaryList = lessonBase?.report?.clientSummaryList?.Where(x => x.groupTaskCompleteCount != 0 || x.groupScore != 0 || x.score != 0 || x.tnteractScore != 0 || x.taskCompleteCount != 0);
+                var clientSummaryLists = lessonBase?.report?.clientSummaryList?.Where(x => x.groupTaskCompleteCount != 0 || x.groupScore != 0 || x.score != 0 || x.tnteractScore != 0 || x.taskCompleteCount != 0);
                 IEnumerable<LessonStudent> students = new List<LessonStudent>();
-                if (clientSummaryList.Any())
+                if (clientSummaryLists.Any())
                 {
-                    var ids = clientSummaryList.Select(x => x.seatID);
+                    var ids = clientSummaryLists.Select(x => x.seatID);
                     students = lessonBase.student.Where(x => ids.Contains(x.seatID));
                 }
                 List<Student> studentsBase = new List<Student>();
@@ -223,18 +223,21 @@ namespace TEAMModelOS.SDK.Models.Service
                 if (!string.IsNullOrWhiteSpace(school)) {
                      schoolBase = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
                      period = schoolBase.period.Find(x => x.id.Equals($"{lessonRecord.periodId}"));
-                     dataSemester= SchoolService.GetSemester(period);
+                    if (period!= null) {
+                        dataSemester= SchoolService.GetSemester(period);
+                    }
+                     
                 }
                 List <Task<ItemResponse<StudentScoreRecord>>> records = new List<Task<ItemResponse<StudentScoreRecord>>>();
                  HashSet <OverallEducation> overallEducations   = new HashSet<OverallEducation>();
-                stuids.ToList().ForEach(async x => {
+                foreach (var x in stuids) {
                     var record = lessonStudentRecords.Find(l => l.stuid.Equals(x.id) && l.code.Equals($"StudentScoreRecord") && l.school.Equals(x.school));
                     ClientSummaryList clientSummaryList = lessonBase.report.clientSummaryList.Find(c => c.seatID == x.seatID);
                     if (record != null)
                     {
                         if (clientSummaryList != null)
                         {
-                            var hasrecord=  record.lessonRecords.Find(x => x.lessonId.Equals(lessonRecord.id));
+                            var hasrecord = record.lessonRecords.Find(x => x.lessonId.Equals(lessonRecord.id));
                             if (hasrecord != null)
                             {
                                 hasrecord.gscore = clientSummaryList.groupScore;
@@ -249,7 +252,8 @@ namespace TEAMModelOS.SDK.Models.Service
                                 hasrecord.subjectId = lessonRecord.subjectId;
                                 hasrecord.time = lessonRecord.startTime;
                             }
-                            else {
+                            else
+                            {
                                 record.lessonRecords.Add(
                                      new StudentLessonRecord
                                      {
@@ -266,7 +270,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                          time = lessonRecord.startTime
                                      }
                                  );
-                            } 
+                            }
                         }
                     }
                     else
@@ -302,13 +306,15 @@ namespace TEAMModelOS.SDK.Models.Service
                     record.gscore = record.lessonRecords.Select(x => x.gscore).Sum();
                     record.pscore = record.lessonRecords.Select(x => x.pscore).Sum();
                     record.tscore = record.lessonRecords.Select(x => x.tscore).Sum();
-                    if (dataSemester.currSemester != null  && (clientSummaryList.groupScore>0  || clientSummaryList.score > 0 || clientSummaryList.tnteractScore > 0)) {
-                        string oid = $"{dataSemester.studyYear}-{dataSemester.currSemester.id}-{record.stuid}"; 
+                    if (dataSemester.currSemester != null  && (clientSummaryList.groupScore>0  || clientSummaryList.score > 0 || clientSummaryList.tnteractScore > 0))
+                    {
+                        string oid = $"{dataSemester.studyYear}-{dataSemester.currSemester.id}-{record.stuid}";
                         string ocode = $"OverallEducation-{school}";
-                        var student= studentsBase.Find(stu => stu.id.Equals(x.id));
+                        var student = studentsBase.Find(stu => stu.id.Equals(x.id));
                         Azure.Response response = await client.GetContainer(Constant.TEAMModelOS, Constant.Student).ReadItemStreamAsync(oid, new PartitionKey(ocode));
                         OverallEducation overallEducation = null;
-                        if (response.Status != 200) {
+                        if (response.Status != 200)
+                        {
                             overallEducation = new OverallEducation
                             {
                                 id =oid,
@@ -339,7 +345,8 @@ namespace TEAMModelOS.SDK.Models.Service
                              }}
                             };
                         }
-                        else {
+                        else
+                        {
                             overallEducation=JsonDocument.Parse(response.Content).RootElement.ToObject<OverallEducation>();
                             var hasrecord = overallEducation.lessonScore.Find(x => x.lessonId.Equals(lessonRecord.id));
                             if (hasrecord != null)
@@ -379,14 +386,17 @@ namespace TEAMModelOS.SDK.Models.Service
                         overallEducations.Add(overallEducation);
                     }
                     records.Add(client.GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(record, partitionKey: new PartitionKey(record.code)));
-                });
-                tmdids.ToList().ForEach(x => {
+
+                }
+
+
+                foreach (var x in tmdids) {
                     var record = lessonStudentRecords.Find(l => l.tmdid.Equals(x.id) && l.code.Equals($"StudentScoreRecord"));
                     ClientSummaryList clientSummaryList = lessonBase.report.clientSummaryList.Find(c => c.seatID == x.seatID);
                     if (record != null)
                     {
                         if (clientSummaryList != null)
-                        {   
+                        {
                             var hasrecord = record.lessonRecords.Find(x => x.lessonId.Equals(lessonRecord.id));
                             if (hasrecord != null)
                             {
@@ -424,7 +434,7 @@ namespace TEAMModelOS.SDK.Models.Service
                         }
                     }
                     else
-                    {  
+                    {
                         record = new StudentScoreRecord
                         {
                             userType = Constant.ScopeTmdUser,
@@ -434,7 +444,7 @@ namespace TEAMModelOS.SDK.Models.Service
                             ttl = -1,
                             year = year,
                             tmdid = x.id,
-                            lessonRecords = new List<StudentLessonRecord> 
+                            lessonRecords = new List<StudentLessonRecord>
                             {
                                 new StudentLessonRecord
                                 {
@@ -458,8 +468,7 @@ namespace TEAMModelOS.SDK.Models.Service
                     record.pscore = record.lessonRecords.Select(x => x.pscore).Sum();
                     record.tscore = record.lessonRecords.Select(x => x.tscore).Sum();
                     records.Add(client.GetContainer(Constant.TEAMModelOS, Constant.Student).UpsertItemAsync(record, partitionKey: new PartitionKey(record.code)));
-                    
-                });
+                }
                 if (records.Any())
                 {
                     await Task.WhenAll(records);

File diff suppressed because it is too large
+ 9 - 0
TEAMModelOS/ClientApp/public/lang/en-US.js


+ 9 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -865,6 +865,7 @@ const LANG_ZH_CN = {
     },
     newCusMgt: {
         sort: '排序方式',
+        sortType0: '按年级排序',
         sortType1: '按名称排序',
         sortType2: '按编号排序',
         setting: '配置',
@@ -928,6 +929,14 @@ const LANG_ZH_CN = {
         joinTip1: '已成功提交申请,请等待审核!',
         joinTip2: '课程人数已满!',
         joinTip3: '课程二维码已过期!',
+        guide: '操作指南',
+        guideTitle: '课程管理操作指南',
+        guideText: `<strong>学校课程安排将课程、班级和教师关联在一起。确立全校课程结构是首要任务,合适的结构可年复一年地重用</strong>。<br>以下建议可供参考:<br>
+				1. 以主要学科名称和年级为命名,如“数学一”、“英语五”。一次性建立全校课程结构,建议确定课程代号的编码规则(尽管系统也可自动生成)。这将使后续的课程表调整更加便捷。<br>
+				2. 初次建立时,建议使用网页操作,逐一为每门课程安排班级和任课教师,以避免错误。可灵活运用教师和名单切换功能,确保没有遗漏。<br>
+				3. 到下个学期,您可以导出上学期的课程表。由于变动较少,调整后再导入新学期,能减轻许多工作负担。<br>
+				4. 迎来新学年时,请先调整班级学生名单,除了新生的加入,部分年级可能会重新编班。导出上学年的课程表,在新学年的新学期中主要修改与课程结构对应的班级名单代号(list栏位),调整后再导入。若排课整体变动较大,建议仍使用网页界面进行操作。`,
+        downloadFiles: '导出备用资料'
     },
     // 课程管理
     cusMgt: {

+ 9 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -865,6 +865,7 @@ const LANG_ZH_TW = {
     },
     newCusMgt: {
         sort: '排序方式',
+        sortType0: '依年級排序',
         sortType1: '依名稱排序',
         sortType2: '依編號排序',
         setting: '配置',
@@ -928,6 +929,14 @@ const LANG_ZH_TW = {
         joinTip1: '需要審核通過才可加入!',
         joinTip2: '課程人數已滿!',
         joinTip3: '課程 QR Code 已過期!',
+        guide: '課程編排指引',
+        guideTitle: '課程編排指引',
+        guideText: `<strong>學校課程編排是將課程, 班級與任課教師關聯起來. 而全校課程架構的準備是第一要務,做好的架構每年可以重用</strong>。<br>建議的作法如下:<br>
+				1. 用主要學科名稱加年級來命名, 例如 數學ㄧ, 數學二, 英語五, 英語六等, 一次將全校所有的課程架構做出來, 建議課程代碼的編碼規則也想好(雖然系統也會自動產生), 後續變動排課表會容易些<br>
+				2. 第一次建立, 建議使用網頁操作來一一為每個課程安排好班級與任課教師避免錯誤, 可以善用教師與名單切換來檢視是漏排<br>
+				3. 到下學期時, 可以將上學期的排課表匯出, 通常變動較少, 調整修改後再匯入到新學期, 可以省掉不少工作<br>
+				4. 跨到新學年時, 先做好班級學生名單調整, 除了新生加入, 有些年級會重新編班. 接著將上學年的排課表匯出, 主要修改課程架構對應的班級名單代號(list欄位), 修改後再匯入到新學年的新學期. 如果排課整體變動過大, 建議還是使用網頁介面來操作`,
+        downloadFiles: '导出备用资料'
     },
     // 课程管理
     cusMgt: {

+ 22 - 5
TEAMModelOS/ClientApp/src/view/coursemgt/NewCusMgt.vue

@@ -4,6 +4,10 @@
 		<div class="header">
 			<div class="left">
 				<span>{{ $t("system.menu.cusMgt") }}</span>
+				<span style="font-size: 14px;margin-left: 10px;color: #1cc0f3;cursor: pointer;" @click="guideModal = true">
+					{{ $t('newCusMgt.guide') }}
+					<Icon type="md-help-circle"></Icon>
+				</span>
 				<!-- <Select v-model="filterYear" @on-change="onFilterYear" :placeholder="$t('newCusMgt.chooseYear')" style="margin: 10px; width: 120px" v-if="originCourseList.length">
 					<Option v-for="(item, index) in yearList" :value="index" :key="index">{{ item }} {{ $t("newCusMgt.year") }}</Option>
 				</Select>
@@ -41,6 +45,7 @@
 								</Select>
 								<p>{{ $t("newCusMgt.sort") }}</p>
 								<Select v-model="sortValue"  @on-change="onFilterSort" style="margin: 10px 0">
+									<Option value="grade">{{ $t("newCusMgt.sortType0") }}</Option>
 									<Option value="name">{{ $t("newCusMgt.sortType1") }}</Option>
 									<Option value="no">{{ $t("newCusMgt.sortType2") }}</Option>
 								</Select>
@@ -75,6 +80,7 @@
 					<Select v-model="searchCourseId" filterable clearable @on-change="onSearch" @on-clear="onSearchClear">
 						<Option v-for="item in originCourseList" :value="item.id" :key="item.id">{{ item.name }}</Option>
 					</Select>
+					<Icon type="md-close" @click="onSearchClear" style="margin-left: 5px;cursor: pointer;"></Icon>
 				</div>
 				<div class="left-content">
 					<empty-data v-if="!isLoading && !courseList.length" :top="200"></empty-data>
@@ -173,6 +179,10 @@
 		<Modal v-model="taskImportModal" :title="$t('newCusMgt.importTask')" width="900" @on-ok="onSaveTask" footer-hide>
 			<ImportTask @importTaskOk="importTaskOk" v-if="taskImportModal"></ImportTask>
 		</Modal>
+		<!-- 排课导入 -->
+		<Modal v-model="guideModal" :title="$t('newCusMgt.guideTitle')" width="900" footer-hide>
+			<p style="line-height: 40px;padding: 10px 20px;" v-html="$t('newCusMgt.guideText')"></p>
+		</Modal>
 	</div>
 </template>
 
@@ -197,7 +207,8 @@
 		},
 		data() {
 			return {
-				sortValue:'name',
+				guideModal:false,
+				sortValue:'grade',
 				taskGroups:[],
 				taskTeachers:[],
 				canModifyTask:true,
@@ -290,9 +301,9 @@
 						code: this.$store.state.userInfo.schoolCode
 					})
 					.then((res) => {
-						this.originCourseList = res.courseBases;
-						this.courseList = res.courseBases;
-						this.onFilterSort('name')
+						this.originCourseList = res.courseBases
+						this.courseList = res.courseBases
+						this.onFilterSort('grade')
 						if (res.courseBases.length) {
 							this.onCourseClick(res.courseBases[0], 0);
 						} else {
@@ -574,7 +585,13 @@
 			},
 			/* 排序方式 */
 			onFilterSort(val){
-				this.courseList = this.$tools.compareSortArrByKey(this.courseList, val)
+				if(!this.courseList.length) return
+				if(val === 'grade'){
+					this.courseList = this.courseList.sort((a,b) => a.grade - b.grade)
+				}else{
+					this.courseList = this.$tools.compareSortArrByKey(this.courseList, val)
+				}
+				this.onCourseClick(this.courseList[0], 0);
 			},
 			/* 过滤 */
 			onFilterGrade(val) {

+ 2 - 2
TEAMModelOS/ClientApp/src/view/coursemgt/components/AddTask.vue

@@ -10,10 +10,10 @@
 			<template slot-scope="{ row, index }" slot="teacher" v-if="curCourseSubInfo">
 				<Select v-model="row.instructorId" filterable clearable :placeholder="$t('user.noSet')" @on-change="onInstructorChange(row,index)" @on-clear="onInstructorClear">
 					<OptionGroup :label="curCourseSubInfo.name + $t('teachermgmt.job.teacher')">
-						<Option v-for="(item, index) in allTeachers.filter(i => i.subjectIds.includes(curCourseSubInfo.id))" :value="item.id" :key="index" :label="item.name">{{ item.name }}({{ item.id }})</Option>
+						<Option v-for="(item, index) in allTeachers.filter(i => i.subjectIds.includes(curCourseSubInfo.id))" :value="item.id" :key="index" :label="item.name">{{ item.name }}(<span v-if="item.iname">{{ item.iname }} - </span>{{ item.id }})</Option>
 					</OptionGroup>
 					<OptionGroup :label="$t('talMgmt.text34')">
-						<Option v-for="(item, index) in allTeachers.filter(i => !i.subjectIds.includes(curCourseSubInfo.id))" :value="item.id" :key="index" :label="item.name">{{ item.name }}({{ item.id }})</Option>
+						<Option v-for="(item, index) in allTeachers.filter(i => !i.subjectIds.includes(curCourseSubInfo.id))" :value="item.id" :key="index" :label="item.name">{{ item.name }}(<span v-if="item.iname">{{ item.iname }} - </span>{{ item.id }})</Option>
 					</OptionGroup>
 				</Select>
 			</template>

+ 79 - 0
TEAMModelOS/ClientApp/src/view/coursemgt/components/ImportTask.vue

@@ -10,6 +10,7 @@
 			</Upload>
 			<div class="tip-wrap">
 				<div class="download-wrap">
+					<span style="color: #41cdec;cursor: pointer;" @click="onDownloadFiles">{{ $t('newCusMgt.downloadFiles') }}</span>
 					<Dropdown @on-click="onDownloadTemp">
 						<a href="javascript:void(0)" style="font-size: 14px; margin-left: 20px; color: #41cdec">
 							{{ $t("evaluation.importFile.templateDownload") }}
@@ -217,6 +218,84 @@
 					a.remove();
 				};
 				downloadRes();
+			},
+			/* 导出备用资料 */
+			onDownloadFiles(){
+				let parentPage = this.$parent.$parent
+				console.log(parentPage.allClassList);
+				console.log(parentPage.teacherList);
+				console.log(parentPage.originCourseList);
+				let curSemesterYear = this.$store.state.user.curSemester.year
+				let sheets = []
+				// 班级名单
+				let class_Headers = ['名称', '学年', '编号', '类型', '学生人数']
+				let class_Keys = ['name', 'year', 'no', 'type', 'scount']
+				let class_datas = []
+				parentPage.allClassList.filter(i => i.type !== 'research').forEach(classItem => {
+					class_datas.push({
+						name:classItem.name,
+						year:classItem.year,
+						no:classItem.year + '-' + classItem.no,
+						type:classItem.type,
+						scount:classItem.scount,
+					})
+				})
+				const classSheet = {
+					title: class_Headers,
+					key: class_Keys,
+					data: class_datas,
+					filename: '班级名单',
+					autoWidth: true
+				}
+				sheets.push(classSheet)
+				// 教师名单
+				let teacher_Headers = ['姓名', 'ID', '备注', '职位', '手机号', '教研组','学科']
+				let teacher_Keys = ['name', 'id', 'iname', 'job', 'phone', 'group', 'subject']
+				let teacher_datas = []
+				parentPage.teacherList.forEach(teacher => {
+					teacher_datas.push({
+						name:teacher.name,
+						id: teacher.id,
+						iname:teacher.iname || '-',
+						job:teacher.job || '-',
+						phone:teacher.phone || '-',
+						group:teacher.groups.map(i => i.name).join(','),
+						subject:teacher.subjectIds.map(i => parentPage.curPeriod.subjects.find(k => k.id === i)?.name || '-').join(','),
+					})
+				})
+				const teacherSheet = {
+					title: teacher_Headers,
+					key: teacher_Keys,
+					data: teacher_datas,
+					filename: '教师名单',
+					autoWidth: true
+				}
+				sheets.push(teacherSheet)
+				// 课程名单
+				let course_Headers = ['名称', '编号', '年级', '学科', '描述', '创建者']
+				let course_Keys = ['name', 'no', 'grade', 'subject', 'desc', 'creator']
+				let course_datas = []
+				parentPage.originCourseList.forEach(course => {
+					course_datas.push({
+						name:course.name,
+						no: course.no || '-',
+						grade: course.grade,
+						subject:course.subject.name,
+						desc:course.desc || '-',
+						creator:course.creatorName + '(' + course.creatorId +')',
+					})
+				})
+				const courseSheet = {
+					title: course_Headers,
+					key: course_Keys,
+					data: course_datas,
+					filename: '课程清单',
+					autoWidth: true
+				}
+				sheets.push(courseSheet)
+				let schoolName = this.$store.state.user.schoolProfile.school_base.name;
+				let periodName = parentPage.curPeriod.name;
+				excel.export_array_to_sheet(sheets, `${schoolName}-${periodName} 课程备用资料`)
 			}
 		},
 		computed: {

+ 1 - 0
TEAMModelOS/ClientApp/src/view/student-account/class/ClassMgt.vue

@@ -632,6 +632,7 @@ export default {
                     return item.classId && item.classId == this.classListShow[this.curClassIndex]?.id
                 })
             }
+            this.students = this.students.sort((a,b) => a.no - b.no)
         },
         //查询毕业班级
         findGraduateClass() {

+ 7 - 2
TEAMModelOS/Controllers/Both/CourseBaseController.cs

@@ -503,10 +503,15 @@ namespace TEAMModelOS.Controllers.Both
                                     var resultBase = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<IdNameCode>(teacherSQLBase, $"Base");
                                     teachersBases = resultBase.list;
                                 }
-                              
+                                int count = 0; 
+                                if (groups.IsNotEmpty())
+                                {
+                                      count = courseTask.schedules.RemoveAll(z => !string.IsNullOrWhiteSpace(z.groupId)  &&  !groups.Select(v => v.id).Contains(z.groupId));
+                                }
+                               
                                 //当courseTask 为空,则matchedClasses可能会有年级匹配建议升学年的班级清单,
                                 //注:比如高二可能会将高三的课程学完,则该高三的课程的学年级应该是手动处理为高中二年级。不区分学期的目的,也是可能存在提前一学期学完第二学期的课程。
-                                return Ok(new { studyYear, semesterId, courseTask, teachers= teachersBases, groups, matchedClasses = classes, matchedTeachers = teachers.Select(z => new { z.id, z.name, z.subjectIds, z.job, z.periodId, z.picture }) });
+                                return Ok(new {count= count, studyYear, semesterId, courseTask, teachers= teachersBases, groups, matchedClasses = classes, matchedTeachers = teachers.Select(z => new { z.id, z.name, z.subjectIds, z.job, z.periodId, z.picture }) });
                             }
                             else
                             {

+ 1 - 0
TEAMModelOS/Controllers/Both/GroupListController.cs

@@ -607,6 +607,7 @@ namespace TEAMModelOS.Controllers
                                     type = "class",
                                     year = item.year,
                                     expire=0,
+                                    no=item.no,
                                     groupName = groupNames,
                                     scount= scount
                                 };

+ 20 - 3
TEAMModelOS/Controllers/Common/AreaController.cs

@@ -1175,6 +1175,7 @@ namespace TEAMModelOS.Controllers
                 }
             }
             //改版内容
+            //获取区级下面所有的学校信息
             List<(string code, string name, List<Period> pd)> schoolBase = new();
             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryStreamIterator(queryText: $"select c.id,c.name,c.period from c  where c.areaId = '{id}'", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
             {
@@ -1191,7 +1192,7 @@ namespace TEAMModelOS.Controllers
                     }
                 }
             }
-            var ped = schoolBase.Select(x => x.pd).ToList();
+           /* var ped = schoolBase.Select(x => x.pd).ToList();
             List<(string ptype, string pId, string name)> perInfos = new();
             foreach (var scpd in ped)
             {
@@ -1202,8 +1203,8 @@ namespace TEAMModelOS.Controllers
                         perInfos.Add((per.periodType, per.id, per.name));
                     }
                 }
-            }
-            var periods = perInfos.GroupBy(g => g.ptype).Select(x => new { key = x.Key, list = x.Select(z => z.pId).ToList() }).Where(c => !string.IsNullOrWhiteSpace(c.key));
+            }*/
+            //var periods = perInfos.GroupBy(g => g.ptype).Select(x => new { key = x.Key, list = x.Select(z => z.pId).ToList() }).Where(c => !string.IsNullOrWhiteSpace(c.key));
             List<(string ptype, List<string> scs, string type)> baseMore = new();
 
             List<(string ptype, string subId, List<(string name, List<string> kno)> knos)> knoledge = new();
@@ -1270,7 +1271,23 @@ namespace TEAMModelOS.Controllers
                 perName = periodType.GetString()
 
             });
+            //处理区级学校数据
+            var schoolScore = schoolBase.Select(x => new { 
+                code = x.code,
+                name = x.name,
+                perCname = x.pd.Where(c => c.periodType.Equals(periodType.GetString())).FirstOrDefault()?.name,
+                perId = x.pd.Where(c => c.periodType.Equals(periodType.GetString())).FirstOrDefault()?.id,
+                perName = periodType.GetString(),
+                scores = As.Where(q => q.code.Equals(x.code)).SelectMany(z => z.ac).GroupBy(c => c.subjectId).Select(v => new {
+                    name = v.Key,
+                    max = v.ToList().Max(y => y.score),
+                    min = v.ToList().Min(y => y.score),
+                    average = v.ToList().Sum(y => y.score) * 1.0 / v.ToList().Count,
+                    excellent = v.ToList().Where(y => y.score >= 80).ToList().Count,
+                    pass = v.ToList().Where(y => y.score >= 60).ToList().Count,
+                })
 
+            });
             List<(List<(string knowledgeName, string gradeId, string classId, string className, double score, double tscore, List<(string name, double score, List<string> dim)> blocks)> values, string subjectId, string examId, string code)> classKnowledge = new();
             await foreach (var persents in DoKnowledgePoint(results, knoledge, setting, periodType.GetString()))
             {

File diff suppressed because it is too large
+ 18 - 2
TEAMModelOS/Controllers/System/WeChatPayController.cs


+ 30 - 21
TEAMModelOS/JsonFile/Core/mail.html

@@ -10,6 +10,7 @@
             margin: 0;
             padding: 0;
         }
+
         .container {
             display: flex;
             flex-direction: column;
@@ -17,33 +18,36 @@
             align-items: center;
             height: 100vh;
         }
+
         p {
             margin-bottom: 20px;
             font-size: 20px;
             text-align: center;
             word-wrap: break-word;
         }
+
         a {
             width: 100%;
             text-decoration: none;
-            color: gray;
+            color: rgba(74, 144, 236, 0.808);
             font-weight: bold
         }
+
         label {
             background-color: lightgrey;
         }
     </style>
 </head>
+
 <body>
     <div class="container">
         <table width="650" bgcolor="#ffffff" align="center" cellpadding="0" cellspacing="0" border="0">
             <tbody>
                 <tr>
                     <td align="left" valign="middle" class="img-wrapper" style="text-align: left;padding-left: 35px;">
-                        <a href="https://www.habook.com.cn/"
-                           rel="noopener" target="_blank">
+                        <a href="https://www.habook.com.cn/" rel="noopener" target="_blank">
                             <img src="https://teammodelos.blob.core.chinacloudapi.cn/0-public/school/88ab1b98-c373-41b1-9917-6dd0577344d8.jpg"
-                                 border="0" height="88px">
+                                border="0" height="88" width="88">
                         </a>
                     </td>
                 </tr>
@@ -58,28 +62,31 @@
                             <tbody>
                                 <tr>
                                     <td align="left"
-                                        style="font-weight:bold; font-size:18px; padding-top:10px; padding-bottom:8px; color:#5b5b5b;">
+                                        style="font-weight:bold; font-size:20px; padding-top:10px; padding-bottom:8px; color:#5b5b5b;">
                                         亲爱的{tmdname}客户,您好:
                                     </td>
                                 </tr>
                                 <tr>
                                     <td align="left"
-                                        style="font-size:18px; color:#5b5b5b; padding-top:5px; padding-bottom:5px; line-height:40px;font-family:Verdana;">
-                                        感谢您在醍摩豆CN ( <a href="https://www.habook.com.cn"
-                                                       rel="noopener" target="_blank">www.habook.com.cn</a> ) 购买产品!<br> 已经为您开具订单<a href=""
-                                                                                                                                   rel="noopener" target="_blank">{产品名称}({orderId})</a>的电子普通发票,如下:
-                                        <br>发票类型:增值税电子普通发票,发票代码:{invoiceCode},发票号码: {invoiceNum},您可以点击“ <a href="{invoiceUrl}"
-                                                                                                           rel="noopener" target="_blank">发票文件下载</a> ”获取该发票文件; <br>同时您也可以访问<a href="https://www.habook.com.cn"
-                                                                                                                                                                              rel="noopener" target="_blank">醍摩豆CN(www.habook.com.cn)</a>网站订单管理及下载安装<a href="https://www.habook.com.cn/download.php?act=view&id=54">HiTA5</a>中的通知中心详情查看下载电子普通发票。
+                                        style="font-size:15px; color:#5b5b5b; padding-top:5px; padding-bottom:5px; line-height:40px;font-family:Verdana;">
+                                        感谢您在醍摩豆CN ( <a href="https://www.habook.com.cn" rel="noopener"
+                                            target="_blank">www.habook.com.cn</a> ) 购买产品!<br> 已经为您开具订单,产品名称:<a href=""
+                                            rel="noopener" target="_blank">{productName},{orderId}</a>的电子普通发票,如下:
+                                        <br>发票类型:增值税电子普通发票,订单支付金额:{pay_amount}元,发票代码:{invoiceCode},发票号码: {invoiceNum},您可以点击“ <a
+                                            href="{invoiceUrl}" rel="noopener" target="_blank">发票文件下载</a> ”获取该发票文件;
+                                        <br>同时您也可以<!--访问<a href="https://www.habook.com.cn" rel="noopener"
+                                            target="_blank">醍摩豆CN(www.habook.com.cn)</a>网站订单管理及-->下载安装<a
+                                            href="https://www.habook.com.cn/download.php?act=view&id=54">HiTA5</a>,在应用中的通知中心详情查看下载电子普通发票。
                                         <br>电子普通发票是税务机关认可的有效收付款凭证,与纸质发票具有同等法律效力,可用于报销入账、售后维权等。
                                     </td>
                                 </tr>
                                 <tr>
                                     <td align="left"
-                                        style="font-size:18px; color:#5b5b5b; padding-top:5px; padding-bottom:5px; line-height:40px;font-family:Verdana;">
-                                        <b>温馨提示</b>:如您有校园采购需求,可<a href="https://www.teammodel.cn/"
-                                                                  rel="noopener"
-                                                                  target="_blank">注册醍摩豆会员</a>在学校管理设置申请建立学校,填写相关学校信息及联系方式后,我们会有工作人员为您介绍产品及服务开通流程。您也可以关注【<u><b>醍摩豆智慧教育研究院</b></u>】微信公众号或拨打企业热线 <a title="028-8600-8651" href="tel:028-8600-8651">028-8600-8651</a>联系我们。
+                                        style="font-size:15px; color:#5b5b5b; padding-top:5px; padding-bottom:5px; line-height:40px;font-family:Verdana;">
+                                        <b>温馨提示</b>:如您有校园采购需求,可<a href="https://www.teammodel.cn/" rel="noopener"
+                                            target="_blank">注册醍摩豆会员</a>在学校管理设置申请建立学校,填写相关学校信息及联系方式后,我们会有工作人员为您介绍产品及服务开通流程。您也可以关注【<u><b
+                                                style="color: rgba(74, 144, 236, 0.808)">醍摩豆智慧教育研究院</b></u>】微信公众号或拨打企业热线
+                                        <a title="028-8600-8651" href="tel:028-8600-8651">028-8600-8651</a>联系我们。
                                     </td>
                                 </tr>
                             </tbody>
@@ -99,13 +106,14 @@
                             <tbody>
                                 <tr>
                                     <td align="center">
-                                        <a href="https://www.habook.com.cn/"
-                                           target="_blank" rel="noopener">
+                                        <a href="https://www.habook.com.cn/" target="_blank" rel="noopener">
                                             <img src="https://teammodelos.blob.core.chinacloudapi.cn/0-public/school/88ab1b98-c373-41b1-9917-6dd0577344d8.jpg"
-                                                 width="30" height="30" border="0" style=" margin-top:5px;">
+                                                width="30" height="30" border="0" style=" margin-top:5px;">
                                         </a> <br> <a href="https://www.habook.com.cn/"
-                                                     style="font-size: 18px;text-decoration: none" target="_blank" rel="noopener">
-                                            <font color="#323335"> Copyright ©2004-2023 HABOOK Group. All rights reserved.  </font>
+                                            style="font-size: 12px;text-decoration: none" target="_blank"
+                                            rel="noopener">
+                                            <font color="#323335"> Copyright ©2004-2023 HABOOK Group. All rights
+                                                reserved. </font>
                                         </a>
                                     </td>
                                 </tr>
@@ -117,4 +125,5 @@
         </table>
     </div>
 </body>
+
 </html>

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -188,7 +188,7 @@ namespace TEAMModelOS
             //services.AddHostedService<>
             //services.AddSingleton<ILoggerProvider, BlobLoggerProvider>();
             // services.AddMvcFilter<RequestAuditFilter>();
-            //services.AddNetMail(Configuration.GetSection("MailOption").Get<MailOptions>());
+            services.AddNetMail(Configuration.GetSection("MailOption").Get<MailOptions>());
 #if !DEBUG
  //第一步: 配置gzip与br的压缩等级为最优
             //builder.Services.AddMyResponseCompression();

+ 5 - 5
TEAMModelOS/appsettings.Development.json

@@ -98,11 +98,11 @@
     "getuserinfo_bycode": "https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=xxx&timestamp=xxx&signature=xxx"
   },
   "MailOption": {
-    "fromMail": "353897079@qq.com",
-    "smtp": "smtp.qq.com",
-    "port": 465,
-    "username": "353897079@qq.com",
-    "password": "gsvpdyglpmlpcadg"
+    "fromMail": "chhabook_customer@163.com",
+    "smtp": "smtp.163.com",
+    "port": 25,
+    "username": "chhabook_customer@163.com",
+    "password": "ZXMIRDYLVLJFWGJD"
   },
   "Third": {
     //"scsyxpt": {

+ 5 - 5
TEAMModelOS/appsettings.json

@@ -41,11 +41,11 @@
     }
   },
   "MailOption": {
-    "fromMail": "353897079@qq.com",
-    "smtp": "smtp.qq.com",
-    "port": 465,
-    "username": "353897079@qq.com",
-    "password": "gsvpdyglpmlpcadg"
+    "fromMail": "chhabook_customer@163.com",
+    "smtp": "smtp.163.com",
+    "port": 25,
+    "username": "chhabook_customer@163.com",
+    "password": "ZXMIRDYLVLJFWGJD"
   },
   "HaBookAuth": {
     "CoreId": {