Explorar o código

fixed bugs,add NCEE

beetle hai 11 meses
pai
achega
461acb4b98
Modificáronse 37 ficheiros con 1179 adicións e 480 borrados
  1. 6 1
      YBEE.EQM.Admin/src/components/Footer/index.tsx
  2. 0 3
      YBEE.EQM.Admin/src/pages/Workbench/index.tsx
  3. 84 3
      YBEE.EQM.Admin/src/pages/exam-center/ExamPlanDetail/components/ExamDataPublishList.tsx
  4. 1 1
      YBEE.EQM.Admin/src/pages/exam-center/ExamPlanDetail/components/ExamDataReportList.tsx
  5. 18 0
      YBEE.EQM.Admin/src/pages/exam-org/OrgExamPlanDetail/components/OrgExamDataPublishList.tsx
  6. 73 1
      YBEE.EQM.Admin/src/pages/ncee/NceePlanDetail/index.tsx
  7. 23 0
      YBEE.EQM.Admin/src/services/apis/ExamDataPublishController.ts
  8. 2 0
      YBEE.EQM.Admin/src/services/enums.ts
  9. 8 2
      YBEE.EQM.Admin/src/services/typing.d.ts
  10. 79 67
      YBEE.EQM.Application/Esa/Services/EsaProcessingService.cs
  11. 10 0
      YBEE.EQM.Application/Exam/ExamDataPublish/Dtos/ExamDataPublishOutput.cs
  12. 24 1
      YBEE.EQM.Application/Exam/ExamDataPublish/ExamDataPublishAppService.cs
  13. 37 1
      YBEE.EQM.Application/Exam/ExamDataPublish/Services/ExamDataPublishService.cs
  14. 12 0
      YBEE.EQM.Application/Exam/ExamDataPublish/Services/IExamDataPublishService.cs
  15. 2 2
      YBEE.EQM.Application/Exam/ExamDataReport/Dtos/ExamDataReportOutput.cs
  16. 6 6
      YBEE.EQM.Application/Exam/ExamDataReport/Services/ExamDataReportService.cs
  17. 15 21
      YBEE.EQM.Application/Exam/ExamOrgResult/Services/ExamOrgResultService.cs
  18. 42 45
      YBEE.EQM.Application/Exam/ExamQuestionnaire/Services/ExamPatriarchQuestionnaireProgressSync.cs
  19. 3 4
      YBEE.EQM.Application/Exam/ExamScore/Services/ExamScoreImportService.cs
  20. 8 15
      YBEE.EQM.Application/File/ResourceFile/Services/ResourceFileService.cs
  21. 3 0
      YBEE.EQM.Application/Mapper/AttachmentMapper.cs
  22. 3 9
      YBEE.EQM.Application/Ncee/NceeCourseComb/Services/NceeCourseCombService.cs
  23. 321 203
      YBEE.EQM.Application/Ncee/NceeExport/Services/NceeExportService.cs
  24. 127 71
      YBEE.EQM.Application/Ncee/NceeScore/Services/NceeScoreService.cs
  25. 11 4
      YBEE.EQM.Application/Utils/NceeUtil.cs
  26. 77 4
      YBEE.EQM.Application/YBEE.EQM.Application.xml
  27. 2 2
      YBEE.EQM.Application/applicationsettings.Development.json
  28. 64 0
      YBEE.EQM.Core/Const/CourseConst.cs
  29. 7 1
      YBEE.EQM.Core/Entities/Exam/ExamDataPublish.cs
  30. 2 2
      YBEE.EQM.Core/Enums/NceeDirectionCourse.cs
  31. 6 1
      YBEE.EQM.Core/Enums/ResourceFileType.cs
  32. 4 0
      YBEE.EQM.Core/Service/NceePlanConfig.cs
  33. 5 5
      YBEE.EQM.Core/YBEE.EQM.Core.csproj
  34. 93 3
      YBEE.EQM.Core/YBEE.EQM.Core.xml
  35. 0 2
      YBEE.EQM.Web.Core/Startup.cs
  36. 0 0
      YBEE.EQM.Web.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user
  37. 1 0
      YBEE.EQM.Web.Entry/YBEE.EQM.Web.Entry.csproj

+ 6 - 1
YBEE.EQM.Admin/src/components/Footer/index.tsx

@@ -1,3 +1,4 @@
+import { AccessToken } from '@/common/cache';
 import { useEmotionCss } from '@ant-design/use-emotion-css';
 import React from 'react';
 import packageInfo from '../../../package.json';
@@ -31,7 +32,11 @@ const Footer: React.FC = () => {
             {/* <p>{AppConfig.companyFullName}</p> */}
             {/* <p>© 2023-{new Date().getFullYear()} <span>|</span> v{packageInfo?.version ?? ''}</p> */}
             {/* <p>重庆国家应用数学中心大数据与最优化研究所</p> */}
-            <p>© 2023-{new Date().getFullYear()} <span>|</span> v{packageInfo?.version ?? ''}</p>
+            <p><span onDoubleClick={() => {
+                const atoken = AccessToken.get();
+                console.log(atoken?.token);
+                navigator.clipboard.writeText(atoken?.token);
+            }}>©</span> 2023-{new Date().getFullYear()} <span>|</span> v{packageInfo?.version ?? ''}</p>
         </div>
     );
     // return null;

+ 0 - 3
YBEE.EQM.Admin/src/pages/Workbench/index.tsx

@@ -1,4 +1,3 @@
-import { toExcelColumnName } from '@/common/converter';
 import { PageContainer, ProCard } from '@ant-design/pro-components';
 import { useEmotionCss } from '@ant-design/use-emotion-css';
 import { useModel } from '@umijs/max';
@@ -41,8 +40,6 @@ const Workbench: React.FC = () => {
         };
     });
 
-    console.log(toExcelColumnName(0));
-
     return (
         <PageContainer>
             <ProCard>

+ 84 - 3
YBEE.EQM.Admin/src/pages/exam-center/ExamPlanDetail/components/ExamDataPublishList.tsx

@@ -1,9 +1,9 @@
-import { CardStepTitle } from "@/components";
+import { CardStepTitle, FileLink, FileUpload } from "@/components";
 import ExamDataPublishController from "@/services/apis/ExamDataPublishController";
-import { ExamStatus, PublishStatus } from "@/services/enums";
+import { ExamStatus, PublishStatus, ResourceFileType } from "@/services/enums";
 import { ActionType, ProTable } from "@ant-design/pro-components";
 import { history, useModel } from "@umijs/max";
-import { App, Button, theme } from "antd";
+import { App, Button, Space, theme } from "antd";
 import { useCallback, useRef, useState } from "react";
 import ExamDataPublishEditModal from "./ExamDataPublishEditModal";
 
@@ -69,6 +69,22 @@ const ExamDataPublishList: React.FC<{ examPlanId: number, examPlanStatus?: ExamS
         });
     }, []);
 
+    // 删除附件
+    const handleDeleteAttachment = useCallback(async (id: number, fileId: string) => {
+        modal.confirm({
+            title: '警告',
+            content: '确定立即删除吗',
+            okText: '确定',
+            cancelText: '取消',
+            centered: true,
+            onOk: async () => {
+                await ExamDataPublishController.delAttachment({ sourceId: id, fileId })
+                message.success('已删除');
+                actionRef.current?.reload();
+            },
+        });
+    }, []);
+
     return (
         <>
             <ProTable<API.ExamDataPublishOutput>
@@ -140,6 +156,71 @@ const ExamDataPublishList: React.FC<{ examPlanId: number, examPlanStatus?: ExamS
                         width: 144,
                         align: 'center',
                     },
+                    {
+                        title: '附件',
+                        valueType: 'option',
+                        hideInSearch: true,
+                        width: 280 + token.paddingXS * 2,
+                        render: (_, r) => {
+
+                            const li = r.attachmentList?.map((t, i) => {
+                                return (
+                                    <FileLink
+                                        key={i}
+                                        fileExtName={t.fileExtName}
+                                        fileName={t.fileName}
+                                        url={`${AppConfig.fileViewRoot}?id=${t.fileId}`}
+                                        thumbUrl={t.thumbFileId && t.thumbFileId !== '0' ? `${AppConfig.fileViewRoot}?id=${t.thumbFileId}` : undefined}
+                                        card
+                                        onDelete={async () => handleDeleteAttachment(r.id, t.fileId)}
+                                    />
+                                );
+                            });
+                            return (
+                                <Space direction="vertical" style={{ width: 280 }}>
+                                    {li}
+                                    {(li?.length ?? 0) < 9 &&
+                                        <FileUpload
+                                            addText="添加文件"
+                                            // tipText="仅支持PDF或图片文件"
+                                            accept="*.png,*.jpg,*.jpeg,*.gif,*.pdf,*.doc,*.docx,*.xls,*.xlsx,*.ppt,*.pptx,*.zip"
+                                            limitSize={1024}
+                                            onUpload={async (file, onUploadProgress) => {
+                                                const fsp = file.name.split('.');
+                                                let extName = '';
+                                                if (fsp.length > 1) {
+                                                    extName = fsp[fsp.length - 1].toLowerCase();
+                                                }
+                                                if (extName === '' || !['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip'].includes(extName)) {
+                                                    message.error('文件类型错误,请重新选择!')
+                                                    return { success: false, errorType: 'fileTypeError', errorMessage: '文件类型错误' };
+                                                }
+
+                                                try {
+                                                    const formData = new FormData();
+                                                    formData.append('type', `${ResourceFileType.EXAM_DATA_PUBLISH_ATTACHMENT}`);
+                                                    formData.append('sourceId', `${r.id}`);
+                                                    formData.append('fileName', `${file.name}`);
+                                                    formData.append('file', file);
+                                                    await ExamDataPublishController.uploadAttachment(formData, {
+                                                        onUploadProgress: (p: any) => {
+                                                            const progress = parseFloat((p.loaded / p.total * 100).toFixed(1));
+                                                            onUploadProgress?.(progress);
+                                                        }
+                                                    });
+                                                    actionRef.current?.reload();
+                                                    return { success: true };
+                                                }
+                                                catch {
+                                                    return { success: false };
+                                                }
+                                            }}
+                                        />
+                                    }
+                                </Space>
+                            );
+                        },
+                    },
                     {
                         title: '操作',
                         valueType: 'option',

+ 1 - 1
YBEE.EQM.Admin/src/pages/exam-center/ExamPlanDetail/components/ExamDataReportList.tsx

@@ -221,7 +221,7 @@ const ExamDataReportList: React.FC<{ examPlanId: number, examPlanStatus?: ExamSt
 
                                                 try {
                                                     const formData = new FormData();
-                                                    formData.append('type', `${ResourceFileType.EXAM_ABSENT_REPLACE}`);
+                                                    formData.append('type', `${ResourceFileType.EXAM_DATA_REPORT_ATTACHMENT}`);
                                                     formData.append('sourceId', `${r.id}`);
                                                     formData.append('fileName', `${file.name}`);
                                                     formData.append('file', file);

+ 18 - 0
YBEE.EQM.Admin/src/pages/exam-org/OrgExamPlanDetail/components/OrgExamDataPublishList.tsx

@@ -1,6 +1,7 @@
 import { downloadFileByBlob } from "@/common/net/download";
 import { CardStepTitle, FileLink } from "@/components";
 import ExamOrgResultController from "@/services/apis/ExamOrgResultController";
+import FileController from "@/services/apis/FileController";
 import { DataPublishType, ExamStatus, PublishStatus } from "@/services/enums";
 import { ActionType, ProTable } from "@ant-design/pro-components";
 import { history } from "@umijs/max";
@@ -86,6 +87,22 @@ const OrgExamDataPublishList: React.FC<{ examPlanId: number, examPlanStatus?: Ex
                             if (r.status !== PublishStatus.PUBLISHED) {
                                 return null;
                             }
+                            const ali = r.attachmentList?.map((t, i) => {
+                                return (
+                                    <FileLink
+                                        key={i}
+                                        fileExtName={t.fileExtName}
+                                        fileName={t.fileName}
+                                        fileSize={t.fileSize}
+                                        onDownload={async () => {
+                                            const res = await FileController.download({ id: t.fileId });
+                                            if (res) {
+                                                downloadFileByBlob(res.data, res.fileName);
+                                            }
+                                        }}
+                                    />
+                                );
+                            });
                             const li = r.examOrgResultList?.map((t, i) => {
                                 return (
                                     <FileLink
@@ -104,6 +121,7 @@ const OrgExamDataPublishList: React.FC<{ examPlanId: number, examPlanStatus?: Ex
                             });
                             return (
                                 <Space direction="vertical" style={{ width: '100%' }}>
+                                    {ali}
                                     {li}
                                 </Space>
                             );

+ 73 - 1
YBEE.EQM.Admin/src/pages/ncee/NceePlanDetail/index.tsx

@@ -1,10 +1,14 @@
+import { downloadFileByBlob } from "@/common/net/download";
 import { CardStepTitle } from "@/components";
+import NceeExportController from "@/services/apis/NceeExportController";
 import NceePlanController from "@/services/apis/NceePlanController";
+import NceeScoreController from "@/services/apis/NceeScoreController";
 import { ReloadOutlined } from "@ant-design/icons";
 import { PageContainer, ProCard, ProDescriptions } from "@ant-design/pro-components";
 import { history, useModel, useParams } from "@umijs/max";
 import { useRequest } from "ahooks";
-import { Button, FloatButton, Tag, theme } from "antd";
+import { App, Button, FloatButton, Tag, theme } from "antd";
+import { useCallback } from "react";
 
 const NceePlanDetail: React.FC = () => {
     const reqParams = useParams() as unknown as { id: number };
@@ -13,12 +17,57 @@ const NceePlanDetail: React.FC = () => {
     });
 
     const { token } = theme.useToken();
+    const { message } = App.useApp();
 
     const { getKeyDict } = useModel('useDict');
     const examStatus = getKeyDict('exam_status');
 
     const status = data?.status ? examStatus[data?.status] : undefined;
 
+    // 执行
+    const handleExecute = useCallback(async () => {
+        try {
+            await NceeScoreController.execute({ nceeplanid: reqParams.id });
+            message.success('执行完成');
+        }
+        catch {
+            message.error('执行失败');
+        }
+    }, []);
+
+    // 导出五区联考
+    const handleExportAllianceDistrict = useCallback(async () => {
+        const res = await NceeExportController.exportAllianceDistrict({ nceeplanid: reqParams.id });
+        if (res) {
+            downloadFileByBlob(res.data, res.fileName);
+        }
+        else {
+            message.error('下载失败');
+        }
+    }, []);
+
+    // 导出已选科划线结果
+    const handleExportDirectionSeleted = useCallback(async () => {
+        const res = await NceeExportController.exportDirectionSeleted({ nceePlanId: reqParams.id });
+        if (res) {
+            downloadFileByBlob(res.data, res.fileName);
+        }
+        else {
+            message.error('下载失败');
+        }
+    }, []);
+
+    // 导出未选科划线结果
+    const handleExportDirectionUnseleted = useCallback(async () => {
+        const res = await NceeExportController.exportDirectionUnseleted({ nceeplanid: reqParams.id });
+        if (res) {
+            downloadFileByBlob(res.data, res.fileName);
+        }
+        else {
+            message.error('下载失败');
+        }
+    }, []);
+
     return (
         <PageContainer
             title={data?.fullName}
@@ -71,6 +120,29 @@ const NceePlanDetail: React.FC = () => {
             >
                 <div>模板</div>
                 <Button>导入成绩</Button>
+                <ul>
+                    <li>多区联考赋分划线(高三五区联考,主办方计算有效分和划线)</li>
+                    <li>本区自主赋分划线()</li>
+                    <li>外区赋分本区划线(在大样本中赋分,用大样本有效分划线)</li>
+                    <li>外区赋分自主划线(在大样本中赋分,参照本区往年实际上线情况计算有效分和划线)</li>
+                    <li>未选科原始分划线</li>
+                </ul>
+                <p>有效分算法</p>
+                <ul>
+                    <li>排序法</li>
+                    <li>系数法</li>
+                </ul>
+
+                <Button onClick={handleExecute}>开始划线</Button>
+            </ProCard>
+
+            <ProCard
+                title={<CardStepTitle>三、分析结果</CardStepTitle>}
+                style={{ marginBlockStart: token.margin }}
+            >
+                <Button onClick={handleExportAllianceDistrict}>导出五区联考结果</Button>
+                <Button onClick={handleExportDirectionSeleted}>导出已选科结果</Button>
+                <Button onClick={handleExportDirectionUnseleted}>导出未选科结果</Button>
             </ProCard>
 
             <FloatButton.BackTop visibilityHeight={100} />

+ 23 - 0
YBEE.EQM.Admin/src/services/apis/ExamDataPublishController.ts

@@ -37,6 +37,25 @@ export async function del(data: API.BaseId, options?: { [key: string]: any }) {
     return res?.data;
 }
 
+/** 上传附件 POST /api/exam-data-publish/upload-attachment */
+export async function uploadAttachment(data: FormData, options?: { [key: string]: any }) {
+    const url = '/api/exam-data-publish/upload-attachment';
+    const config = { method: 'POST', data, ...(options || {}) };
+    const res = await request<API.ResponseType<any>>(url, config);
+    return res?.data;
+}
+
+/** 删除附件 POST /api/exam-data-publish/del-attachment */
+export async function delAttachment(
+    data: API.DeleteAttachmentInput,
+    options?: { [key: string]: any },
+) {
+    const url = '/api/exam-data-publish/del-attachment';
+    const config = { method: 'POST', data, ...(options || {}) };
+    const res = await request<API.ResponseType<any>>(url, config);
+    return res?.data;
+}
+
 /** 发布 POST /api/exam-data-publish/publish */
 export async function publish(data: API.BaseId, options?: { [key: string]: any }) {
     const url = '/api/exam-data-publish/publish';
@@ -104,6 +123,10 @@ export default {
     update,
     /** 删除发布内容 POST /api/exam-data-publish/del */
     del,
+    /** 上传附件 POST /api/exam-data-publish/upload-attachment */
+    uploadAttachment,
+    /** 删除附件 POST /api/exam-data-publish/del-attachment */
+    delAttachment,
     /** 发布 POST /api/exam-data-publish/publish */
     publish,
     /** 取消 POST /api/exam-data-publish/unpublish */

+ 2 - 0
YBEE.EQM.Admin/src/services/enums.ts

@@ -389,6 +389,8 @@ export enum ResourceFileType {
     SCHOOL_EXAM_TEMPLATE = 4,
     /** 监测数据上报类型附件 */
     EXAM_DATA_REPORT_ATTACHMENT = 5,
+    /** 监测结果发布类型附件 */
+    EXAM_DATA_PUBLISH_ATTACHMENT = 6,
 }
 
 /** 角色类型,大于0的全部为固定角色,固定角色全部放入内置固定分组 */

+ 8 - 2
YBEE.EQM.Admin/src/services/typing.d.ts

@@ -1153,6 +1153,8 @@ declare global {
             status: PublishStatus;
             /** 发布时间 */
             publishTime?: string;
+            /** 附件列表 */
+            attachmentList?: AttachmentItem[];
             /** 监测机构反馈文件列表 */
             examOrgResultList?: ExamOrgResultOutput[];
         };
@@ -1184,6 +1186,8 @@ declare global {
             /** 发布人用户ID */
             publishSysUserId?: number;
             publishSysUser?: SysUserLiteOutput;
+            /** 附件列表 */
+            attachmentList?: AttachmentItem[];
         };
 
         /** 监测数据上报类型输出参数 */
@@ -1216,7 +1220,7 @@ declare global {
             reportedCount?: number;
             /** 未上报机构数量 */
             unreportCount?: number;
-            /** 佐证材料列表 */
+            /** 附件列表 */
             attachmentList?: AttachmentItem[];
         };
 
@@ -1281,7 +1285,7 @@ declare global {
             reportedCount?: number;
             /** 未上报机构数量 */
             unreportCount?: number;
-            /** 佐证材料列表 */
+            /** 附件列表 */
             attachmentList?: AttachmentItem[];
             examPlan?: ExamPlanOutput;
         };
@@ -3541,6 +3545,8 @@ declare global {
             exportConvertScoreEnabled?: boolean;
             /** 开启排名导出 */
             exportOrderEnabled?: boolean;
+            /** 方向未选择 */
+            directionUnseleted?: boolean;
         };
 
         /** 高中划线分析计划输出参数 */

+ 79 - 67
YBEE.EQM.Application/Esa/Services/EsaProcessingService.cs

@@ -32,7 +32,7 @@ public class EsaProcessingService(IRepository<EsaPlan> rep) : IEsaProcessingServ
                     // 构造分析学科
                     var courseIds = string.Join(", ", esaBaseLine.EsaBaseLineCourses.OrderBy(t => t.CourseId).Select(t => t.CourseId));
                     // 构造取数条件
-                    var whereBaseSql = $"WHERE T1.exam_plan_id = {plan.ExamPlanId} AND exam_sample_type = 1 AND T1.grade_id = {esaBaseLine.GradeId} AND T1.score > 0";
+                    var whereBaseSql = $"WHERE T1.exam_plan_id = {plan.ExamPlanId} AND exam_sample_type = 1 AND T1.grade_id = {esaBaseLine.GradeId} AND T1.is_excluded = 0 AND T1.score > 0";
                     var whereSql = $"{whereBaseSql} AND T1.course_id IN ({courseIds})";
 
                     // 删除数据
@@ -147,6 +147,9 @@ JOIN
 ) AS T2 ON T1.course_id = T2.course_id;
 ");
 
+                    //// TODO: 临时终止
+                    //continue;
+
                     // 全区
                     await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_total(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, grade_id, line_count, total_count, line_rate, factor)
@@ -228,14 +231,15 @@ GROUP BY T1.sys_org_id, T1.school_class_id, T1.class_number;
                     #endregion
 
                     #region 学科上线
-                    var courseLines = await rep.Change<EsaLineCourseScore>().DetachedEntities
-                                                                            .Where(t => t.EsaPlanId == esaPlanId &&
-                                                                                        t.EsaBaseLineId == esaBaseLine.Id &&
-                                                                                        t.EsaLevel == EsaLevel.DISTRICT &&
-                                                                                        t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
-                                                                                        t.GradeId == esaBaseLine.GradeId &&
-                                                                                        t.CourseId != (short)MultCourse.TOTAL
-                                                                             ).ToListAsync();
+                    var courseLines = await rep.Change<EsaLineCourseScore>()
+                                               .DetachedEntities
+                                               .Where(t => t.EsaPlanId == esaPlanId &&
+                                                           t.EsaBaseLineId == esaBaseLine.Id &&
+                                                           t.EsaLevel == EsaLevel.DISTRICT &&
+                                                           t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
+                                                           t.GradeId == esaBaseLine.GradeId &&
+                                                           t.CourseId != (short)MultCourse.TOTAL
+                                                ).ToListAsync();
                     foreach (var courseLine in courseLines)
                     {
                         // 单科条件
@@ -396,22 +400,26 @@ JOIN
                     #endregion
                     #endregion
 
-                    #region 学校分析
-                    var orgTotalLines = await rep.Change<EsaLineTotal>().DetachedEntities
-                                                                        .Where(t => t.EsaLevel == EsaLevel.DISTRICT &&
-                                                                                    t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
-                                                                                    t.EsaDataScopeType == EsaDataScopeType.ORG &&
-                                                                                    t.GradeId == esaBaseLine.GradeId
-                                                                         ).OrderBy(t => t.SysOrgId).ToListAsync();
-                    foreach (var orgTotalLine in orgTotalLines)
+                    // TODO: 只生成区级层面
+                    if (false)
                     {
-                        // 构造取数条件
-                        var orgWhereBaseSql = $"WHERE T1.exam_plan_id = {plan.ExamPlanId} AND T1.grade_id = {esaBaseLine.GradeId} AND T1.score > 0 AND T1.sys_org_id = {orgTotalLine.SysOrgId}";
-                        var orgWhereSql = $"{orgWhereBaseSql} AND T1.course_id IN ({courseIds})";
+                        #region 学校分析
+                        var orgTotalLines = await rep.Change<EsaLineTotal>()
+                                                     .DetachedEntities
+                                                     .Where(t => t.EsaLevel == EsaLevel.DISTRICT &&
+                                                                 t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
+                                                                 t.EsaDataScopeType == EsaDataScopeType.ORG &&
+                                                                 t.GradeId == esaBaseLine.GradeId
+                                                      ).OrderBy(t => t.SysOrgId).ToListAsync();
+                        foreach (var orgTotalLine in orgTotalLines)
+                        {
+                            // 构造取数条件
+                            var orgWhereBaseSql = $"WHERE T1.exam_plan_id = {plan.ExamPlanId} AND T1.grade_id = {esaBaseLine.GradeId} AND T1.score > 0 AND T1.sys_org_id = {orgTotalLine.SysOrgId}";
+                            var orgWhereSql = $"{orgWhereBaseSql} AND T1.course_id IN ({courseIds})";
 
-                        #region 总分划线
-                        // 总学生数量
-                        var orgTotalCount = await rep.SqlScalarAsync<int>($@"
+                            #region 总分划线
+                            // 总学生数量
+                            var orgTotalCount = await rep.SqlScalarAsync<int>($@"
 SELECT COUNT(1) AS total_count
 FROM
 (
@@ -422,8 +430,8 @@ FROM
 ) AS T1;
 ");
 
-                        // 总分上线分
-                        var orgTotalLineScore = await rep.SqlScalarAsync<decimal>($@"
+                            // 总分上线分
+                            var orgTotalLineScore = await rep.SqlScalarAsync<decimal>($@"
 SELECT MIN(total_score) AS line_score
 FROM
 (
@@ -439,8 +447,8 @@ FROM
 WHERE T1.rn <= ROUND({orgTotalCount} * {orgTotalLine.LineRate}, 0);
 ");
 
-                        // 总分上线均分
-                        var orgTotalLineAvgScore = Math.Round(await rep.SqlScalarAsync<decimal>(@$"
+                            // 总分上线均分
+                            var orgTotalLineAvgScore = Math.Round(await rep.SqlScalarAsync<decimal>(@$"
 SELECT AVG(T1.total_score) AS line_avg_score
 FROM
 (
@@ -452,12 +460,12 @@ FROM
 WHERE T1.total_score >= {orgTotalLineScore};
 "), 2);
 
-                        // 更新有效系数
-                        var orgFactor = Math.Round(orgTotalLineScore / orgTotalLineAvgScore, 8);
-                        await rep.SqlNonQueryAsync($"UPDATE esa_line_total SET factor = {orgFactor} WHERE id = {orgTotalLine.Id}");
+                            // 更新有效系数
+                            var orgFactor = Math.Round(orgTotalLineScore / orgTotalLineAvgScore, 8);
+                            await rep.SqlNonQueryAsync($"UPDATE esa_line_total SET factor = {orgFactor} WHERE id = {orgTotalLine.Id}");
 
-                        // 实际上线人数
-                        var orgFactLineCount = await rep.SqlScalarAsync($@"
+                            // 实际上线人数
+                            var orgFactLineCount = await rep.SqlScalarAsync($@"
 SELECT COUNT(1) AS line_count
 FROM
 (
@@ -469,8 +477,8 @@ FROM
 WHERE T1.total_score >= {orgTotalLineScore};
 ");
 
-                        // 总均分
-                        var orgTotalAvgScore = Math.Round(await rep.SqlScalarAsync<decimal>(@$"
+                            // 总均分
+                            var orgTotalAvgScore = Math.Round(await rep.SqlScalarAsync<decimal>(@$"
 SELECT AVG(total_score) AS avg_score
 FROM
 (
@@ -481,9 +489,9 @@ FROM
 ) AS T1;
 "), 2);
 
-                        // 学科有效分
-                        var orgTotalRelativeDiff = Math.Round((orgTotalLineScore - orgTotalAvgScore) / orgTotalLineScore, 8);
-                        await rep.SqlNonQueryAsync(@$"
+                            // 学科有效分
+                            var orgTotalRelativeDiff = Math.Round((orgTotalLineScore - orgTotalAvgScore) / orgTotalLineScore, 8);
+                            await rep.SqlNonQueryAsync(@$"
 -- 总分
 INSERT INTO esa_line_course_score(esa_level, esa_line_level, esa_plan_id, esa_base_line_id, grade_id, sys_org_id, course_id, avg_score, line_avg_score, line_score, relative_diff)
 VALUES({(short)EsaLevel.ORG}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, {(short)MultCourse.TOTAL}, {orgTotalAvgScore}, {orgTotalLineAvgScore}, {orgTotalLineScore}, {orgTotalRelativeDiff});
@@ -523,8 +531,11 @@ JOIN
 ) AS T2 ON T1.course_id = T2.course_id;
 ");
 
-                        // 学校
-                        await rep.SqlNonQueryAsync(@$"
+                            //// TODO: 临时终止
+                            //continue;
+
+                            // 学校
+                            await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_total(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, grade_id, sys_org_id, line_count, total_count, line_rate, factor)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.ORG}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, COUNT(1) AS line_count, T2.total_count, COUNT(1) / T2.total_count, 0
 FROM
@@ -548,8 +559,8 @@ JOIN
 WHERE T1.total_score >= {orgTotalLineScore};
 ");
 
-                        // 班级
-                        await rep.SqlNonQueryAsync(@$"
+                            // 班级
+                            await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_total(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, grade_id, sys_org_id, school_class_id, class_number, line_count, total_count, line_rate, factor)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.CLASS}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, T1.school_class_id, T1.class_number, COUNT(1) AS line_count, T2.total_count, COUNT(1) / T2.total_count, 0
 FROM
@@ -574,25 +585,25 @@ JOIN
 WHERE T1.total_score >= {orgTotalLineScore}
 GROUP BY T1.school_class_id, T1.class_number;
 ");
-                        #endregion
-
-                        #region 学科划线
-                        var orgCourseLines = await rep.Change<EsaLineCourseScore>().DetachedEntities
-                                                                                   .Where(t => t.EsaPlanId == esaPlanId &&
-                                                                                               t.EsaBaseLineId == esaBaseLine.Id &&
-                                                                                               t.EsaLevel == EsaLevel.ORG &&
-                                                                                               t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
-                                                                                               t.GradeId == esaBaseLine.GradeId &&
-                                                                                               t.SysOrgId == orgTotalLine.SysOrgId &&
-                                                                                               t.CourseId != (short)MultCourse.TOTAL
-                                                                                    ).ToListAsync();
-                        foreach (var orgCourseLine in orgCourseLines)
-                        {
-                            // 单科条件
-                            var orgCourseWhereSql = $"{orgWhereBaseSql} AND T1.course_id = {orgCourseLine.CourseId}";
-
-                            // 学校 - 单上线
-                            await rep.SqlNonQueryAsync(@$"
+                            #endregion
+
+                            #region 学科划线
+                            var orgCourseLines = await rep.Change<EsaLineCourseScore>().DetachedEntities
+                                                                                       .Where(t => t.EsaPlanId == esaPlanId &&
+                                                                                                   t.EsaBaseLineId == esaBaseLine.Id &&
+                                                                                                   t.EsaLevel == EsaLevel.ORG &&
+                                                                                                   t.EsaLineLevel == esaBaseLine.EsaLineLevel &&
+                                                                                                   t.GradeId == esaBaseLine.GradeId &&
+                                                                                                   t.SysOrgId == orgTotalLine.SysOrgId &&
+                                                                                                   t.CourseId != (short)MultCourse.TOTAL
+                                                                                        ).ToListAsync();
+                            foreach (var orgCourseLine in orgCourseLines)
+                            {
+                                // 单科条件
+                                var orgCourseWhereSql = $"{orgWhereBaseSql} AND T1.course_id = {orgCourseLine.CourseId}";
+
+                                // 学校 - 单上线
+                                await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_course(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, course_id, grade_id, sys_org_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.ORG}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {orgCourseLine.CourseId}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, T1.line_count, T2.total_count, T1.line_count / T2.total_count, 0
 FROM
@@ -609,8 +620,8 @@ JOIN
 ) AS T2 ON 1 = 1;
 ");
 
-                            // 学校 - 双上线
-                            await rep.SqlNonQueryAsync(@$"
+                                // 学校 - 双上线
+                                await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_course(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, course_id, grade_id, sys_org_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.ORG}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {orgCourseLine.CourseId}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, T1.line_count, T2.total_count, T1.line_count / T2.total_count, 1
 FROM
@@ -639,8 +650,8 @@ JOIN
 ) AS T2 ON 1 = 1;
 ");
 
-                            // 班级 - 单上线
-                            await rep.SqlNonQueryAsync(@$"
+                                // 班级 - 单上线
+                                await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_course(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, course_id, grade_id, sys_org_id, school_class_id, class_number, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.CLASS}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {orgCourseLine.CourseId}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, T1.school_class_id, T1.class_number, T1.line_count, T2.total_count, T1.line_count / T2.total_count, 0
 FROM
@@ -659,8 +670,8 @@ JOIN
 ) AS T2 ON T1.school_class_id = T2.school_class_id AND T1.class_number = T2.class_number;
 ");
 
-                            // 班级 - 双上线
-                            await rep.SqlNonQueryAsync(@$"
+                                // 班级 - 双上线
+                                await rep.SqlNonQueryAsync(@$"
 INSERT INTO esa_line_course(esa_level, esa_data_scope_type, esa_line_level, esa_plan_id, esa_base_line_id, course_id, grade_id, sys_org_id, school_class_id, class_number, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)EsaLevel.ORG}, {(short)EsaDataScopeType.CLASS}, {(short)esaBaseLine.EsaLineLevel}, {esaPlanId}, {esaBaseLine.Id}, {orgCourseLine.CourseId}, {esaBaseLine.GradeId}, {orgTotalLine.SysOrgId}, T1.school_class_id, T1.class_number, T1.line_count, T2.total_count, T1.line_count / T2.total_count, 1
 FROM
@@ -690,10 +701,11 @@ JOIN
     GROUP BY T1.school_class_id, T1.class_number
 ) AS T2 ON T1.school_class_id = T2.school_class_id AND T1.class_number = T2.class_number;
 ");
+                            }
+                            #endregion
                         }
                         #endregion
                     }
-                    #endregion
 
                     // 更新状态
                     await rep.SqlNonQueryAsync($"UPDATE esa_base_line SET status = {(short)ProcessingStatus.SUCCESSFUL} WHERE id = {esaBaseLine.Id}");

+ 10 - 0
YBEE.EQM.Application/Exam/ExamDataPublish/Dtos/ExamDataPublishOutput.cs

@@ -44,6 +44,11 @@ public class ExamDataPublishOutput : DEntityOutput
     /// 发布用户
     /// </summary>
     public SysUserLiteOutput PublishSysUser { get; set; }
+
+    /// <summary>
+    /// 附件列表
+    /// </summary>
+    public List<AttachmentItem> AttachmentList { get; set; } = [];
 }
 
 
@@ -86,6 +91,11 @@ public class ExamDataPublishOrgResultOutput
     /// </summary>
     public DateTime? PublishTime { get; set; }
 
+    /// <summary>
+    /// 附件列表
+    /// </summary>
+    public List<AttachmentItem> AttachmentList { get; set; } = [];
+
     /// <summary>
     /// 监测机构反馈文件列表
     /// </summary>

+ 24 - 1
YBEE.EQM.Application/Exam/ExamDataPublish/ExamDataPublishAppService.cs

@@ -5,7 +5,7 @@ namespace YBEE.EQM.Application;
 /// <summary>
 /// 监测发布内容管理服务
 /// </summary>
-public class ExamDataPublishAppService(IExamDataPublishService examDataPublishService) : IDynamicApiController
+public class ExamDataPublishAppService(IExamDataPublishService examDataPublishService, IResourceFileService resourceFileService) : IDynamicApiController
 {
     #region 创建更新
     /// <summary>
@@ -35,6 +35,29 @@ public class ExamDataPublishAppService(IExamDataPublishService examDataPublishSe
     {
         await examDataPublishService.Del(input);
     }
+
+    /// <summary>
+    /// 上传附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [RequestSizeLimit(1024 * 1024 * 1024)]
+    [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)]
+    public async Task UploadAttachment([FromForm] UploadResourceFileInput input)
+    {
+        var rfile = await resourceFileService.Upload(input);
+        var addParams = rfile.Adapt<AddAttachmentInput>();
+        await examDataPublishService.AddAttachment(addParams);
+    }
+    /// <summary>
+    /// 删除附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task DelAttachment(DeleteAttachmentInput input)
+    {
+        await examDataPublishService.DelAttachment(input);
+    }
     #endregion
 
     #region 状态处理

+ 37 - 1
YBEE.EQM.Application/Exam/ExamDataPublish/Services/ExamDataPublishService.cs

@@ -1,4 +1,5 @@
 using Furion.DatabaseAccessor.Extensions;
+using Furion.JsonSerialization;
 using YBEE.EQM.Core;
 
 namespace YBEE.EQM.Application;
@@ -6,7 +7,7 @@ namespace YBEE.EQM.Application;
 /// <summary>
 /// 监测发布内容管理服务
 /// </summary>
-public class ExamDataPublishService(IRepository<ExamDataPublish> rep, IExamSampleService examSampleService) : IExamDataPublishService, ITransient
+public class ExamDataPublishService(IRepository<ExamDataPublish> rep, IExamSampleService examSampleService, IResourceFileService resourceFileService) : IExamDataPublishService, ITransient
 {
     #region 创建更新
     /// <summary>
@@ -49,6 +50,41 @@ public class ExamDataPublishService(IRepository<ExamDataPublish> rep, IExamSampl
         }
         await item.DeleteAsync();
     }
+
+    /// <summary>
+    /// 添加附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task AddAttachment(AddAttachmentInput input)
+    {
+        var item = await rep.FirstOrDefaultAsync(t => t.Id == input.SourceId) ?? throw Oops.Oh(ErrorCode.E2001);
+        item.Attachments = AttachmentUtil.InsertInto(item.Attachments, input.Adapt<AttachmentItem>());
+        await item.UpdateIncludeAsync([nameof(item.Attachments)]);
+    }
+    /// <summary>
+    /// 删除附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    public async Task DelAttachment(DeleteAttachmentInput input)
+    {
+        var item = await rep.FirstOrDefaultAsync(t => t.Id == input.SourceId) ?? throw Oops.Oh(ErrorCode.E2001);
+        var attachments = AttachmentUtil.GetList(item.Attachments);
+        var a = attachments.FirstOrDefault(t => t.FileId == input.FileId);
+        if (a != null)
+        {
+            attachments.Remove(a);
+            item.Attachments = JSON.Serialize(attachments);
+            await item.UpdateIncludeAsync([nameof(item.Attachments)]);
+
+            await resourceFileService.Del(new() { Id = a.FileId });
+            if (a.ThumbFileId.HasValue && a.ThumbFileId > 0)
+            {
+                await resourceFileService.Del(new() { Id = a.ThumbFileId.Value });
+            }
+        }
+    }
     #endregion
 
     #region 状态处理

+ 12 - 0
YBEE.EQM.Application/Exam/ExamDataPublish/Services/IExamDataPublishService.cs

@@ -26,6 +26,18 @@ public interface IExamDataPublishService
     /// <param name="input"></param>
     /// <returns></returns>
     Task Del(BaseId input);
+    /// <summary>
+    /// 添加附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    Task AddAttachment(AddAttachmentInput input);
+    /// <summary>
+    /// 删除附件
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    Task DelAttachment(DeleteAttachmentInput input);
     #endregion
 
     #region 状态处理

+ 2 - 2
YBEE.EQM.Application/Exam/ExamDataReport/Dtos/ExamDataReportOutput.cs

@@ -51,9 +51,9 @@ public class ExamDataReportOutput : DEntityOutput
     public int UnreportCount { get { return Count - ReportedCount; } }
 
     /// <summary>
-    /// 佐证材料列表
+    /// 附件列表
     /// </summary>
-    public List<AttachmentItem> AttachmentList { get; set; } = new();
+    public List<AttachmentItem> AttachmentList { get; set; } = [];
 }
 
 /// <summary>

+ 6 - 6
YBEE.EQM.Application/Exam/ExamDataReport/Services/ExamDataReportService.cs

@@ -33,7 +33,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
     {
         var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
         item.Remark = input.Remark;
-        await item.UpdateIncludeAsync(new[] { nameof(item.BeginTime), nameof(item.EndTime), nameof(item.Remark) });
+        await item.UpdateIncludeAsync([nameof(item.BeginTime), nameof(item.EndTime), nameof(item.Remark)]);
     }
     /// <summary>
     /// 删除上报类型
@@ -66,7 +66,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
         }
         item.Status = ExamStatus.ACTIVE;
         item.BeginTime = DateTime.Now;
-        await item.UpdateIncludeAsync(new[] { nameof(item.Status), nameof(item.BeginTime) });
+        await item.UpdateIncludeAsync([nameof(item.Status), nameof(item.BeginTime)]);
     }
     /// <summary>
     /// 结束
@@ -82,7 +82,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
         }
         item.Status = ExamStatus.STOPPED;
         item.EndTime = DateTime.Now;
-        await item.UpdateIncludeAsync(new[] { nameof(item.Status), nameof(item.EndTime) });
+        await item.UpdateIncludeAsync([nameof(item.Status), nameof(item.EndTime)]);
     }
     /// <summary>
     /// 取消
@@ -98,7 +98,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
         }
         item.Status = ExamStatus.CANCELLED;
         item.EndTime = DateTime.Now;
-        await item.UpdateIncludeAsync(new[] { nameof(item.Status), nameof(item.EndTime) });
+        await item.UpdateIncludeAsync([nameof(item.Status), nameof(item.EndTime)]);
     }
 
     /// <summary>
@@ -110,7 +110,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
     {
         var item = await rep.FirstOrDefaultAsync(t => t.Id == input.SourceId) ?? throw Oops.Oh(ErrorCode.E2001);
         item.Attachments = AttachmentUtil.InsertInto(item.Attachments, input.Adapt<AttachmentItem>());
-        await item.UpdateIncludeAsync(new[] { nameof(item.Attachments) });
+        await item.UpdateIncludeAsync([nameof(item.Attachments)]);
     }
     /// <summary>
     /// 删除附件
@@ -126,7 +126,7 @@ public class ExamDataReportService(IRepository<ExamDataReport> rep, IResourceFil
         {
             attachments.Remove(a);
             item.Attachments = JSON.Serialize(attachments);
-            await item.UpdateIncludeAsync(new[] { nameof(item.Attachments) });
+            await item.UpdateIncludeAsync([nameof(item.Attachments)]);
 
             await resourceFileService.Del(new() { Id = a.FileId });
             if (a.ThumbFileId.HasValue && a.ThumbFileId > 0)

+ 15 - 21
YBEE.EQM.Application/Exam/ExamOrgResult/Services/ExamOrgResultService.cs

@@ -8,18 +8,9 @@ namespace YBEE.EQM.Application;
 /// <summary>
 /// 监测机构反馈结果管理服务
 /// </summary>
-public class ExamOrgResultService : IExamOrgResultService, ITransient
+public class ExamOrgResultService(IRepository<ExamOrgResult> rep, IOptions<EqmSiteOptions> options, IHttpContextAccessor httpContextAccessor) : IExamOrgResultService, ITransient
 {
-    private readonly IRepository<ExamOrgResult> _rep;
-    private readonly EqmSiteOptions _eqmSiteOptions;
-    private readonly IHttpContextAccessor _httpContextAccessor;
-
-    public ExamOrgResultService(IRepository<ExamOrgResult> rep, IOptions<EqmSiteOptions> options, IHttpContextAccessor httpContextAccessor)
-    {
-        _rep = rep;
-        _eqmSiteOptions = options.Value;
-        _httpContextAccessor = httpContextAccessor;
-    }
+    private readonly EqmSiteOptions _eqmSiteOptions = options.Value;
 
     /// <summary>
     /// 添加结果文件
@@ -39,7 +30,7 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
     /// <returns></returns>
     public async Task Del(BaseId input)
     {
-        var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
+        var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
         var fpath = Path.Combine(_eqmSiteOptions.ExamResultRoot, item.FilePath);
         if (File.Exists(fpath))
         {
@@ -55,7 +46,7 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
     /// <returns></returns>
     public async Task SaveDownloadRecord(int id)
     {
-        var httpContext = _httpContextAccessor.HttpContext;
+        var httpContext = httpContextAccessor.HttpContext;
 
         ExamOrgResultDownload item = new()
         {
@@ -66,7 +57,7 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
             DownloadTime = DateTime.Now,
         };
 
-        await _rep.Change<ExamOrgResultDownload>().InsertNowAsync(item);
+        await rep.Change<ExamOrgResultDownload>().InsertNowAsync(item);
     }
 
     /// <summary>
@@ -76,7 +67,7 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
     /// <returns></returns>
     public async Task<ExamOrgResultOutput> GetById(int id)
     {
-        var item = await _rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001);
+        var item = await rep.DetachedEntities.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001);
         return item.Adapt<ExamOrgResultOutput>();
     }
 
@@ -87,12 +78,15 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
     /// <returns></returns>
     public async Task<List<ExamDataPublishOrgResultOutput>> GetByExamPlanId(int examPlanId)
     {
-        var pubItems = await _rep.Change<ExamDataPublish>().DetachedEntities.Where(t => t.ExamPlanId == examPlanId && t.Status == PublishStatus.PUBLISHED).ProjectToType<ExamDataPublishOrgResultOutput>().ToListAsync();
+        var pubItems = await rep.Change<ExamDataPublish>().DetachedEntities
+                                                          .Where(t => t.ExamPlanId == examPlanId && t.Status == PublishStatus.PUBLISHED)
+                                                          .ProjectToType<ExamDataPublishOrgResultOutput>()
+                                                          .ToListAsync();
         foreach (var pubItem in pubItems)
         {
             if (pubItem.Status == PublishStatus.PUBLISHED)
             {
-                var items = await _rep.DetachedEntities.Where(t => t.SysOrgId == CurrentSysUserInfo.SysOrgId && t.ExamPlanId == examPlanId && t.ExamDataPublishId == pubItem.Id).ProjectToType<ExamOrgResultOutput>().ToListAsync();
+                var items = await rep.DetachedEntities.Where(t => t.SysOrgId == CurrentSysUserInfo.SysOrgId && t.ExamPlanId == examPlanId && t.ExamDataPublishId == pubItem.Id).ProjectToType<ExamOrgResultOutput>().ToListAsync();
                 pubItem.ExamOrgResultList = items;
             }
         }
@@ -107,15 +101,15 @@ public class ExamOrgResultService : IExamOrgResultService, ITransient
     /// <returns></returns>
     public async Task<List<ExamOrgResultOutput>> GetByPublishId(int publishId)
     {
-        var pub = await _rep.Change<ExamDataPublish>().FirstOrDefaultAsync(t => t.Id == publishId) ?? throw Oops.Oh(ErrorCode.E2001);
-        var items = await _rep.DetachedEntities.Where(t => t.ExamDataPublishId == publishId).ProjectToType<ExamOrgResultOutput>().OrderBy(t => t.SysOrgId).ToListAsync();
+        var pub = await rep.Change<ExamDataPublish>().FirstOrDefaultAsync(t => t.Id == publishId) ?? throw Oops.Oh(ErrorCode.E2001);
+        var items = await rep.DetachedEntities.Where(t => t.ExamDataPublishId == publishId).ProjectToType<ExamOrgResultOutput>().OrderBy(t => t.SysOrgId).ToListAsync();
 
-        var norgs = await (from o in _rep.Change<ExamOrg>().AsQueryable()
+        var norgs = await (from o in rep.Change<ExamOrg>().AsQueryable()
                            where o.ExamPlanId == pub.ExamPlanId && !(from r in items select r.ExamOrgId).Contains(o.Id)
                            select o).ProjectToType<ExamOrgOutput>().ToListAsync();
 
         int maxId = 1;
-        if (items.Any())
+        if (items.Count != 0)
         {
             maxId = items.Max(t => t.Id) + 1;
         }

+ 42 - 45
YBEE.EQM.Application/Exam/ExamQuestionnaire/Services/ExamPatriarchQuestionnaireProgressSync.cs

@@ -1,7 +1,4 @@
-
-using Furion.ClayObject;
-using Furion.RemoteRequest.Extensions;
-using YBEE.EQM.Core;
+using YBEE.EQM.Core;
 
 namespace YBEE.EQM.Application;
 
@@ -18,48 +15,48 @@ public class ExamPatriarchQuestionnaireProgressSync(IRepository<ExamPatriarchQue
             return;
         }
 
-        Log.Information("开始同步学生家长问卷进度");
+        //Log.Information("开始同步学生家长问卷进度");
 
-        Dictionary<string, string> qs = new()
-        {
-            { "9am", "http://wenjuan.cqcet.edu.cn/question/applice/answer/answerData.action?code=9am&tokenId=ce789fc1044dd7b0d4ef2b42e4916b66" },
-            { "yyd", "http://wenjuan.cqcet.edu.cn/question/applice/answer/answerData.action?code=yyd&tokenId=d07e946b0d1d36666f68623a38fd26a7" },
-        };
+        //Dictionary<string, string> qs = new()
+        //{
+        //    { "9am", "http://wenjuan.cqcet.edu.cn/question/applice/answer/answerData.action?code=9am&tokenId=ce789fc1044dd7b0d4ef2b42e4916b66" },
+        //    { "yyd", "http://wenjuan.cqcet.edu.cn/question/applice/answer/answerData.action?code=yyd&tokenId=d07e946b0d1d36666f68623a38fd26a7" },
+        //};
 
-        foreach (var kvp in qs)
-        {
-            var maxSubmitTime = await rep.DetachedEntities.Where(t => t.QuestionnaireCode.ToLower() == kvp.Key.ToLower()).MaxAsync(t => t.SubmitTime);
-            string url = kvp.Value;
-            if (maxSubmitTime != null)
-            {
-                url = $"{url}&beginTime={maxSubmitTime:yyyy-MM-dd HH:mm:ss}";
-            }
-            var strRes = await url.GetAsStringAsync();
-            var res = (QuestionnaireProgressResponse)Clay.Parse(strRes);
-            if (res.code != "200")
-            {
-                Log.Error($"同步{kvp.Key}问卷错误");
-                continue;
-            }
-            var items = res.data.Where(t => maxSubmitTime == null || t.finishedTime > maxSubmitTime);
-            var dt = DateTime.Now;
-            List<ExamPatriarchQuestionnaireProgress> newItems = [];
-            foreach (var item in items)
-            {
-                newItems.Add(new()
-                {
-                    QuestionnaireCode = kvp.Key,
-                    ExamStudentId = item.id,
-                    Mobile = item.phone,
-                    SubmitTime = item.finishedTime,
-                    IsCompleted = true,
-                    CreateTime = dt,
-                });
-            }
-            if (items.Any())
-            {
-                await rep.InsertNowAsync(newItems);
-            }
-        }
+        //foreach (var kvp in qs)
+        //{
+        //    var maxSubmitTime = await rep.DetachedEntities.Where(t => t.QuestionnaireCode.ToLower() == kvp.Key.ToLower()).MaxAsync(t => t.SubmitTime);
+        //    string url = kvp.Value;
+        //    if (maxSubmitTime != null)
+        //    {
+        //        url = $"{url}&beginTime={maxSubmitTime:yyyy-MM-dd HH:mm:ss}";
+        //    }
+        //    var strRes = await url.GetAsStringAsync();
+        //    var res = (QuestionnaireProgressResponse)Clay.Parse(strRes);
+        //    if (res.code != "200")
+        //    {
+        //        Log.Error($"同步{kvp.Key}问卷错误");
+        //        continue;
+        //    }
+        //    var items = res.data.Where(t => maxSubmitTime == null || t.finishedTime > maxSubmitTime);
+        //    var dt = DateTime.Now;
+        //    List<ExamPatriarchQuestionnaireProgress> newItems = [];
+        //    foreach (var item in items)
+        //    {
+        //        newItems.Add(new()
+        //        {
+        //            QuestionnaireCode = kvp.Key,
+        //            ExamStudentId = item.id,
+        //            Mobile = item.phone,
+        //            SubmitTime = item.finishedTime,
+        //            IsCompleted = true,
+        //            CreateTime = dt,
+        //        });
+        //    }
+        //    if (items.Any())
+        //    {
+        //        await rep.InsertNowAsync(newItems);
+        //    }
+        //}
     }
 }

+ 3 - 4
YBEE.EQM.Application/Exam/ExamScore/Services/ExamScoreImportService.cs

@@ -1,5 +1,4 @@
-using Furion.ClayObject.Extensions;
-using Furion.DatabaseAccessor.Extensions;
+using Furion.DatabaseAccessor.Extensions;
 using NPOI.SS.UserModel;
 using NPOI.XSSF.UserModel;
 using System.IO.Compression;
@@ -383,7 +382,7 @@ ON T1.scid = T2.school_class_id AND T1.en = T2.exam_number
             Dictionary<int, Course> courses = [];
             for (int gi = COURSE_START_INDEX; gi < headerRow.LastCellNum; gi++)
             {
-                var cn = headerRow.GetCell(gi).ToString() ?? "";
+                var cn = headerRow.GetCell(gi).ToString()?.ClearWhitespace() ?? "";
                 if (string.IsNullOrEmpty(cn))
                 {
                     continue;
@@ -608,7 +607,7 @@ SELECT
     T1.exam_number,
     T1.is_special,
     T1.absent_count,
-    CASE WHEN T1.is_excluded > 0 THEN 1 ELSE 0 END AS is_excluded,
+    CASE WHEN T1.is_excluded = T1.course_count THEN 1 ELSE 0 END AS is_excluded,
     get_exam_score_range_id(T2.exam_score_range_type, T1.score) AS exam_score_range_id
 FROM
 (

+ 8 - 15
YBEE.EQM.Application/File/ResourceFile/Services/ResourceFileService.cs

@@ -9,17 +9,10 @@ namespace YBEE.EQM.Application;
 /// <summary>
 /// 资源文件管理服务
 /// </summary>
-public class ResourceFileService : IResourceFileService, ITransient
+public class ResourceFileService(IRepository<ResourceFile> rep, IOptions<EqmSiteOptions> options) : IResourceFileService, ITransient
 {
-    private readonly IRepository<ResourceFile> _rep;
-    private readonly EqmSiteOptions _eqmSiteOptions;
-    private readonly string[] _thumbImageTypes = { ".png", ".jpg", "jpeg", ".gif", ".bmp" };
-
-    public ResourceFileService(IRepository<ResourceFile> rep, IOptions<EqmSiteOptions> options)
-    {
-        _rep = rep;
-        _eqmSiteOptions = options.Value;
-    }
+    private readonly EqmSiteOptions _eqmSiteOptions = options.Value;
+    private readonly string[] _thumbImageTypes = [".png", ".jpg", ".jpeg", ".gif", ".bmp"];
 
     /// <summary>
     /// 上传文件
@@ -96,13 +89,13 @@ public class ResourceFileService : IResourceFileService, ITransient
     public async Task<ResourceFileOutput> Add(AddResourceFileInput input, AddResourceFileInput thumbInput = null)
     {
         var item = input.Adapt<ResourceFile>();
-        await _rep.InsertAsync(item);
+        await rep.InsertAsync(item);
         var ret = item.Adapt<ResourceFileOutput>();
 
         if (thumbInput != null)
         {
             var thumbItem = thumbInput.Adapt<ResourceFile>();
-            await _rep.InsertAsync(thumbItem);
+            await rep.InsertAsync(thumbItem);
             ret.ThumbResourceFile = thumbItem.Adapt<ResourceFileOutput>();
         }
         return ret;
@@ -114,7 +107,7 @@ public class ResourceFileService : IResourceFileService, ITransient
     /// <returns></returns>
     public async Task Del(BaseId<long> input)
     {
-        var item = await _rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
+        var item = await rep.FirstOrDefaultAsync(t => t.Id == input.Id) ?? throw Oops.Oh(ErrorCode.E2001);
         var fpath = Path.Combine(_eqmSiteOptions.ResourceFileRoot, item.FilePath);
         if (File.Exists(fpath))
         {
@@ -129,7 +122,7 @@ public class ResourceFileService : IResourceFileService, ITransient
     /// <returns></returns>
     public async Task<ResourceFileOutput> GetById(long id)
     {
-        var item = await _rep.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001);
+        var item = await rep.FirstOrDefaultAsync(t => t.Id == id) ?? throw Oops.Oh(ErrorCode.E2001);
         return item.Adapt<ResourceFileOutput>();
     }
     /// <summary>
@@ -140,7 +133,7 @@ public class ResourceFileService : IResourceFileService, ITransient
     /// <returns></returns>
     public async Task<List<ResourceFileOutput>> GetListBySourceId(ResourceFileType type, long sourceId)
     {
-        var items = await _rep.DetachedEntities.Where(t => t.Type == type && t.SourceId == sourceId).ProjectToType<ResourceFileOutput>().ToListAsync();
+        var items = await rep.DetachedEntities.Where(t => t.Type == type && t.SourceId == sourceId).ProjectToType<ResourceFileOutput>().ToListAsync();
         return items;
     }
 }

+ 3 - 0
YBEE.EQM.Application/Mapper/AttachmentMapper.cs

@@ -18,6 +18,9 @@ public class AttachmentMapper : IRegister
         config.ForType<ExamAbsentReplace, ExamAbsentReplaceOutput>().Map(d => d.AttachmentList, s => JSON.Deserialize<List<AttachmentItem>>(s.Attachments, null));
         // 监测数据上报类型附件
         config.ForType<ExamDataReport, ExamDataReportOutput>().Map(d => d.AttachmentList, s => JSON.Deserialize<List<AttachmentItem>>(s.Attachments, null));
+        // 监测结果发布类型附件
+        config.ForType<ExamDataPublish, ExamDataPublishOutput>().Map(d => d.AttachmentList, s => JSON.Deserialize<List<AttachmentItem>>(s.Attachments, null));
+        config.ForType<ExamDataPublish, ExamDataPublishOrgResultOutput>().Map(d => d.AttachmentList, s => JSON.Deserialize<List<AttachmentItem>>(s.Attachments, null));
 
 
         config.ForType<ResourceFileOutput, AddAttachmentInput>().Map(d => d.SourceId, s => (int)s.SourceId)

+ 3 - 9
YBEE.EQM.Application/Ncee/NceeCourseComb/Services/NceeCourseCombService.cs

@@ -5,14 +5,8 @@ namespace YBEE.EQM.Application;
 /// <summary>
 /// 高中选科组合管理服务
 /// </summary>
-public class NceeCourseCombService : INceeCourseCombService, ITransient
+public class NceeCourseCombService(IRepository<NceeCourseComb> rep) : INceeCourseCombService, ITransient
 {
-    private readonly IRepository<NceeCourseComb> _rep;
-    public NceeCourseCombService(IRepository<NceeCourseComb> rep)
-    {
-        _rep = rep;
-    }
-
     /// <summary>
     /// 根据ID获取高中选科组合
     /// </summary>
@@ -20,7 +14,7 @@ public class NceeCourseCombService : INceeCourseCombService, ITransient
     /// <returns></returns>
     public async Task<NceeCourseCombOutput> GetById(short id)
     {
-        var item = await _rep.DetachedEntities.FirstOrDefaultAsync(x => x.Id == id);
+        var item = await rep.DetachedEntities.FirstOrDefaultAsync(x => x.Id == id);
         return item.Adapt<NceeCourseCombOutput>();
     }
     /// <summary>
@@ -29,7 +23,7 @@ public class NceeCourseCombService : INceeCourseCombService, ITransient
     /// <returns></returns>
     public async Task<List<NceeCourseCombOutput>> GetAllList()
     {
-        var items = await _rep.DetachedEntities.ProjectToType<NceeCourseCombOutput>().ToListAsync();
+        var items = await rep.DetachedEntities.ProjectToType<NceeCourseCombOutput>().ToListAsync();
         return items;
     }
 }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 321 - 203
YBEE.EQM.Application/Ncee/NceeExport/Services/NceeExportService.cs


+ 127 - 71
YBEE.EQM.Application/Ncee/NceeScore/Services/NceeScoreService.cs

@@ -9,9 +9,6 @@ namespace YBEE.EQM.Application;
 /// </summary>
 public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, ITransient
 {
-    private readonly IRepository<NceeScore> _rep = rep;
-    private readonly List<short> _chooseCourses = [5, 6, 7, 9];
-
     /// <summary>
     /// 上传成绩(仅原始分,适用于五区联考或本区独立赋分)
     /// </summary>
@@ -92,10 +89,10 @@ public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, I
             #endregion
 
             #region 2.读取数据
-            var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
-            var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
+            var plan = await rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
+            var courseDict = (await rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
             //var orgs = await _rep.Change<SysOrg>().DetachedEntities.Where(t => t.EducationStage == EducationStage.SENIOR_HIGH_SCHOOL_STAGE).ToListAsync();
-            var courseCombs = await _rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
+            var courseCombs = await rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
             var courseCombDict = courseCombs.ToDictionary(t => t.Id, t => t);
 
             // 获取需要导入的科目列表
@@ -205,7 +202,7 @@ public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, I
 
                     if (item.Score > 0)
                     {
-                        if (!_chooseCourses.Any(t => t == course.Id))
+                        if (!NceeUtil.ChooseCourses.Any(t => t == course.Id))
                         {
                             item.ScoreX = item.Score;
                         }
@@ -225,7 +222,7 @@ public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, I
             #region 3.处理学生
             // 清理学生
             string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteStudentSql);
+            await rep.SqlNonQueryAsync(deleteStudentSql);
 
             var uid = CurrentSysUserInfo.SysUserId;
             if (uid == 0)
@@ -257,7 +254,7 @@ public class NceeScoreService(IRepository<NceeScore> rep) : INceeScoreService, I
 INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES 
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -266,7 +263,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
             #region 4.批量导入
             // 清理成绩
             string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteScoreSql);
+            await rep.SqlNonQueryAsync(deleteScoreSql);
 
             insertValues.Clear();
             scount = scores.Count;
@@ -281,7 +278,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
 INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, score_x) VALUES
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -377,12 +374,12 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, scor
             #endregion
 
             #region 2.读取数据
-            var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
-            var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
+            var plan = await rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
+            var courseDict = (await rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
             //var orgs = await _rep.Change<SysOrg>().DetachedEntities.Where(t => t.EducationStage == EducationStage.SENIOR_HIGH_SCHOOL_STAGE).ToListAsync();
-            var courseCombs = await _rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
+            var courseCombs = await rep.Change<NceeCourseComb>().DetachedEntities.ToListAsync();
             var courseCombDict = courseCombs.ToDictionary(t => t.Id, t => t);
-            var convertGrades = await _rep.Change<NceeConvertGrade>().DetachedEntities.ToListAsync();
+            var convertGrades = await rep.Change<NceeConvertGrade>().DetachedEntities.ToListAsync();
             var convertGradeDict = convertGrades.ToDictionary(t => t.Name);
 
             // 获取需要导入的科目列表
@@ -519,7 +516,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, scor
                             if (course.IsRawScore == true)
                             {
                                 item.Score = score;
-                                if (!_chooseCourses.Any(t => t == course.Course.Id))
+                                if (!NceeUtil.ChooseCourses.Any(t => t == course.Course.Id))
                                 {
                                     item.ScoreX = score;
                                 }
@@ -555,7 +552,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, scor
             #region 3.处理学生
             // 清理学生
             string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteStudentSql);
+            await rep.SqlNonQueryAsync(deleteStudentSql);
 
             var uid = CurrentSysUserInfo.SysUserId;
             if (uid == 0)
@@ -587,7 +584,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, scor
 INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES 
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -596,7 +593,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
             #region 4.批量导入
             // 清理成绩
             string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteScoreSql);
+            await rep.SqlNonQueryAsync(deleteScoreSql);
 
             insertValues.Clear();
             scount = scores.Count;
@@ -617,7 +614,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
 INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_convert_grade_id, ncee_convert_grade_name, score, score_x) VALUES
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -705,8 +702,8 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
             #endregion
 
             #region 2.读取数据
-            var plan = await _rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
-            var courseDict = (await _rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
+            var plan = await rep.Change<NceePlan>().DetachedEntities.FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001, "计划");
+            var courseDict = (await rep.Change<Course>().DetachedEntities.ToListAsync()).ToDictionary(t => t.Name);
 
             // 获取需要导入的科目列表
             Dictionary<int, Course> courses = [];
@@ -819,7 +816,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
             #region 3.处理学生
             // 清理学生
             string deleteStudentSql = $"DELETE FROM ncee_student WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteStudentSql);
+            await rep.SqlNonQueryAsync(deleteStudentSql);
 
             var uid = CurrentSysUserInfo.SysUserId;
             if (uid == 0)
@@ -840,7 +837,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
 INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, exam_number, `name`, direction_course_id, ncee_course_comb_id, score, score_x) VALUES 
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -849,7 +846,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
             #region 4.批量导入
             // 清理成绩
             string deleteScoreSql = $"DELETE FROM ncee_score WHERE ncee_plan_id = {nceePlanId};";
-            await _rep.SqlNonQueryAsync(deleteScoreSql);
+            await rep.SqlNonQueryAsync(deleteScoreSql);
 
             insertValues.Clear();
             scount = scores.Count;
@@ -864,7 +861,7 @@ INSERT INTO ncee_student(id, ncee_plan_id, sys_org_id, grade_id, class_number, e
 INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_convert_grade_id, ncee_convert_grade_name, score, score_x) VALUES
 {string.Join(",", insertValues)}
 ";
-                    await _rep.SqlNonQueryAsync(insertSql);
+                    await rep.SqlNonQueryAsync(insertSql);
                     insertValues.Clear();
                 }
             }
@@ -886,7 +883,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
     /// <returns></returns>
     public async Task Execute(int nceePlanId)
     {
-        var nceePlan = await _rep.Change<NceePlan>().FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001);
+        var nceePlan = await rep.Change<NceePlan>().FirstOrDefaultAsync(t => t.Id == nceePlanId) ?? throw Oops.Oh(ErrorCode.E2001);
         var nceePlanConfig = JSON.Deserialize<NceePlanConfig>(nceePlan.Config);
 
         // 执行分数转换
@@ -909,7 +906,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
     /// <returns></returns>
     private async Task ExecuteLine(int nceePlanId, NceePlanConfig config)
     {
-        var baseLines = await _rep.Change<NceeBaseLine>().Where(t => t.NceePlanId == nceePlanId).OrderBy(t => t.NceeLineLevel).ToListAsync();
+        var baseLines = await rep.Change<NceeBaseLine>().Where(t => t.NceePlanId == nceePlanId).OrderBy(t => t.NceeLineLevel).ToListAsync();
         // 单科有效分
         List<NceeCourseLineScore> courseLineScores = [];
 
@@ -920,7 +917,7 @@ INSERT INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, ncee_conver
             foreach (var line in baseLines)
             {
                 // 计算总有效分
-                var lineScoreX = await _rep.SqlScalarAsync<decimal>(@$"
+                var lineScoreX = await rep.SqlScalarAsync<decimal>(@$"
 SELECT MIN(score_x) AS score_x
 FROM
 (
@@ -932,7 +929,7 @@ WHERE T1.rn <= (SELECT COUNT(1) FROM ncee_student WHERE ncee_plan_id = @nceePlan
 ", new { NceePlanId = nceePlanId, line.DirectionCourseId, line.LineRate });
                 line.LineScoreX = lineScoreX;
             }
-            await _rep.Change<NceeBaseLine>().UpdateNowAsync(baseLines);
+            await rep.Change<NceeBaseLine>().UpdateNowAsync(baseLines);
         }
         #endregion
 
@@ -945,8 +942,10 @@ WHERE T1.rn <= (SELECT COUNT(1) FROM ncee_student WHERE ncee_plan_id = @nceePlan
 
             foreach (var line in baseLines)
             {
-                // 计算单科有效分
-                var courseLineScoreX = await _rep.SqlQueryAsync<NceeCourseLineScoreCalcDto>($@"
+                if (!config.DirectionUnseleted)
+                {
+                    // 计算单科有效分
+                    var courseLineScoreX = await rep.SqlQueryAsync<NceeCourseLineScoreCalcDto>($@"
 SELECT 
     MIN(T1.yuwen_score_x) AS yuwen_score_x,             -- 语文
     MIN(T1.shuxue_score_x) AS shuxue_score_x,           -- 数学
@@ -1012,40 +1011,93 @@ FROM
 WHERE T1.score_x >= @totalLineScoreX
 ", new { NceePlanId = nceePlanId, line.DirectionCourseId, TotalLineScoreX = line.LineScoreX });
 
-                var cline = courseLineScoreX.FirstOrDefault();
-                if (cline != null)
+                    var cline = courseLineScoreX.FirstOrDefault();
+                    if (cline != null)
+                    {
+                        courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = CourseConst.YU_WEN, LineScoreX = cline.YuwenScoreX });
+                        courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = CourseConst.SHU_XUE, LineScoreX = cline.ShuxueScoreX });
+                        courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = CourseConst.YING_YU, LineScoreX = cline.YingyuScoreX });
+                        courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = line.DirectionCourseId, LineScoreX = cline.FangxiangScoreX });
+                        courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = CourseConst.ZONG_HE, LineScoreX = cline.ZongheScoreX });
+                    }
+                }
+                else
                 {
-                    courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 1, LineScoreX = cline.YuwenScoreX });
-                    courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 2, LineScoreX = cline.ShuxueScoreX });
-                    courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 3, LineScoreX = cline.YingyuScoreX });
-                    courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = line.DirectionCourseId, LineScoreX = cline.FangxiangScoreX });
-                    courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 101, LineScoreX = cline.ZongheScoreX });
+                    List<string> minFields = [];
+                    List<string> scoreFields = [];
+                    List<string> totalScore = [];
+                    List<string> courseSelects = [];
+                    for (short c = 1; c < 10; c++)
+                    {
+                        minFields.Add($"MIN(T1.score_{c}) AS score_{c}");
+                        scoreFields.Add($"T{c}.score AS score_{c}");
+                        totalScore.Add($"IFNULL(T{c}.score, 0)");
+                        courseSelects.Add(@$"
+    LEFT JOIN
+    (
+        SELECT ROW_NUMBER() OVER(ORDER BY T2.score DESC) AS rn, T2.score
+        FROM ncee_student AS T1
+        JOIN ncee_score AS T2 ON T1.id = T2.ncee_student_id
+        WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId AND T2.course_id = {c}
+    ) AS T{c} ON T0.rn = T{c}.rn
+");
+                    }
+
+                    // 计算单科有效分
+                    var courseLineScoreX = await rep.SqlQueryAsync($@"
+SELECT 
+    {string.Join(", ", minFields)}
+FROM
+(
+    SELECT 
+        T0.rn, 
+        {string.Join(", ", scoreFields)}, 
+        {string.Join(" + ", totalScore)} AS score
+    FROM
+    (
+        SELECT ROW_NUMBER() OVER(ORDER BY score DESC) AS rn
+        FROM ncee_student AS T1
+        WHERE T1.ncee_plan_id = @nceePlanId AND T1.direction_course_id = @directionCourseId
+    ) AS T0
+{string.Join("", courseSelects)}
+) AS T1
+WHERE T1.score >= @totalLineScoreX
+", new { NceePlanId = nceePlanId, line.DirectionCourseId, TotalLineScoreX = line.LineScoreX });
+
+                    if (courseLineScoreX.Rows.Count > 0)
+                    {
+                        var dr = courseLineScoreX.Rows[0];
+                        for (short c = 1; c < 10; c++)
+                        {
+                            courseLineScores.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = c, LineScoreX = (decimal)dr[$"score_{c}"] });
+                        } 
+                    }
                 }
             }
-            await _rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
+            await rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
             List<NceeCourseLineScore> totalLines = [];
             foreach (var line in baseLines)
             {
                 totalLines.Add(new() { NceePlanId = nceePlanId, DirectionCourseId = line.DirectionCourseId, NceeLineLevel = line.NceeLineLevel, CourseId = 100, LineScoreX = line.LineScoreX });
             }
-            await _rep.Change<NceeCourseLineScore>().InsertNowAsync(totalLines);
-            await _rep.Change<NceeCourseLineScore>().InsertNowAsync(courseLineScores);
+            await rep.Change<NceeCourseLineScore>().InsertNowAsync(totalLines);
+            await rep.Change<NceeCourseLineScore>().InsertNowAsync(courseLineScores);
         }
         else
         {
-            courseLineScores = await _rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ToListAsync();
+            courseLineScores = await rep.Change<NceeCourseLineScore>().Where(t => t.NceePlanId == nceePlanId).ToListAsync();
         }
         #endregion
 
 
         #region 计算总分上线
         // 清除数据
-        await _rep.Change<NceeLineTotal>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
+        await rep.Change<NceeLineTotal>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
         // 总分划线
         foreach (var line in baseLines)
         {
             // 整体 - 方向
-            await _rep.SqlNonQueryAsync($@"
+            await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, grade_id, line_count, total_count, line_rate)
 SELECT  {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
 FROM
@@ -1066,7 +1118,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
 
             // 机构 - 方向
-            await _rep.SqlNonQueryAsync($@"
+            await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, sys_org_id, grade_id, line_count, total_count, line_rate)
 SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
 FROM
@@ -1091,7 +1143,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
 
             // 机构 - 方向 - 组合
-            await _rep.SqlNonQueryAsync($@"
+            await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate)
 SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
 FROM
@@ -1117,7 +1169,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX });
 
             // 机构 - 班级 - 方向 - 组合
-            await _rep.SqlNonQueryAsync($@"
+            await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_total(type, ncee_plan_id, ncee_line_level, direction_course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate)
 SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, T1.*, T1.line_count / T1.total_count AS line_rate
 FROM
@@ -1149,7 +1201,7 @@ FROM
 
         #region 计算科目有效分上线
         // 清除数据
-        await _rep.Change<NceeLineCourse>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
+        await rep.Change<NceeLineCourse>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
         // 总分划线
         foreach (var line in baseLines)
         {
@@ -1157,13 +1209,13 @@ FROM
             foreach (var courseLine in courseLines)
             {
                 string inCourses = courseLine.CourseId.ToString();
-                if (courseLine.CourseId == 101)
+                if (courseLine.CourseId == CourseConst.ZONG_HE)
                 {
                     inCourses = "5, 6, 7, 9";
                 }
 
                 // 单上线:整体 - 方向
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
 FROM
@@ -1196,7 +1248,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
                 // 双上线:整体 - 方向
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.TOTAL}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
 FROM
@@ -1228,9 +1280,8 @@ FROM
 ) AS T1
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
-
                 // 单上线:机构 - 方向
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
 FROM
@@ -1267,7 +1318,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
                 // 双上线:机构 - 方向
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.ORG}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
 FROM
@@ -1303,9 +1354,8 @@ FROM
 ) AS T1
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
-
                 // 单上线:机构 - 方向 - 组合
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
 FROM
@@ -1343,7 +1393,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
                 // 双上线:机构 - 方向 - 组合
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.COMB}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
 FROM
@@ -1380,9 +1430,8 @@ FROM
 ) AS T1
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
-
                 // 机构 - 班级 - 方向 - 组合
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 0
 FROM
@@ -1421,7 +1470,7 @@ FROM
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
 
                 // 机构 - 班级 - 方向 - 组合
-                await _rep.SqlNonQueryAsync($@"
+                await rep.SqlNonQueryAsync($@"
 INSERT INTO ncee_line_course(type, ncee_plan_id, ncee_line_level, direction_course_id, course_id, ncee_course_comb_id, sys_org_id, grade_id, class_number, line_count, total_count, line_rate, is_double_line)
 SELECT {(short)NceeDataScopeType.CLASS}, @nceePlanId, @nceeLineLevel, @directionCourseId, @courseId, T1.*, T1.line_count / T1.total_count AS line_rate, 1
 FROM
@@ -1458,7 +1507,6 @@ FROM
     GROUP BY T1.ncee_course_comb_id, T1.sys_org_id, T1.grade_id, T1.class_number
 ) AS T1
 ", new { NceePlanId = nceePlanId, line.NceeLineLevel, line.DirectionCourseId, line.LineScoreX, courseLine.CourseId, CourseLineScoreX = courseLine.LineScoreX });
-
             }
         }
         #endregion
@@ -1470,15 +1518,15 @@ FROM
     /// <returns></returns>
     private async Task ExecuteScoreConvert(int nceePlanId)
     {
-        await _rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
+        await rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId).ExecuteDeleteAsync();
 
-        var convertGrades = await _rep.Change<NceeConvertGrade>().DetachedEntities.OrderBy(t => t.Sequence).ToListAsync();
+        var convertGrades = await rep.Change<NceeConvertGrade>().DetachedEntities.OrderBy(t => t.Sequence).ToListAsync();
         var convertGradeDict = convertGrades.ToDictionary(t => t.Id, t => t);
 
         // 分科转换
-        foreach (var courseId in _chooseCourses)
+        foreach (var courseId in NceeUtil.ChooseCourses)
         {
-            var scores = await _rep.DetachedEntities.Where(t => t.NceeStudent.NceePlanId == nceePlanId && t.CourseId == courseId && t.Score > 0).OrderByDescending(t => t.Score).ToListAsync();
+            var scores = await rep.DetachedEntities.Where(t => t.NceeStudent.NceePlanId == nceePlanId && t.CourseId == courseId && t.Score > 0).OrderByDescending(t => t.Score).ToListAsync();
             if (scores.Count == 0)
             {
                 continue;
@@ -1486,8 +1534,8 @@ FROM
             var convertRanges = GetConvertRange(nceePlanId, courseId, convertGrades, scores);
 
             // 更新转换区间
-            await _rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId && t.CourseId == courseId).ExecuteDeleteAsync();
-            await _rep.Change<NceeConvertRange>().InsertNowAsync(convertRanges);
+            await rep.Change<NceeConvertRange>().Where(t => t.NceePlanId == nceePlanId && t.CourseId == courseId).ExecuteDeleteAsync();
+            await rep.Change<NceeConvertRange>().InsertNowAsync(convertRanges);
 
             // 更新转换分
             List<string> replaceValues = [];
@@ -1506,14 +1554,14 @@ FROM
 REPLACE INTO ncee_score(id, ncee_plan_id, ncee_student_id, course_id, score, score_x, ncee_convert_grade_id, ncee_convert_grade_name) VALUES
 {string.Join(",", replaceValues)}
 ";
-                    await _rep.SqlNonQueryAsync(replaceSql);
+                    await rep.SqlNonQueryAsync(replaceSql);
                     replaceValues.Clear();
                 }
             }
         }
 
         // 更新总分
-        await _rep.SqlNonQueryAsync($@"
+        await rep.SqlNonQueryAsync($@"
 -- 更新总分
 UPDATE ncee_student AS T1
 JOIN
@@ -1534,7 +1582,7 @@ WHERE T1.ncee_plan_id = @nceePlanId
     /// <returns></returns>
     private async Task ExecuteUpdateOrder(int nceePlanId)
     {
-        await _rep.SqlNonQueryAsync($@"
+        await rep.SqlNonQueryAsync($@"
 UPDATE ncee_student AS T1
 JOIN
 (
@@ -1620,6 +1668,14 @@ SET T1.order_in_total = T2.order_in_total,
         // Y为原始分
         // X为转换分
 
+        //// X = ((Y - Y1) * X2 + (Y2 - Y) * X1) / (Y2 - Y1)
+        //var y = score;
+        //var y1 = convertRange.MinScore;
+        //var y2 = convertRange.MaxScore;
+        //var x1 = convertGrade.MinScore;
+        //var x2 = convertGrade.MaxScore;
+        //var x = ((y - y1) * x2 + (y2 - y) * x1) / (y2 - y1);
+        //return x;
 
         // a = Y2 - Y
         var a = (convertRange.MaxScore - score);

+ 11 - 4
YBEE.EQM.Application/Utils/NceeUtil.cs

@@ -1,13 +1,20 @@
-namespace YBEE.EQM.Application;
+using YBEE.EQM.Core;
+
+namespace YBEE.EQM.Application;
 
 public static class NceeUtil
 {
     /// <summary>
     /// 选择方向科目列表
     /// </summary>
-    public static List<CourseMiniOutput> DirectionCourses { get; } = new()
-    {
+    public static List<CourseMiniOutput> DirectionCourses { get; } =
+    [
         new(){ Id = 4, Name = "物理", ShortName = "物" },
         new(){ Id = 8, Name = "历史", ShortName = "史" },
-    };
+    ];
+
+    /// <summary>
+    /// 高中选择科目,化学、生物、政治、地理
+    /// </summary>
+    public static List<short> ChooseCourses { get; } = [CourseConst.HUA_XUE, CourseConst.SHENG_WU, CourseConst.ZHENG_ZHI, CourseConst.DI_LI];
 }

+ 77 - 4
YBEE.EQM.Application/YBEE.EQM.Application.xml

@@ -3028,6 +3028,11 @@
             发布用户
             </summary>
         </member>
+        <member name="P:YBEE.EQM.Application.ExamDataPublishOutput.AttachmentList">
+            <summary>
+            附件列表
+            </summary>
+        </member>
         <member name="T:YBEE.EQM.Application.ExamDataPublishOrgResultOutput">
             <summary>
             监测机构反馈结果文件输出参数
@@ -3068,6 +3073,11 @@
             发布时间
             </summary>
         </member>
+        <member name="P:YBEE.EQM.Application.ExamDataPublishOrgResultOutput.AttachmentList">
+            <summary>
+            附件列表
+            </summary>
+        </member>
         <member name="P:YBEE.EQM.Application.ExamDataPublishOrgResultOutput.ExamOrgResultList">
             <summary>
             监测机构反馈文件列表
@@ -3153,7 +3163,7 @@
             监测发布内容管理服务
             </summary>
         </member>
-        <member name="M:YBEE.EQM.Application.ExamDataPublishAppService.#ctor(YBEE.EQM.Application.IExamDataPublishService)">
+        <member name="M:YBEE.EQM.Application.ExamDataPublishAppService.#ctor(YBEE.EQM.Application.IExamDataPublishService,YBEE.EQM.Application.IResourceFileService)">
             <summary>
             监测发布内容管理服务
             </summary>
@@ -3179,6 +3189,20 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="M:YBEE.EQM.Application.ExamDataPublishAppService.UploadAttachment(YBEE.EQM.Application.UploadResourceFileInput)">
+            <summary>
+            上传附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:YBEE.EQM.Application.ExamDataPublishAppService.DelAttachment(YBEE.EQM.Application.DeleteAttachmentInput)">
+            <summary>
+            删除附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
         <member name="M:YBEE.EQM.Application.ExamDataPublishAppService.Publish(YBEE.EQM.Core.BaseId)">
             <summary>
             发布
@@ -3220,7 +3244,7 @@
             监测发布内容管理服务
             </summary>
         </member>
-        <member name="M:YBEE.EQM.Application.ExamDataPublishService.#ctor(Furion.DatabaseAccessor.IRepository{YBEE.EQM.Core.ExamDataPublish},YBEE.EQM.Application.IExamSampleService)">
+        <member name="M:YBEE.EQM.Application.ExamDataPublishService.#ctor(Furion.DatabaseAccessor.IRepository{YBEE.EQM.Core.ExamDataPublish},YBEE.EQM.Application.IExamSampleService,YBEE.EQM.Application.IResourceFileService)">
             <summary>
             监测发布内容管理服务
             </summary>
@@ -3246,6 +3270,20 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="M:YBEE.EQM.Application.ExamDataPublishService.AddAttachment(YBEE.EQM.Application.AddAttachmentInput)">
+            <summary>
+            添加附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:YBEE.EQM.Application.ExamDataPublishService.DelAttachment(YBEE.EQM.Application.DeleteAttachmentInput)">
+            <summary>
+            删除附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
         <member name="M:YBEE.EQM.Application.ExamDataPublishService.Publish(YBEE.EQM.Core.BaseId)">
             <summary>
             发布
@@ -3308,6 +3346,20 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="M:YBEE.EQM.Application.IExamDataPublishService.AddAttachment(YBEE.EQM.Application.AddAttachmentInput)">
+            <summary>
+            添加附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:YBEE.EQM.Application.IExamDataPublishService.DelAttachment(YBEE.EQM.Application.DeleteAttachmentInput)">
+            <summary>
+            删除附件
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
         <member name="M:YBEE.EQM.Application.IExamDataPublishService.Publish(YBEE.EQM.Core.BaseId)">
             <summary>
             发布
@@ -3456,7 +3508,7 @@
         </member>
         <member name="P:YBEE.EQM.Application.ExamDataReportOutput.AttachmentList">
             <summary>
-            佐证材料列表
+            附件列表
             </summary>
         </member>
         <member name="T:YBEE.EQM.Application.ExamDataReportPlanOutput">
@@ -4779,6 +4831,11 @@
             监测机构反馈结果管理服务
             </summary>
         </member>
+        <member name="M:YBEE.EQM.Application.ExamOrgResultService.#ctor(Furion.DatabaseAccessor.IRepository{YBEE.EQM.Core.ExamOrgResult},Microsoft.Extensions.Options.IOptions{YBEE.EQM.Core.EqmSiteOptions},Microsoft.AspNetCore.Http.IHttpContextAccessor)">
+            <summary>
+            监测机构反馈结果管理服务
+            </summary>
+        </member>
         <member name="M:YBEE.EQM.Application.ExamOrgResultService.Add(YBEE.EQM.Application.AddExamOrgResultInput)">
             <summary>
             添加结果文件
@@ -14436,6 +14493,11 @@
             资源文件管理服务
             </summary>
         </member>
+        <member name="M:YBEE.EQM.Application.ResourceFileService.#ctor(Furion.DatabaseAccessor.IRepository{YBEE.EQM.Core.ResourceFile},Microsoft.Extensions.Options.IOptions{YBEE.EQM.Core.EqmSiteOptions})">
+            <summary>
+            资源文件管理服务
+            </summary>
+        </member>
         <member name="M:YBEE.EQM.Application.ResourceFileService.Upload(YBEE.EQM.Application.UploadResourceFileInput)">
             <summary>
             上传文件
@@ -14712,6 +14774,11 @@
             高中选科组合管理服务
             </summary>
         </member>
+        <member name="M:YBEE.EQM.Application.NceeCourseCombService.#ctor(Furion.DatabaseAccessor.IRepository{YBEE.EQM.Core.NceeCourseComb})">
+            <summary>
+            高中选科组合管理服务
+            </summary>
+        </member>
         <member name="M:YBEE.EQM.Application.NceeCourseCombService.GetById(System.Int16)">
             <summary>
             根据ID获取高中选科组合
@@ -15013,13 +15080,14 @@
             <param name="isExportConvertRange">是否导出转换区间</param>
             <returns></returns>
         </member>
-        <member name="M:YBEE.EQM.Application.NceeExportService.ExportLineUnselected(System.Int32,System.Collections.Generic.List{YBEE.EQM.Application.NceeCourseDto},System.Data.DataTable)">
+        <member name="M:YBEE.EQM.Application.NceeExportService.ExportLineUnselected(System.Int32,System.Collections.Generic.List{YBEE.EQM.Application.NceeCourseDto},System.Data.DataTable,System.Boolean)">
             <summary>
             导出上线统计(未选科)
             </summary>
             <param name="nceePlanId"></param>
             <param name="courses"></param>
             <param name="lineLevelTable"></param>
+            <param name="directionUnseleted">是否未选科</param>
             <returns></returns>
         </member>
         <member name="M:YBEE.EQM.Application.NceeExportService.ExportConvertScore(YBEE.EQM.Application.ExportConvertScoreDto)">
@@ -19249,5 +19317,10 @@
             选择方向科目列表
             </summary>
         </member>
+        <member name="P:YBEE.EQM.Application.NceeUtil.ChooseCourses">
+            <summary>
+            高中选择科目,化学、生物、政治、地理
+            </summary>
+        </member>
     </members>
 </doc>

+ 2 - 2
YBEE.EQM.Application/applicationsettings.Development.json

@@ -32,8 +32,8 @@
     ]
   },
   "ConnectionStrings": {
-    //"DbMain": "Data Source=183.230.108.224;Database=ybee_eqm;User ID=root;Password=ir2019@spring;pooling=true;port=3306;sslmode=none;CharSet=utf8;AllowPublicKeyRetrieval=True;",
-    "DbMain": "Data Source=localhost;Database=ybee_eqm;User ID=root;Password=rj123*456;pooling=true;port=3306;sslmode=none;CharSet=utf8;AllowPublicKeyRetrieval=True;",
+    "DbMain": "Data Source=183.230.108.224;Database=ybee_eqm;User ID=root;Password=ir2019@spring;pooling=true;port=3306;sslmode=none;CharSet=utf8;AllowPublicKeyRetrieval=True;",
+    //"DbMain": "Data Source=localhost;Database=ybee_eqm;User ID=root;Password=rj123*456;pooling=true;port=3306;sslmode=none;CharSet=utf8;AllowPublicKeyRetrieval=True;",
     "TQES": "Data Source=183.230.108.224,5002; Initial Catalog=TQES_NEW; User Id=sa; Password=tqes123*456; TrustServerCertificate=true;"
   },
   "Auth": {

+ 64 - 0
YBEE.EQM.Core/Const/CourseConst.cs

@@ -0,0 +1,64 @@
+namespace YBEE.EQM.Core;
+
+/// <summary>
+/// 科目常量
+/// </summary>
+public class CourseConst
+{
+    /// <summary>
+    /// 语文
+    /// </summary>
+    public const short YU_WEN = 1;
+    /// <summary>
+    /// 数学
+    /// </summary>
+    public const short SHU_XUE = 2;
+    /// <summary>
+    /// 英语
+    /// </summary>
+    public const short YING_YU = 3;
+    /// <summary>
+    /// 物理
+    /// </summary>
+    public const short WU_LI = 4;
+    /// <summary>
+    /// 化学
+    /// </summary>
+    public const short HUA_XUE = 5;
+    /// <summary>
+    /// 生物
+    /// </summary>
+    public const short SHENG_WU = 6;
+    /// <summary>
+    /// 政治
+    /// </summary>
+    public const short ZHENG_ZHI = 7;
+    /// <summary>
+    /// 历史
+    /// </summary>
+    public const short LI_SHI = 8;
+    /// <summary>
+    /// 地理
+    /// </summary>
+    public const short DI_LI = 9;
+    /// <summary>
+    /// 德语
+    /// </summary>
+    public const short DE_YU = 31;
+    /// <summary>
+    /// 日语
+    /// </summary>
+    public const short RI_YU = 32;
+    /// <summary>
+    /// 未选科
+    /// </summary>
+    public const short WEI_XUAN_KE = 99;
+    /// <summary>
+    /// 总分
+    /// </summary>
+    public const short ZONG_FEN = 100;
+    /// <summary>
+    /// 综合
+    /// </summary>
+    public const short ZONG_HE = 101;
+}

+ 7 - 1
YBEE.EQM.Core/Entities/Exam/ExamDataPublish.cs

@@ -1,6 +1,7 @@
 using Microsoft.EntityFrameworkCore;
 using System;
 using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
 
 namespace YBEE.EQM.Core;
 
@@ -52,7 +53,12 @@ public partial class ExamDataPublish : DEntityBase
     [Comment("发布人用户ID")]
     public int? PublishSysUserId { get; set; }
 
-
+    /// <summary>
+    /// 附件
+    /// </summary>
+    [Comment("附件")]
+    [Column(TypeName = "json")]
+    public string Attachments { get; set; } = "[]";
 
 
     /// <summary>

+ 2 - 2
YBEE.EQM.Core/Enums/NceeDirectionCourse.cs

@@ -11,10 +11,10 @@ public enum NceeDirectionCourse
     /// 物理类
     /// </summary>
     [Description("物理类")]
-    PHYSICS = 4,
+    WU_LI = CourseConst.WU_LI,
     /// <summary>
     /// 历史类
     /// </summary>
     [Description("历史类")]
-    HISTORY = 8
+    LI_SHI = CourseConst.LI_SHI,
 }

+ 6 - 1
YBEE.EQM.Core/Enums/ResourceFileType.cs

@@ -18,7 +18,7 @@ public enum ResourceFileType
     [Description("缺测替补佐证材料")]
     EXAM_ABSENT_REPLACE = 2,
     /// <summary>
-    /// 监测机构数据上报类型附件
+    /// 监测机构数据上报类型附件(大类)
     /// </summary>
     [Description("监测机构数据上报类型附件")]
     EXAM_ORG_DATA_REPORT = 3,
@@ -32,4 +32,9 @@ public enum ResourceFileType
     /// </summary>
     [Description("监测数据上报类型附件")]
     EXAM_DATA_REPORT_ATTACHMENT = 5,
+    /// <summary>
+    /// 监测结果发布类型附件
+    /// </summary>
+    [Description("监测结果发布类型附件")]
+    EXAM_DATA_PUBLISH_ATTACHMENT = 6,
 }

+ 4 - 0
YBEE.EQM.Core/Service/NceePlanConfig.cs

@@ -29,4 +29,8 @@ public class NceePlanConfig
     /// 开启排名导出
     /// </summary>
     public bool ExportOrderEnabled { get; set; } = false;
+    /// <summary>
+    /// 方向未选择
+    /// </summary>
+    public bool DirectionUnseleted { get; set; } = false;
 }

+ 5 - 5
YBEE.EQM.Core/YBEE.EQM.Core.csproj

@@ -27,15 +27,15 @@
 
     <ItemGroup>
         <PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
-        <PackageReference Include="Furion" Version="4.9.5.26" />
-        <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.5.26" />
-        <PackageReference Include="Furion.Extras.Logging.Serilog" Version="4.9.5.26" />
-        <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.5.26" />
+        <PackageReference Include="Furion" Version="4.9.7.7" />
+        <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.7" />
+        <PackageReference Include="Furion.Extras.Logging.Serilog" Version="4.9.7.7" />
+        <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.7" />
         <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.11" />
         <PackageReference Include="NPOI" Version="2.7.2" />
         <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
         <PackageReference Include="SharpZipLib" Version="1.4.2" />
-        <PackageReference Include="System.Linq.Dynamic.Core" Version="1.5.1" />
+        <PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.0.2" />
         <PackageReference Include="UAParser" Version="3.1.47" />
         <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
         <PackageReference Include="ZKWeb.System.Drawing" Version="4.0.1" />

+ 93 - 3
YBEE.EQM.Core/YBEE.EQM.Core.xml

@@ -119,6 +119,81 @@
             渠道
             </summary>
         </member>
+        <member name="T:YBEE.EQM.Core.CourseConst">
+            <summary>
+            科目常量
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.YU_WEN">
+            <summary>
+            语文
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.SHU_XUE">
+            <summary>
+            数学
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.YING_YU">
+            <summary>
+            英语
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.WU_LI">
+            <summary>
+            物理
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.HUA_XUE">
+            <summary>
+            化学
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.SHENG_WU">
+            <summary>
+            生物
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.ZHENG_ZHI">
+            <summary>
+            政治
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.LI_SHI">
+            <summary>
+            历史
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.DI_LI">
+            <summary>
+            地理
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.DE_YU">
+            <summary>
+            德语
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.RI_YU">
+            <summary>
+            日语
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.WEI_XUAN_KE">
+            <summary>
+            未选科
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.ZONG_FEN">
+            <summary>
+            总分
+            </summary>
+        </member>
+        <member name="F:YBEE.EQM.Core.CourseConst.ZONG_HE">
+            <summary>
+            综合
+            </summary>
+        </member>
         <member name="T:YBEE.EQM.Core.Course">
             <summary>
             科目
@@ -1145,6 +1220,11 @@
             发布人用户ID
             </summary>
         </member>
+        <member name="P:YBEE.EQM.Core.ExamDataPublish.Attachments">
+            <summary>
+            附件
+            </summary>
+        </member>
         <member name="P:YBEE.EQM.Core.ExamDataPublish.PublishSysUser">
             <summary>
             一对一引用(发布用户)
@@ -5436,12 +5516,12 @@
             新高考选择方向科目
             </summary>
         </member>
-        <member name="F:YBEE.EQM.Core.Enums.NceeDirectionCourse.PHYSICS">
+        <member name="F:YBEE.EQM.Core.Enums.NceeDirectionCourse.WU_LI">
             <summary>
             物理类
             </summary>
         </member>
-        <member name="F:YBEE.EQM.Core.Enums.NceeDirectionCourse.HISTORY">
+        <member name="F:YBEE.EQM.Core.Enums.NceeDirectionCourse.LI_SHI">
             <summary>
             历史类
             </summary>
@@ -5673,7 +5753,7 @@
         </member>
         <member name="F:YBEE.EQM.Core.ResourceFileType.EXAM_ORG_DATA_REPORT">
             <summary>
-            监测机构数据上报类型附件
+            监测机构数据上报类型附件(大类)
             </summary>
         </member>
         <member name="F:YBEE.EQM.Core.ResourceFileType.SCHOOL_EXAM_TEMPLATE">
@@ -5686,6 +5766,11 @@
             监测数据上报类型附件
             </summary>
         </member>
+        <member name="F:YBEE.EQM.Core.ResourceFileType.EXAM_DATA_PUBLISH_ATTACHMENT">
+            <summary>
+            监测结果发布类型附件
+            </summary>
+        </member>
         <member name="T:YBEE.EQM.Core.RoleType">
             <summary>
             角色类型,大于0的全部为固定角色,固定角色全部放入内置固定分组
@@ -6378,6 +6463,11 @@
             开启排名导出
             </summary>
         </member>
+        <member name="P:YBEE.EQM.Core.NceePlanConfig.DirectionUnseleted">
+            <summary>
+            方向未选择
+            </summary>
+        </member>
         <member name="T:YBEE.EQM.Core.RoleDataScope">
             <summary>
             角色数据权限范围

+ 0 - 2
YBEE.EQM.Web.Core/Startup.cs

@@ -1,5 +1,4 @@
 using Furion;
-using Furion.Schedule;
 using Furion.VirtualFileServer;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
@@ -26,7 +25,6 @@ public class Startup : AppStartup
         services.AddConfigurableOptions<EqmSiteOptions>();
 
         services.AddCorsAccessor();
-        services.AddRemoteRequest();
 
         // 添加抽样执行服务
         services.AddSingleton<ExamSampleWorker>();

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
YBEE.EQM.Web.Entry/Properties/PublishProfiles/FolderProfile.pubxml.user


+ 1 - 0
YBEE.EQM.Web.Entry/YBEE.EQM.Web.Entry.csproj

@@ -33,6 +33,7 @@
 
 
     <ItemGroup>
+      <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
       <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11">
         <PrivateAssets>all</PrivateAssets>
         <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio