Просмотр исходного кода

Merge branch 'master' of http://49.234.186.218:9000/root/ieplus

abpcoder 2 месяцев назад
Родитель
Сommit
5d4504d871
100 измененных файлов с 4125 добавлено и 912 удалено
  1. BIN
      back-ui/admin.zip
  2. BIN
      back-ui/public/favicon.ico
  3. 53 0
      back-ui/src/api/dz/campus.js
  4. 5 3
      back-ui/src/api/dz/cards.js
  5. 8 0
      back-ui/src/api/dz/classes.js
  6. 3 3
      back-ui/src/api/dz/open.js
  7. 9 0
      back-ui/src/api/dz/school.js
  8. BIN
      back-ui/src/assets/images/profile.jpg
  9. BIN
      back-ui/src/assets/logo/logo.png
  10. 1 1
      back-ui/src/utils/request.js
  11. 12 11
      back-ui/src/views/dz/agent/index.vue
  12. 428 0
      back-ui/src/views/dz/campus/index.vue
  13. 94 6
      back-ui/src/views/dz/cards/components/ApplyCardDialog.vue
  14. 14 14
      back-ui/src/views/dz/cards/components/AssignCardDialog.vue
  15. 3 2
      back-ui/src/views/dz/cards/components/AssociateCampusDialog.vue
  16. 5 8
      back-ui/src/views/dz/cards/config/form.js
  17. 12 0
      back-ui/src/views/dz/cards/config/table.js
  18. 53 46
      back-ui/src/views/dz/cards/index.vue
  19. 12 12
      back-ui/src/views/dz/classes/index.vue
  20. 9 9
      back-ui/src/views/dz/open/config/form.js
  21. 3 3
      back-ui/src/views/dz/open/config/table.js
  22. 98 34
      back-ui/src/views/dz/open/index.vue
  23. 35 50
      back-ui/src/views/dz/school/index.vue
  24. 57 8
      back-ui/src/views/dz/teacher/index.vue
  25. 1 1
      back-ui/vite.config.js
  26. 36 5
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzAgentController.java
  27. 152 0
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCampusController.java
  28. 27 7
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsController.java
  29. 5 1
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsOpenController.java
  30. 50 2
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzClassesController.java
  31. 74 8
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzSchoolController.java
  32. 28 3
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherController.java
  33. 1 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/CommController.java
  34. 80 110
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontExamController.java
  35. 55 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontFavoritesController.java
  36. 2 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontNewsController.java
  37. 58 20
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontPaperController.java
  38. 40 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontQuestionsController.java
  39. 155 76
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontStudentController.java
  40. 3 1
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontSyMajorRelationController.java
  41. 4 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontSyVocationalController.java
  42. 1 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontUniversitiesController.java
  43. 53 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontWrongBookController.java
  44. 165 16
      ie-admin/src/main/java/com/ruoyi/web/controller/front/UserController.java
  45. 43 2
      ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnTeacherController.java
  46. 12 3
      ie-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
  47. 1 1
      ie-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
  48. 5 5
      ie-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
  49. 12 0
      ie-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
  50. 1 1
      ie-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
  51. 427 30
      ie-admin/src/main/java/com/ruoyi/web/service/ExamService.java
  52. 92 0
      ie-admin/src/main/java/com/ruoyi/web/service/LearnStatService.java
  53. 132 26
      ie-admin/src/main/java/com/ruoyi/web/service/LearnTeacherService.java
  54. 215 45
      ie-admin/src/main/java/com/ruoyi/web/service/PaperService.java
  55. 105 0
      ie-admin/src/main/java/com/ruoyi/web/service/StudentService.java
  56. 40 4
      ie-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java
  57. 34 1
      ie-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java
  58. 15 1
      ie-admin/src/main/java/com/ruoyi/web/service/UserDetailsServiceImpl.java
  59. 37 0
      ie-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
  60. 13 0
      ie-common/src/main/java/com/ruoyi/common/core/domain/model/LoginCard.java
  61. 4 0
      ie-common/src/main/java/com/ruoyi/common/utils/NumberUtils.java
  62. 12 0
      ie-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
  63. 3 0
      ie-common/src/main/java/com/ruoyi/common/utils/dz/SubjectScore.java
  64. 1 0
      ie-system/src/main/java/com/ruoyi/dz/domain/DzAgent.java
  65. 63 44
      ie-system/src/main/java/com/ruoyi/dz/domain/DzCards.java
  66. 16 3
      ie-system/src/main/java/com/ruoyi/dz/domain/DzCardsOpen.java
  67. 26 6
      ie-system/src/main/java/com/ruoyi/dz/domain/DzClasses.java
  68. 12 0
      ie-system/src/main/java/com/ruoyi/dz/domain/DzSchool.java
  69. 19 0
      ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacher.java
  70. 9 8
      ie-system/src/main/java/com/ruoyi/dz/mapper/DzAgentMapper.java
  71. 9 9
      ie-system/src/main/java/com/ruoyi/dz/mapper/DzClassesMapper.java
  72. 2 0
      ie-system/src/main/java/com/ruoyi/dz/mapper/DzSubjectMapper.java
  73. 9 8
      ie-system/src/main/java/com/ruoyi/dz/service/IDzAgentService.java
  74. 3 2
      ie-system/src/main/java/com/ruoyi/dz/service/IDzCardsService.java
  75. 9 8
      ie-system/src/main/java/com/ruoyi/dz/service/IDzClassesService.java
  76. 2 0
      ie-system/src/main/java/com/ruoyi/dz/service/IDzSubjectService.java
  77. 9 9
      ie-system/src/main/java/com/ruoyi/dz/service/IDzTeacherService.java
  78. 24 9
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzAgentServiceImpl.java
  79. 48 11
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzCardsServiceImpl.java
  80. 14 8
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzClassesServiceImpl.java
  81. 13 1
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSchoolServiceImpl.java
  82. 3 0
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSubjectServiceImpl.java
  83. 31 7
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzTeacherServiceImpl.java
  84. 13 0
      ie-system/src/main/java/com/ruoyi/enums/CardAudit.java
  85. 37 0
      ie-system/src/main/java/com/ruoyi/enums/QuestionType.java
  86. 45 1
      ie-system/src/main/java/com/ruoyi/enums/UserTypeEnum.java
  87. 53 3
      ie-system/src/main/java/com/ruoyi/learn/domain/AnswerSheet.java
  88. 33 13
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnAnswer.java
  89. 11 0
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnDirectedKnowledge.java
  90. 50 7
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnExaminee.java
  91. 55 23
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaper.java
  92. 12 1
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaperQuestion.java
  93. 14 1
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnPlan.java
  94. 30 7
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnPlanStudy.java
  95. 109 90
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnQuestions.java
  96. 13 3
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnTestStudent.java
  97. 130 50
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnWrongBook.java
  98. 78 1
      ie-system/src/main/java/com/ruoyi/learn/domain/PaperVO.java
  99. 7 0
      ie-system/src/main/java/com/ruoyi/learn/mapper/LearnAnswerMapper.java
  100. 6 0
      ie-system/src/main/java/com/ruoyi/learn/mapper/LearnExamineeMapper.java

BIN
back-ui/admin.zip


BIN
back-ui/public/favicon.ico


+ 53 - 0
back-ui/src/api/dz/campus.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询查询院校列表
+export function listUniversity(query) {
+  return request({
+    url: '/front/university/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询机构校区列表
+export function listCampus(query) {
+  return request({
+    url: '/dz/campus/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询机构校区详细
+export function getCampus(id) {
+  return request({
+    url: '/dz/campus/' + id,
+    method: 'get'
+  })
+}
+
+// 新增机构校区
+export function addCampus(data) {
+  return request({
+    url: '/dz/campus',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改机构校区
+export function updateCampus(data) {
+  return request({
+    url: '/dz/campus',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除机构校区
+export function delCampus(id) {
+  return request({
+    url: '/dz/campus/' + id,
+    method: 'delete'
+  })
+}

+ 5 - 3
back-ui/src/api/dz/cards.js

@@ -161,9 +161,9 @@ export function getSchoolList(query) {
 }
 
 // 获取校区列表
-export function getCampusList(query) {
+export function getCampusSchoolList(query) {
   return request({
-    url: "/dz/school/getCampusList",
+    url: "/dz/school/getCampusSchoolList",
     method: "get",
     params: query,
   });
@@ -183,11 +183,13 @@ export function associateCampus(beginCardNo, endCardNo, campusId) {
 }
 
 // 申请开卡
-export function requestOpenCard(schoolId, beginCardNo, endCardNo) {
+export function requestOpenCard(agentId, province, schoolId, beginCardNo, endCardNo) {
   return request({
     url: "/dz/cards/openCard",
     method: "post",
     params: {
+      agentId: agentId,
+      location: province,
       schoolId,
       begin: beginCardNo,
       end: endCardNo,

+ 8 - 0
back-ui/src/api/dz/classes.js

@@ -8,6 +8,14 @@ export function listClasses(query) {
     params: query
   })
 }
+// 根据学校查询班级
+export function getClassesBySchoolId(query) {
+  return request({
+    url: '/dz/classes/getClassesBySchoolId',
+    method: 'get',
+    params: query
+  })
+}
 
 export function listAllClass(query) {
   return request({

+ 3 - 3
back-ui/src/api/dz/open.js

@@ -26,11 +26,11 @@ export function addOpen(data) {
   })
 }
 
-// 修改开卡申请
+// 审核开卡申请
 export function updateOpen(data) {
   return request({
-    url: '/dz/open',
-    method: 'put',
+    url: '/dz/open/audit',
+    method: 'post',
     data: data
   })
 }

+ 9 - 0
back-ui/src/api/dz/school.js

@@ -9,6 +9,15 @@ export function listUniversity(query) {
   })
 }
 
+// 查询机构校区列表
+export function listCampusSchoolList(query) {
+  return request({
+    url: '/dz/school/getCampusSchoolList',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询机构校区列表
 export function listAllSchool(query) {
   return request({

BIN
back-ui/src/assets/images/profile.jpg


BIN
back-ui/src/assets/logo/logo.png


+ 1 - 1
back-ui/src/utils/request.js

@@ -93,7 +93,7 @@ service.interceptors.response.use(res => {
         isRelogin.show = false
       })
     }
-      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
+      return Promise.reject('请重新登录。')
     } else if (code === 500) {
       ElMessage({ message: msg, type: 'error' })
       return Promise.reject(new Error(msg))

+ 12 - 11
back-ui/src/views/dz/agent/index.vue

@@ -72,12 +72,13 @@
                       >
                           <!--      <el-table-column label="用户ID" prop="userId" />-->
                           <el-table-column label="名称" align="center" prop="name" />
+                          <el-table-column label="账号/邀请码" align="center" prop="agentId" />
 <!--                          <el-table-column label="机构" align="center" prop="deptId" />-->
                           <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
                           <el-table-column label="联系电话" align="center" prop="phonenumber" />
                           <!--      <el-table-column label="上级代理商ID" align="center" prop="parentId" />-->
 <!--                          <el-table-column label="学校/校区" align="center" prop="schools" />-->
-                          <el-table-column label="学校/校区" align="center" prop="schoolName" />
+<!--                          <el-table-column label="学校/校区" align="center" prop="schoolName" />-->
                           <el-table-column label="备注" align="center" prop="remark" />
                           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
                               <template #default="scope">
@@ -122,16 +123,16 @@
 <!--          <el-input v-model="form.schools" placeholder="请输入负责校区" />-->
 <!--        </el-form-item>-->
 
-          <el-form-item label="关联校区" prop="classIds">
-              <el-select v-model="form.schoolIds" multiple placeholder="请选择校区" style="width: 100%">
-                  <el-option
-                          v-for="item in schoolOptions"
-                          :key="item.id"
-                          :label="item.name"
-                          :value="item.id"
-                  />
-              </el-select>
-          </el-form-item>
+<!--          <el-form-item label="关联校区" prop="classIds">-->
+<!--              <el-select v-model="form.schoolIds" multiple placeholder="请选择校区" style="width: 100%">-->
+<!--                  <el-option-->
+<!--                          v-for="item in schoolOptions"-->
+<!--                          :key="item.id"-->
+<!--                          :label="item.name"-->
+<!--                          :value="item.id"-->
+<!--                  />-->
+<!--              </el-select>-->
+<!--          </el-form-item>-->
         <el-form-item label="备注" prop="remark">
           <el-input v-model="form.remark" placeholder="请输入备注" />
         </el-form-item>

+ 428 - 0
back-ui/src/views/dz/campus/index.vue

@@ -0,0 +1,428 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
+        <!--机构数据-->
+        <pane size="16">
+          <el-col>
+            <div class="head-container">
+              <el-input v-model="deptName" placeholder="请输入机构名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />
+            </div>
+            <div class="head-container">
+              <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
+            </div>
+          </el-col>
+        </pane>
+        <pane size="84">
+          <el-col>
+            <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
+              <el-form-item label="名称" prop="name">
+                <el-input
+                        v-model="queryParams.name"
+                        placeholder="请输入名称"
+                        clearable
+                        @keyup.enter="handleQuery"
+                />
+              </el-form-item>
+<!--              <el-form-item label="省份" prop="location">-->
+<!--                <el-input-->
+<!--                        v-model="queryParams.location"-->
+<!--                        placeholder="请输入省份"-->
+<!--                        clearable-->
+<!--                        @keyup.enter="handleQuery"-->
+<!--                />-->
+<!--              </el-form-item>-->
+              <el-form-item label="省市区" prop="areaIds">
+                <AddressSelect class="w-[198px]" v-model="areaIds" />
+              </el-form-item>
+
+              <el-form-item label="状态" prop="status">
+                <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 170px">
+                  <el-option
+                          v-for="dict in bool_values"
+                          :key="dict.value"
+                          :label="dict.label"
+                          :value="dict.value"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-form-item>
+            </el-form>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button
+                        type="primary"
+                        plain
+                        icon="Plus"
+                        @click="handleAdd"
+                        v-hasPermi="['dz:campus:add']"
+                >新增</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button
+                        type="success"
+                        plain
+                        icon="Edit"
+                        :disabled="single"
+                        @click="handleUpdate"
+                        v-hasPermi="['dz:campus:edit']"
+                >修改</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button
+                        type="danger"
+                        plain
+                        icon="Delete"
+                        :disabled="multiple"
+                        @click="handleDelete"
+                        v-hasPermi="['dz:campus:remove']"
+                >删除</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button
+                        type="warning"
+                        plain
+                        icon="Download"
+                        @click="handleExport"
+                        v-hasPermi="['dz:campus:export']"
+                >导出</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+
+            </el-row>
+
+            <el-table v-loading="loading" :data="campusList" @selection-change="handleSelectionChange">
+              <el-table-column type="selection" width="55" align="center" />
+              <el-table-column label="ID" align="center" prop="id" />
+              <el-table-column label="名称" align="center" prop="name" />
+<!--              <el-table-column label="机构ID" align="center" prop="deptId" />-->
+              <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
+<!--              <el-table-column label="省份" align="center" prop="location" />-->
+
+<!--              <el-table-column label="省" align="center" prop="pro" />-->
+<!--              <el-table-column label="市" align="center" prop="city" />-->
+<!--              <el-table-column label="区" align="center" prop="area" />-->
+              <el-table-column label="所属地区" align="center" prop="proCityAreaName" />
+              <el-table-column label="状态" align="center" prop="status">
+                <template #default="scope">
+                  <dict-tag :options="bool_values" :value="scope.row.status"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="备注" align="center" prop="remark" />
+              <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+                <template #default="scope">
+                  <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['dz:campus:edit']">修改</el-button>
+                  <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['dz:campus:remove']">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+
+            <pagination
+                    v-show="total>0"
+                    :total="total"
+                    v-model:page="queryParams.pageNum"
+                    v-model:limit="queryParams.pageSize"
+                    @pagination="getList"
+            />
+          </el-col>
+        </pane>
+      </splitpanes>
+    </el-row>
+
+    <!-- 添加或修改机构校区对话框 -->
+    <el-dialog :title="title" v-model="open" width="500px" append-to-body>
+      <el-form ref="campusRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入名称" />
+        </el-form-item>
+<!--        <el-form-item label="机构ID" prop="deptId">-->
+<!--          <el-input v-model="form.deptId" placeholder="请输入机构ID" />-->
+<!--        </el-form-item>-->
+        <el-form-item label="归属机构" prop="deptId">
+          <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属机构" clearable check-strictly />
+        </el-form-item>
+        <el-form-item label="省份" prop="location">
+          <el-input v-model="form.location" placeholder="请输入省份" />
+        </el-form-item>
+        <el-form-item label="省市区" prop="areaIds" style="width: 220px;">
+          <AddressSelect class="w-[198px]" v-model="areaIds" />
+        </el-form-item>
+<!--        <el-form-item label="省" prop="pro">-->
+<!--          <el-input v-model="form.pro" placeholder="请输入省" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="市" prop="city">-->
+<!--          <el-input v-model="form.city" placeholder="请输入市" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="区" prop="area">-->
+<!--          <el-input v-model="form.area" placeholder="请输入区" />-->
+<!--        </el-form-item>-->
+        <el-form-item label="状态" prop="status">
+          <el-select v-model="form.status" placeholder="请选择状态">
+            <el-option
+                    v-for="dict in bool_values"
+                    :key="dict.value"
+                    :label="dict.label"
+                    :value="parseInt(dict.value)"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Campus">
+  import { listCampus, getCampus, delCampus, addCampus, updateCampus } from "@/api/dz/campus"
+  import { deptTreeSelect } from "@/api/system/user"
+  import useAppStore from '@/store/modules/app'
+  import { Splitpanes, Pane } from "splitpanes"
+  import "splitpanes/dist/splitpanes.css"
+  import AddressSelect from '@/components/AddressSelect';
+
+  const { proxy } = getCurrentInstance()
+  const appStore = useAppStore()
+  const { bool_values } = proxy.useDict('bool_values')
+  const deptName = ref("")
+  const deptOptions = ref(undefined)
+  const enabledDeptOptions = ref(undefined)
+
+  const campusList = ref([])
+  const open = ref(false)
+  const loading = ref(true)
+  const showSearch = ref(true)
+  const ids = ref([])
+  const single = ref(true)
+  const multiple = ref(true)
+  const total = ref(0)
+  const title = ref("")
+
+  const dataList = ref([]);
+  const areaIds = ref([]);
+  let selectedIds = [];
+
+  const data = reactive({
+    form: {},
+    queryParams: {
+      pageNum: 1,
+      pageSize: 10,
+      name: null,
+      deptId: null,
+      location: null,
+      pro: null,
+      city: null,
+      area: null,
+      status: null,
+    },
+    rules: {
+      // status: [
+      //   { required: true, message: "状态不能为空", trigger: "change" },
+      //   { required: true, message: "机构不能为空", trigger: "change" }
+      // ],
+    }
+  })
+
+  const { queryParams, form, rules } = toRefs(data)
+  const queryRef = ref(null);
+  const dateRange = ref([]);
+
+  /** 查询机构校区列表 */
+  function getList() {
+    loading.value = true
+    let areaObj = { pro: areaIds.value?.[0] || '', city: areaIds.value?.[1] || '', area: areaIds.value?.[2] || '' };
+    let params = { ...proxy.addDateRange(queryParams.value), ...areaObj }
+    listCampus(params).then(response => {
+      campusList.value = response.rows
+      total.value = response.total
+      loading.value = false
+    })
+  }
+
+  /** 通过条件过滤节点  */
+  const filterNode = (value, data) => {
+    if (!value) return true
+    return data.label.indexOf(value) !== -1
+  }
+
+  /** 根据名称筛选机构树 */
+  watch(deptName, val => {
+    proxy.$refs["deptTreeRef"].filter(val)
+  })
+
+  /** 查询机构下拉树结构 */
+  function getDeptTree() {
+    deptTreeSelect().then(response => {
+      deptOptions.value = response.data
+      enabledDeptOptions.value = filterDisabledDept(JSON.parse(JSON.stringify(response.data)))
+    })
+  }
+
+  /** 过滤禁用的机构 */
+  function filterDisabledDept(deptList) {
+    return deptList.filter(dept => {
+      if (dept.disabled) {
+        return false
+      }
+      if (dept.children && dept.children.length) {
+        dept.children = filterDisabledDept(dept.children)
+      }
+      return true
+    })
+  }
+
+  /** 节点单击事件 */
+  function handleNodeClick(data) {
+    queryParams.value.deptId = data.id
+    handleQuery()
+  }
+
+  /** 查询机构代理下拉树结构 */
+  function getTreeselect() {
+    listAgent().then(response => {
+      agentOptions.value = []
+      const data = { agentId: 0, name: '顶级节点', children: [] }
+      data.children = proxy.handleTree(response.data, "agentId", "parentId")
+      agentOptions.value.push(data)
+    })
+  }
+
+  // 取消按钮
+  function cancel() {
+    open.value = false
+    reset()
+  }
+
+  // 表单重置
+  function reset() {
+    form.value = {
+      id: null,
+      name: null,
+      deptId: null,
+      location: null,
+      remark: null,
+      pro: null,
+      city: null,
+      area: null,
+      status: null,
+      createTime: null,
+      updateTime: null
+    }
+    proxy.resetForm("campusRef")
+  }
+
+  /** 搜索按钮操作 */
+  function handleQuery() {
+    queryParams.value.pageNum = 1
+    getList()
+  }
+
+  /** 重置按钮操作 */
+  function resetQuery() {
+    // 清空机构ID
+    queryParams.value.deptId = undefined
+    // 清空机构名称
+    deptName.value = ""
+    // 取消树节点的选中状态
+    proxy.$refs.deptTreeRef.setCurrentKey(null)
+    proxy.resetForm("queryRef")
+    // 清空省市区选择
+    areaIds.value = []
+
+    handleQuery()
+  }
+
+  // 多选框选中数据
+  function handleSelectionChange(selection) {
+    ids.value = selection.map(item => item.id)
+    single.value = selection.length != 1
+    multiple.value = !selection.length
+  }
+
+  /** 新增按钮操作 */
+  function handleAdd() {
+    reset()
+    open.value = true
+    title.value = "添加机构校区"
+  }
+
+  /** 修改按钮操作 */
+  function handleUpdate(row) {
+    reset()
+    const _id = row.id || ids.value
+    getCampus(_id).then(response => {
+      form.value = response.data
+      open.value = true
+      title.value = "修改机构校区"
+    })
+  }
+
+  /** 提交按钮 */
+  function submitForm() {
+    proxy.$refs["campusRef"].validate(valid => {
+      if (valid) {
+        // 构建省市区对象
+        let areaObj = {
+          pro: areaIds.value?.[0] || '',
+          city: areaIds.value?.[1] || '',
+          area: areaIds.value?.[2] || ''
+        };
+
+        // 合并表单数据和省市区数据
+        const submitData = {
+          ...form.value,
+          ...areaObj
+        };
+
+        if (form.value.id != null) {
+          // updateCampus(form.value).then(response => {
+          updateCampus(submitData).then(response => {
+            proxy.$modal.msgSuccess("修改成功")
+            open.value = false
+            getList()
+          })
+        } else {
+          // addCampus(form.value).then(response => {
+          addCampus(submitData).then(response => {
+            proxy.$modal.msgSuccess("新增成功")
+            open.value = false
+            getList()
+          })
+        }
+      }
+    })
+  }
+
+  /** 删除按钮操作 */
+  function handleDelete(row) {
+    const _ids = row.id || ids.value
+    proxy.$modal.confirm('是否确认删除机构校区编号为"' + _ids + '"的数据项?').then(function() {
+      return delCampus(_ids)
+    }).then(() => {
+      getList()
+      proxy.$modal.msgSuccess("删除成功")
+    }).catch(() => {})
+  }
+
+  /** 导出按钮操作 */
+  function handleExport() {
+    proxy.download('dz/campus/export', {
+      ...queryParams.value
+    }, `campus_${new Date().getTime()}.xlsx`)
+  }
+
+  onMounted(() => {
+    getDeptTree()
+    getList()
+  })
+</script>

+ 94 - 6
back-ui/src/views/dz/cards/components/ApplyCardDialog.vue

@@ -35,12 +35,44 @@
             </el-form-item>
           </div>
         </el-form-item>
-
+        <!-- 代理商 -->
+        <el-form-item label="代理商" prop="agentId" :required="true">
+          <el-select
+              v-model="form.agentId"
+              placeholder="请选择代理商"
+              style="width: 100%"
+              clearable
+          >
+            <el-option
+                v-for="agent in agentList"
+                :key="agent.id"
+                :label="agent.name"
+                :value="agent.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+        <!-- 省份 -->
+        <el-form-item label="省份" prop="province">
+          <el-select
+              v-model="form.province"
+              placeholder="请选择省份"
+              style="width: 100%"
+              clearable
+          >
+            <el-option
+                v-for="province in provinceList"
+                :key="province.dictValue"
+                :label="province.dictLabel"
+                :value="province.dictValue"
+            ></el-option>
+          </el-select>
+        </el-form-item>
         <!-- 学校 -->
         <el-form-item label="学校" prop="schoolId">
           <el-select
             v-model="form.schoolId"
             placeholder="请选择学校"
+            :disabled="!form.province"
             style="width: 100%"
           >
             <el-option
@@ -72,9 +104,10 @@
 
 <script setup>
 import { ref, computed, watch } from "vue";
-import { requestOpenCard } from "@/api/dz/cards";
+import {getAgentList, requestOpenCard} from "@/api/dz/cards";
 import { listUniversity } from "@/api/dz/school";
-import { associateCampus, getCampusList } from "@/api/dz/cards";
+import { associateCampus, getCampusSchoolList } from "@/api/dz/cards";
+import {getPaperProvinces} from "@/api/dz/papers.js";
 
 const props = defineProps({
   modelValue: {
@@ -96,16 +129,20 @@ const visible = computed({
 
 const applyCardFormRef = ref();
 const loading = ref(false);
+const provinceList = ref([]); // 省份列表
+const agentList = ref([]); // 代理商列表
 const schoolList = ref([]);
 const campusList = ref([]);
 
 const form = ref({
+  agentId: null,
   beginCardNo: "",
   endCardNo: "",
   schoolId: null,
 });
 
 const rules = {
+  province: [{ required: true, message: "省份不能为空", trigger: "change" }],
   beginCardNo: [
     { required: true, message: "开始卡号不能为空", trigger: "blur" },
   ],
@@ -113,10 +150,44 @@ const rules = {
   schoolId: [{ required: true, message: "学校不能为空", trigger: "change" }],
 };
 
+
+// 获取代理商列表
+async function getAgentListData() {
+  try {
+    const response = await getAgentList({});
+    if (response.code === 200) {
+      // 处理代理商数据,映射字段名
+      agentList.value = (response.data || []).map((item) => ({
+        id: item.agentId,
+        name: item.name,
+        agentId: item.agentId,
+        deptId: item.deptId,
+        phonenumber: item.phonenumber,
+        schoolName: item.schoolName,
+      }));
+    }
+  } catch (error) {
+    console.error("获取代理商列表失败:", error);
+  }
+}
+
+// 获取省份列表
+async function getProvinceList() {
+  try {
+    const response = await getPaperProvinces();
+    if (response.code === 200) {
+      provinceList.value = response.data || [];
+    }
+    campusList.value = []
+  } catch (error) {
+    console.error("获取省份列表失败:", error);
+  }
+}
+
 // 获取校区列表
 async function getCampusListData() {
   try {
-    const response = await getCampusList({ pageNum: 1, pageSize: 1000 });
+    const response = await getCampusSchoolList({ campus: false, location: form.value.province, pageNum: 1, pageSize: 1000 });
     if (response.code === 200) {
       campusList.value = response.data || [];
     }
@@ -189,6 +260,7 @@ function resetForm() {
   form.value = {
     beginCardNo: "",
     endCardNo: "",
+    agentId: null,
     schoolId: null,
   };
 }
@@ -208,6 +280,8 @@ async function handleSubmit() {
         const begin = form.value.beginCardNo;
         const end = form.value.endCardNo;
         const schoolId = form.value.schoolId;
+        const province = form.value.province;
+        const agentId = form.value.agentId;
 
         if (!begin || !end) {
           throw new Error("请填写完整的卡号段");
@@ -216,7 +290,7 @@ async function handleSubmit() {
           throw new Error("请选择学校");
         }
 
-        await requestOpenCard(schoolId, begin, end);
+        await requestOpenCard(agentId, province, schoolId, begin, end);
         emit("success", "申请开卡成功!");
         visible.value = false;
         resetForm();
@@ -235,10 +309,24 @@ watch(visible, (newVal) => {
   if (!newVal) {
     resetForm();
   } else {
-    getCampusListData();
+    // 获取代理商列表
+    getAgentListData();
+    // 弹窗打开时获取省份列表
+    getProvinceList();
     autoFillCardRange();
   }
 });
+
+// 监听省份变化,获取对应学校
+watch(
+    () => form.value.province,
+    (newProvince) => {
+      // 重置学校选择
+      form.value.schoolId = null;
+      // 获取对应省份的学校列表
+      getCampusListData(newProvince);
+    }
+);
 </script>
 
 <style scoped>

+ 14 - 14
back-ui/src/views/dz/cards/components/AssignCardDialog.vue

@@ -20,6 +20,7 @@
           v-model="form.cardType"
           placeholder="请选择卡类型"
           style="width: 100%"
+          clearable
         >
           <el-option
             v-for="dict in cardTypeOptions"
@@ -57,6 +58,7 @@
           v-model="form.agentId"
           placeholder="请选择代理商"
           style="width: 100%"
+          clearable
         >
           <el-option
             v-for="agent in agentList"
@@ -73,6 +75,7 @@
           v-model="form.province"
           placeholder="请选择省份"
           style="width: 100%"
+          clearable
         >
           <el-option
             v-for="province in provinceList"
@@ -90,6 +93,7 @@
           placeholder="请选择学校"
           style="width: 100%"
           :disabled="!form.province"
+          clearable
         >
           <el-option
             v-for="school in schoolList"
@@ -106,6 +110,7 @@
           v-model="form.studentCategory"
           placeholder="请选择考生类别"
           style="width: 100%"
+          clearable
         >
           <el-option label="全部" value="all"></el-option>
           <el-option
@@ -120,13 +125,13 @@
 
     <template #footer>
       <div class="dialog-footer">
-        <el-button @click="handleCancel">取消</el-button>
         <el-button type="primary" @click="handleSubmit" :loading="loading"
           >分配</el-button
         >
-        <el-button @click="handleReassign" :loading="reassignLoading"
-          >重新分配</el-button
-        >
+        <el-button @click="handleCancel">取消</el-button>
+        <!--        <el-button @click="handleReassign" :loading="reassignLoading"-->
+<!--          >重新分配</el-button-->
+<!--        >-->
       </div>
     </template>
   </el-dialog>
@@ -138,7 +143,7 @@ import {
   assignCard,
   getExamTypes,
   getAgentList,
-  getSchoolList,
+  getCampusSchoolList,
 } from "@/api/dz/cards";
 import { getPaperProvinces } from "@/api/dz/papers";
 
@@ -260,7 +265,8 @@ async function getSchoolListByProvince(province) {
       return;
     }
 
-    const response = await getSchoolList({
+    const response = await getCampusSchoolList({
+      campus: false,
       location: province,
       pageNum: 1,
       pageSize: 9999,
@@ -322,14 +328,8 @@ function autoFillCardRange() {
       const minCardNoNum = parseInt(minCardNo);
       const beginCardNo = Math.floor(minCardNoNum / 10) * 10;
 
-      console.log(
-        "Setting card range:",
-        beginCardNo.toString(),
-        "to",
-        maxCardNo
-      );
-      form.value.beginCardNo = beginCardNo.toString();
-      form.value.endCardNo = maxCardNo;
+      // form.value.beginCardNo = beginCardNo.toString();
+      // form.value.endCardNo = maxCardNo;
     }
   }
 }

+ 3 - 2
back-ui/src/views/dz/cards/components/AssociateCampusDialog.vue

@@ -72,7 +72,7 @@
 
 <script setup>
 import { ref, computed, watch } from "vue";
-import { associateCampus, getCampusList } from "@/api/dz/cards";
+import { associateCampus, getCampusSchoolList } from "@/api/dz/cards";
 
 const props = defineProps({
   modelValue: {
@@ -113,7 +113,8 @@ const rules = {
 // 获取校区列表
 async function getCampusListData() {
   try {
-    const response = await getCampusList({
+    const response = await getCampusSchoolList({
+      campus: true,
       pageNum: 1,
       pageSize: 9999, // 获取所有校区
     });

+ 5 - 8
back-ui/src/views/dz/cards/config/form.js

@@ -29,7 +29,7 @@ const info = [
     search: true,
   },
   {
-    label: "校",
+    label: "培训学校",
     name: "campusId",
     value: "",
     type: "select",
@@ -80,8 +80,8 @@ const info = [
     search: true,
   },
   {
-    label: "分配考生类",
-    name: "studentCategory",
+    label: "分配考生类",
+    name: "examType",
     value: "",
     type: "select",
     option: [],
@@ -123,13 +123,10 @@ const info = [
   },
   {
     label: "结算状态",
-    name: "isSettlement",
+    name: "payStatus",
     value: "",
     type: "select",
-    option: [
-      { label: "是", value: 1 },
-      { label: "否", value: 0 },
-    ],
+    option: [],
     optionLabel: "label",
     optionValue: "value",
     search: true,

+ 12 - 0
back-ui/src/views/dz/cards/config/table.js

@@ -44,6 +44,18 @@ const tableConfig = {
 
       width: 120,
     },
+    {
+      label: "机构",
+      prop: "deptName",
+      align: "center",
+      width: 120,
+    },
+    {
+      label: "代理商",
+      prop: "agentName",
+      align: "center",
+      width: 120,
+    },
     {
       label: "注册学校",
       prop: "schoolName",

+ 53 - 46
back-ui/src/views/dz/cards/index.vue

@@ -29,7 +29,7 @@
           type="success"
           plain
           @click="handleAssignCard"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:assign']"
           style="border-color: #67c23a; color: #67c23a; font-weight: 500"
         >
           <svg-icon icon-class="peoples" class="mr-1" style="font-size: 16px" />
@@ -53,7 +53,7 @@
           plain
           :disabled="multiple"
           @click="handlePayment"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:pay']"
           style="border-color: #e6a23c; color: #e6a23c; font-weight: 500"
         >
           <svg-icon icon-class="money" class="mr-1" style="font-size: 16px" />
@@ -66,7 +66,7 @@
           plain
           :disabled="multiple"
           @click="handleCloseCard"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:close']"
           style="border-color: #f56c6c; color: #f56c6c; font-weight: 500"
         >
           <svg-icon icon-class="lock" class="mr-1" style="font-size: 16px" />
@@ -79,7 +79,7 @@
           plain
           :disabled="multiple"
           @click="handleReopenCard"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:reopen']"
           style="border-color: #13c2c2; color: #13c2c2; font-weight: 500"
         >
           <svg-icon icon-class="enter" class="mr-1" style="font-size: 16px" />
@@ -92,7 +92,7 @@
           plain
           :disabled="multiple"
           @click="handleRefund"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:refund']"
           style="border-color: #ff4d4f; color: #ff4d4f; font-weight: 500"
         >
           <svg-icon icon-class="money" class="mr-1" style="font-size: 16px" />
@@ -104,7 +104,7 @@
           type="primary"
           plain
           @click="handleAssociateCampus"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:associateCampus']"
           style="border-color: #1890ff; color: #1890ff; font-weight: 500"
         >
           <svg-icon icon-class="link" class="mr-1" style="font-size: 16px" />
@@ -116,7 +116,7 @@
           type="success"
           plain
           @click="handleApplyCard"
-          v-hasPermi="['dz:cards:add']"
+          v-hasPermi="['dz:cards:openFinished']"
           style="border-color: #52c41a; color: #52c41a; font-weight: 500"
         >
           <svg-icon icon-class="edit" class="mr-1" style="font-size: 16px" />
@@ -175,7 +175,7 @@
       <template #studentInfo="{ row }">
         <div class="student-info">
           <div class="student-name">{{ row.nickName || "-" }}</div>
-          <div class="student-phone">{{ row.mobile || "-" }}</div>
+          <div class="student-phone">{{ row.phonenumber || "-" }}</div>
         </div>
       </template>
     </Table>
@@ -285,13 +285,15 @@ import AssociateCampusDialog from "./components/AssociateCampusDialog.vue";
 import ApplyCardDialog from "./components/ApplyCardDialog.vue";
 import formInfo from "./config/form.js";
 import tableConfig from "./config/table.js";
-import { listUniversity } from "@/api/dz/school";
-import { assignCard, issueCard, getCampusList } from "@/api/dz/cards";
+// import { listUniversity } from "@/api/dz/school";
+import { assignCard, issueCard, getCampusSchoolList } from "@/api/dz/cards";
 import { listDept } from "@/api/system/dept";
 import { listAgent } from "@/api/dz/agent";
+import { getClassesBySchoolId } from "@/api/dz/classes";
 
 const { proxy } = getCurrentInstance();
 const {
+  exam_type,
   card_status,
   card_distribute_status,
   card_time_status,
@@ -299,6 +301,7 @@ const {
   card_pay_status,
   card_type,
 } = proxy.useDict(
+  "exam_type",
   "card_status",
   "card_distribute_status",
   "card_time_status",
@@ -374,6 +377,9 @@ const searchConfig = computed(() => {
       case "campusId":
         item.option = campusList.value || [];
         break;
+      case "examType":
+        item.option = exam_type.value || [];
+        break;
     }
   });
 
@@ -545,46 +551,47 @@ function getSchoolList() {
     pageSize: 9999, // 获取所有学校选项
   };
 
-  listUniversity(requestParams)
-    .then((response) => {
-      // 根据API返回数据结构处理
-      let schoolData = [];
-      if (response.data) {
-        schoolData = Array.isArray(response.data)
-          ? response.data
-          : response.data.rows || response.data.list || [];
-      } else if (response.rows) {
-        schoolData = response.rows;
-      } else if (response.list) {
-        schoolData = response.list;
-      } else if (Array.isArray(response)) {
-        schoolData = response;
-      }
-
-      // 确保数据格式符合配置要求
-      schoolData = schoolData.map((item) => {
-        // 如果API返回的是 {id, name, ...} 格式,直接使用
-        if (item.id && item.name) {
-          return item;
-        }
-        // 如果是其他格式,需要转换
-        return {
-          id: item.id || item.value || item.schoolId,
-          name: item.name || item.label || item.schoolName || item.title,
-        };
-      });
-
-      schoolList.value = schoolData;
-    })
-    .catch((error) => {
-      console.error("获取学校列表失败:", error);
-      schoolList.value = [];
-    });
+  // listUniversity(requestParams)
+  //   .then((response) => {
+  //     // 根据API返回数据结构处理
+  //     let schoolData = [];
+  //     if (response.data) {
+  //       schoolData = Array.isArray(response.data)
+  //         ? response.data
+  //         : response.data.rows || response.data.list || [];
+  //     } else if (response.rows) {
+  //       schoolData = response.rows;
+  //     } else if (response.list) {
+  //       schoolData = response.list;
+  //     } else if (Array.isArray(response)) {
+  //       schoolData = response;
+  //     }
+  //
+  //     // 确保数据格式符合配置要求
+  //     schoolData = schoolData.map((item) => {
+  //       // 如果API返回的是 {id, name, ...} 格式,直接使用
+  //       if (item.id && item.name) {
+  //         return item;
+  //       }
+  //       // 如果是其他格式,需要转换
+  //       return {
+  //         id: item.id || item.value || item.schoolId,
+  //         name: item.name || item.label || item.schoolName || item.title,
+  //       };
+  //     });
+  //
+  //     schoolList.value = schoolData;
+  //   })
+  //   .catch((error) => {
+  //     console.error("获取学校列表失败:", error);
+  //     schoolList.value = [];
+  //   });
 }
 
 /** 获取校区列表 */
 function getCampusListData() {
-  getCampusList({
+  getCampusSchoolList({
+    campus: true,
     pageNum: 1,
     pageSize: 9999, // 获取所有校区
   })

+ 12 - 12
back-ui/src/views/dz/classes/index.vue

@@ -9,7 +9,7 @@
 <!--                @keyup.enter="handleQuery"-->
 <!--        />-->
 <!--      </el-form-item>-->
-      <el-form-item label="所在校区" prop="schoolId">
+      <el-form-item label="学校校区" prop="schoolId">
         <el-select
                 v-model="queryParams.schoolId"
                 placeholder="请选择所在校区"
@@ -25,10 +25,10 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="开班年份" prop="year">
+      <el-form-item label="毕业年份" prop="year">
         <el-input
                 v-model="queryParams.year"
-                placeholder="请输入开班年份"
+                placeholder="请输入毕业年份"
                 clearable
                 @keyup.enter="handleQuery"
         />
@@ -113,7 +113,7 @@
       <el-table-column label="名称" align="center" prop="name" />
 <!--      <el-table-column label="学校" align="center" prop="schoolId" />-->
       <el-table-column label="学校" align="center" prop="school.name" />
-      <el-table-column label="开班年份" align="center" prop="year" />
+      <el-table-column label="毕业年份" align="center" prop="year" />
       <el-table-column label="是否线上" align="center" prop="online">
         <template #default="scope">
           <dict-tag :options="bool_values" :value="scope.row.online"/>
@@ -147,11 +147,11 @@
 <!--        <el-form-item label="学校" prop="schoolId">-->
 <!--          <el-input v-model="form.schoolId" placeholder="请输入学校" />-->
 <!--        </el-form-item>-->
-        <el-form-item label="名称" prop="name">
-          <el-input v-model="form.name" placeholder="请输入名称" />
+        <el-form-item label="班级名称" prop="name">
+          <el-input v-model="form.name" placeholder="多个名称使用英文逗号隔开" />
         </el-form-item>
-        <el-form-item label="所在校区" prop="schoolId">
-          <el-select v-model="form.schoolId" placeholder="请选择所在校区" style="width: 100%">
+        <el-form-item label="学校校区" prop="schoolId">
+          <el-select v-model="form.schoolId" placeholder="请选择学校校区" style="width: 100%">
             <el-option
                     v-for="item in schoolOptions"
                     :key="item.id"
@@ -160,8 +160,8 @@
             />
           </el-select>
         </el-form-item>
-        <el-form-item label="开班年份" prop="year">
-          <el-input v-model="form.year" placeholder="请输入开班年份" />
+        <el-form-item label="毕业年份" prop="year">
+          <el-input v-model="form.year" placeholder="请输入毕业年份" />
         </el-form-item>
 
         <el-form-item label="是否线上" prop="online">
@@ -190,7 +190,7 @@
 
 <script setup name="Classes">
   import { listClasses, getClasses, delClasses, addClasses, updateClasses } from "@/api/dz/classes"
-  import { listAllSchool } from "@/api/dz/school"
+  import {listAllSchool, listCampusSchoolList} from "@/api/dz/school"
 
   const { proxy } = getCurrentInstance()
   const { bool_values } = proxy.useDict('bool_values')
@@ -245,7 +245,7 @@
 
   /** 查询校区列表 */
   function getSchoolList() {
-    listAllSchool().then(response => {
+    listCampusSchoolList().then(response => {
       schoolOptions.value = response.data || []
     })
   }

+ 9 - 9
back-ui/src/views/dz/open/config/form.js

@@ -1,12 +1,12 @@
 const info = [
-  {
-    label: "申请人",
-    name: "applicant",
-    value: "",
-    type: "text",
-    search: true,
-    placeholder: "请输入申请人",
-  },
+  // {
+  //   label: "申请人",
+  //   name: "applicant",
+  //   value: "",
+  //   type: "text",
+  //   search: true,
+  //   placeholder: "请输入申请人",
+  // },
   {
     label: "卡号",
     name: "cardNo",
@@ -25,7 +25,7 @@ const info = [
     option: [
       { label: "待审核", value: "0" },
       { label: "审核通过", value: "1" },
-      { label: "拒绝", value: "2" },
+      { label: "拒绝", value: "2" },
     ],
     optionLabel: "label",
     optionValue: "value",

+ 3 - 3
back-ui/src/views/dz/open/config/table.js

@@ -53,11 +53,11 @@ const tableConfig = {
     },
     {
       label: "原因",
-      prop: "reason",
+      prop: "auditDesc",
       align: "center",
       minWidth: 120,
       type: "slot",
-      slotName: "reason",
+      slotName: "auditDesc",
     },
   ],
 
@@ -69,7 +69,7 @@ const tableConfig = {
       type: "primary",
       link: true,
       permission: ["dz:open:review"],
-      condition: (row) => row.status === 0, // 只有待审核状态才显示审核按钮
+      condition: (row) => row.status === 1, // 只有待审核状态才显示审核按钮
     },
   ],
 

+ 98 - 34
back-ui/src/views/dz/open/index.vue

@@ -69,36 +69,55 @@
       </template>
 
       <!-- 原因插槽 -->
-      <template #reason="{ row }">
-        <span>{{ row.reason || "-" }}</span>
+      <template #auditDesc="{ row }">
+        <span>{{ row.auditDesc || "-" }}</span>
       </template>
     </Table>
 
     <!-- 添加或修改开卡申请对话框 -->
     <el-dialog :title="title" v-model="open" width="500px" append-to-body>
       <el-form ref="openRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="申请人" prop="applicant">
-          <el-input v-model="form.applicant" placeholder="请输入申请人" />
-        </el-form-item>
         <el-form-item label="起始卡号" prop="startNo">
           <el-input v-model="form.startNo" placeholder="请输入起始卡号" />
         </el-form-item>
-        <el-form-item label="截卡号" prop="endNo">
+        <el-form-item label="截卡号" prop="endNo">
           <el-input v-model="form.endNo" placeholder="请输入截至卡号" />
         </el-form-item>
-        <el-form-item label="学校" prop="schoolName">
-          <el-input v-model="form.schoolName" placeholder="请输入学校" />
-        </el-form-item>
-        <el-form-item label="申请状态" prop="status">
-          <el-select v-model="form.status" placeholder="请选择申请状态">
-            <el-option label="待审核" :value="0" />
-            <el-option label="审核通过" :value="1" />
-            <el-option label="已拒绝" :value="2" />
+        <!-- 省份 -->
+        <el-form-item label="省份" prop="province">
+          <el-select
+              v-model="form.province"
+              placeholder="请选择省份"
+              style="width: 100%"
+              clearable
+          >
+            <el-option
+                v-for="province in provinceList"
+                :key="province.dictValue"
+                :label="province.dictLabel"
+                :value="province.dictValue"
+            ></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="原因" prop="reason">
-          <el-input v-model="form.reason" placeholder="请输入原因" />
+        <!-- 学校 -->
+        <el-form-item label="学校" prop="schoolId">
+          <el-select
+              v-model="form.schoolId"
+              placeholder="请选择学校"
+              :disabled="!form.province"
+              style="width: 100%"
+          >
+            <el-option
+                v-for="school in schoolList"
+                :key="school.id"
+                :label="school.name"
+                :value="school.id"
+            ></el-option>
+          </el-select>
         </el-form-item>
+<!--        <el-form-item label="原因" prop="reason">-->
+<!--          <el-input v-model="form.reason" placeholder="请输入原因" />-->
+<!--        </el-form-item>-->
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -149,13 +168,13 @@
       >
         <el-form-item label="审核结果" prop="status">
           <el-radio-group v-model="reviewForm.status">
-            <el-radio :value="1">审核通过</el-radio>
-            <el-radio :value="2">已拒绝</el-radio>
+            <el-radio :value="2">审核通过</el-radio>
+            <el-radio :value="3">已拒绝</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="原因" prop="reason">
+        <el-form-item label="原因" prop="auditDesc">
           <el-input
-            v-model="reviewForm.reason"
+            v-model="reviewForm.auditDesc"
             placeholder="请输入审核原因"
             type="textarea"
             :rows="3"
@@ -178,17 +197,23 @@ import SearchForm from "@/components/SearchForm/index.vue";
 import Table from "@/components/Table/index.vue";
 import formConfig from "./config/form.js";
 import tableConfig from "./config/table.js";
+import {ref} from "vue";
+import {getPaperProvinces} from "@/api/dz/papers.js";
+import {getCampusSchoolList} from "@/api/dz/cards.js";
 
 const { proxy } = getCurrentInstance();
 
 const openList = ref([]);
 const open = ref(false);
 const reviewOpen = ref(false);
+const provinceList = ref([]); // 省份列表
+const schoolList = ref([]); // 学校列表
 const loading = ref(true);
 const showSearch = ref(true);
 const total = ref(0);
 const title = ref("");
 const selectedRows = ref([]);
+const { card_status, card_distribute_status, card_time_status, bool_values, card_pay_status, audit_status, } = proxy.useDict( "card_status", "card_distribute_status", "card_time_status", "bool_values", "card_pay_status", "audit_status" );
 
 // 搜索配置
 const searchConfig = computed(() => {
@@ -203,12 +228,13 @@ const data = reactive({
   reviewForm: {
     id: null,
     status: null,
-    reason: null,
+    auditDesc: null,
     startNo: null,
     endNo: null,
     applicant: null,
     createTime: null,
-    schoolName: null,
+    province:null,
+    schoolId: null,
   },
   queryParams: {
     pageNum: 1,
@@ -221,7 +247,8 @@ const data = reactive({
     applicant: [{ required: true, message: "申请人不能为空", trigger: "blur" }],
     startNo: [{ required: true, message: "起始卡号不能为空", trigger: "blur" }],
     endNo: [{ required: true, message: "截至卡号不能为空", trigger: "blur" }],
-    schoolName: [{ required: true, message: "学校不能为空", trigger: "blur" }],
+    province: [{ required: true, message: "省份不能为空", trigger: "change" }],
+    schoolId: [{ required: true, message: "学校不能为空", trigger: "change" }],
     status: [
       { required: true, message: "申请状态不能为空", trigger: "change" },
     ],
@@ -230,7 +257,7 @@ const data = reactive({
     status: [
       { required: true, message: "审核结果不能为空", trigger: "change" },
     ],
-    reason: [],
+    auditDesc: [],
   },
 });
 
@@ -246,6 +273,32 @@ function getList() {
   });
 }
 
+
+// 获取省份列表
+async function getProvinceList() {
+  try {
+    const response = await getPaperProvinces();
+    if (response.code === 200) {
+      provinceList.value = response.data || [];
+    }
+    schoolList.value = []
+  } catch (error) {
+    console.error("获取省份列表失败:", error);
+  }
+}
+
+// 获取校区列表
+async function getCampusListData() {
+  try {
+    const response = await getCampusSchoolList({ campus: false, location: form.value.province, pageNum: 1, pageSize: 1000 });
+    if (response.code === 200) {
+      schoolList.value = response.data || [];
+    }
+  } catch (error) {
+    console.error("获取校区列表失败:", error);
+  }
+}
+
 // 计算申请数量
 function calculateQuantity(startNo, endNo) {
   if (!startNo || !endNo) return 0;
@@ -257,9 +310,9 @@ function calculateQuantity(startNo, endNo) {
 // 获取状态文本
 function getStatusText(status) {
   const statusMap = {
-    0: "待审核",
-    1: "审核通过",
-    2: "已拒绝",
+    1: "待审核",
+    2: "审核通过",
+    3: "已拒绝",
   };
   return statusMap[status] || "未知";
 }
@@ -267,9 +320,9 @@ function getStatusText(status) {
 // 获取状态类型
 function getStatusType(status) {
   const typeMap = {
-    0: "warning",
-    1: "success",
-    2: "danger",
+    1: "warning",
+    2: "success",
+    3: "danger",
   };
   return typeMap[status] || "info";
 }
@@ -306,7 +359,7 @@ function resetReview() {
   reviewForm.value = {
     id: null,
     status: null,
-    reason: null,
+    auditDesc: null,
     startNo: null,
     endNo: null,
     applicant: null,
@@ -361,7 +414,7 @@ function handleReview(row) {
   reviewForm.value = {
     id: row.id,
     status: null,
-    reason: null,
+    auditDesc: null,
     startNo: row.startNo || "-",
     endNo: row.endNo || "-",
     applicant: row.applicant || "-",
@@ -410,7 +463,7 @@ function submitReview() {
       const updateData = {
         id: reviewForm.value.id,
         status: reviewForm.value.status,
-        reason: reviewForm.value.reason,
+        auditDesc: reviewForm.value.auditDesc,
       };
       updateOpen(updateData).then((response) => {
         proxy.$modal.msgSuccess("审核成功");
@@ -431,8 +484,19 @@ function handleExport() {
     `open_${new Date().getTime()}.xlsx`
   );
 }
-
+getProvinceList();
 getList();
+
+// 监听省份变化,获取对应学校
+watch(
+    () => form.value.province,
+    (newProvince) => {
+      // 重置学校选择
+      form.value.schoolId = null;
+      // 获取对应省份的学校列表
+      getCampusListData(newProvince);
+    }
+);
 </script>
 <style>
 .detail {

+ 35 - 50
back-ui/src/views/dz/school/index.vue

@@ -3,17 +3,17 @@
     <el-row :gutter="20">
       <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
         <!--机构数据-->
-        <pane size="16">
-          <el-col>
-            <div class="head-container">
-              <el-input v-model="deptName" placeholder="请输入机构名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />
-            </div>
-            <div class="head-container">
-              <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
-            </div>
-          </el-col>
-        </pane>
-        <pane size="84">
+<!--        <pane size="16">-->
+<!--          <el-col>-->
+<!--            <div class="head-container">-->
+<!--              <el-input v-model="deptName" placeholder="请输入机构名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />-->
+<!--            </div>-->
+<!--            <div class="head-container">-->
+<!--              <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />-->
+<!--            </div>-->
+<!--          </el-col>-->
+<!--        </pane>-->
+        <pane size="100">
           <el-col>
             <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
               <el-form-item label="名称" prop="name">
@@ -24,14 +24,14 @@
                         @keyup.enter="handleQuery"
                 />
               </el-form-item>
-              <el-form-item label="省份" prop="location">
-                <el-input
-                        v-model="queryParams.location"
-                        placeholder="请输入省份"
-                        clearable
-                        @keyup.enter="handleQuery"
-                />
-              </el-form-item>
+<!--              <el-form-item label="省份" prop="location">-->
+<!--                <el-input-->
+<!--                        v-model="queryParams.location"-->
+<!--                        placeholder="请输入省份"-->
+<!--                        clearable-->
+<!--                        @keyup.enter="handleQuery"-->
+<!--                />-->
+<!--              </el-form-item>-->
               <el-form-item label="省市区" prop="areaIds">
                 <AddressSelect class="w-[198px]" v-model="areaIds" />
               </el-form-item>
@@ -98,13 +98,8 @@
               <el-table-column type="selection" width="55" align="center" />
               <el-table-column label="ID" align="center" prop="id" />
               <el-table-column label="名称" align="center" prop="name" />
-<!--              <el-table-column label="机构ID" align="center" prop="deptId" />-->
-              <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
-              <el-table-column label="省份" align="center" prop="location" />
-
-<!--              <el-table-column label="省" align="center" prop="pro" />-->
-<!--              <el-table-column label="市" align="center" prop="city" />-->
-<!--              <el-table-column label="区" align="center" prop="area" />-->
+<!--              <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />-->
+<!--              <el-table-column label="省份" align="center" prop="location" />-->
               <el-table-column label="所属地区" align="center" prop="proCityAreaName" />
               <el-table-column label="状态" align="center" prop="status">
                 <template #default="scope">
@@ -138,27 +133,15 @@
         <el-form-item label="名称" prop="name">
           <el-input v-model="form.name" placeholder="请输入名称" />
         </el-form-item>
-<!--        <el-form-item label="机构ID" prop="deptId">-->
-<!--          <el-input v-model="form.deptId" placeholder="请输入机构ID" />-->
+<!--        <el-form-item label="归属机构" prop="deptId">-->
+<!--          <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属机构" clearable check-strictly />-->
 <!--        </el-form-item>-->
-        <el-form-item label="归属机构" prop="deptId">
-          <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属机构" clearable check-strictly />
-        </el-form-item>
         <el-form-item label="省份" prop="location">
           <el-input v-model="form.location" placeholder="请输入省份" />
         </el-form-item>
         <el-form-item label="省市区" prop="areaIds" style="width: 220px;">
           <AddressSelect class="w-[198px]" v-model="areaIds" />
         </el-form-item>
-<!--        <el-form-item label="省" prop="pro">-->
-<!--          <el-input v-model="form.pro" placeholder="请输入省" />-->
-<!--        </el-form-item>-->
-<!--        <el-form-item label="市" prop="city">-->
-<!--          <el-input v-model="form.city" placeholder="请输入市" />-->
-<!--        </el-form-item>-->
-<!--        <el-form-item label="区" prop="area">-->
-<!--          <el-input v-model="form.area" placeholder="请输入区" />-->
-<!--        </el-form-item>-->
         <el-form-item label="状态" prop="status">
           <el-select v-model="form.status" placeholder="请选择状态">
             <el-option
@@ -226,10 +209,10 @@
       status: null,
     },
     rules: {
-      status: [
-        { required: true, message: "状态不能为空", trigger: "change" },
-        { required: true, message: "机构不能为空", trigger: "change" }
-      ],
+      // status: [
+      //   { required: true, message: "状态不能为空", trigger: "change" },
+      //   { required: true, message: "机构不能为空", trigger: "change" }
+      // ],
     }
   })
 
@@ -331,11 +314,11 @@
   /** 重置按钮操作 */
   function resetQuery() {
     // 清空机构ID
-    queryParams.value.deptId = undefined
+    // queryParams.value.deptId = undefined
     // 清空机构名称
-    deptName.value = ""
+    // deptName.value = ""
     // 取消树节点的选中状态
-    proxy.$refs.deptTreeRef.setCurrentKey(null)
+    // proxy.$refs.deptTreeRef.setCurrentKey(null)
     proxy.resetForm("queryRef")
     // 清空省市区选择
     areaIds.value = []
@@ -386,13 +369,15 @@
         };
 
         if (form.value.id != null) {
-          updateSchool(form.value).then(response => {
+          // updateSchool(form.value).then(response => {
+          updateSchool(submitData).then(response => {
             proxy.$modal.msgSuccess("修改成功")
             open.value = false
             getList()
           })
         } else {
-          addSchool(form.value).then(response => {
+          // addSchool(form.value).then(response => {
+          addSchool(submitData).then(response => {
             proxy.$modal.msgSuccess("新增成功")
             open.value = false
             getList()
@@ -421,7 +406,7 @@
   }
 
   onMounted(() => {
-    getDeptTree()
+    // getDeptTree()
     getList()
   })
 </script>

+ 57 - 8
back-ui/src/views/dz/teacher/index.vue

@@ -17,10 +17,10 @@
           @keyup.enter="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="所在校" prop="schoolId">
+      <el-form-item label="所在校" prop="schoolId">
         <el-select
                 v-model="queryParams.schoolId"
-                placeholder="请选择所在校"
+                placeholder="请选择所在校"
                 clearable
                 style="width: 240px"
                 @keyup.enter="handleQuery"
@@ -33,6 +33,22 @@
           />
         </el-select>
       </el-form-item>
+        <el-form-item label="培训校区" prop="campusId">
+            <el-select
+                    v-model="queryParams.campusId"
+                    placeholder="请选择培训校区"
+                    clearable
+                    style="width: 240px"
+                    @keyup.enter="handleQuery"
+            >
+                <el-option
+                        v-for="item in campusOptions"
+                        :key="item.id"
+                        :label="item.name"
+                        :value="item.id"
+                />
+            </el-select>
+        </el-form-item>
       <el-form-item>
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
         <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -83,9 +99,10 @@
 
     <el-table v-loading="loading" :data="teacherList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="ID" align="center" prop="teacherId" />
+      <el-table-column label="教师ID/账号" align="center" prop="teacherId" />
       <el-table-column label="教师姓名" align="center" prop="name" />
-      <el-table-column label="所在校区" align="center" prop="schoolName" />
+      <el-table-column label="所在学校" align="center" prop="schoolName" />
+      <el-table-column label="培训校区" align="center" prop="campusName" />
       <el-table-column label="所在机构" align="center" prop="deptName" />
       <el-table-column label="用户ID" align="center" prop="userId" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
@@ -114,8 +131,8 @@
         <el-form-item label="教师姓名" prop="name">
           <el-input v-model="form.name" placeholder="请输入教师姓名" />
         </el-form-item>
-        <el-form-item label="所在校" prop="schoolId">
-          <el-select v-model="form.schoolId" placeholder="请选择所在校" style="width: 100%">
+        <el-form-item label="所在校" prop="schoolId">
+          <el-select v-model="form.schoolId" placeholder="请选择所在校" style="width: 100%">
             <el-option
                     v-for="item in schoolOptions"
                     :key="item.id"
@@ -124,6 +141,16 @@
             />
           </el-select>
         </el-form-item>
+        <el-form-item label="培训校区" prop="campusId">
+          <el-select v-model="form.campusId" placeholder="请选择培训校区" style="width: 100%">
+            <el-option
+                    v-for="item in campusOptions"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -142,6 +169,9 @@
               <el-form-item label="教师姓名" prop="name">
                   <el-input v-model="formTeacherClass.name" placeholder="请输入教师姓名" disabled/>
               </el-form-item>
+              <el-form-item label="学校校区" prop="name">
+                  <el-input v-model="formTeacherClass.schoolName" placeholder="请输入教师姓名" disabled/>
+              </el-form-item>
               <el-form-item label="关联班级" prop="classIds">
                   <el-select v-model="formTeacherClass.classIds" multiple placeholder="请选择班级" style="width: 100%">
                       <el-option
@@ -165,14 +195,17 @@
 
 <script setup name="Teacher">
 import { listTeacher, getTeacher, delTeacher, addTeacher, updateTeacher } from "@/api/dz/teacher"
-import { listAllSchool } from "@/api/dz/school"
+import {listAllSchool} from "@/api/dz/school"
+import {listCampus} from "@/api/dz/campus"
 import { listAllClass } from "@/api/dz/classes"
 import { batchBindTeacherClass,listAllTeacherClass } from "@/api/dz/teacherclass"
+import {getCampusSchoolList} from "@/api/dz/cards.js";
 
 const { proxy } = getCurrentInstance()
 
 const teacherList = ref([])
 const schoolOptions = ref([])
+const campusOptions = ref([])
 const classOptions = ref([])
 const open = ref(false)
 const openTeacherClass = ref(false)
@@ -189,6 +222,7 @@ const data = reactive({
   form: {},
   formTeacherClass: {
       teacherId: null,
+      schoolName: null,
       name: null,
       classIds: []
   },
@@ -197,6 +231,7 @@ const data = reactive({
     pageSize: 10,
     userId: null,
     schoolId: null,
+    campusId: null,
     name: null
   },
   rules: {
@@ -223,10 +258,20 @@ function getList() {
 
 /** 查询校区列表 */
 function getSchoolList() {
-  listAllSchool().then(response => {
+  listAllSchool({
+    pageNum: 1,
+    pageSize: 9999,
+  }).then(response => {
     schoolOptions.value = response.data || []
   })
 }
+/** 查询校区列表 */
+function getCampusList() {
+  listCampus().then(response => {
+    campusOptions.value = response.rows || []
+  })
+}
+
 
 // function getClassListBySchool() {
 //     listAll().then(response => {
@@ -247,6 +292,7 @@ function reset() {
     teacherId: null,
     userId: null,
     schoolId: null,
+    campusId: null,
     name: null
   }
   proxy.resetForm("teacherRef")
@@ -262,6 +308,7 @@ function cancelTeacherClass() {
 function resetTeacherClass() {
     formTeacherClass.value = {
         teacherId: null,
+        schoolName: null,
         classIds: [],
         name: null
     }
@@ -358,6 +405,7 @@ function handleUpdateTeacherClass(row) {
         // 然后设置数据
         formTeacherClass.value.teacherId = row.teacherId
         formTeacherClass.value.name = row.name
+        formTeacherClass.value.schoolName = row.schoolName
 
         listAllTeacherClass(submitData).then(response => {
             formTeacherClass.value.classIds = (response.data || []).map(item => item.classId)
@@ -394,6 +442,7 @@ function submitFormTeacherClass() {
 
 onMounted(() => {
   getSchoolList()
+  getCampusList()
   getList()
   // getClassListBySchool()
 })

+ 1 - 1
back-ui/vite.config.js

@@ -3,7 +3,7 @@ import path from 'path'
 import createVitePlugins from './vite/plugins'
 import tailwindcss from '@tailwindcss/vite'
 
-const baseUrl = 'https://dz.shineking.top/prod-api' // 后端接口
+const baseUrl = 'http://localhost:8080' // 后端接口
 
 // https://vitejs.dev/config/
 export default defineConfig(({ mode, command }) => {

+ 36 - 5
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzAgentController.java

@@ -8,9 +8,14 @@ import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
 
 import cn.hutool.core.collection.CollectionUtil;
+import com.google.common.collect.Lists;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.dz.domain.DzSchool;
 import com.ruoyi.dz.service.IDzSchoolService;
+import com.ruoyi.enums.UserTypeEnum;
+import com.ruoyi.system.service.ISysUserService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -43,6 +48,8 @@ public class DzAgentController extends BaseController
     private IDzAgentService dzAgentService;
     @Autowired
     private IDzSchoolService schoolService;
+    @Autowired
+    private ISysUserService userService;
 
     /**
      * 查询机构代理列表
@@ -51,9 +58,17 @@ public class DzAgentController extends BaseController
     @GetMapping("/list")
     public AjaxResult list(DzAgent dzAgent)
     {
-        List<DzAgent> list = dzAgentService.selectDzAgentList(dzAgent);
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        List<DzAgent> allList = Lists.newArrayList();
+        if(UserTypeEnum.isAgent(sysUser.getUserType())) {
+            dzAgent.setParentId(sysUser.getUserTypeId());
+            allList.add(dzAgentService.selectDzAgentByAgentId(sysUser.getUserTypeId()));
+        } else if(!UserTypeEnum.isSys(sysUser.getUserType())) {
+            dzAgent.setDeptId(sysUser.getDeptId());
+        }
+        allList.addAll(dzAgentService.selectDzAgentList(dzAgent));
         //处理关联学校的显示
-        List<Long> distinctSchoolIds = list.stream()
+        List<Long> distinctSchoolIds = allList.stream()
                 .map(DzAgent::getSchoolIds)
                 .filter(Objects::nonNull)        // 过滤null数组
                 .filter(array -> array.length > 0) // 过滤空数组
@@ -63,7 +78,7 @@ public class DzAgentController extends BaseController
         //查询学校名称
         if (CollectionUtil.isNotEmpty(distinctSchoolIds)){
             Map<Long, DzSchool> schoolMap = schoolService.selectDzSchoolListByIds(distinctSchoolIds).stream().collect(Collectors.toMap(DzSchool::getId, school -> school));
-            for (DzAgent agent : list) {
+            for (DzAgent agent : allList) {
                 if (agent.getSchoolIds() != null&&agent.getSchoolIds().length>0) {
                     String schoolName = Arrays.stream(agent.getSchoolIds())
                             .filter(Objects::nonNull)  // 过滤null值
@@ -78,7 +93,7 @@ public class DzAgentController extends BaseController
             }
         }
 
-        return success(list);
+        return success(allList);
     }
 
     /**
@@ -112,6 +127,13 @@ public class DzAgentController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody DzAgent dzAgent)
     {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        if(UserTypeEnum.isAgent(sysUser.getUserType()) && null != sysUser.getUserTypeId()) {
+            dzAgent.setParentId(sysUser.getUserTypeId());
+        }
+        if(null == dzAgent.getDeptId()) {
+            dzAgent.setDeptId(sysUser.getDeptId());
+        }
         setSchools(dzAgent);
         return toAjax(dzAgentService.insertDzAgent(dzAgent));
     }
@@ -125,7 +147,16 @@ public class DzAgentController extends BaseController
     public AjaxResult edit(@RequestBody DzAgent dzAgent)
     {
         setSchools(dzAgent);
-        return toAjax(dzAgentService.updateDzAgent(dzAgent));
+        dzAgentService.updateDzAgent(dzAgent);
+        //同时修改sys_user表的nickName
+        SysUser user = userService.selectUserByUserName(String.valueOf(dzAgent.getAgentId()));
+        if (null!=user){
+            if (!user.getNickName().equalsIgnoreCase(dzAgent.getName())){
+                user.setNickName(dzAgent.getName());
+                userService.updateUserInfo(user);
+            }
+        }
+        return AjaxResult.success();
     }
 
     private void setSchools(DzAgent dzAgent){

+ 152 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCampusController.java

@@ -0,0 +1,152 @@
+package com.ruoyi.web.controller.dz;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.dz.domain.DzSchool;
+import com.ruoyi.dz.service.IDzSchoolService;
+import com.ruoyi.enums.UserTypeEnum;
+import com.ruoyi.system.domain.SysArea;
+import com.ruoyi.system.service.ISysAreaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 机构校区Controller
+ *
+ * @author ruoyi
+ * @date 2025-09-12
+ */
+@RestController
+@RequestMapping("/dz/campus")
+public class DzCampusController extends BaseController
+{
+    @Autowired
+    private IDzSchoolService dzSchoolService;
+    @Autowired
+    private ISysAreaService areaService;
+
+    /**
+     * 查询机构校区列表
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(DzSchool dzSchool)
+    {
+        dzSchool.setCampus(true);
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        if(!UserTypeEnum.isSys(sysUser.getUserType())) {
+            dzSchool.setDeptId(SecurityUtils.getDeptId());
+            if (UserTypeEnum.isSchool(sysUser.getUserType())) {
+                dzSchool.setId(sysUser.getUserTypeId());
+            }
+        }
+        startPage();
+        List<DzSchool> list= dzSchoolService.selectDzSchoolList(dzSchool);
+        //处理省市区
+        List<Long> areaIds = list.stream()
+                .flatMap(school -> Stream.of(
+                        school.getPro(),school.getCity(),school.getArea()
+                ))
+                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+        if (CollectionUtil.isNotEmpty(areaIds)){
+            Map<Long, SysArea> areaMap = areaService.selectSysAreaListByIds(areaIds)
+                    .stream().collect(Collectors.toMap(SysArea::getAreaId,area -> area));
+            list.forEach(school -> {
+                StringBuilder proCityAreaName = new StringBuilder();
+                if (null!=school.getPro()&&areaMap.containsKey(school.getPro())){
+                    proCityAreaName.append(areaMap.get(school.getPro()).getAreaName());
+                }
+                if (null!=school.getCity()&&areaMap.containsKey(school.getCity())){
+                    proCityAreaName.append(areaMap.get(school.getCity()).getAreaName());
+                }
+                if (null!=school.getArea()&&areaMap.containsKey(school.getArea())){
+                    proCityAreaName.append(areaMap.get(school.getArea()).getAreaName());
+                }
+                if (StringUtils.isNotEmpty(proCityAreaName.toString())){
+                    school.setProCityAreaName(proCityAreaName.toString());
+                }
+            });
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出机构校区列表
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:export')")
+    @Log(title = "机构校区", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, DzSchool dzSchool)
+    {
+        dzSchool.setCampus(true);
+        if(!UserTypeEnum.isSys(SecurityUtils.getLoginUser().getUser().getUserType())) {
+            dzSchool.setDeptId(SecurityUtils.getDeptId());
+        }
+        List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
+        ExcelUtil<DzSchool> util = new ExcelUtil<DzSchool>(DzSchool.class);
+        util.exportExcel(response, list, "机构校区数据");
+    }
+
+    /**
+     * 获取机构校区详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(dzSchoolService.selectDzSchoolById(id));
+    }
+
+    /**
+     * 新增机构校区
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:add')")
+    @Log(title = "机构校区", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody DzSchool dzSchool)
+    {
+        if(null == dzSchool.getDeptId()) {
+            dzSchool.setDeptId(SecurityUtils.getDeptId());
+        }
+        return toAjax(dzSchoolService.insertDzSchool(dzSchool));
+    }
+
+    /**
+     * 修改机构校区
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:edit')")
+    @Log(title = "机构校区", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody DzSchool dzSchool)
+    {
+        dzSchool.setDeptId(null);
+        return toAjax(dzSchoolService.updateDzSchool(dzSchool));
+    }
+
+    /**
+     * 删除机构校区
+     */
+    @PreAuthorize("@ss.hasPermi('dz:campus:remove')")
+    @Log(title = "机构校区", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(dzSchoolService.deleteDzSchoolByIds(ids));
+    }
+}

+ 27 - 7
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsController.java

@@ -4,8 +4,10 @@ import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.ValidationException;
 
+import cn.hutool.core.util.NumberUtil;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.enums.ExamType;
+import com.ruoyi.common.utils.NumberUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzAgent;
 import com.ruoyi.dz.service.IDzAgentService;
@@ -29,7 +31,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 
 /**
  * 学习卡Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -51,6 +53,25 @@ public class DzCardsController extends BaseController
     @ApiOperation("列表")
     public TableDataInfo list(DzCards dzCards)
     {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        String userType = sysUser.getUserType();
+        if(UserTypeEnum.isInstitution(userType)){
+            dzCards.setDeptId(SecurityUtils.getDeptId());
+        } else if(UserTypeEnum.isSchool(userType)){
+            dzCards.setCampusId(sysUser.getUserTypeId());
+        }
+        if((UserTypeEnum.isAgent(userType)||UserTypeEnum.isTeacher(userType))&&null==dzCards.getAgentId()){
+            //代理商及老师只能查看分配给自己的卡
+            DzAgent agent = agentService.selectDzAgentByUserId(SecurityUtils.getUserId());
+            if (null!=agent){
+                if(NumberUtils.isPositive(agent.getParentId())) {
+                    dzCards.setLeafAgentId(agent.getAgentId());
+                } else {
+                    dzCards.setAgentId(agent.getAgentId());
+                }
+            }
+        }
+
         startPage();
         List<DzCards> list = dzCardsService.selectDzCardsList(dzCards);
         return getDataTable(list);
@@ -141,9 +162,9 @@ public class DzCardsController extends BaseController
             if (!sysUser.getDeptId().equals(agent.getDeptId())) {
                 throw new ValidationException("只能分配给下级代理");
             }
-            dzCardsService.assignCard(null == agent.getParentId() ? agentId : agent.getParentId(), agentId, begin, end, location, examType, schoolId);
-        } else if(null == agent.getParentId()) { // 平台指定, TODO 暂只支持二级代理
-            dzCardsService.assignCard(null == agent.getParentId() ? agentId : agent.getParentId(), agentId, begin, end, location, examType, schoolId);
+            dzCardsService.assignCard(NumberUtils.isPositive(agent.getParentId()) ? agent.getParentId() : agentId, agentId, begin, end, location, examType, schoolId);
+        } else { // 平台指定, TODO 暂只支持二级代理
+            dzCardsService.assignCard(NumberUtils.isPositive(agent.getParentId()) ? agent.getParentId() : agentId, agentId, begin, end, location, examType, schoolId);
         }
         return AjaxResult.success();
     }
@@ -151,10 +172,9 @@ public class DzCardsController extends BaseController
     @Log(title = "直接开卡", businessType = BusinessType.INSERT)
     @PostMapping("/openCard")
     @ApiOperation("直接开卡")
-    public AjaxResult openCard(@ApiParam("学校") Long schoolId, @ApiParam("开始号") String begin, @ApiParam("结束号") String end)
+    public AjaxResult openCard(@ApiParam("代理商") Long agentId, @ApiParam("省份") String location, @ApiParam("学校") Long schoolId, @ApiParam("开始号") String begin, @ApiParam("结束号") String end)
     {
-        Long agentId = 0L;
-        return AjaxResult.success(dzCardsService.openCard(schoolId, agentId, begin, end));
+        return AjaxResult.success(dzCardsService.openCard(agentId, location, schoolId, begin, end));
     }
 
     @Log(title = "分配校区", businessType = BusinessType.INSERT)

+ 5 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzCardsOpenController.java

@@ -7,6 +7,8 @@ import javax.validation.ValidationException;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.service.IDzCardsService;
+import com.ruoyi.enums.CardAudit;
+import com.ruoyi.enums.CardDistributeStatus;
 import com.ruoyi.enums.RequestStatus;
 import com.ruoyi.enums.UserTypeEnum;
 import io.swagger.annotations.Api;
@@ -31,7 +33,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 
 /**
  * 开卡申请Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -94,6 +96,8 @@ public class DzCardsOpenController extends BaseController
     @ApiOperation("申请开卡")
     public AjaxResult add(@RequestBody DzCardsOpen dzCardsOpen)
     {
+        dzCardsOpen.setSender(String.valueOf(SecurityUtils.getUserId()));
+        dzCardsOpen.setStatus(CardAudit.NoAudit.getVal());
         return toAjax(dzCardsService.requestOpenCard(dzCardsOpen));
     }
 

+ 50 - 2
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzClassesController.java

@@ -5,9 +5,18 @@ import java.util.Objects;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
 
+import cn.hutool.core.collection.CollectionUtil;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.enums.BoolValues;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.dz.domain.DzSchool;
 import com.ruoyi.dz.domain.DzTeacher;
 import com.ruoyi.dz.domain.DzTeacherClass;
+import com.ruoyi.dz.mapper.DzSchoolMapper;
 import com.ruoyi.dz.service.IDzTeacherClassService;
+import com.ruoyi.enums.UserTypeEnum;
+import io.lettuce.core.dynamic.annotation.Param;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -41,6 +50,8 @@ public class DzClassesController extends BaseController
     private IDzClassesService dzClassesService;
     @Autowired
     private IDzTeacherClassService teacherClassService;
+    @Autowired
+    private DzSchoolMapper dzSchoolMapper;
 
     /**
      * 查询学生班级列表
@@ -50,6 +61,13 @@ public class DzClassesController extends BaseController
     public TableDataInfo list(DzClasses dzClasses)
     {
         startPage();
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        if (!UserTypeEnum.isSys(sysUser.getUserType())) {
+            dzClasses.setDeptId(sysUser.getDeptId());
+            if (UserTypeEnum.isSchool(sysUser.getUserType())) {
+                dzClasses.setSchoolId(sysUser.getUserTypeId());
+            }
+        }
         List<DzClasses> list = dzClassesService.selectDzClassesList(dzClasses);
         return getDataTable(list);
     }
@@ -70,6 +88,13 @@ public class DzClassesController extends BaseController
         return AjaxResult.success(list);
     }
 
+    @GetMapping("/getClassesBySchoolId")
+    public AjaxResult getClassesBySchoolId(@Param("schoolId") Long schoolId)
+    {
+        List<DzClasses> list = dzClassesService.selectDzClassesListBySchoolId(schoolId);
+        return AjaxResult.success(list);
+    }
+
     /**
      * 导出学生班级列表
      */
@@ -101,7 +126,30 @@ public class DzClassesController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody DzClasses dzClasses)
     {
-        return toAjax(dzClassesService.insertDzClasses(dzClasses));
+        String name = dzClasses.getName();
+        DzSchool dzSchool = dzSchoolMapper.selectDzSchoolById(dzClasses.getSchoolId());
+        dzClasses.setDeptId(dzSchool.getDeptId());
+        if(StringUtils.isNotEmpty(name)){
+            for (String s : name.split(",")) {
+                dzClasses.setName(s);
+                dzClasses.setIsDefault(BoolValues.no.getValue());
+                dzClassesService.insertDzClasses(dzClasses);
+            }
+            //把创建的默认旧班级删除
+            if (null!=dzClasses.getSchoolId()){
+                DzClasses query = new DzClasses();
+                query.setIsDefault(BoolValues.yes.getValue());
+                query.setSchoolId(dzClasses.getSchoolId());
+                List<DzClasses> dzClassesList = dzClassesService.selectDzClassesList(query);
+                if (CollectionUtil.isNotEmpty(dzClassesList)) {
+                    Long[] classesIds = dzClassesList.stream().map(DzClasses::getClassId).filter(id -> id != null).toArray(Long[]::new);
+                    if (null!=classesIds&&classesIds.length>0){
+                        dzClassesService.deleteDzClassesByClassIds(classesIds);
+                    }
+                }
+            }
+        }
+        return toAjax(1);
     }
 
     /**
@@ -120,7 +168,7 @@ public class DzClassesController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('dz:classes:remove')")
     @Log(title = "学生班级", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{classIds}")
+    @DeleteMapping("/{classIds}")
     public AjaxResult remove(@PathVariable Long[] classIds)
     {
         return toAjax(dzClassesService.deleteDzClassesByClassIds(classIds));

+ 74 - 8
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzSchoolController.java

@@ -1,5 +1,6 @@
 package com.ruoyi.web.controller.dz;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -9,8 +10,13 @@ import javax.servlet.http.HttpServletResponse;
 import javax.validation.ValidationException;
 
 import cn.hutool.core.collection.CollectionUtil;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.enums.BoolValues;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.dz.domain.DzClasses;
+import com.ruoyi.dz.service.IDzClassesService;
+import com.ruoyi.dz.service.impl.DzClassesServiceImpl;
 import com.ruoyi.enums.UserTypeEnum;
 import com.ruoyi.system.domain.SysArea;
 import com.ruoyi.system.service.ISysAreaService;
@@ -47,6 +53,8 @@ public class DzSchoolController extends BaseController
     private IDzSchoolService dzSchoolService;
     @Autowired
     private ISysAreaService areaService;
+    @Autowired
+    private IDzClassesService classesService;
 
     /**
      * 查询机构校区列表
@@ -55,6 +63,14 @@ public class DzSchoolController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(DzSchool dzSchool)
     {
+//        if (null==dzSchool.getDeptId()){
+//            dzSchool.setDeptId(getLoginUser().getDeptId());
+//        }
+//        if(!UserTypeEnum.Sys.getVal().equals(SecurityUtils.getLoginUser().getUser().getUserType())) {
+//            dzSchool.setCampus(true);
+//        }else {
+//            dzSchool.setCampus(false);
+//        }
         startPage();
         List<DzSchool> list= dzSchoolService.selectDzSchoolList(dzSchool);
         //处理省市区
@@ -86,22 +102,38 @@ public class DzSchoolController extends BaseController
         return getDataTable(list);
     }
 
-    @GetMapping("/getCampusList")
-    public AjaxResult getCampusList(DzSchool dzSchool)
+    @GetMapping("/getCampusSchoolList")
+    public AjaxResult getCampusSchoolList(DzSchool dzSchool)
     {
-        if(!UserTypeEnum.Sys.getVal().equals(SecurityUtils.getLoginUser().getUser().getUserType())) {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        if(null == dzSchool.getCampus() && !UserTypeEnum.isSys(sysUser.getUserType())) {
             dzSchool.setCampus(true);
         }
+        if (UserTypeEnum.isSchool(sysUser.getUserType())) {
+            dzSchool.setId(sysUser.getUserTypeId());
+        }
         List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
         return AjaxResult.success(list);
     }
 
+//    @GetMapping("/getSchoolList")
+//    public AjaxResult getSchoolList(DzSchool dzSchool)
+//    {
+//        if(!UserTypeEnum.Sys.getVal().equals(SecurityUtils.getLoginUser().getUser().getUserType())) {
+//            dzSchool.setCampus(false);
+//        }
+//        List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
+//        return AjaxResult.success(list);
+//    }
+
     @GetMapping("/getSchoolList")
     public AjaxResult getSchoolList(DzSchool dzSchool)
     {
-        if(!UserTypeEnum.Sys.getVal().equals(SecurityUtils.getLoginUser().getUser().getUserType())) {
-            dzSchool.setCampus(false);
-        }
+//        if(!UserTypeEnum.Sys.getVal().equals(SecurityUtils.getLoginUser().getUser().getUserType())) {
+//            dzSchool.setCampus(true);
+//        }else {
+//            dzSchool.setCampus(false);
+//        }
         List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
         return AjaxResult.success(list);
     }
@@ -137,7 +169,41 @@ public class DzSchoolController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody DzSchool dzSchool)
     {
-        return toAjax(dzSchoolService.insertDzSchool(dzSchool));
+        int id = dzSchoolService.insertDzSchool(dzSchool);
+        //创建学校时,如果没有班级则默认创建30个班级
+        for (int i = 0; i < 30; i++) {
+            DzClasses dzClass = new DzClasses();
+            dzClass.setSchoolId(Long.valueOf(id));
+            dzClass.setName( String.valueOf(i + 1));
+            dzClass.setIsDefault(BoolValues.yes.getValue());
+            dzClass.setOnline(BoolValues.yes.getValue());
+            dzClass.setStatus(BoolValues.yes.getValue());
+            classesService.insertDzClasses(dzClass);
+        }
+//        generate();
+        return toAjax(id);
+    }
+
+    private void generate(){
+        List<Integer> schoolList = Arrays.asList(2,4,8,14,29,30,31,32,33,34,42,43,45,51,54,55,56,66,70,71,73,74,76,77,79,80,86,87,89,91,
+                92,93,94,95,97,98,108,110,111,112,114,115,117,118,119,121,122,124,126,127,130,131,132,133,134,135,136,137,138,139,140,142,143,145,146,149,151,153,173,174,175,176,182,183,184,185,186,187,188,189,204,205,206,207,250,251,252,253,254,255,257,262,263,264,265,266,273,274,275,284,358,359,360,361,362,363,364,365,366,376,377,427,428,440,441,452,453,454,455,456,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,480,481,482,483,484,485,486,487,488,489,527,528,555,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,591,592,593,594,595,596,
+                597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,668,669,670,671,672,673,674,675,679,680,681,682,683,688,689,690,691,692,723,724,725,726,727,728,732,733,734,735,736,747,748,749,760,761,762,763,764,765,766,784,785,786,787,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,832,833,834,835,836,837,838,839,840,
+                841,842,843,847,848,856,857,865,866,867,868,877,878,879,880,881,882,883,884,889,890,906,907,908,909,910,911,920,921,922,923,924,929,930,931,932,933,934,935,936,937,938,939,1000,1001,1002,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1071,1072,1073,1074,1075,1076,1077,1078,1083,1084,1085,1086,1087,1114,1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126,1127,1128,1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,
+                1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1208,1209,1210,1211,1212,1247,1248,1267,1268,1269,1270,1271,1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1285,1286,1287,1288,1289,1299,1300,1301,1302,1303,1304,1305,1306,1307,1317,1318,1319,1320,1326,1327,1328,1329,1330,1331,1332,1333,1343,1344,1345,1349,1350,1351,1361,1362,1366,1367,1368,1369,1370,1371,1372,1373,1374,1375,1376,1377,1378,1379,1380,1381,1382,1383,1384,1385,1386,
+                1387,1388,1389,1390,1391,1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1417,1430,1439,1440,1447,1448,1452,1453,1454,1455,1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487,1488,1489,1490,1491,1492,1493,1494,1495,1595,1596,1597,1598,1599,1600,1601,1602,1603,1620,1621,1622,1623,1624,1625,1639,1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,1664,1665,1666,1667,1668,1669,1670,1671,1672,1673,1674,1675,1683,1684,1685,1686,1687,1688,1689,1723,1724,1725,1727,1728
+                );
+        for (Integer id : schoolList) {
+            for (int i = 0; i < 30; i++) {
+                DzClasses dzClass = new DzClasses();
+                dzClass.setSchoolId(Long.valueOf(id));
+                dzClass.setName( String.valueOf(i + 1));
+                dzClass.setIsDefault(BoolValues.yes.getValue());
+                dzClass.setOnline(BoolValues.yes.getValue());
+                dzClass.setStatus(BoolValues.yes.getValue());
+                classesService.insertDzClasses(dzClass);
+            }
+        }
+
     }
 
     /**
@@ -156,7 +222,7 @@ public class DzSchoolController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('dz:school:remove')")
     @Log(title = "机构校区", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{ids}")
+    @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {
         return toAjax(dzSchoolService.deleteDzSchoolByIds(ids));

+ 28 - 3
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherController.java

@@ -3,7 +3,11 @@ package com.ruoyi.web.controller.dz;
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.service.IDzTeacherClassService;
+import com.ruoyi.enums.UserTypeEnum;
+import com.ruoyi.system.service.ISysUserService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -36,7 +40,7 @@ public class DzTeacherController extends BaseController
     @Autowired
     private IDzTeacherService dzTeacherService;
     @Autowired
-    private IDzTeacherClassService teacherClassService;
+    private ISysUserService userService;
 
     /**
      * 查询老师列表
@@ -45,6 +49,13 @@ public class DzTeacherController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(DzTeacher dzTeacher)
     {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        if(!UserTypeEnum.isSys(sysUser.getUserType())){
+            dzTeacher.setDeptId(SecurityUtils.getLoginUser().getUser().getDeptId());
+            if (UserTypeEnum.isSchool(sysUser.getUserType())) {
+                dzTeacher.setCampusId(sysUser.getUserTypeId());
+            }
+        }
         startPage();
         List<DzTeacher> list = dzTeacherService.selectDzTeacherList(dzTeacher);
         return getDataTable(list);
@@ -81,7 +92,7 @@ public class DzTeacherController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody DzTeacher dzTeacher)
     {
-        return toAjax(dzTeacherService.insertDzTeacher(dzTeacher));
+        return AjaxResult.success(dzTeacherService.insertDzTeacher(dzTeacher));
     }
 
     /**
@@ -92,7 +103,21 @@ public class DzTeacherController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody DzTeacher dzTeacher)
     {
-        return toAjax(dzTeacherService.updateDzTeacher(dzTeacher));
+        if (UserTypeEnum.isSys(SecurityUtils.getLoginUser().getUser().getUserType())) {
+            dzTeacher.setCampusId(null);
+        } else {
+            dzTeacher.setSchoolId(null);
+        }
+        dzTeacherService.updateDzTeacher(dzTeacher);
+        //同时修改sys_user表的nickName
+        SysUser user = userService.selectUserByUserName(String.valueOf(dzTeacher.getTeacherId()));
+        if (null != user) {
+            if (null == user.getNickName() || !user.getNickName().equalsIgnoreCase(dzTeacher.getName())) {
+                user.setNickName(dzTeacher.getName());
+                userService.updateUserInfo(user);
+            }
+        }
+        return AjaxResult.success();
     }
 
     /**

+ 1 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/CommController.java

@@ -163,6 +163,7 @@ public class CommController {
 
     @ApiOperation("验证码校验")
     @PostMapping(value = "validateSms")
+    @Anonymous
     public AjaxResult validate(String mobile, String code){
         boolean isSuccess = shortMessageService.checkCode(mobile, code);
         if(!isSuccess){

+ 80 - 110
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontExamController.java

@@ -1,144 +1,122 @@
 package com.ruoyi.web.controller.front;
 
-import com.alibaba.fastjson.JSONObject;
-import com.google.common.collect.Sets;
+import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.core.domain.AjaxResult2;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.enums.ExamType;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.domain.DzSubject;
 import com.ruoyi.dz.service.IDzControlService;
-import com.ruoyi.enums.ExamineeStatus;
+import com.ruoyi.dz.service.IDzSubjectService;
 import com.ruoyi.enums.PaperType;
-import com.ruoyi.ie.domain.AMarjorPlan;
-import com.ruoyi.ie.service.IAMarjorPlanService;
-import com.ruoyi.learn.domain.LearnExaminee;
-import com.ruoyi.learn.domain.LearnPaper;
-import com.ruoyi.learn.service.ILearnExamineeService;
-import com.ruoyi.learn.service.ILearnPaperService;
+import com.ruoyi.learn.domain.AnswerSheet;
+import com.ruoyi.mxjb.domain.MxjbContants;
 import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.web.domain.PaperDto;
+import com.ruoyi.web.service.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
-import javax.validation.ValidationException;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/front/exam")
 @Api(tags = "前台-学习-考试练习")
 public class FrontExamController {
-    private Set<PaperType> paperTypeSet = Sets.newHashSet(PaperType.Real, PaperType.Custom, PaperType.Test);
-    private final IDzControlService controlService;
+    private final ExamService examService;
+    private final SyTestMajorService syTestMajorService;
+    private final CacheService cacheService;
+    private final IDzSubjectService dzSubjectService;
+    private final StudentService studentService;
     private final ISysUserService sysUserService;
-    private final ILearnPaperService paperService;
-    private final ILearnExamineeService examineeService;
-    private final IAMarjorPlanService marjorPlanService;
 
-    public FrontExamController(IDzControlService controlService, ISysUserService sysUserService, ILearnPaperService paperService, ILearnExamineeService examineeService, IAMarjorPlanService marjorPlanService) {
-        this.controlService = controlService;
+    public FrontExamController(ExamService examService, SyTestMajorService syTestMajorService, CacheService cacheService, IDzSubjectService dzSubjectService, IDzSubjectService dzSubjectService1, StudentService studentService, ISysUserService sysUserService) {
+        this.examService = examService;
+        this.syTestMajorService = syTestMajorService;
+        this.cacheService = cacheService;
+        this.dzSubjectService = dzSubjectService1;
+        this.studentService = studentService;
         this.sysUserService = sysUserService;
-        this.paperService = paperService;
-        this.examineeService = examineeService;
-        this.marjorPlanService = marjorPlanService;
+    }
+
+    @GetMapping("/subjects")
+    @ApiOperation("科目列表")
+    public AjaxResult subjects()
+    {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        List<DzSubject> list = dzSubjectService.selectDzSubjectBySubjectIds(ExamType.OHS.equals(sysUser.getExamType()) ? new Long[] {1L, 2L, 3L, 11L} : new Long[] {11L});
+        JSONObject evalCounts = JSONObject.parseObject(sysUserService.selectUserById(sysUser.getUserId()).getEvalCounts());
+        return AjaxResult.success(list.stream().map(t -> {
+            JSONObject o = new JSONObject();
+            o.put("subjectId", t.getSubjectId());
+            o.put("subject", t.getSubjectName());
+            o.put("examTime", evalCounts.getIntValue(t.getSubjectId().toString()));
+            return o;
+        }).collect(Collectors.toList()));
     }
 
     // 试卷: 真题卷,批次测试卷,自组卷, 生成考试记录
     // 定向模拟卷: 根据用户选择生成记录
     // 组卷: 知识点 错题 必刷题 实时组卷后才生成记录
     @ApiOperation("01 开卷")
-    @GetMapping(value = "openExaminee")
-    public AjaxResult openExamineePaper(@ApiParam("考卷类型PaperType") PaperType paperType,
-                                        @ApiParam("考卷类型关联ID") Long relateId,
-                                        @RequestBody JSONObject param) {
-        LearnPaper paper = paperService.selectLearnPaperById(relateId);
-        LearnExaminee examinee = new LearnExaminee();
-        examinee.setStudentId(SecurityUtils.getLoginUser().getUser().getUserId());
-        examinee.setPaperType(paperType.getVal());
-        if(!paperTypeSet.contains(paperType)) {
-            if(PaperType.Simulated.equals(paperType)) { // 检查是否超过最大值从顺序选择未做过的考卷来
-                return AjaxResult.success(openSimulatedPaper(examinee, param));
-            } else { // 根据几种场景实时组卷,然后返回PaperId
-                return AjaxResult.success(openCustomPaper(paperType, examinee, param));
-            }
+    @PostMapping(value = "openExaminee")
+    public AjaxResult openExaminee(@ApiParam("0默认1价值2职业3知识") @RequestParam(required = false) Integer testType,
+                                   @ApiParam("定向") @RequestParam(defaultValue = "false") boolean directed,
+                                   @ApiParam("考卷类型PaperType") @RequestParam(required = false) PaperType paperType,
+                                   @ApiParam("考卷类型关联ID") @RequestParam(required = false) Long relateId,
+                                   @ApiParam("考卷类型关联ID") @RequestParam(required = false) Long subjectId,
+                                   @RequestBody JSONObject body) {
+        if(null != testType && testType > 0) {
+            PaperDto paperDto = syTestMajorService.loadPaperByTestMajorTypeId(testType);
+            Map<String, String> stateMap = cacheService.selectDictDataMapByType(MxjbContants.StateTypeExaminee);
+            paperDto.setStateStr(stateMap.get(String.valueOf(paperDto.getState())));
+            paperDto.calculateAllow();
+            return AjaxResult.success(paperDto);
+        }
+        if(null == subjectId && null == paperType) {
+            subjectId = body.getLongValue("subjectId");
+            paperType = PaperType.valueOf(body.getString("paperType"));
         }
-        examinee.setPaperKey(paperType.name() + "_" + paper.getId());
-        return AjaxResult.success(newExamineePcondaper(examinee, paper, param));
-    }
 
-    private LearnExaminee newExamineePcondaper(LearnExaminee examinee, LearnPaper paper, JSONObject param) {
-        examinee.setPaperId(paper.getId());
-        examinee.setParams(param);
-        examinee.setState(ExamineeStatus.Exam.getVal());
-        examinee.setBeginTime(DateUtils.getNowDate());
-        examineeService.insertLearnExaminee(examinee);
-        return examinee;
+        return AjaxResult.success(examService.openExaminee(directed, paperType, relateId, subjectId));
     }
 
-    private LearnExaminee openCustomPaper(PaperType paperType, LearnExaminee examinee, JSONObject param) {
-        Long knowledgeId = param.getLong("knowledgeId");
-        examinee.setPaperKey(paperType.name() + knowledgeId);
-        examinee.setState(ExamineeStatus.Exam.getVal());
-        List<LearnExaminee> examineeList = examineeService.selectLearnExamineeList(examinee);
-        if(CollectionUtils.isNotEmpty(examineeList)) {
-            return examineeList.get(0);
-        }
-        // 全局配置模板参数或知识点级的参数
-        LearnPaper paper = paperService.selectLearnPaperById(0L);
-        if(null == paper) {
-            throw new ValidationException("未组卷");
-        }
-        return newExamineePcondaper(examinee, paper, param);
+    @ApiOperation("04 开始考试")
+    @GetMapping(value = "beginExaminee")
+    public AjaxResult beginExaminee(@ApiParam("答卷ID") Long examineeId) {
+        // 检查状态,以决定是否返回答案
+        return AjaxResult.success(examService.loadExaminee(examineeId, true));
     }
 
-    private LearnExaminee openSimulatedPaper(LearnExaminee examinee, JSONObject param) {
-        List<LearnExaminee> examineeList = examineeService.selectLearnExamineeList(examinee);
-        Set<Long> existPaperIdSet = Sets.newHashSet();
-        for(LearnExaminee e : examineeList) {
-            if(ExamineeStatus.Exam.getVal().equals(e.getState())) {
-                return e;
-            }
-            existPaperIdSet.add(e.getPaperId());
-        }
-        Long id = param.getLong("id");
-        AMarjorPlan plan = marjorPlanService.selectAMarjorPlanById(id);
-        if(null == plan) {
-            throw new ValidationException("专业id无效");
+    @ApiOperation("04 取答卷")
+    @GetMapping(value = "loadExaminee")
+    public AjaxResult loadExaminee(@ApiParam("答卷ID") @RequestParam(required = false) Long examineeId, @RequestBody JSONObject body) {
+        // 检查状态,以决定是否返回答案
+        if(null == examineeId) {
+            examineeId = body.getLongValue("examineeId");
         }
-        LearnPaper paper = getBestPaper(plan, existPaperIdSet);
-        examinee.setPaperKey(PaperType.Simulated.name() + "_" + paper.getId());
-        return newExamineePcondaper(examinee, paper, param);
+        return AjaxResult.success(examService.loadExaminee(examineeId, false));
     }
 
-    private LearnPaper getBestPaper(AMarjorPlan plan, Set<Long> existPaperIdSet) {
-        String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
-        LearnPaper paperCond = new LearnPaper();
-        paperCond.setPaperType(PaperType.Simulated.name());
-        for(int i = 3; i>0; i--) {
-            if(i == 3) {
-                paperCond.setDirectKey(plan.getUniversityId() + "_" + groupName + "_" + plan.getMajorName());
-            } else if(i == 2) {
-                paperCond.setDirectKey(plan.getUniversityId() + "_" + groupName);
-            } else if(StringUtils.isBlank(groupName)) {
-                break;
-            }
-            List<LearnPaper> paperList = paperService.selectLearnPaperList(paperCond);
-            for(LearnPaper paper : paperList) {
-                if(existPaperIdSet.add(paper.getId())) {
-                    return paper;
-                }
-            }
+    @ApiOperation("02 交卷")
+    @PostMapping(value = "commitExamineePaper")
+    public AjaxResult commitExamineePaper(@RequestBody AnswerSheet answerSheet) {
+        if(null == answerSheet.getIsDone()) {
+            answerSheet.setIsDone(false);
         }
-        throw new ValidationException("未初始化院校定向模拟题库: " + plan.getId());
+        examService.commitAnswerSheet(answerSheet);
+        return AjaxResult.success();
     }
 
-    @ApiOperation("04 取答案")
-    @GetMapping(value = "answerExaminee")
-    public AjaxResult loadAnswers(@ApiParam("答卷ID") Long examineeId) {
-        // 检查状态,以决定是否返回答案
-        return AjaxResult.success();
+    @ApiOperation("02 原交卷,测试")
+    @PostMapping(value = "commitTestPaper")
+    public AjaxResult commitExamineePaper(@RequestBody PaperDto paperDto) {
+        return syTestMajorService.saveTestPaper(paperDto);
     }
 
     /*@ApiOperation("02 答题")
@@ -147,14 +125,6 @@ public class FrontExamController {
         return adminExaminationService.saveQuestion(paperDto);
     }
 
-    @ApiOperation("02 交卷")
-    @PostMapping(value = "commitExamineePaper")
-    public AjaxResult commitExamineePaper(@RequestBody PaperDto paperDto) {
-        if (MxjbContants.ExamineeTypeIeValue.equals(paperDto.getExamineeType())) {
-            return mxjbPaperExamService.saveExamPaper(paperDto);
-        }
-        return syTestMajorService.saveTestPaper(paperDto);
-    }
 
     @ApiOperation("04 阅卷打分")
     @PostMapping(value = "scoreExamineeQuestions")

+ 55 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontFavoritesController.java

@@ -0,0 +1,55 @@
+package com.ruoyi.web.controller.front;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.learn.domain.LearnPaper;
+import com.ruoyi.learn.domain.LearnQuestions;
+import com.ruoyi.learn.service.ILearnPaperService;
+import com.ruoyi.learn.service.ILearnQuestionsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Api(tags = "前端V2 收藏")
+@RestController
+@RequestMapping("front/favorites")
+public class FrontFavoritesController extends BaseController {
+    @Autowired
+    private ILearnPaperService papersService;
+    @Autowired
+    private ILearnQuestionsService questionsService;
+
+    @ApiOperation("收藏的试卷列表")
+    @GetMapping("papers")
+    public TableDataInfo papers(Long subjectId) {
+        Long userId = SecurityUtils.getLoginUser().getUserId();
+        startPage();
+        LearnPaper papers = new LearnPaper();
+        papers.setSubjectId(subjectId);
+        papers.setExamineeId(userId);
+        papers.setCollect(true);
+        List<LearnPaper> list = papersService.selectPapersListFromFavorites(papers);
+        list.stream().forEach(t -> t.setCollect(true));
+        return getDataTable(list);
+    }
+
+    @ApiOperation("收藏的问题列表")
+    @GetMapping("questions")
+    public TableDataInfo questions(Long subjectId) {
+        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
+        startPage();
+        LearnQuestions questions = new LearnQuestions();
+        questions.setSubjectId(subjectId);
+        questions.setUserId(userId);
+        List<LearnQuestions> list = questionsService.selectCollectedList(questions);
+        questionsService.fillCollectInfo(SecurityUtils.getLoginUser().getUser().getUserId(), list);
+        return getDataTable(list);
+    }
+
+}

+ 2 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontNewsController.java

@@ -1,6 +1,7 @@
 package com.ruoyi.web.controller.front;
 
 import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.content.VistorContextHolder;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -145,6 +146,7 @@ public class FrontNewsController extends BaseController {
 
     @GetMapping("info")
     @ApiOperation("02 资讯详情")
+    @Anonymous
     public AjaxResult videoInfo(@ApiParam("课程id") Long id) {
         saveClicked(id);
         BWwwNews info = newsService.selectBWwwNewsById(id);

+ 58 - 20
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontPaperController.java

@@ -1,28 +1,35 @@
 package com.ruoyi.web.controller.front;
 
-import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.common.core.content.VistorContextHolder;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.AjaxResult2;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzControl;
 import com.ruoyi.dz.domain.DzSubject;
 import com.ruoyi.dz.service.IDzControlService;
 import com.ruoyi.dz.service.IDzSubjectService;
 import com.ruoyi.enums.PaperType;
-import com.ruoyi.learn.domain.LearnPaper;
+import com.ruoyi.learn.domain.LearnStudent;
 import com.ruoyi.learn.domain.LearnTest;
-import com.ruoyi.learn.service.ILearnPaperQuestionService;
-import com.ruoyi.learn.service.ILearnPaperService;
+import com.ruoyi.learn.service.ILearnStudentService;
 import com.ruoyi.learn.service.ILearnTestService;
 import com.ruoyi.web.service.LearnTeacherService;
+import com.ruoyi.web.service.PaperService;
+import com.ruoyi.web.service.StudentService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import org.apache.commons.compress.utils.Lists;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/front/paper")
@@ -30,18 +37,20 @@ import java.util.List;
 public class FrontPaperController {
     private final IDzControlService dzControlService;
     private final IDzSubjectService dzSubjectService;
+    private final PaperService paperService;
     private final ILearnTestService testService;
-    private final ILearnPaperService paperService;
+    private final ILearnStudentService learnStudentService;
     private final LearnTeacherService learnTeacherService;
-    private final ILearnPaperQuestionService paperQuestionService;
+    private final StudentService studentService;
 
-    public FrontPaperController(IDzControlService dzControlService, IDzSubjectService dzSubjectService, ILearnTestService testService, ILearnPaperService paperService, LearnTeacherService learnTeacherService, ILearnPaperQuestionService paperQuestionService) {
+    public FrontPaperController(IDzControlService dzControlService, IDzSubjectService dzSubjectService, PaperService paperService, ILearnTestService testService, ILearnStudentService learnStudentService, LearnTeacherService learnTeacherService, StudentService studentService) {
         this.dzControlService = dzControlService;
         this.dzSubjectService = dzSubjectService;
-        this.testService = testService;
         this.paperService = paperService;
+        this.testService = testService;
+        this.learnStudentService = learnStudentService;
         this.learnTeacherService = learnTeacherService;
-        this.paperQuestionService = paperQuestionService;
+        this.studentService = studentService;
     }
 
     @ApiOperation("01 考试批次")
@@ -55,24 +64,53 @@ public class FrontPaperController {
 
     @ApiOperation("02 考试科目")
     @GetMapping(value = "subject")
-    public AjaxResult2<List<DzSubject>> getSubject() {
-        DzSubject sCond = new DzSubject();
-        List<DzSubject> list = dzSubjectService.selectDzSubjectList(sCond);
-        return AjaxResult2.success(list);
+    public AjaxResult2<List<DzSubject>> getSubject(@ApiParam("定向") @RequestParam(defaultValue = "false") boolean directed) {
+        return AjaxResult2.success(studentService.getSubjectList(directed));
     }
 
     @ApiOperation("03 知识点树")
-    @GetMapping(value = "knownledge")
-    public AjaxResult getKnownledge(@ApiParam("科目ID") Long subjectId) {
-        return AjaxResult.success(learnTeacherService.getKnowledgeTree(subjectId, null));
+    @GetMapping(value = "knowledge")
+    public AjaxResult getKnowledge(@ApiParam("定向") @RequestParam(defaultValue = "false") boolean directed, @ApiParam("科目ID") Long subjectId) {
+        Set<Long> knowledgeIdSet = null;
+        if(directed) {
+            LearnStudent learnStudent = learnStudentService.selectLearnStudentByStudentId(SecurityUtils.getUserId());
+            if(null != learnStudent) {
+                knowledgeIdSet = learnTeacherService.getKnowledgeIdSet(new Long[] { learnStudent.getMajorPlanId() });
+            }
+        }
+        List<LearnTeacherService.TreeNode> nodeList = learnTeacherService.getKnowledgeTree(subjectId, knowledgeIdSet, true);
+        if(CollectionUtils.isEmpty(knowledgeIdSet)) {
+            return AjaxResult.success(nodeList);
+        }
+        List<LearnTeacherService.TreeNode> finalList = Lists.newArrayList();
+        for(LearnTeacherService.TreeNode parent : nodeList) {
+            if(CollectionUtils.isEmpty(parent.getChildren())) {
+                if(parent.getQuestionCount() > 0 && parent.getStatus().equals(1)) {
+                    finalList.add(parent);
+                }
+                continue;
+            }
+            parent.setQuestionCount(0);
+            parent.setFinishedCount(0);
+            List<LearnTeacherService.TreeNode> childList = Lists.newArrayList();
+            for(LearnTeacherService.TreeNode child : parent.getChildren()) {
+                if(child.getStatus().equals(1)) { // TODO 暂不过滤无题的  && child.getQuestionCount() > 0
+                    childList.add(child);
+                    parent.setQuestionCount(parent.getQuestionCount() + child.getQuestionCount());
+                    parent.setFinishedCount(parent.getFinishedCount() + child.getFinishedCount());
+                }
+            }
+            if(childList.size() > 0) {
+                parent.setChildren(childList);
+                finalList.add(parent);
+            }
+        }
+        return AjaxResult.success(finalList);
     }
 
     @ApiOperation("04 取试卷")
     @GetMapping(value = "paper")
     public AjaxResult loadPaper(@ApiParam("考卷类型PaperType") PaperType type, @ApiParam("考卷标识") Long id) {
-        LearnPaper paper = paperService.selectLearnPaperById(id);
-        JSONObject root = JSONObject.from(paper);
-        root.put("questions", paperQuestionService.selectLearnPaperQuestionById(id));
-        return AjaxResult.success(root);
+        return AjaxResult.success(paperService.loadPaper(id));
     }
 }

+ 40 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontQuestionsController.java

@@ -0,0 +1,40 @@
+package com.ruoyi.web.controller.front;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.mingxue.domain.QuestionCollection;
+import com.ruoyi.mingxue.service.IQuestionCollectionService;
+import io.lettuce.core.dynamic.annotation.Param;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "前端V2 题库")
+@RestController
+@RequestMapping("front/questions")
+public class FrontQuestionsController extends BaseController {
+    @Autowired
+    private IQuestionCollectionService questionCollectionService;
+
+    @ApiOperation("收藏试题")
+    @PostMapping("collect")
+    public AjaxResult collect(Long questionId) {
+        QuestionCollection questionCollection = new QuestionCollection();
+        questionCollection.setUserId(SecurityUtils.getLoginUser().getUser().getUserId());
+        questionCollection.setQuestionId(questionId);
+        if(CollectionUtils.isEmpty(questionCollectionService.selectQuestionCollectionList(questionCollection))) {
+            return AjaxResult.success(questionCollectionService.insertQuestionCollection(questionCollection));
+        }
+        return AjaxResult.success(1);
+    }
+
+    @ApiOperation("取消收藏试题")
+    @PostMapping("cancelCollect")
+    public AjaxResult cancelQuestionCollection(Long questionId) {
+        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
+        return AjaxResult.success(questionCollectionService.deleteQuestionCollectionById(questionId, userId));
+    }
+}

+ 155 - 76
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontStudentController.java

@@ -1,28 +1,39 @@
 package com.ruoyi.web.controller.front;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.lang.Dict;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.google.common.collect.Lists;
 import com.ruoyi.common.core.content.VistorContextHolder;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.AjaxResult2;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.ExamType;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzControl;
+import com.ruoyi.dz.domain.DzSubject;
 import com.ruoyi.dz.service.IDzControlService;
+import com.ruoyi.dz.service.IDzSubjectService;
+import com.ruoyi.enums.PaperType;
 import com.ruoyi.ie.domain.AEnrollUniversity;
 import com.ruoyi.ie.domain.AMarjorPlan;
 import com.ruoyi.ie.service.IAEnrollUniversityService;
 import com.ruoyi.ie.service.IAMarjorPlanService;
-import com.ruoyi.learn.domain.LearnDirectedKnowledge;
-import com.ruoyi.learn.domain.LearnPlan;
-import com.ruoyi.learn.domain.LearnPlanStudy;
+import com.ruoyi.learn.domain.*;
 import com.ruoyi.learn.service.ILearnDirectedKnowledgeService;
+import com.ruoyi.learn.service.ILearnExamineeService;
 import com.ruoyi.learn.service.ILearnPlanService;
 import com.ruoyi.learn.service.ILearnPlanStudyService;
 import com.ruoyi.sy.service.ISyMajorService;
 import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.web.service.ExamService;
+import com.ruoyi.web.service.LearnStatService;
+import com.ruoyi.web.service.LearnTeacherService;
+import com.ruoyi.web.service.StudentService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -33,7 +44,6 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.validation.ValidationException;
 import java.text.ParseException;
-import java.time.LocalDate;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
@@ -51,9 +61,15 @@ public class FrontStudentController extends BaseController {
     private final ILearnPlanStudyService learnPlanStudyService;
     private final IAMarjorPlanService marjorPlanService;
     private final ILearnDirectedKnowledgeService learnDirectedKnowledgeService;
+    private final StudentService studentService;
+    private final IDzSubjectService dzSubjectService;
+    private final LearnTeacherService learnTeacherService;
+    private final LearnStatService learnStatService;
+    private final ExamService examService;
+    private final ILearnExamineeService learnExamineeService;
 
     public FrontStudentController(IDzControlService dzControlService, IAEnrollUniversityService universityService, ISyMajorService syMajorService, ISysUserService sysUserService,
-                                  ILearnPlanService learnPlanService, ILearnPlanStudyService learnPlanStudyService, IAMarjorPlanService marjorPlanService, ILearnDirectedKnowledgeService learnDirectedKnowledgeService) {
+                                  ILearnPlanService learnPlanService, ILearnPlanStudyService learnPlanStudyService, IAMarjorPlanService marjorPlanService, ILearnDirectedKnowledgeService learnDirectedKnowledgeService, StudentService studentService, IDzSubjectService dzSubjectService, LearnTeacherService learnTeacherService, LearnStatService learnStatService, ExamService examService, ILearnExamineeService learnExamineeService) {
         this.dzControlService = dzControlService;
         this.universityService = universityService;
         this.syMajorService = syMajorService;
@@ -62,6 +78,39 @@ public class FrontStudentController extends BaseController {
         this.learnPlanStudyService = learnPlanStudyService;
         this.marjorPlanService = marjorPlanService;
         this.learnDirectedKnowledgeService = learnDirectedKnowledgeService;
+        this.studentService = studentService;
+        this.dzSubjectService = dzSubjectService;
+        this.learnTeacherService = learnTeacherService;
+        this.learnStatService = learnStatService;
+        this.examService = examService;
+        this.learnExamineeService = learnExamineeService;
+    }
+
+    @ApiOperation("02 全部科目")
+    @GetMapping(value = "subject")
+    public AjaxResult2<List<DzSubject>> getSubject(@ApiParam("定向") @RequestParam(defaultValue = "false") boolean directed) {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        DzSubject sCond = new DzSubject();
+        sCond.setLocations(sysUser.getLocation());
+        sCond.setExamTypes(sysUser.getExamType().name());
+        List<DzSubject> list = dzSubjectService.selectDzSubjectList(sCond);
+        return AjaxResult2.success(list);
+    }
+
+    @ApiOperation("试卷作业统计")
+    @GetMapping("paperStats")
+    public AjaxResult paperStats()
+    {
+        return AjaxResult.success(studentService.selectStatsForStudent(SecurityUtils.getUserId()));
+    }
+
+    @ApiOperation("试卷作业列表")
+    @GetMapping("papers")
+    public TableDataInfo papers(@ApiParam("status=1未做3正做4完成") Integer status)
+    {
+        startPage();
+        List<LearnPaper> list = studentService.selectLearnPaperForStudent(SecurityUtils.getUserId(), status);
+        return getDataTable(list);
     }
 
     @ApiOperation("01 计划院校列表")
@@ -83,7 +132,7 @@ public class FrontStudentController extends BaseController {
 
     @ApiOperation("30 计划院校专业信息")
     @GetMapping("university/plan")
-    public AjaxResult getuniversityPlan(@ApiParam("专业") Long planId) {
+    public AjaxResult getUniversityPlan(@ApiParam("专业") Long planId) {
         AMarjorPlan mp = marjorPlanService.selectAMarjorPlanById(planId);
         LearnDirectedKnowledge cond = new LearnDirectedKnowledge();
         String groupName = StringUtils.trimToEmpty(mp.getMajorGroup());
@@ -103,6 +152,21 @@ public class FrontStudentController extends BaseController {
         return CollectionUtils.isNotEmpty(result) ? AjaxResult.success(result.get(0)) : AjaxResult.error("无计划");
     }
 
+    @ApiOperation("03 查询学生定向院校")
+    @GetMapping(value = "simulated/info")
+    public AjaxResult getSimulateInfo() {
+        JSONObject result = new JSONObject();
+        SysUser user = SecurityUtils.getLoginUser().getUser();
+        if(ExamType.OHS.name().equals(user.getUserType())) {
+            result.put("subjects", Lists.newArrayList("职业技能", "语数外"));
+        } else {
+            result.put("subjects", Lists.newArrayList("职业技能"));
+        }
+        SysUser sysUser = sysUserService.selectUserById(SecurityUtils.getUserId());
+        result.put("evalCount", sysUser.getEvalCounts());
+        return AjaxResult.success(result);
+    }
+
     @ApiOperation("03 查询学生定向院校")
     @GetMapping(value = "directed/school")
     public AjaxResult getDirectionSchools() {
@@ -115,6 +179,12 @@ public class FrontStudentController extends BaseController {
     public AjaxResult saveStudySchool(@RequestBody JSONArray directionStudy) {
         SysUser user = new SysUser();
         user.setUserId(SecurityUtils.getLoginUser().getUserId());
+        if(!directionStudy.isEmpty()) {
+            JSONObject directed = directionStudy.getJSONObject(0);
+            Long planId = directed.getLongValue("majorId");
+            Long universityId = directed.getLongValue("universityId");
+            directed.put("notice", learnTeacherService.updateDirected(user.getUserId(), universityId, planId));
+        }
         user.setDirectedStudy(directionStudy.toJSONString());
         return sysUserService.updateUserProfile(user) > 0 ? AjaxResult.success("更新成功") : AjaxResult.success("无更新");
     }
@@ -123,31 +193,34 @@ public class FrontStudentController extends BaseController {
     @GetMapping("plan")
     public AjaxResult getPlan()
     {
-        return AjaxResult.success(getCurrLearnPlan());
+        return AjaxResult.success(learnPlanService.getCurrLearnPlan());
     }
 
     @ApiOperation("11 保存学习计划")
     @PostMapping("plan")
     public AjaxResult savePlan(@RequestBody LearnPlan plan)
     {
-        plan.setStudentId(SecurityUtils.getLoginUser().getUserId());
-
-        LearnPlan curr = getCurrLearnPlan();
+        LearnPlan curr = learnPlanService.getCurrLearnPlan();
         Date today = DateUtils.truncate(new Date(), Calendar.DATE);
-        if(curr.getStatus().equals(1)) {
-            if(DateUtils.isSameDay(curr.getBeginTime(), today)) { // 当天的更新计划
-                curr.setVideoTime(plan.getVideoTime());
-                curr.setQuestionCnt(plan.getQuestionCnt());
-            } else {
-                curr.setStatus(0);
+        if(null != curr) {
+            if(curr.getStatus().equals(1)) {
+                if(DateUtils.isSameDay(curr.getBeginTime(), today)) { // 当天的更新计划
+                    curr.setVideoTime(plan.getVideoTime());
+                    curr.setQuestionCnt(plan.getQuestionCnt());
+                } else {
+                    curr.setStatus(0);
+                }
+                curr.setStudentId(null);
+                curr.setBeginTime(null);
+                curr.setStats(null);
+                plan.setFirstTime(curr.getFirstTime());
+                learnPlanService.updateLearnPlan(curr);
             }
-            curr.setStudentId(null);
-            curr.setBeginTime(null);
-            curr.setStats(null);
-            learnPlanService.updateLearnPlan(plan);
-        }
-        if(curr.getStatus().equals(1)) {
-            return AjaxResult.success();
+            if(curr.getStatus().equals(1)) {
+                return AjaxResult.success();
+            }
+        } else {
+            plan.setFirstTime(today);
         }
         plan.setStudentId(SecurityUtils.getLoginUser().getUserId());
         plan.setBeginTime(today);
@@ -160,7 +233,10 @@ public class FrontStudentController extends BaseController {
     @GetMapping("plan/stats")
     public AjaxResult getPlanStats(@ApiParam String reportMonth)
     {
-        LearnPlan curr = getCurrLearnPlan();
+        LearnPlan curr = learnPlanService.getCurrLearnPlan();
+        if(null == curr) {
+            throw new RuntimeException("未配置学习计划");
+        }
         // 统计每天的学习情况, 再汇总
         LearnPlanStudy cond = new LearnPlanStudy();
         cond.setStudentId(SecurityUtils.getLoginUser().getUserId());
@@ -169,45 +245,78 @@ public class FrontStudentController extends BaseController {
         } catch (ParseException e) {
             throw new ValidationException("日期格式错误");
         }
-        List<LearnPlanStudy> studyList = learnPlanStudyService.selectLearnPlanStudyList(cond);
+        List<LearnPlanStudy> studyList = learnPlanStudyService.selectPlanStudyStats(cond);
         Integer doneDay = 0;
         Long videoTimes = 0L;
         Long questionCnt = 0L;
-        Date today = new Date();
-        LearnPlanStudy todayStudy = null;
+        List<Dict> monthStudyList = Lists.newArrayList();
+        Date firstDate = null;
+        JSONObject today = null;
         for(LearnPlanStudy study : studyList) {
-            if(!study.getReportDate().before(today)) { // 今天
-                doneDay++;
+            JSONObject planStudy = toStudyStats(study);
+            if(DateUtils.isSameDay(cond.getReportDate(), study.getReportDate())) {
+                today = planStudy;
+            }
+            boolean videoCompleted;
+            if(null != study.getVideoCount()) {
+                videoCompleted = study.getVideoCount() >= study.getVideoPlan();
+            } else {
+                videoCompleted = study.getVideoPlan().equals(0);
+            }
+            if(null != study.getVideoTime()) {
                 videoTimes += study.getVideoTime();
-                questionCnt = study.getQuestionCount();
+            }
+            boolean questionCompleted;
+            if(null != study.getQuestionCount()) {
+                questionCnt += study.getQuestionCount();
+                questionCompleted = study.getQuestionCount() >= study.getQuestionPlan();
             } else {
-                todayStudy = study;
+                questionCompleted = study.getQuestionPlan().equals(0);
+            }
+            if(videoCompleted && questionCompleted) {
+                doneDay++;
+            }
+            if(null == firstDate) {
+                firstDate = study.getReportDate();
             }
+            monthStudyList.add(Dict.create().set("date", DateUtil.format(study.getReportDate(), "yyyy-MM-dd")).set("data", planStudy));
         }
 
-        Integer preDay = 0;
-        if(DateUtils.isSameDay(cond.getReportDate(), DateUtils.truncate(curr.getBeginTime(), Calendar.MONTH))) {
+        Integer preDay = 0; // 同月的排队计划开始之前的 历史月不好计算,待定 TODO MF
+        Date month = DateUtils.truncate(cond.getReportDate(), Calendar.MONTH);
+        if(DateUtils.isSameDay(month, DateUtils.truncate(curr.getFirstTime(), Calendar.MONTH))) {
             Calendar cal = Calendar.getInstance();
-            cal.setTime(curr.getBeginTime());
+            cal.setTime(curr.getFirstTime());
             preDay = cal.get(Calendar.DAY_OF_MONTH) - 1;
         }
         JSONObject stats = new JSONObject();
+        stats.put("firstDay", DateUtil.format(curr.getFirstTime(), "yyyy-MM-dd"));
+        stats.put("preDay", preDay);
         stats.put("doneDay", doneDay);
-        stats.put("unDoneDay", LocalDate.now().getDayOfMonth() - doneDay - preDay);
+        stats.put("undoneDay", LocalDateTimeUtil.of(DateUtils.addDays(DateUtils.addMonths(month, 1), -1)).getDayOfMonth() - doneDay - preDay);
         stats.put("videoTimes", videoTimes);
         stats.put("questionCnt", questionCnt);
-        stats.put("studyList", studyList);
-        stats.put("today", todayStudy);
-        stats.put("plan", curr);
+        stats.put("studyList", monthStudyList);
+        stats.put("today", today);
         return AjaxResult.success(stats);
     }
 
+    private JSONObject toStudyStats(LearnPlanStudy todayStudy) {
+        JSONObject study = new JSONObject();
+        study.put("questionPlan", todayStudy.getQuestionPlan());
+        study.put("videoPlan", todayStudy.getVideoPlan());
+        study.put("questionCnt", todayStudy.getQuestionCount());
+        study.put("videoTime", todayStudy.getVideoCount());
+        study.put("videoTimes", todayStudy.getVideoTime());
+        study.put("rightRate", Math.round((float)(todayStudy.getRightCount() * 100 / todayStudy.getQuestionCount())));
+        return study;
+    }
+
     @ApiOperation("50 记录-知识点统计")
     @GetMapping("record/knowledge")
     public TableDataInfo getRecordKnowledge()
     {
-        JSONObject data = JSONObject.of("name", "", "directed", true, "rate", 82, "total", 1470);
-        List<JSONObject> list = Lists.newArrayList(data);
+        List<JSONObject> list = learnStatService.getRecordKnowledge(SecurityUtils.getUserId());
         return getDataTable(list);
     }
 
@@ -215,16 +324,15 @@ public class FrontStudentController extends BaseController {
     @GetMapping("record/simulated")
     public AjaxResult getRecordSimulated()
     {
-        JSONObject data = JSONObject.of("name", "考试", "date", "2025-09-26 15:46", "total", 300, "score", 80, "rate", 82);
-        List<JSONObject> list = Lists.newArrayList(data);
-        return AjaxResult.success(list);
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        return AjaxResult.success(studentService.getSimulateList(sysUser.getUserId()));
     }
 
     @ApiOperation("52 记录-模拟卷结果")
     @GetMapping("record/simulated/{recordId}")
     public AjaxResult getSimulatedDetail(@ApiParam("记录ID") @PathVariable("recordId") Long recordId)
     {
-        return AjaxResult.success();
+        return AjaxResult.success(examService.loadExaminee(recordId, false));
     }
 
     @ApiOperation("53 记录-测试卷")
@@ -245,44 +353,15 @@ public class FrontStudentController extends BaseController {
 
     @ApiOperation("55 记录-计划刷题")
     @GetMapping("record/planStudy")
-    public AjaxResult getRecordPlanStudy(Integer year, Integer monthSeq, Integer weekSeq)
+    public AjaxResult getRecordPlanStudy(@ApiParam Integer year, @ApiParam @RequestParam(required = false) Long month)
     {
-        JSONObject result = new JSONObject();
-        result.put("total", 140);
-        result.put("rate", 85);
-        result.put("studyDays", 3);
-        JSONObject data = JSONObject.of("date", "2025.09.03", "study", "90", "rate", "80", "pass", true);
-        List<JSONObject> list = Lists.newArrayList(data);
-        result.put("list", list);
-        return AjaxResult.success(result);
+        return AjaxResult.success(learnStatService.getRecordPlanStudy(SecurityUtils.getUserId(), year, month));
     }
 
     @ApiOperation("56 记录-视频学习")
     @GetMapping("record/video")
     public AjaxResult getRecordVideo()
     {
-        JSONObject result = new JSONObject();
-        result.put("total", 98);
-        result.put("study", 759);
-        JSONObject data = JSONObject.of("name", "充分条件", "date", "2025.09.11", "study", "02:46");
-        List<JSONObject> list = Lists.newArrayList(data);
-        result.put("list", list);
-        return AjaxResult.success(result);
+        return AjaxResult.success(learnStatService.getRecordVideo(SecurityUtils.getUserId()));
     }
-
-    private LearnPlan getCurrLearnPlan() {
-        LearnPlan cond = new LearnPlan();
-        cond.setStudentId(SecurityUtils.getLoginUser().getUserId());
-        cond.setStatus(1);
-        List<LearnPlan> planList = learnPlanService.selectLearnPlanList(cond);
-        if(CollectionUtils.isNotEmpty(planList)) {
-            return planList.get(0);
-        }
-        cond.setQuestionCnt(90);
-        cond.setVideoTime(5);
-        cond.setStatus(0);
-        cond.setBeginTime(DateUtils.truncate(new Date(), Calendar.DATE));
-        return cond;
-    }
-
 }

+ 3 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontSyMajorRelationController.java

@@ -1,6 +1,7 @@
 package com.ruoyi.web.controller.front;
 
 
+import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysUser;
@@ -202,9 +203,10 @@ public class FrontSyMajorRelationController extends BaseController {
      */
     @GetMapping("getAllMajor")
     @ApiOperation("00 获取所有专业(三级树形结构)")
+    @Anonymous
     public AjaxResult getAllMajor(String type,Integer level,String parentCode,Integer batch) {
         SyMajor query = new SyMajor().setLevel(level).setParentCode(parentCode);
-        SysUser user = SecurityUtils.getLoginUser().getUser();
+        SysUser user = VistorContextHolder.getContext();
         if(StringUtils.isNotBlank(type)) {
             if("高职本科".equals(type)) {
                 query.setType("职教本科");

+ 4 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontSyVocationalController.java

@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.front;
 
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
@@ -48,6 +49,7 @@ public class FrontSyVocationalController extends BaseController {
 
     @GetMapping("getVocationalPosts")
     @ApiOperation("04 获取就业岗位")
+    @Anonymous
     public AjaxResult getVocationalPosts(@RequestParam(required = true) String code) {
         List<SyVocationalPost> vocationalPost = vocationalPostService.selectSyVocationalPostByVocationalCode(code);
         return AjaxResult.success(vocationalPost);
@@ -55,6 +57,7 @@ public class FrontSyVocationalController extends BaseController {
 
     @GetMapping("getVocationalOverview")
     @ApiOperation("03 获取职业概况")
+    @Anonymous
     public AjaxResult getVocationalOverview(@RequestParam String code) {
         SyVocationalOverview vocationalOverview = vocationalOverviewService.selectSyVocationalOverviewByCode(code);
         JSONArray marjorList = new JSONArray();
@@ -70,6 +73,7 @@ public class FrontSyVocationalController extends BaseController {
 
     @GetMapping("getAllVocation")
     @ApiOperation("02 获取所有职业(三级树形结构)")
+    @Anonymous
     public AjaxResult getAllVocation(String name,@ApiParam("层级")  Integer level) {
         SyVocational query = new SyVocational();
         if(StringUtils.isNotBlank(name)){

+ 1 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontUniversitiesController.java

@@ -1,6 +1,7 @@
 package com.ruoyi.web.controller.front;
 
 import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysDictData;

+ 53 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontWrongBookController.java

@@ -0,0 +1,53 @@
+package com.ruoyi.web.controller.front;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.learn.service.ILearnWrongBookService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "前端V2 错题本")
+@RestController
+@RequestMapping("front/v2/wrongBook")
+public class FrontWrongBookController extends BaseController {
+    private final ILearnWrongBookService wrongBookService;
+
+    public FrontWrongBookController(ILearnWrongBookService wrongBookService) {
+        this.wrongBookService = wrongBookService;
+    }
+
+    /**
+     *
+     * @return
+     */
+    @ApiOperation("错题涉及的学科")
+    @GetMapping("subjects")
+    public AjaxResult subjects() {
+        return AjaxResult.success(wrongBookService.findSubject());
+    }
+
+    @ApiOperation("错题列表")
+    @GetMapping("wrongQuestions")
+    public TableDataInfo wrongQuestions(@ApiParam("科目id") @RequestParam Long subjectId,
+                                        @ApiParam(value = "页数", example = "1") @RequestParam Integer pageNum,
+                                        @ApiParam(value = "页大小", example = "15") @RequestParam Integer pageSize) {
+        startPage();
+        return getDataTable(wrongBookService.findWrongQuestions(subjectId));
+    }
+
+    @ApiOperation("错题考卷列表")
+    @GetMapping("wrongExaminees")
+    public TableDataInfo wrongExaminees(@ApiParam("错题id") @RequestParam Long wrongId) {
+        return getDataTable(wrongBookService.findWrongExaminees(wrongId));
+    }
+
+    @ApiOperation("删除错题")
+    @PostMapping("deleteWrongQuestion")
+    public AjaxResult deleteWrongQuestion(Long questionId) {
+        return  AjaxResult.success(wrongBookService.deleteByQuestion(SecurityUtils.getLoginUser().getUserId(), questionId));
+    }
+}

+ 165 - 16
ie-admin/src/main/java/com/ruoyi/web/controller/front/UserController.java

@@ -2,24 +2,43 @@ package com.ruoyi.web.controller.front;
 
 import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.core.domain.model.LoginBody;
+import com.ruoyi.common.core.domain.model.LoginCard;
 import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.AccessFromType;
+import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.enums.ExamType;
+import com.ruoyi.common.utils.NumberUtils;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.domain.DzCards;
+import com.ruoyi.dz.domain.DzClasses;
 import com.ruoyi.dz.domain.DzControl;
+import com.ruoyi.dz.domain.DzSchool;
+import com.ruoyi.dz.mapper.DzClassesMapper;
+import com.ruoyi.dz.mapper.DzSchoolMapper;
+import com.ruoyi.dz.service.IDzCardsService;
+import com.ruoyi.dz.service.IDzClassesService;
 import com.ruoyi.dz.service.IDzControlService;
+import com.ruoyi.dz.service.IDzSchoolService;
+import com.ruoyi.enums.CardDistributeStatus;
+import com.ruoyi.enums.CardStatus;
+import com.ruoyi.enums.UserTypeEnum;
 import com.ruoyi.framework.web.service.SysPermissionService;
 import com.ruoyi.framework.web.service.TokenService;
 import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.web.service.CommService;
 import com.ruoyi.web.service.SysLoginService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
@@ -31,21 +50,33 @@ import java.util.stream.Collectors;
 @RestController
 @RequestMapping("/front/user")
 @Api(tags = "前台-用户")
-public class UserController {
+public class UserController extends BaseController {
     private final IDzControlService dzControlService;
     private final SysLoginService loginService;
+    private final ISysUserService userService;
     private final SysPermissionService permissionService;
     private final TokenService tokenService;
     private final CommService commService;
     private final ISysConfigService configService;
+    private final IDzCardsService dzCardsService;
+    private final IDzSchoolService dzSchoolService;
+    private final IDzClassesService dzClassesService;
+    private final DzSchoolMapper dzSchoolMapper;
+    private final DzClassesMapper dzClassesMapper;
 
-    public UserController(IDzControlService dzControlService, SysLoginService loginService, SysPermissionService permissionService, TokenService tokenService, CommService commService, ISysConfigService configService) {
+    public UserController(IDzControlService dzControlService, SysLoginService loginService, ISysUserService userService, SysPermissionService permissionService, TokenService tokenService, CommService commService, ISysConfigService configService, IDzCardsService dzCardsService, IDzSchoolService dzSchoolService, IDzClassesService dzClassesService, DzSchoolMapper dzSchoolMapper, DzClassesMapper dzClassesMapper) {
         this.dzControlService = dzControlService;
         this.loginService = loginService;
+        this.userService = userService;
         this.permissionService = permissionService;
         this.tokenService = tokenService;
         this.commService = commService;
         this.configService = configService;
+        this.dzCardsService = dzCardsService;
+        this.dzSchoolService = dzSchoolService;
+        this.dzClassesService = dzClassesService;
+        this.dzSchoolMapper = dzSchoolMapper;
+        this.dzClassesMapper = dzClassesMapper;
     }
 
     @GetMapping(value = "provinces")
@@ -108,28 +139,64 @@ public class UserController {
     @ApiOperation("毕业年份列表")
     public AjaxResult graduateYear(@RequestParam String location, @RequestParam ExamType examType)
     {
-        DzControl cond = new DzControl();
-        cond.setIsValid(1);
-        cond.setLocation(location);
-        List<DzControl> list = dzControlService.selectDzControlList(cond);
-        Integer year;
-        if(CollectionUtils.isEmpty(list) || null == (year = list.get(0).getSubmitYear())) {
-            year = Calendar.getInstance().get(Calendar.YEAR);
-        }
+//        DzControl cond = new DzControl();
+//        cond.setIsValid(1);
+//        cond.setLocation(location);
+//        List<DzControl> list = dzControlService.selectDzControlList(cond);
+//        Integer year;
+//        if(CollectionUtils.isEmpty(list) || null == (year = list.get(0).getSubmitYear())) {
+//            year = Calendar.getInstance().get(Calendar.YEAR);
+//        }
+//        List<JSONObject> resultList = new ArrayList<>();
+//        for(int i = year; i <= year + 6; i++) {
+//            JSONObject o = new JSONObject();
+//            o.put("dictValue", i);
+//            o.put("dictLabel", String.valueOf(i));
+//            resultList.add(o);
+//        }ss
+        int begin = 2026;
+        int end = ExamType.VHS.equals(examType) ? 2028 : 2026;
         List<JSONObject> resultList = new ArrayList<>();
-        for(int i = year; i <= year + 6; i++) {
+        for(int i = begin; i<= end; i++) {
             JSONObject o = new JSONObject();
-            o.put("dictValue", year);
-            o.put("dictLabel", year.toString());
+            o.put("dictValue", i);
+            o.put("dictLabel", String.valueOf(i));
             resultList.add(o);
         }
+
         return AjaxResult.success(resultList);
     }
 
+    @GetMapping("/getSchoolList")
+    @Anonymous
+    @ApiOperation("查询学校列表")
+    public TableDataInfo getSchoolList(@ApiParam("keyword") String keyword)
+    {
+        DzSchool dzSchool = new DzSchool();
+        dzSchool.setCampus(false);
+        dzSchool.setName(keyword);
+        startPage();
+        List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
+        return getDataTable(list);
+    }
+
+    @GetMapping("/getClassList")
+    @Anonymous
+    @ApiOperation("查询学校班级列表")
+    public AjaxResult getClassList(@ApiParam("schoolId") Long schoolId)
+    {
+        DzClasses cCond = new DzClasses();
+        cCond.setSchoolId(schoolId);
+        List<DzClasses> list = dzClassesService.selectDzClassesList(cCond);
+        return AjaxResult.success(list);
+    }
+
+
+
     /**
      * 登录方法
      *
-     * @param loginBody 登录信息
+     * @param loginBody 登录信息i
      * @return 结果
      */
     @PostMapping("userLogin")
@@ -138,7 +205,7 @@ public class UserController {
     public AjaxResult userLogin(@RequestBody LoginBody loginBody)
     {
         // 生成令牌
-        AjaxResult ajax = loginService.login(loginBody.getMobile(), loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid());
+        AjaxResult ajax = loginService.login(loginBody.getMobile(), loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid(),AccessFromType.frontApp.name());
         return ajax;
     }
 
@@ -179,4 +246,86 @@ public class UserController {
         }
         return ajax;
     }
+
+    /**
+     * 修改用户
+     */
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
+    @PutMapping("userInfo")
+    @Transactional(rollbackFor = Exception.class)
+    @ApiOperation("更新个人信息")
+    public AjaxResult updateProfile(@RequestBody SysUser user)
+    {
+        LoginUser loginUser = getLoginUser();
+        SysUser currentUser = loginUser.getUser();
+        boolean updateUserName = null != user.getPhonenumber() && !user.getPhonenumber().equals(currentUser.getPhonenumber()) && currentUser.getUserName().equals(currentUser.getPhonenumber());
+        currentUser.setNickName(user.getNickName());
+        currentUser.setEmail(user.getEmail());
+        currentUser.setPhonenumber(user.getPhonenumber());
+        currentUser.setSex(user.getSex());
+        currentUser.setScores(user.getScores());
+        if (com.ruoyi.common.utils.StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
+        {
+            return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
+        }
+        if (com.ruoyi.common.utils.StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
+        {
+            return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
+        }
+        if(NumberUtils.isPositive(user.getCardId())) {
+            DzCards dzCards = new DzCards();
+            dzCards.setCardId(user.getCardId());
+            dzCards.setSchoolId(user.getSchoolId());
+            dzCards.setClassId(user.getClassId());
+            dzCardsService.updateDzCards(dzCards);
+            LoginCard loginCard = loginUser.getCard();
+            if(null != user.getSchoolId() && !user.getSchoolId().equals(loginCard.getSchoolId())) {
+                loginCard.setSchoolId(user.getSchoolId());
+                loginCard.setSchoolName(dzSchoolMapper.selectDzSchoolById(user.getSchoolId()).getName());
+            }
+            if(null != user.getClassId() && !user.getClassId().equals(loginCard.getClassId())) {
+                loginCard.setClassId(user.getClassId());
+                loginCard.setClassName(dzClassesMapper.selectDzClassesByClassId(user.getClassId()).getName());
+            }
+        }
+        if(!UserTypeEnum.isCard(user.getUserType())) {
+            currentUser.setLocation(user.getLocation());
+            currentUser.setExamType(user.getExamType());
+            currentUser.setEndYear(user.getEndYear());
+        }
+        if (updateUserName) {
+            currentUser.setUserName(user.getPhonenumber());
+        }
+        if (userService.updateUserProfile(currentUser) > 0)
+        {
+            // 更新缓存用户信息
+            tokenService.setLoginUser(loginUser);
+            return success();
+        }
+        return error("修改个人信息异常,请联系管理员");
+    }
+
+    @PostMapping("verifyCard")
+    @ApiOperation("校验卡")
+    @Anonymous
+    public AjaxResult verifyCard(@ApiParam("卡号") @RequestParam String cardNo, @ApiParam("密码") @RequestParam String password)
+    {
+        DzCards cards = dzCardsService.selectDzCardsByCardNo(cardNo);
+        if(null == cards || !cards.getPassword().equals(password)) {
+            return AjaxResult.error("卡号或密码不正确");
+        }
+//        if(CardStatus.Free.getVal().equals(cards.getStatus())||CardStatus.Open.getVal().equals(cards.getStatus())) {
+//            return AjaxResult.error("卡未分配");
+//        }else if(CardStatus.Active.getVal().equals(cards.getStatus())) {
+//            return AjaxResult.error("卡已使用");
+//        }
+
+        if(!CardStatus.Paid.getVal().equals(cards.getStatus())) {
+            return AjaxResult.error("无效卡");
+        }
+        if(null != cards.getAssignSchoolId()) {
+            cards.setAssignSchoolName(dzSchoolService.selectDzSchoolById(cards.getAssignSchoolId()).getName());
+        }
+        return AjaxResult.success(cards);
+    }
 }

+ 43 - 2
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnTeacherController.java

@@ -8,11 +8,18 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.AjaxResult2;
 import com.ruoyi.common.enums.ExamType;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzControl;
 import com.ruoyi.dz.domain.DzSubject;
 import com.ruoyi.dz.service.IDzControlService;
 import com.ruoyi.dz.service.IDzSubjectService;
+import com.ruoyi.enums.PaperType;
+import com.ruoyi.learn.domain.LearnPaper;
+import com.ruoyi.learn.domain.LearnTestPaper;
 import com.ruoyi.learn.domain.TestPaperVO;
+import com.ruoyi.learn.service.ILearnPaperQuestionService;
+import com.ruoyi.learn.service.ILearnPaperService;
+import com.ruoyi.learn.service.ILearnTestPaperService;
 import com.ruoyi.web.service.LearnTeacherService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -25,6 +32,7 @@ import org.springframework.web.bind.annotation.*;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @RestController
@@ -34,11 +42,17 @@ public class LearnTeacherController extends BaseController {
     private final IDzControlService dzControlService;
     private final IDzSubjectService dzSubjectService;
     private final LearnTeacherService learnTeacherService;
+    private final ILearnPaperService paperService;
+    private final ILearnTestPaperService testPaperService;
+    private final ILearnPaperQuestionService paperQuestionService;
 
-    public LearnTeacherController(IDzControlService dzControlService, IDzSubjectService dzSubjectService, LearnTeacherService learnTeacherService) {
+    public LearnTeacherController(IDzControlService dzControlService, IDzSubjectService dzSubjectService, LearnTeacherService learnTeacherService, ILearnPaperService paperService, ILearnTestPaperService testPaperService, ILearnPaperQuestionService paperQuestionService) {
         this.dzControlService = dzControlService;
         this.dzSubjectService = dzSubjectService;
         this.learnTeacherService = learnTeacherService;
+        this.paperService = paperService;
+        this.testPaperService = testPaperService;
+        this.paperQuestionService = paperQuestionService;
     }
 
     /**
@@ -131,7 +145,8 @@ public class LearnTeacherController extends BaseController {
     @ApiOperation("知识点列表")
     public AjaxResult knowledges(@ApiParam("科目ID") Long subjectId, @RequestParam(required = false) @ApiParam("专业计划ID") Long[] majorPlanIds)
     {
-        return AjaxResult.success(learnTeacherService.getKnowledgeTree(subjectId, majorPlanIds));
+        Set<Long> knowledgeIdSet = learnTeacherService.getKnowledgeIdSet(majorPlanIds);
+        return AjaxResult.success(learnTeacherService.getKnowledgeTree(subjectId, knowledgeIdSet, false));
     }
 
     @GetMapping("/questionTypes")
@@ -187,4 +202,30 @@ public class LearnTeacherController extends BaseController {
     {
         return toAjax(true);
     }
+
+
+    @ApiOperation("04 取试卷列表")
+    @GetMapping(value = "papers")
+    public AjaxResult papers(@ApiParam("批次") @RequestParam(required = false) Integer batchId) {
+        LearnTestPaper tpCond = new LearnTestPaper();
+        tpCond.setCreatorId(SecurityUtils.getUserId());
+        tpCond.setBatchId(batchId);
+        List<LearnTestPaper> testPaperList = testPaperService.selectLearnTestPaperList(tpCond);
+        List<LearnPaper> paperList = Lists.newArrayList();
+        for(LearnTestPaper tp : testPaperList) {
+            JSONObject root = JSONObject.from(tp);
+            paperList.add(paperService.selectLearnPaperById(tp.getPaperId()));
+        }
+        return AjaxResult.success(paperList);
+    }
+
+    @PreAuthorize("@ss.hasPermi('learn:test_paper:add')")
+    @ApiOperation("04 取试卷详情")
+    @GetMapping(value = "paper")
+    public AjaxResult loadPaper(@ApiParam("考卷类型PaperType") PaperType type, @ApiParam("考卷标识") Long id) {
+        LearnPaper paper = paperService.selectLearnPaperById(id);
+        JSONObject root = JSONObject.from(paper);
+        root.put("questions", paperQuestionService.selectLearnPaperQuestionById(id));
+        return AjaxResult.success(root);
+    }
 }

+ 12 - 3
ie-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java

@@ -2,6 +2,8 @@ package com.ruoyi.web.controller.system;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
+import jdk.nashorn.api.scripting.AbstractJSObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
@@ -24,7 +26,7 @@ import com.ruoyi.system.service.ISysConfigService;
 
 /**
  * 参数配置 信息操作处理
- * 
+ *
  * @author ruoyi
  */
 @RestController
@@ -88,7 +90,10 @@ public class SysConfigController extends BaseController
             return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
         }
         config.setCreateBy(getUsername());
-        return toAjax(configService.insertConfig(config));
+        configService.insertConfig(config);
+
+        configService.resetConfigCache();
+        return AjaxResult.success();
     }
 
     /**
@@ -104,7 +109,10 @@ public class SysConfigController extends BaseController
             return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
         }
         config.setUpdateBy(getUsername());
-        return toAjax(configService.updateConfig(config));
+        configService.updateConfig(config);
+
+        configService.resetConfigCache();
+        return AjaxResult.success();
     }
 
     /**
@@ -116,6 +124,7 @@ public class SysConfigController extends BaseController
     public AjaxResult remove(@PathVariable Long[] configIds)
     {
         configService.deleteConfigByIds(configIds);
+        configService.resetConfigCache();
         return success();
     }
 

+ 1 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java

@@ -37,7 +37,7 @@ public class SysDeptController extends BaseController
     /**
      * 获取机构列表
      */
-    @PreAuthorize("@ss.hasPermi('system:dept:list')")
+    // @PreAuthorize("@ss.hasPermi('system:dept:list')")
     @GetMapping("/list")
     public AjaxResult list(SysDept dept)
     {

+ 5 - 5
ie-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest;
 
 /**
  * 登录验证
- * 
+ *
  * @author ruoyi
  */
 @Api(tags = "系统-登陆")
@@ -59,7 +59,7 @@ public class SysLoginController
 
     /**
      * 登录方法
-     * 
+     *
      * @param loginBody 登录信息
      * @return 结果
      */
@@ -68,13 +68,13 @@ public class SysLoginController
     public AjaxResult login(@RequestBody LoginBody loginBody)
     {
         // 生成令牌
-        AjaxResult ajax = loginService.login(loginBody.getMobile(), loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid());
+        AjaxResult ajax = loginService.login(loginBody.getMobile(), loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid(),AccessFromType.back.name());
         return ajax;
     }
 
     /**
      * 获取用户信息
-     * 
+     *
      * @return 用户信息
      */
     @GetMapping("getInfo")
@@ -114,7 +114,7 @@ public class SysLoginController
 
     /**
      * 获取路由信息
-     * 
+     *
      * @return 路由信息
      */
     @GetMapping("getRouters")

+ 12 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java

@@ -1,6 +1,8 @@
 package com.ruoyi.web.controller.system;
 
 import java.util.Map;
+
+import com.ruoyi.enums.UserTypeEnum;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -64,10 +66,17 @@ public class SysProfileController extends BaseController
     {
         LoginUser loginUser = getLoginUser();
         SysUser currentUser = loginUser.getUser();
+        // 变更手机时,如果未绑定卡,这时用户名也要修改
+        boolean updateUserName = null != user.getPhonenumber() && !user.getPhonenumber().equals(currentUser.getPhonenumber()) && currentUser.getUserName().equals(currentUser.getPhonenumber());
         currentUser.setNickName(user.getNickName());
         currentUser.setEmail(user.getEmail());
         currentUser.setPhonenumber(user.getPhonenumber());
         currentUser.setSex(user.getSex());
+        if(!UserTypeEnum.isCard(user.getUserType())) {
+            currentUser.setLocation(user.getLocation());
+            currentUser.setExamType(user.getExamType());
+            currentUser.setEndYear(user.getEndYear());
+        }
         if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
         {
             return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
@@ -76,6 +85,9 @@ public class SysProfileController extends BaseController
         {
             return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
         }
+        if (updateUserName) {
+            currentUser.setUserName(user.getPhonenumber());
+        }
         if (userService.updateUserProfile(currentUser) > 0)
         {
             // 更新缓存用户信息

+ 1 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

@@ -249,7 +249,7 @@ public class SysUserController extends BaseController
     /**
      * 获取机构树列表
      */
-    @PreAuthorize("@ss.hasPermi('system:user:list')")
+    // @PreAuthorize("@ss.hasPermi('system:user:list')")
     @GetMapping("/deptTree")
     public AjaxResult deptTree(SysDept dept)
     {

+ 427 - 30
ie-admin/src/main/java/com/ruoyi/web/service/ExamService.java

@@ -1,70 +1,346 @@
 package com.ruoyi.web.service;
 
+import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.util.DateUtils;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.enums.ExamType;
+import com.ruoyi.common.enums.UserRegStatus;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.service.IDzSubjectService;
+import com.ruoyi.dz.service.impl.DzSubjectServiceImpl;
 import com.ruoyi.enums.ExamineeStatus;
 import com.ruoyi.enums.PaperStatus;
 import com.ruoyi.enums.PaperType;
+import com.ruoyi.enums.UserTypeEnum;
+import com.ruoyi.ie.domain.AMarjorPlan;
+import com.ruoyi.ie.service.IAMarjorPlanService;
 import com.ruoyi.learn.domain.*;
-import com.ruoyi.learn.mapper.LearnExamineeMapper;
-import com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper;
-import com.ruoyi.learn.mapper.LearnPaperMapper;
+import com.ruoyi.learn.mapper.*;
+import com.ruoyi.learn.service.ILearnPaperService;
+import com.ruoyi.learn.service.ILearnPlanService;
+import com.ruoyi.mxjb.domain.MxjbContants;
+import com.ruoyi.system.service.ISysUserService;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
+import javax.validation.ValidationException;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 考试服务
  */
 @Service
 public class ExamService {
+    private final LearnAnswerMapper learnAnswerMapper;
+    private final LearnExamineeMapper learnExamineeMapper;
+    private final LearnStudentMapper learnStudentMapper;
+    private final ILearnPlanService learnPlanService;
+    private final LearnQuestionsMapper learnQuestionsMapper;
+    private final IDzSubjectService dzSubjectService;
+    private Set<PaperType> paperTypeSet = Sets.newHashSet(PaperType.Real, PaperType.Custom, PaperType.Test);
     private final LearnPaperMapper paperMapper;
     private final LearnKnowledgeTreeMapper knowledgeTreeMapper;
     private final LearnExamineeMapper examineeMapper;
+    private final LearnWrongBookMapper wrongBookMapper;
+    private final LearnWrongDetailMapper wrongDetailMapper;
+    private final ILearnPaperService learnPaperService;
     private final PaperService paperService;
+    private final IAMarjorPlanService marjorPlanService;
+    private final ISysUserService sysUserService;
 
-    public ExamService(LearnPaperMapper paperMapper, LearnKnowledgeTreeMapper knowledgeTreeMapper, LearnExamineeMapper examineeMapper, PaperService paperService) {
+    public ExamService(LearnPaperMapper paperMapper, LearnKnowledgeTreeMapper knowledgeTreeMapper, LearnExamineeMapper examineeMapper, ILearnPaperService learnPaperService, PaperService paperService, IAMarjorPlanService marjorPlanService, LearnAnswerMapper learnAnswerMapper, LearnExamineeMapper learnExamineeMapper, ISysUserService sysUserService, LearnStudentMapper learnStudentMapper, ILearnPlanService learnPlanService, LearnQuestionsMapper learnQuestionsMapper, IDzSubjectService dzSubjectService, LearnWrongBookMapper wrongBookMapper, LearnWrongDetailMapper wrongDetailMapper) {
         this.paperMapper = paperMapper;
         this.knowledgeTreeMapper = knowledgeTreeMapper;
         this.examineeMapper = examineeMapper;
+        this.learnPaperService = learnPaperService;
         this.paperService = paperService;
+        this.marjorPlanService = marjorPlanService;
+        this.learnAnswerMapper = learnAnswerMapper;
+        this.learnExamineeMapper = learnExamineeMapper;
+        this.sysUserService = sysUserService;
+        this.learnStudentMapper = learnStudentMapper;
+        this.learnPlanService = learnPlanService;
+        this.learnQuestionsMapper = learnQuestionsMapper;
+        this.dzSubjectService = dzSubjectService;
+        this.wrongBookMapper = wrongBookMapper;
+        this.wrongDetailMapper = wrongDetailMapper;
     }
 
     /**
      * 开卷 (卷, 实时组卷)
      * @return
      */
-    public AnswerSheet openExaminee(PaperType paperType, Long relatedId) {
-        if(PaperType.Practice.equals(paperType)) { // TODO 先连接学生的定向信息
-            return openExaminee(relatedId, SecurityUtils.getUserId(), "");
+    @Transactional(rollbackFor = Exception.class)
+    public AnswerSheet openExaminee(boolean directed, PaperType paperType, Long relatedId, Long subjectId) {
+        SysUser user = SecurityUtils.getLoginUser().getUser();
+        if(paperTypeSet.contains(paperType)) {
+            if(UserTypeEnum.isCard(user.getUserType()) && !UserRegStatus.Student.equals(user.getRegStatus())) {
+                throw new RuntimeException("VIP功能不可用");
+            }
+            return openExaminee(paperType, relatedId, SecurityUtils.getUserId());
+        } else if(PaperType.Practice.equals(paperType)) {
+            if(UserTypeEnum.isCard(user.getUserType()) && !UserRegStatus.Student.equals(user.getRegStatus())) {
+                throw new RuntimeException("VIP功能不可用");
+            }
+            return openExaminee(relatedId, SecurityUtils.getUserId(), getDirectedKey(directed));
+        } else if(PaperType.Simulated.equals(paperType)) {
+            SysUser exist = sysUserService.selectUserById(SecurityUtils.getUserId());
+            LearnStudent ls = learnStudentMapper.selectLearnStudentByStudentId(exist.getUserId());
+            if(null == ls) {
+                throw new RuntimeException("请先定设置定向学习" + exist.getUserId());
+            }
+            return openExaminee(ls, subjectId, exist);
         }
-        return openExaminee(paperType, relatedId, SecurityUtils.getUserId(), "");
+        throw new RuntimeException("错误类型: " + paperType);
     }
 
-    /**
-     * 临时保存
-     */
-    public void updateAnswerSheet(AnswerSheet answer) {
+    private String getDirectedKey(boolean directed) {
+        if(!directed) {
+            return "";
+        }
+        LearnStudent ls = learnStudentMapper.selectLearnStudentByStudentId(SecurityUtils.getUserId());
+        return StringUtils.trimToEmpty(ls.getDirectKey());
+    }
+
+    public AnswerSheet loadExaminee(Long examineeId, Boolean examContinue) {
+        LearnExaminee examinee = examineeMapper.selectLearnExamineeByExamineeId(examineeId);
+        if(examContinue) {
+            if(ExamineeStatus.Sign.getVal().equals(examinee.getState())) {
+                LearnExaminee upExaminee = new LearnExaminee();
+                upExaminee.setExamineeId(examineeId);
+                upExaminee.setBeginTime(new Date());
+                upExaminee.setState(ExamineeStatus.Exam.getVal());
+                examineeMapper.updateLearnExaminee(upExaminee);
+            } else if(!ExamineeStatus.Exam.getVal().equals(examinee.getState())) {
+                throw new RuntimeException("已考完的卷不能继续做");
+            }
+        } else if(ExamineeStatus.Exam.getVal().equals(examinee.getState())) {
+            throw new RuntimeException("考试中,不能提取答案");
+        }
+        LearnAnswer aCond = new LearnAnswer();
+        aCond.setExamineeId(examineeId);
+        Map<Long, LearnAnswer> answerMap = learnAnswerMapper.selectLearnAnswerList(aCond).stream().collect(Collectors.toMap(LearnAnswer::getQuestionId, t -> t));
+        LearnPaper learnPaper = learnPaperService.selectLearnPaperById(examinee.getPaperId());
+        List<PaperVO.QuestionAnswer> questionList = paperService.loadPaperQuestionAnswers(SecurityUtils.getUserId(), examinee.getPaperId(), answerMap, !examContinue);
+        AnswerSheet answerSheet = buildAnswerSheet(learnPaper, examinee);
+        answerSheet.setTotalCount(questionList.size());
+        answerSheet.setWrongCount(examinee.getWrongCount());
+        answerSheet.setQuestions(questionList);
 
+        if(null != examinee.getPaperInfo()) {
+            JSONObject info = JSONObject.parseObject(examinee.getPaperInfo());
+            answerSheet.setCollegeId(info.getLong("universityId"));
+            answerSheet.setCollegeName(info.getString("universityName"));
+            answerSheet.setMajorId(info.getLong("planId"));
+            answerSheet.setMajorName(info.getString("majorName"));
+            answerSheet.setSubjectId(learnPaper.getSubjectId());
+            answerSheet.setSubjectName(dzSubjectService.selectDzSubjectBySubjectId(learnPaper.getSubjectId()).getSubjectName());
+        }
+        if(null != examinee.getStats()) {
+            answerSheet.setStats(JSONObject.parseObject(examinee.getStats()));
+        }
+        return answerSheet;
     }
 
     /**
      * 交卷
-     * @param answer
+     * @param answerSheet
      */
-    public void commitAnswerSheet(AnswerSheet answer) {
+    @Transactional(rollbackFor = Exception.class)
+    public void commitAnswerSheet(AnswerSheet answerSheet) {
+        LearnExaminee exitExaminee = learnExamineeMapper.selectLearnExamineeByExamineeId(answerSheet.getExamineeId());
+        if(null == exitExaminee) {
+            throw new RuntimeException("无此答卷" + answerSheet.getExamineeId());
+        }
+        LearnAnswer aCond = new LearnAnswer();
+        aCond.setExamineeId(answerSheet.getExamineeId());
+        Map<Long, LearnAnswer> answerMap = learnAnswerMapper.selectLearnAnswerList(aCond).stream().collect(Collectors.toMap(LearnAnswer::getQuestionId, t -> t));
+        List<PaperVO.QuestionAnswer> qaList = paperService.loadPaperQuestionAnswers(null, exitExaminee.getPaperId(), answerMap, true);
+        Map<Long, PaperVO.QuestionAnswer> questionMap = Maps.newHashMap();
+        for(PaperVO.QuestionAnswer qa : qaList) {
+            if(CollectionUtils.isNotEmpty(qa.getSubQuestions())) {
+                for(PaperVO.QuestionAnswer sqa : qa.getSubQuestions()) {
+                    questionMap.put(sqa.getId(), qa);
+                }
+            } else {
+                questionMap.put(qa.getId(), qa);
+            }
+        }
 
+        List<LearnAnswer> answersList = Lists.newArrayList();
+        Integer wrongCount = 0;
+        Integer totalScore = 0;
+        Integer score = 0;
+        for(PaperVO.QuestionAnswer question : answerSheet.getQuestions()) {
+            if(CollectionUtils.isNotEmpty(question.getSubQuestions())) {
+                for(PaperVO.QuestionAnswer sq : question.getSubQuestions()) {
+                    LearnAnswer answer = buildAnswer(answerSheet, questionMap, sq, answersList);
+                    if(answerSheet.getIsDone()) {
+                        totalScore += sq.getScore();
+                        score += answer.getScore();
+                        if(!answer.getState().equals(1)) {
+                            wrongCount++;
+                        }
+                    }
+                }
+            } else {
+                LearnAnswer answer = buildAnswer(answerSheet, questionMap, question, answersList);
+                if(answerSheet.getIsDone()) {
+                    totalScore += question.getScore();
+                    score += answer.getScore();
+                    if(!answer.getState().equals(1)) {
+                        wrongCount++;
+                    }
+                }
+            }
+        }
+        if(answersList.size() > 0) {
+            answersList.stream().forEach(t -> {
+                if(null == t.getAnswerId()) {
+                    learnAnswerMapper.insertLearnAnswer(t);
+                } else {
+                    learnAnswerMapper.updateLearnAnswer(t);
+                }
+            });
+        }
+        LearnExaminee upExaminee = new LearnExaminee();
+        upExaminee.setExamineeId(answerSheet.getExamineeId());
+        upExaminee.setDuration(answerSheet.getDuration());
+        if(answerSheet.getIsDone()) {
+            upExaminee.setEndTime(new Date());
+            upExaminee.setState(ExamineeStatus.Commit.getVal());
+            upExaminee.setScore(score);
+            upExaminee.setScoreRate(score * 100 / totalScore);
+            upExaminee.setWrongCount(wrongCount);
+            learnExamineeMapper.updateLearnExaminee(upExaminee);
+            if(PaperType.Simulated.getVal().equals(exitExaminee.getPaperType())) {
+                List<JSONObject> paperStatList = learnExamineeMapper.selectExamRankingStats(upExaminee.getScore(), Lists.newArrayList(exitExaminee.getPaperId()));
+                JSONObject o = paperStatList.get(0);
+                Integer totalCount = o.getInteger("totalCount");
+                Integer maxScore = o.getInteger("maxScore");
+                Integer avgScore = o.getInteger("avgScore");
+                Integer lowCount = o.getInteger("lowCount");
+                JSONObject stats = new JSONObject();
+                stats.put("score", upExaminee.getScore());
+                stats.put("rate", upExaminee.getScoreRate());
+                stats.put("maxScore", maxScore);
+                stats.put("averageScore", avgScore);
+                stats.put("hitRate", lowCount * 100 / totalCount);
+                upExaminee.setStats(JSONObject.toJSONString(stats));
+                learnExamineeMapper.updateLearnExaminee(upExaminee);
+            }
+        } else {
+            learnExamineeMapper.updateLearnExaminee(upExaminee);
+        }
+
+        if(answerSheet.getIsDone()) {
+            // 关闭试卷
+            LearnPaper up = new LearnPaper();
+            up.setId(exitExaminee.getPaperId());
+            up.setStatus(PaperStatus.Valid.getVal());
+            paperMapper.updateLearnPaper(up);
+
+            if(PaperType.Practice.getVal().equals(exitExaminee.getPaperType())) {
+                learnPlanService.updateLearnPlan(exitExaminee.getStudentId());
+                calculateWrongAnswers(exitExaminee, questionMap, answersList, PaperType.Practice.name());
+            }
+        }
     }
 
-    public AnswerSheet openExaminee(PaperType paperType, Long paperId, Long studentId, String directKey) {
-        AnswerSheet answerSheet = new AnswerSheet();
+    public Set<Integer> invalidStatusSet = Sets.newHashSet(2, 3);
+    public void  calculateWrongAnswers(LearnExaminee examinee, Map<Long, PaperVO.QuestionAnswer> questionMap, List<LearnAnswer> answersList, String source) {
+        if (CollectionUtils.isEmpty(answersList)){
+            return;
+        }
+        LearnPaper learnPaper = paperMapper.selectLearnPaperById(examinee.getPaperId());
+        answersList.forEach(t -> {
+            if (invalidStatusSet.contains(t.getState())) {
+                LearnWrongBook wrongBookCond = new LearnWrongBook();
+                wrongBookCond.setStudentId(examinee.getStudentId());
+                wrongBookCond.setQuestionId(t.getQuestionId());
+                PaperVO.QuestionAnswer stdAnswer = questionMap.get(t.getQuestionId());
+                wrongBookCond.setKnownledgeId(stdAnswer.getKnowledgeId());
+                List<LearnWrongBook> wrongBookList = wrongBookMapper.selectLearnWrongBookList(wrongBookCond);
+                if (CollectionUtils.isNotEmpty(wrongBookList)) {
+                    LearnWrongBook wrongBook = wrongBookList.get(0);
+                    if (null == wrongBook.getWrongCount()) {
+                        wrongBook.setWrongCount(0L);
+                    }
+                    if (null == wrongBook.getTotalCount()) {
+                        wrongBook.setTotalCount(0L);
+                    }
+                    wrongBookCond.setWrongId(wrongBook.getWrongId());
+                    wrongBookCond.setStudentId(null);
+                    wrongBookCond.setQuestionId(null);
+                    wrongBookCond.setWrongCount(wrongBook.getWrongCount() + 1);
+                    wrongBookCond.setTotalCount(wrongBook.getTotalCount() + 1);
+                } else {
+                    wrongBookCond.setAnswer1(stdAnswer.getAnswer1());
+                    wrongBookCond.setAnswer2(stdAnswer.getAnswer2());
+                    wrongBookCond.setSubjectId(learnPaper.getSubjectId());
+                    wrongBookCond.setRightCount(0L);
+                    wrongBookCond.setWrongCount(1L);
+                    wrongBookCond.setTotalCount(1L);
+                    // wrongBookCond.setScoreTotal(standard.getScoreTotal());
+                }
+                // wrongBookCond.setKnownledgeId(standard.getKnowledgeId());
+                wrongBookCond.setPaperId(examinee.getPaperId());
+                wrongBookCond.setScore(t.getScore());
+                wrongBookCond.setSource(source);
+                wrongBookCond.setAnswer(t.getAnswer());
+                wrongBookCond.setState(MxjbContants.QuestionScoreWrong);
+                if(null == wrongBookCond.getWrongId()) {
+                    wrongBookMapper.insertLearnWrongBook(wrongBookCond);
+                } else {
+                    wrongBookMapper.updateLearnWrongBook(wrongBookCond);
+                }
+                LearnWrongDetail wrongDetail = new LearnWrongDetail();
+                wrongDetail.setWrongId(wrongBookCond.getWrongId());
+                wrongDetail.setCreatedTime(new Date());
+                wrongDetail.setKnownledgeId(stdAnswer.getKnowledgeId());
+                wrongDetail.setSource(wrongBookCond.getSource());
+                wrongDetail.setExamineeId(examinee.getExamineeId());
+                wrongDetail.setStudentId(examinee.getStudentId());
+                wrongDetail.setPaperId(examinee.getPaperId());
+                wrongDetailMapper.insertLearnWrongDetail(wrongDetail);
+            }
+        });
+    }
 
+    private LearnAnswer buildAnswer(AnswerSheet answerSheet, Map<Long, PaperVO.QuestionAnswer> questionMap, PaperVO.QuestionAnswer question,
+                             List<LearnAnswer> answerList) {
+        LearnAnswer answer = new LearnAnswer();
+        PaperVO.QuestionAnswer stdAnswer = questionMap.get(question.getId());
+        if(null == stdAnswer.getAnswerId()) {
+            answer.setExamineeId(answerSheet.getExamineeId());
+            answer.setStudentId(SecurityUtils.getUserId());
+            answer.setSeq(question.getSeq());
+        } else {
+            answer.setAnswerId(stdAnswer.getAnswerId());
+        }
+        answerList.add(answer);
+        answer.setQuestionId(question.getId()); // 错题计算要使用
+        answer.setKnowledgeId(stdAnswer.getKnowledgeId());
+        answer.setAnswer(StringUtils.join(question.getAnswers(), ","));
+        answer.setMark(null != question.getIsMark() && question.getIsMark());
+        answer.setNotKnow(null != question.getIsNotKnow() && question.getIsNotKnow());
+        answer.setState(question.calcState(stdAnswer, answerSheet.getIsDone()));
+        if (answerSheet.getIsDone()) {
+            answer.setScore(answer.getState() == 1 ? stdAnswer.getScore() : 0);
+            answer.setScoreRate(answer.getScore() * 100 / stdAnswer.getScore());
+        }
+        return answer;
+    }
+
+    private AnswerSheet openExaminee(PaperType paperType, Long paperId, Long studentId) {
         LearnPaper paper = paperMapper.selectLearnPaperById(paperId);
 
         LearnExaminee learnExaminee = new LearnExaminee();
@@ -78,35 +354,106 @@ public class ExamService {
         } else {
             learnExaminee.setState(ExamineeStatus.Exam.getVal());
             learnExaminee.setBeginTime(new Date());
+            learnExaminee.setDuration(0L);
             examineeMapper.insertLearnExaminee(learnExaminee);
         }
+        return buildAnswerSheet(paper, learnExaminee);
+    }
+
+    private AnswerSheet openExaminee(LearnStudent ls, Long subjectId, SysUser sysUser) {
+        AMarjorPlan plan = marjorPlanService.selectAMarjorPlanById(ls.getMajorPlanId());
+        if(null == plan) {
+            throw new ValidationException("专业id无效");
+        }
+        LearnExaminee examinee = new LearnExaminee();
+        examinee.setStudentId(SecurityUtils.getLoginUser().getUser().getUserId());
+        examinee.setPaperType(PaperType.Simulated.getVal());
+        examinee.setPaperKey(subjectId.toString());
+        List<LearnExaminee> examineeList = examineeMapper.selectLearnExamineeList(examinee);
+        Set<Long> existPaperIdSet = Sets.newHashSet();
+        for(LearnExaminee e : examineeList) {
+            if(ExamineeStatus.Sign.getVal().equals(e.getState())) {
+                LearnPaper learnPaper = paperMapper.selectLearnPaperById(e.getPaperId());
+                AnswerSheet answerSheet = buildAnswerSheet(learnPaper, e);
+                if(null != e.getPaperInfo()) {
+                    JSONObject info = JSONObject.parseObject(e.getPaperInfo());
+                    answerSheet.setCollegeId(info.getLong("universityId"));
+                    answerSheet.setCollegeName(info.getString("universityName"));
+                    answerSheet.setMajorId(info.getLong("planId"));
+                    answerSheet.setMajorName(info.getString("majorName"));
+                    answerSheet.setSubjectId(learnPaper.getSubjectId());
+                    answerSheet.setSubjectName(dzSubjectService.selectDzSubjectBySubjectId(learnPaper.getSubjectId()).getSubjectName());
+                }
+                return answerSheet;
+            }
+            existPaperIdSet.add(e.getPaperId());
+        }
+        JSONObject evalCountObj = JSONObject.parseObject(sysUser.getEvalCounts());
+        Integer evalCount = evalCountObj.getInteger(subjectId.toString());
+        if(null == evalCount || existPaperIdSet.size() >= evalCount) {
+            throw new ValidationException("超过最大次数限制" + evalCount );
+        }
+        SysUser upUser = new SysUser();
+        upUser.setUserId(sysUser.getUserId());
+        evalCountObj.put(subjectId.toString(), evalCount - 1);
+        upUser.setEvalCounts(evalCountObj.toJSONString());
+        sysUserService.updateUserProfile(upUser);
+
+        existPaperIdSet.clear(); // TODO 临时清除重复检查
+        LearnPaper paper = getBestPaper(sysUser.getExamType(), plan, subjectId, existPaperIdSet);
+        examinee.setPaperId(paper.getId());
+        examinee.setState(ExamineeStatus.Sign.getVal());
+        examinee.setDuration(0L);
+
+        JSONObject info = new JSONObject();
+        info.put("universityId", ls.getUniversityId());
+        info.put("universityName", plan.getUniversityName());
+        info.put("planId", ls.getMajorPlanId());
+        info.put("majorName", plan.getMajorName());
+        examinee.setPaperInfo(info.toJSONString());
+        examineeMapper.insertLearnExaminee(examinee);
+        AnswerSheet answerSheet = buildAnswerSheet(paper, examinee);
+
+        answerSheet.setCollegeId(ls.getUniversityId());
+        answerSheet.setCollegeName(plan.getUniversityName());
+        answerSheet.setMajorId(ls.getMajorPlanId());
+        answerSheet.setMajorName(plan.getMajorName());
+        answerSheet.setSubjectId(paper.getSubjectId());
+        answerSheet.setSubjectName(dzSubjectService.selectDzSubjectBySubjectId(paper.getSubjectId()).getSubjectName());
         return answerSheet;
+
     }
 
     /**
      * 根据知识点生成一次性练习卷并开始做题
      * @return
      */
-    @Transactional(rollbackFor = Exception.class)
-    public AnswerSheet openExaminee(Long knowledgeId, Long studentId, String directKey) {
-        AnswerSheet answerSheet = new AnswerSheet();
-
+    private AnswerSheet openExaminee(Long knowledgeId, Long studentId, String directKey) {
         LearnKnowledgeTree knowledgeTree = knowledgeTreeMapper.selectLearnKnowledgeTreeById(knowledgeId);
         LearnPaper paper = new LearnPaper();
         paper.setPaperType(PaperType.Practice.name());
         paper.setRelateId(knowledgeId);
         paper.setYear(Calendar.getInstance().get(Calendar.YEAR));
         paper.setStatus(PaperStatus.TmpValid.getVal());
-        paper.setDirectKey(StringUtils.trimToEmpty(directKey));
+        paper.setDirectKey(studentId + "_" + StringUtils.trimToEmpty(directKey));
         List<LearnPaper> paperList = paperMapper.selectLearnPaperList(paper);
         if (CollectionUtils.isNotEmpty(paperList)) { // 有未做完的
+            LearnPaper existPaper = paperList.get(0);
             LearnExaminee learnExaminee = new LearnExaminee();
             learnExaminee.setStudentId(studentId);
-            learnExaminee.setPaperId(paperList.get(0).getId());
+            learnExaminee.setPaperId(existPaper.getId());
             learnExaminee.setPaperType(PaperType.Practice.getVal());
+            learnExaminee.setState(ExamineeStatus.Exam.getVal());
             List<LearnExaminee> examineeList = examineeMapper.selectLearnExamineeList(learnExaminee);
-            if (CollectionUtils.isNotEmpty(examineeList)) {
-                examineeList.get(0);
+            if (CollectionUtils.isNotEmpty(examineeList) && ExamineeStatus.Exam.getVal().equals(examineeList.get(0).getState())) {
+                LearnExaminee examinee = examineeList.get(0);
+                LearnAnswer aCond = new LearnAnswer();
+                aCond.setExamineeId(examinee.getExamineeId());
+                Map<Long, LearnAnswer> answerMap = learnAnswerMapper.selectLearnAnswerList(aCond).stream().collect(Collectors.toMap(LearnAnswer::getQuestionId, t -> t));
+                List<PaperVO.QuestionAnswer> questionList = paperService.loadPaperQuestionAnswers(SecurityUtils.getUserId(), examinee.getPaperId(), answerMap, false);
+                AnswerSheet answerSheet = buildAnswerSheet(existPaper, examinee);
+                answerSheet.setQuestions(questionList);
+                answerSheet.setKnowledgeId(existPaper.getRelateId());
                 return answerSheet;
             }
             // 关闭试卷
@@ -121,23 +468,73 @@ public class ExamService {
         paperDef.setFillExclude(true);
         paperDef.setKnowIds(Lists.newArrayList(knowledgeId));
         List<TestPaperVO.TypeDef> typeDefList= Lists.newArrayList();
-        typeDefList.add(new TestPaperVO.TypeDef("单选题", "单选题", 80, 1));
-        typeDefList.add(new TestPaperVO.TypeDef("判断题", "判断题", 10, 2));
+        typeDefList.add(new TestPaperVO.TypeDef("单选题", "单选题", 10, 1));
+        typeDefList.add(new TestPaperVO.TypeDef("判断题", "判断题", 5, 2));
         paperDef.setTypes(typeDefList);
         List<LearnPaperQuestion> pqList = paperService.getQuestions(studentId,  paperDef);
-
         paper.setPaperSource(0);
+        paper.setFenshu(0);
         paper.setSubjectId(knowledgeTree.getSubjectId());
         paper.setPaperName(studentId + "-" + knowledgeTree.getName() + "-" + DateUtils.format(new Date(), "yyyyMMddHHmmss"));
+        paper.setRelateId(knowledgeId);
         paperService.savePaper(paper, pqList);
 
         LearnExaminee learnExaminee = new LearnExaminee();
         learnExaminee.setStudentId(studentId);
         learnExaminee.setPaperType(PaperType.Practice.getVal());
         learnExaminee.setPaperId(paper.getId());
+        learnExaminee.setPaperKey(String.valueOf(knowledgeId));
         learnExaminee.setState(ExamineeStatus.Exam.getVal());
         learnExaminee.setBeginTime(new Date());
+        learnExaminee.setDuration(0L);
         examineeMapper.insertLearnExaminee(learnExaminee);
+
+        AnswerSheet answerSheet = buildAnswerSheet(paper, learnExaminee);
+        answerSheet.setKnowledgeId(paper.getRelateId());
+        return answerSheet;
+    }
+
+    private LearnPaper getBestPaper(ExamType examType, AMarjorPlan plan, Long subjectId, Set<Long> existPaperIdSet) {
+        String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
+        LearnPaper paperCond = new LearnPaper();
+        paperCond.setSubjectId(subjectId);
+        paperCond.setPaperType(PaperType.Simulated.name());
+        String baseKey = plan.getUniversityId() + "_" + examType.name();
+        for(int i = 3; i>0; i--) {
+            if(i == 3) {
+                paperCond.setDirectKey(baseKey + "_" + groupName + "_" + plan.getMajorName());
+            } else if(i == 2) {
+                paperCond.setDirectKey(baseKey + "_" + groupName);
+            } else if(StringUtils.isBlank(groupName)) {
+                break;
+            } else {
+                paperCond.setDirectKey(baseKey);
+            }
+            List<LearnPaper> paperList = learnPaperService.selectLearnPaperList(paperCond);
+            for(LearnPaper paper : paperList) {
+                if(existPaperIdSet.add(paper.getId())) {
+                    return paper;
+                }
+            }
+        }
+        throw new ValidationException("未初始化院校定向模拟题库: " + subjectId + ":" + plan.getId());
+    }
+
+    private AnswerSheet buildAnswerSheet(LearnPaper paper, LearnExaminee examinee) {
+        AnswerSheet answerSheet = new AnswerSheet();
+        answerSheet.setExamineeId(examinee.getExamineeId());
+        answerSheet.setPaperId(examinee.getPaperId());
+        answerSheet.setName(paper.getPaperName());
+        answerSheet.setBeginTime(examinee.getBeginTime());
+        answerSheet.setDuration(examinee.getDuration());
+        if(StringUtils.isNotBlank(paper.getPaperInfo())) {
+            answerSheet.setPaperInfo(JSONObject.parseObject(paper.getPaperInfo(), AnswerSheet.PaperCond.class));
+        }
+        answerSheet.setScoringType("1");
+        answerSheet.setMode(0L);
+        answerSheet.setState(examinee.getState());
+        answerSheet.setAllowAnswer(true);
+        answerSheet.setAllowScore(false);
         return answerSheet;
     }
 }

+ 92 - 0
ie-admin/src/main/java/com/ruoyi/web/service/LearnStatService.java

@@ -0,0 +1,92 @@
+package com.ruoyi.web.service;
+
+import cn.hutool.core.lang.Dict;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.util.DateUtils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.ruoyi.common.utils.NumberUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.learn.domain.LearnStudent;
+import com.ruoyi.learn.mapper.LearnAnswerMapper;
+import com.ruoyi.learn.service.ILearnStudentService;
+import com.ruoyi.mingxue.mapper.CustomerVideoWatchesMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Service
+public class LearnStatService {
+    private final CustomerVideoWatchesMapper customerVideoWatchesMapper;
+    private final LearnAnswerMapper answerMapper;
+    private final ILearnStudentService learnStudentService;
+    private final LearnTeacherService learnTeacherService;
+
+    public LearnStatService(CustomerVideoWatchesMapper customerVideoWatchesMapper, LearnAnswerMapper answerMapper, LearnTeacherService learnTeacherService, ILearnStudentService learnStudentService, LearnTeacherService learnTeacherService1) {
+        this.customerVideoWatchesMapper = customerVideoWatchesMapper;
+        this.answerMapper = answerMapper;
+        this.learnStudentService = learnStudentService;
+        this.learnTeacherService = learnTeacherService1;
+    }
+
+    public List<JSONObject> getRecordKnowledge(Long studentId) {
+        LearnStudent learnStudent = learnStudentService.selectLearnStudentByStudentId(studentId);
+        Set<Long> knowledgeIdSet = null != learnStudent ? learnTeacherService.getKnowledgeIdSet(new Long[] { learnStudent.getMajorPlanId() }) : Sets.newHashSet();
+        knowledgeIdSet.add(0L);
+        List<JSONObject> list = answerMapper.selectKnowledgeStats(Dict.create().set("studentId", studentId).set("knowIds", knowledgeIdSet));
+        list.forEach(o -> {
+            o.put("directed", "1".equals(o.get("directed")));
+        });
+        return list;
+    }
+
+    public JSONObject getRecordVideo(Long studentId) {
+        Map cond = Dict.create().set("studentId", studentId);
+        JSONObject header = customerVideoWatchesMapper.selectLearnStatsHeader(cond);
+        JSONObject result = new JSONObject();
+        result.put("total", header.getIntValue("total"));
+        result.put("study", header.getIntValue("value"));
+        List<JSONObject> list = Lists.newArrayList();
+        customerVideoWatchesMapper.selectLearnStatsDetail(cond).stream().forEach(s -> {
+            JSONObject data = JSONObject.of("name", s.getString("name"), "date", DateUtils.format(s.getDate("time"), "yyyy-MM-dd"), "study", s.getString("value"));
+            list.add(data);
+        });
+        result.put("list", list);
+        return result;
+    }
+
+    public JSONObject getRecordPlanStudy(Long studentId, Integer year, Long month) {
+        Date beginDate, endDate;
+        Calendar cal  = Calendar.getInstance();
+        if(NumberUtils.isPositive(month)) {
+            cal.set(year, month.intValue() - 1, 1);
+            beginDate = cal.getTime();
+            cal.add(Calendar.MONTH, 1);
+            endDate = cal.getTime();
+        } else {
+            cal.set(year, 0, 1);
+            beginDate = cal.getTime();
+            cal.add(Calendar.YEAR, 1);
+            endDate = cal.getTime();
+        }
+
+        Map cond = Dict.create().set("studentId", studentId).set("beginDate", beginDate).set("endDate", endDate);
+        JSONObject header = answerMapper.selectQuestionStatsHeader(cond);
+        JSONObject result = new JSONObject();
+        result.put("total", header.getIntValue("total"));
+        result.put("rate", header.getIntValue("rate"));
+        result.put("studyDays", header.getIntValue("time"));
+        if(null == month || month.equals(0)) {
+            return result;
+        }
+        List<JSONObject> list = Lists.newArrayList();
+        answerMapper.selectQuestionStatsDetail(cond).stream().forEach(s -> {
+            JSONObject data = JSONObject.of("date", DateUtils.format(s.getDate("time"), "yyyy-MM-dd"), "study", s.getString("value"), "rate", s.getString("rate"));
+            list.add(data);
+        });
+        result.put("list", list);
+        return result;
+
+
+    }
+}

+ 132 - 26
ie-admin/src/main/java/com/ruoyi/web/service/LearnTeacherService.java

@@ -1,10 +1,10 @@
 package com.ruoyi.web.service;
 
 import cn.hutool.core.lang.Dict;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import com.ruoyi.common.annotation.Excel;
-import com.ruoyi.common.core.domain.TreeEntity;
+import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzClasses;
 import com.ruoyi.dz.mapper.DzClassesMapper;
@@ -12,14 +12,10 @@ import com.ruoyi.enums.PaperType;
 import com.ruoyi.ie.domain.AMarjorPlan;
 import com.ruoyi.ie.mapper.AMarjorPlanMapper;
 import com.ruoyi.learn.domain.*;
-import com.ruoyi.learn.mapper.LearnDirectedKnowledgeMapper;
-import com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper;
-import com.ruoyi.learn.mapper.LearnStudentMapper;
-import com.ruoyi.learn.mapper.LearnTestPaperMapper;
+import com.ruoyi.learn.mapper.*;
 import com.ruoyi.syzy.domain.BBusiWishUniversities;
 import com.ruoyi.syzy.mapper.BBusiWishUniversitiesMapper;
 import lombok.Data;
-import org.apache.commons.compress.utils.Lists;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -28,10 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
 import javax.validation.ValidationException;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -45,8 +38,9 @@ public class LearnTeacherService {
     private final LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper;
     private final PaperService paperService;
     private final LearnTestPaperMapper learnTestPaperMapper;
+    private final LearnQuestionsMapper learnQuestionsMapper;
 
-    public LearnTeacherService(DzClassesMapper dzClassesMapper, LearnKnowledgeTreeMapper learnKnowledgeTreeMapper, LearnStudentMapper learnStudentMapper, AMarjorPlanMapper marjorPlanMapper, BBusiWishUniversitiesMapper busiWishUniversitiesMapper, LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper, PaperService paperService, LearnTestPaperMapper learnTestPaperMapper) {
+    public LearnTeacherService(DzClassesMapper dzClassesMapper, LearnKnowledgeTreeMapper learnKnowledgeTreeMapper, LearnStudentMapper learnStudentMapper, AMarjorPlanMapper marjorPlanMapper, BBusiWishUniversitiesMapper busiWishUniversitiesMapper, LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper, PaperService paperService, LearnTestPaperMapper learnTestPaperMapper, LearnQuestionsMapper learnQuestionsMapper) {
         this.dzClassesMapper = dzClassesMapper;
         this.learnKnowledgeTreeMapper = learnKnowledgeTreeMapper;
         this.learnStudentMapper = learnStudentMapper;
@@ -55,6 +49,7 @@ public class LearnTeacherService {
         this.learnDirectedKnowledgeMapper = learnDirectedKnowledgeMapper;
         this.paperService = paperService;
         this.learnTestPaperMapper = learnTestPaperMapper;
+        this.learnQuestionsMapper = learnQuestionsMapper;
     }
 
     public List<DzClasses> getClasses(Long teacherId)
@@ -103,6 +98,7 @@ public class LearnTeacherService {
             paper.setPaperType(PaperType.Test.name());
             paper.setSubjectId(req.getSubjectId());
             paper.setDirectKey("");
+            paper.setFenshu(0);
             for(Long universityId : universityMap.keySet()) {
                 Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
                 if(null != directPaperMap && directPaperMap.containsKey("")) {
@@ -166,6 +162,7 @@ public class LearnTeacherService {
                 paper.setYear(plan.getYear());
                 paper.setPaperType(PaperType.Test.name());
                 paper.setDirectKey(directedKey);
+                paper.setFenshu(0);
                 Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = paperService.buildPaper(null, paper, paperDef);
                 paperService.savePaper(paperResult.getKey(), paperResult.getValue());
 
@@ -213,6 +210,7 @@ public class LearnTeacherService {
         if(null == req.getDirectType() || !req.getDirectType()) { // 全量
             paper.setSubjectId(req.getSubjectId());
             paper.setDirectKey("");
+            paper.setFenshu(0);
             for(Long universityId : universityMap.keySet()) {
                 Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
                 if(null != directPaperMap && directPaperMap.containsKey("")) {
@@ -270,39 +268,142 @@ public class LearnTeacherService {
         return "";
     }
 
-    public List<TreeNode> getKnowledgeTree(Long subjectId, Long[] planIds) {
-        Set<Long> knowledgeIdSet = Sets.newHashSet();
+    public List<TreeNode> getKnowledgeTree(Long subjectId, Set<Long> knowledgeIdSet, boolean count) {
+        LearnKnowledgeTree ktCond = new LearnKnowledgeTree();
+        ktCond.setSubjectId(subjectId);
+        List<LearnKnowledgeTree> ktList = learnKnowledgeTreeMapper.selectLearnKnowledgeTreeList(ktCond);
+        Map<Long, Integer[]> knowCountMap = null;
+        if(count) {
+            Map cond = Maps.newHashMap();
+            cond.put("studentId", SecurityUtils.getUserId());
+            cond.put("knowIds", ktList.stream().map(LearnKnowledgeTree::getId).collect(Collectors.toList()));
+            cond.put("types", Lists.newArrayList("单选题", "判断题"));
+            knowCountMap = Maps.newHashMap();
+            for(LearnQuestions qs : learnQuestionsMapper.statByKnowledge(cond)) {
+                knowCountMap.put(qs.getKnowledgeId(), new Integer[] { qs.getNumber().intValue(), qs.getId().intValue()} );
+            }
+        }
+        return buildTree(ktList, knowledgeIdSet, knowCountMap);
+    }
+
+    public String updateDirected(Long studentId, Long universityId, Long planId) {
+        LearnStudent upStudent = new LearnStudent();
+        upStudent.setStudentId(studentId);
+        upStudent.setUniversityId(universityId);
+        upStudent.setMajorPlanId(planId);
+        upStudent.setDirectKey("");
+        AMarjorPlan plan = marjorPlanMapper.selectAMarjorPlanById(planId);
+        LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
+        dkCond.setUniversityId(plan.getUniversityId());
+        dkCond.setYear(plan.getYear());
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        dkCond.setLocations(sysUser.getLocation());
+        dkCond.setExamineeTypes(sysUser.getExamType().name());
+        Map<String, LearnDirectedKnowledge> directedKnowledgeMap = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond).stream().collect(Collectors.toMap(LearnDirectedKnowledge::getDirectKey, t -> t));
+        LearnDirectedKnowledge directedKnowledge = getBestLearnDirectedKnowledge(plan, directedKnowledgeMap);
+        String modules = null;
+        if(null != directedKnowledge) {
+            upStudent.setDirectKey(directedKnowledge.getDirectKey());
+            modules = directedKnowledge.getModules();
+        }
+        learnStudentMapper.updateLearnStudent(upStudent);
+        Set<String> moduleSet = Sets.newHashSet(StringUtils.trimToEmpty(modules).split(","));
+        return moduleSet.size() == 1 && moduleSet.contains("技能展示") ? "专业只考技能展示" : "";
+    }
+
+    private LearnDirectedKnowledge getBestLearnDirectedKnowledge(AMarjorPlan plan, Map<String, LearnDirectedKnowledge> directedKnowledgeMap) {
+        LearnDirectedKnowledge directedKnowledge = null;
+        String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
+        if(null != (directedKnowledge = directedKnowledgeMap.get(groupName + "_" + plan.getMajorName()))) {
+            return directedKnowledge;
+        }
+        for(String directKey : directedKnowledgeMap.keySet()) {
+            if(directKey.contains("、") && (directKey.startsWith(groupName + "_"))) {
+                Set<String> marjorSet = Arrays.stream(directKey.split("_|、")).filter(t -> !t.isEmpty()).collect(Collectors.toSet());
+                if(marjorSet.contains(plan.getMajorName())) {
+                    return directedKnowledgeMap.get(directKey);
+                }
+            }
+        }
+        if(null != (directedKnowledge = directedKnowledgeMap.get(groupName))) {
+            return directedKnowledge;
+        }
+        if(null != (directedKnowledge = directedKnowledgeMap.get(""))) {
+            return directedKnowledge;
+        }
+        return directedKnowledge;
+    }
+
+    public Set<Long> getSubjectIdSet(Long[] planIds) {
+        Set<Long> subjectIdSet = Sets.newHashSet();
         if(ArrayUtils.isNotEmpty(planIds)) {
             List<AMarjorPlan> planList = marjorPlanMapper.selectAMarjorPlanByIds(planIds);
             AMarjorPlan curr = planList.get(0);
             LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
             dkCond.setUniversityId(curr.getUniversityId());
             dkCond.setYear(curr.getYear());
+            SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+            dkCond.setLocations(sysUser.getLocation());
+            dkCond.setExamineeTypes(sysUser.getExamType().name());
             Map<String, LearnDirectedKnowledge> directedKnowledgeMap = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond).stream().collect(Collectors.toMap(LearnDirectedKnowledge::getDirectKey, t -> t));
             LearnDirectedKnowledge directedKnowledge = null;
             for(AMarjorPlan plan : planList) {
-                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
-                if(null != (directedKnowledge = directedKnowledgeMap.get(groupName + "_" + plan.getMajorName()))) {
-                    knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
-                } else if(null != (directedKnowledge = directedKnowledgeMap.get(groupName))) {
+                if(null != (directedKnowledge = getBestLearnDirectedKnowledge(plan, directedKnowledgeMap))) {
+                    subjectIdSet.addAll(Stream.of(directedKnowledge.getSubjects().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
+                }
+            }
+        }
+        return subjectIdSet;
+    }
+
+    public Set<Long> getKnowledgeIdSet(Long[] planIds) {
+        Set<Long> knowledgeIdSet = Sets.newHashSet();
+        if(ArrayUtils.isNotEmpty(planIds) && null != planIds[0]) {
+            List<AMarjorPlan> planList = marjorPlanMapper.selectAMarjorPlanByIds(planIds);
+            if(CollectionUtils.isEmpty(planList)) {
+                return knowledgeIdSet;
+            }
+            AMarjorPlan curr = planList.get(0);
+            LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
+            dkCond.setUniversityId(curr.getUniversityId());
+            dkCond.setYear(curr.getYear());
+            SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+            dkCond.setLocations(sysUser.getLocation());
+            dkCond.setExamineeTypes(sysUser.getExamType().name());
+            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond).stream().collect(Collectors.toMap(LearnDirectedKnowledge::getDirectKey, t -> t));
+            LearnDirectedKnowledge directedKnowledge = null;
+            for(AMarjorPlan plan : planList) {
+                if(null != (directedKnowledge = getBestLearnDirectedKnowledge(plan, directedKnowledgeMap))) {
                     knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
                 }
             }
         }
-        LearnKnowledgeTree ktCond = new LearnKnowledgeTree();
-        ktCond.setSubjectId(subjectId);
-        List<LearnKnowledgeTree> ktList = learnKnowledgeTreeMapper.selectLearnKnowledgeTreeList(ktCond);
+        return knowledgeIdSet;
+    }
+
+    public List<TreeNode> buildTree(List<LearnKnowledgeTree> ktList, Set<Long> knowledgeIdSet, Map<Long, Integer[]> knowCountMap) {
         List<TreeNode> treeNodeList = Lists.newArrayList();
-        Map<Long, TreeNode> teMap = ktList.stream().collect(Collectors.toMap(LearnKnowledgeTree::getId, t -> new TreeNode(t.getId(), t.getName())));
+        Map<Long, TreeNode> teMap = Maps.newHashMap();
+        for(LearnKnowledgeTree kt : ktList) {
+            TreeNode tn = new TreeNode(kt.getId(), kt.getName(), null != knowCountMap ? knowCountMap.get(kt.getId()) : null);
+            teMap.put(kt.getId(), tn);
+        }
         for(LearnKnowledgeTree kt : ktList) {
             if(null == kt.getPid()) {
                 treeNodeList.add(teMap.get(kt.getId()));
                 continue;
             }
-            teMap.get(kt.getPid()).getChildren().add(teMap.get(kt.getId()));
+            TreeNode parent = teMap.get(kt.getPid());
+            TreeNode node = teMap.get(kt.getId());
+            if(null != node.getQuestionCount()) {
+                parent.setQuestionCount(parent.getQuestionCount() + node.getQuestionCount());
+            }
+            parent.getChildren().add(node);
         }
-        for(TreeNode tn : treeNodeList) {
-            tn.setChecked(knowledgeIdSet);
+        if(!CollectionUtils.isEmpty(knowledgeIdSet)) {
+            for(TreeNode tn : treeNodeList) {
+                tn.setChecked(knowledgeIdSet);
+            }
         }
         return treeNodeList;
     }
@@ -312,11 +413,16 @@ public class LearnTeacherService {
         private Long id;
         private String name;
         private Integer status;
+        private Integer questionCount;
+        private Integer finishedCount;
+
         List<TreeNode> children = Lists.newArrayList();
-        public TreeNode(Long id, String name) {
+        public TreeNode(Long id, String name, Integer[] counts) {
             this.id = id;
             this.name = name;
             this.status = 0;
+            this.questionCount = null != counts ? counts[0] : 0;
+            this.finishedCount = null != counts ? counts[1] : 0;
         }
 
         public boolean setChecked(Set<Long> idSet) {

+ 215 - 45
ie-admin/src/main/java/com/ruoyi/web/service/PaperService.java

@@ -5,12 +5,18 @@ import com.alibaba.fastjson2.JSONObject;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.enums.PaperStatus;
 import com.ruoyi.enums.PaperType;
+import com.ruoyi.enums.QuestionType;
 import com.ruoyi.learn.domain.*;
 import com.ruoyi.learn.mapper.*;
+import com.ruoyi.learn.service.ILearnQuestionsService;
+import com.ruoyi.syzy.domain.BBusiWishUniversities;
+import com.ruoyi.syzy.service.IBBusiWishUniversitiesService;
 import lombok.Data;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.springframework.stereotype.Service;
 
@@ -27,12 +33,19 @@ public class PaperService {
     private final LearnPaperMapper paperMapper;
     private final LearnPaperQuestionMapper paperQuestionMapper;
     private final LearnQuestionsMapper questionsMapper;
+    private final ILearnQuestionsService learnQuestionsService;
+    private final LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper;
+    private final IBBusiWishUniversitiesService wishUniversitiesService;
 
 
-    PaperService(LearnPaperMapper paperMapper, LearnPaperQuestionMapper paperQuestionMapper, LearnQuestionsMapper questionsMapper) {
+    PaperService(LearnPaperMapper paperMapper, LearnPaperQuestionMapper paperQuestionMapper, LearnQuestionsMapper questionsMapper, ILearnQuestionsService learnQuestionsService, LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper, IBBusiWishUniversitiesService wishUniversitiesService) {
         this.paperMapper = paperMapper;
         this.paperQuestionMapper = paperQuestionMapper;
         this.questionsMapper = questionsMapper;
+        this.learnQuestionsService = learnQuestionsService;
+        this.learnDirectedKnowledgeMapper = learnDirectedKnowledgeMapper;
+        // buildSimulatedPaper(20154L, 11L);
+        this.wishUniversitiesService = wishUniversitiesService;
     }
 
     public void test() {
@@ -46,28 +59,130 @@ public class PaperService {
 //        getQuestions(1L, 100L, paperDef);
     }
 
+    public PaperVO loadPaper(Long paperId) {
+        LearnPaper learnPaper = paperMapper.selectLearnPaperById(paperId);
+        PaperVO result = new PaperVO();
+        BeanUtils.copyProperties(learnPaper, result);
+        result.setQuestions(loadPaperQuestions(paperId));
+        return result;
+    }
+
     /**
      * 加载试卷
      * @param paperId
      * @return
      */
-    public PaperVO loadPaper(Long paperId) {
-        PaperVO result = new PaperVO();
-        LearnPaper learnPaper = paperMapper.selectLearnPaperById(paperId);
-        BeanUtils.copyProperties(learnPaper, result);
+    public List<PaperVO.QuestionSeq> loadPaperQuestions(Long paperId) {
         List<LearnQuestions> questions = questionsMapper.selectQuestionByPaperId(paperId);
-        result.setQuestions(questions.stream().map(t -> {
+        Map<String, PaperVO.QuestionSeq> gropuMap = Maps.newHashMap();
+        List<PaperVO.QuestionSeq> paperQuestionList = Lists.newArrayList();
+        for(LearnQuestions lqs : questions) {
             PaperVO.QuestionSeq qs = new PaperVO.QuestionSeq();
-            BeanUtils.copyProperties(t, qs);
-            return qs;
-        }).collect(Collectors.toList()));
-        return result;
+            BeanUtils.copyProperties(lqs, qs, "options", "parse", "answer1", "answer2");
+            QuestionType qt = QuestionType.of(lqs.getQtpye());
+            qs.setTypeId(qt.getVal());
+            qs.setType(qt.getTitle());
+            qs.setOptions(StringUtils.getOptions(lqs.getOptionA(), lqs.getOptionB(), lqs.getOptionC(), lqs.getOptionD(), lqs.getOptionE(), lqs.getOptionF(), lqs.getOptionG()));
+            if(StringUtils.isNotBlank(lqs.getTitle0())) { // 大题
+                PaperVO.QuestionSeq qg = gropuMap.get(lqs.getTitle0());
+                if(qg == null) {
+                    qg = new PaperVO.QuestionSeq();
+                    qg.setTypeId(99);
+                    qg.setSubQuestions(Lists.newArrayList());
+                    qg.setTitle(lqs.getTitle0());
+                    gropuMap.put(lqs.getTitle0(), qg);
+                    paperQuestionList.add(qg);
+                }
+                qg.getSubQuestions().add(qs);
+            } else {
+                paperQuestionList.add(qs);
+            }
+        }
+        return paperQuestionList;
+    }
+
+    public List<PaperVO.QuestionAnswer> loadPaperQuestionAnswers(Long userId, Long paperId, Map<Long, LearnAnswer> answerMap, boolean withParse) {
+        List<LearnQuestions> questions = questionsMapper.selectQuestionByPaperId(paperId);
+        learnQuestionsService.fillCollectInfo(userId, questions);
+        Map<String, PaperVO.QuestionAnswer> gropuMap = Maps.newHashMap();
+        List<PaperVO.QuestionAnswer> paperQuestionList = Lists.newArrayList();
+        LearnAnswer answer;
+        for(LearnQuestions lqs : questions) {
+            PaperVO.QuestionAnswer qs = new PaperVO.QuestionAnswer();
+            if(withParse) {
+                BeanUtils.copyProperties(lqs, qs, "title", "options");
+            } else {
+                BeanUtils.copyProperties(lqs, qs, "title", "options", "parse", "answer1", "answer2");
+            }
+            QuestionType qt = QuestionType.of(lqs.getQtpye());
+            qs.setTypeId(qt.getVal());
+            qs.setIsFavorite(lqs.isCollect());
+            if(null != answerMap && null != (answer = answerMap.get(lqs.getId()))) {
+                qs.setAnswerId(answer.getAnswerId());
+                qs.setAnswers(Arrays.asList(answer.getAnswer().split(",")));
+                qs.setState(answer.getState());
+                qs.setIsMark(answer.getMark());
+                qs.setIsNotKnow(answer.getNotKnow());
+            }
+            if(StringUtils.isNotBlank(lqs.getTitle0())) { // 大题
+                PaperVO.QuestionAnswer qg = gropuMap.get(lqs.getTitle0());
+                if(qg == null) {
+                    qg = new PaperVO.QuestionAnswer();
+                    qg.setTypeId(99);
+                    qg.setSubQuestions(Lists.newArrayList());
+                    qg.setTitle(lqs.getTitle0());
+                    gropuMap.put(lqs.getTitle0(), qg);
+                    paperQuestionList.add(qg);
+                }
+                qg.getSubQuestions().add(qs);
+            } else {
+                paperQuestionList.add(qs);
+            }
+        }
+        return paperQuestionList;
     }
 
     /**
      * 根据院校专业要求生成模拟试卷
      * @return
      */
+
+    public int buildSimulatedPaper(Long universityId, Long subjectId) {
+        LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
+        dkCond.setUniversityId(universityId);
+        List<LearnDirectedKnowledge> directedKnowledgeList = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond);
+        BBusiWishUniversities universities = wishUniversitiesService.selectBBusiWishUniversitiesById(universityId);
+        for(LearnDirectedKnowledge dk : directedKnowledgeList) {
+            TestPaperVO.PaperDef paperDef = new TestPaperVO.PaperDef();
+            paperDef.setTotal(120L);  // 120题
+            paperDef.setFillExclude(false);
+            paperDef.setKnowIds(Stream.of(dk.getKnowledges().split(",")).map(Long::parseLong).collect(Collectors.toList()));
+            List<TestPaperVO.TypeDef> typeDefList= Lists.newArrayList();
+            typeDefList.add(new TestPaperVO.TypeDef("单选题", "单选题", 40, 3));
+            typeDefList.add(new TestPaperVO.TypeDef("多选题", "多选题", 40, 3));
+            typeDefList.add(new TestPaperVO.TypeDef("判断题", "判断题", 40, 3));
+            paperDef.setTypes(typeDefList);
+
+            LearnPaper paper = new LearnPaper();
+            paper.setSubjectId(subjectId);
+            paper.setPaperName(StringUtils.isNotBlank(dk.getDirectKey()) ? universities.getName() + "(" + dk.getDirectKey() + ")" : universities.getName());
+            paper.setPaperType(PaperType.Simulated.name());
+            paper.setRelateId(dk.getId()); // 定向ID
+            paper.setYear(dk.getYear());
+            paper.setStatus(PaperStatus.Valid.getVal());
+            paper.setDirectKey(universityId + "_" + dk.getExamineeTypes() + "_" + dk.getDirectKey());
+            paper.setNumber(120);
+            paper.setFenshu(360);
+            AnswerSheet.PaperCond info = new AnswerSheet.PaperCond();
+            info.setScore(360);
+            info.setTime(90 * 60);
+            info.setTypes(typeDefList.stream().map(t -> new AnswerSheet.PaperCondType(t.getType(), t.getCount(), t.getScore())).collect(Collectors.toList()));
+            paper.setPaperInfo(JSONObject.toJSONString(info));
+            Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = buildPaper(null, paper, paperDef);
+            savePaper(paperResult.getKey(), paperResult.getValue());
+        }
+        return 0;
+    }
     public Pair<LearnPaper, List<LearnPaperQuestion>> buildSimulatedPaper(LearnDirectedKnowledge directedKnowledge) {
         LearnPaper paper = new LearnPaper();
         paper.setPaperType(PaperType.Simulated.name());
@@ -105,7 +220,6 @@ public class PaperService {
      */
     public LearnPaper savePaper(LearnPaper paper, List<LearnPaperQuestion> pqList) {
         paper.setNumber(pqList.size());
-        paper.setFenshu(0);
         paperMapper.insertLearnPaper(paper);
         Long paperId = paper.getId();
         pqList.stream().forEach(t -> {
@@ -116,7 +230,7 @@ public class PaperService {
     }
 
     /**
-     * 动态组卷查题
+     * 按知识点,题型平均分配组卷
      * @param studentId
      * @param paperDef
      * @return
@@ -124,27 +238,21 @@ public class PaperService {
     public List<LearnPaperQuestion> getQuestions(Long studentId, TestPaperVO.PaperDef paperDef) {
         // 题型分布定义, 知识点列表, 分值定义
         // 统计知识点+类型的有效数量 TODO 总量可以缓存
-        Map<String, KnowTypeAssign> knowTypeAssignMap = Maps.newHashMap();
-        List<String> typeSet = paperDef.getTypes().stream().map(TestPaperVO.TypeDef::getType).collect(Collectors.toList());
-        Map cond = Maps.newHashMap();
-        cond.put("studentId", studentId);
-        cond.put("knowIds", paperDef.getKnowIds());
-        cond.put("types", typeSet);
-        setValue(knowTypeAssignMap, cond, 1); // 填充排除总量
-        if (paperDef.getFillExclude()) {
-            cond.remove("studentId");
-            setValue(knowTypeAssignMap, cond, 2); // 按需填充总量
-        }
+        Map<String, KnowTypeAssign> knowTypeAssignMap = buildKnowTypeAssignMap(studentId, paperDef);
+        assignKnowFirst(paperDef, knowTypeAssignMap); // 知识优先,类型可变
+        return getQuestions(studentId, paperDef, knowTypeAssignMap);
+    }
 
+    private void assignKnowFirst(TestPaperVO.PaperDef paperDef, Map<String, KnowTypeAssign> knowTypeAssignMap) {
         // 循环补充未做+已做,如果知识点总数不够时才填充其他知识点的
         Long lackTotal = paperDef.getTotal();
         AtomicLong assignCount = new AtomicLong(0);
         Map<Long, Integer> knowTypesMap = Maps.newHashMap();
         int typeCount = paperDef.getTypes().size();
+        Set<Long> knowSet = knowTypeAssignMap.values().stream().map(KnowTypeAssign::getKnowId).collect(Collectors.toSet());
         for (Long knowId : paperDef.getKnowIds()) {
-            knowTypesMap.put(knowId, typeCount);
+            knowTypesMap.put(knowId, knowSet.contains(knowId) ? typeCount : 0);
         }
-        Set<Long> knowSet = Sets.newHashSet(paperDef.getKnowIds());
         Long minKnowTypeCount = Long.MAX_VALUE;
         do {
             Integer knowCount = knowSet.size();
@@ -174,44 +282,104 @@ public class PaperService {
                 break;
             }
         } while (true);
+    }
 
+    /**
+     * 根据计划的分配数生成题关系
+     * @param studentId
+     * @param paperDef
+     * @param knowTypeAssignMap
+     * @return
+     */
+    public List<LearnPaperQuestion> getQuestions(Long studentId, TestPaperVO.PaperDef paperDef, Map<String, KnowTypeAssign> knowTypeAssignMap) {
         // 知识点已经分配,准备题型分配
         LearnQuestions qCond = new LearnQuestions();
         Random random = new Random();
         List<LearnPaperQuestion> pqList = Lists.newArrayList();
         Set<Long> existQuestionIdSet = Sets.newHashSet();
+        int total = paperDef.getTotal().intValue();
         for (TestPaperVO.TypeDef typeDef : paperDef.getTypes()) {
             for (Long knowId : paperDef.getKnowIds()) {
                 String key = knowId + "_" + typeDef.getType();
                 KnowTypeAssign ktc = knowTypeAssignMap.get(key);
+                if(null == ktc) {
+                    continue;
+                }
                 qCond.setKnowledgeId(ktc.getKnowId());
                 qCond.setQtpye(ktc.getType());
                 if(ktc.exclAssign > 0){
                     qCond.setId(studentId);
-                    qCond.setNumber(ktc.assign > 500 ? (long) random.nextInt(ktc.exclAssign.intValue() - 500) :  0L);
+                    qCond.setNumber(ktc.exclAssign > 500 ? (long) random.nextInt(ktc.exclAssign.intValue() - 500) :  0L);
                     List<LearnQuestions> questions = questionsMapper.selectQuestionsForPaper(qCond);
-                    addRandomList(questions, random, paperDef.getTotal(), ktc.exclAssign, typeDef.getScore(), existQuestionIdSet, pqList);
+                    ktc.exclAssign = addRandomList(knowId, ktc.getType(), questions, random, paperDef.getTotal(), ktc.exclAssign, typeDef.getScore(), existQuestionIdSet, pqList);
+                    if(pqList.size() == total) {
+                        break;
+                    }
                 }
                 if(ktc.assign > 0L) {
                     qCond.setId(null);
                     qCond.setNumber(ktc.assign > 500 ? (long) random.nextInt(ktc.assign.intValue() - 500) :  0L);
                     List<LearnQuestions> questions = questionsMapper.selectQuestionsForPaper(qCond);
-                    addRandomList(questions, random, paperDef.getTotal(), ktc.exclAssign, typeDef.getScore(), existQuestionIdSet, pqList);
+                    ktc.assign = addRandomList(knowId, ktc.getType(), questions, random, paperDef.getTotal(), ktc.assign, typeDef.getScore(), existQuestionIdSet, pqList);
+                    if(pqList.size() == total) {
+                        break;
+                    }
                 }
-
+            }
+            if(pqList.size() == total) {
+                break;
             }
         }
+        if(CollectionUtils.isEmpty(pqList)) {
+            throw new RuntimeException("题数不足");
+        }
         return pqList;
     }
 
-    private void addRandomList(List<LearnQuestions> questions, Random random, Long totalCount, Long count, Integer score, Set<Long> existQuestionIdSet, List<LearnPaperQuestion> pqList) {
-        while(count > 0 && questions.size() > 0) {
+
+    /**
+     * 初始化当前用户卷情况
+     * @param studentId
+     * @param paperDef
+     * @return
+     */
+    private Map<String, KnowTypeAssign> buildKnowTypeAssignMap(Long studentId, TestPaperVO.PaperDef paperDef) {
+        Map<String, KnowTypeAssign> knowTypeAssignMap = Maps.newHashMap();
+        List<String> typeSet = paperDef.getTypes().stream().map(TestPaperVO.TypeDef::getType).collect(Collectors.toList());
+        Map cond = Maps.newHashMap();
+        cond.put("studentId", studentId);
+        cond.put("knowIds", paperDef.getKnowIds());
+        cond.put("types", typeSet);
+        setValue(knowTypeAssignMap, cond, 1); // 填充排除后总量
+        if (null != studentId && paperDef.getFillExclude()) {
+            cond.remove("studentId");
+            setValue(knowTypeAssignMap, cond, 2); // 按需填充已做总量
+        }
+        return knowTypeAssignMap;
+    }
+
+    /**
+     * 随机从 knowId 题池中提取需要个数的题
+     * @param knowId 知识点
+     * @param type 类型
+     * @param questions 题池
+     * @param random 随机数
+     * @param totalCount 总题数
+     * @param count  本池分配数
+     * @param score 题分
+     * @param existQuestionIdSet 不能使用的题
+     * @param pqList 卷题关系
+     */
+    private Long addRandomList(Long knowId, String type, List<LearnQuestions> questions, Random random, Long totalCount, Long count, Integer score, Set<Long> existQuestionIdSet, List<LearnPaperQuestion> pqList) {
+        while(count > 0L && !questions.isEmpty()) {
             LearnQuestions q = questions.size() > 1 ? questions.remove(random.nextInt(questions.size() - 1)) : questions.remove(0);
             if(existQuestionIdSet.add(q.getId())) {
                 LearnPaperQuestion pq = new LearnPaperQuestion();
                 pq.setSeq(pqList.size() + 1);
+                pq.setKnowledgeId(knowId);
                 pq.setScore(score);
                 pq.setQuestionId(q.getId());
+                pq.setType(type);
                 pqList.add(pq);
                 count--;
                 if(pqList.size() == totalCount) {
@@ -219,6 +387,7 @@ public class PaperService {
                 }
             }
         }
+        return count;
     }
 
     /**
@@ -235,7 +404,7 @@ public class PaperService {
         String key = knowId + "_" + qtype;
         KnowTypeAssign knowTypeAssign = knowTypeAssignMap.get(key);
         long lackCount;
-        if (knowTypeAssign.exclCount > 0) {
+        if (null != knowTypeAssign && knowTypeAssign.exclCount > 0) {
             lackCount = knowTypeCount - knowTypeAssign.exclCount;
             if (lackCount <= 0) { // 足量
                 assignCount.getAndAdd(knowTypeCount);
@@ -251,7 +420,7 @@ public class PaperService {
         }
         long lack = 0;
         if (lackCount > 0) { // 差额优先补充已做过的
-            if(fillExclude && knowTypeAssign.total > 0) {
+            if(fillExclude && null != knowTypeAssign && knowTypeAssign.total > 0) {
                 lack = lackCount - knowTypeAssign.total;
                 if (lack <= 0) { // 足量
                     assignCount.getAndAdd(lackCount);
@@ -275,10 +444,10 @@ public class PaperService {
     }
 
     /**
-     * 合并 排除总量和总量
-     * @param knowTypeAssignMap
+     * 合并 未用总量和已有总量, 分配数置0
+     * @param knowTypeAssignMap key=knowId +"_" + qtype
      * @param cond
-     * @param index 1 free 2 total
+     * @param index 1 free 2 used
      */
     private void setValue(Map<String, KnowTypeAssign> knowTypeAssignMap, Map cond, Integer index) {
         for (LearnQuestions q : questionsMapper.statByKnowledgeType(cond)) {
@@ -288,12 +457,13 @@ public class PaperService {
                 knowTypeAssign = new KnowTypeAssign();
                 knowTypeAssign.setKnowId(q.getKnowledgeId());
                 knowTypeAssign.setType(q.getQtpye());
-                knowTypeAssignMap.put(key, knowTypeAssign);
-            }
-            if (1 == index) {
                 knowTypeAssign.total = 0L;
                 knowTypeAssign.exclAssign = 0L;
                 knowTypeAssign.assign = 0L;
+                knowTypeAssign.exclCount = 0L;
+                knowTypeAssignMap.put(key, knowTypeAssign);
+            }
+            if (1 == index) {
                 knowTypeAssign.exclCount = q.getNumber();
             } else {
                 knowTypeAssign.total = q.getNumber() - knowTypeAssign.exclCount;
@@ -303,11 +473,11 @@ public class PaperService {
 
     @Data
     public static class KnowTypeAssign {
-        Long knowId;
-        String type;
-        Long exclAssign; // 分配
-        Long assign; // 全量分配个
-        Long exclCount; // 排除总数
-        Long total; // 全量总数
+        Long knowId; // 知识点
+        String type; // 题类型
+        Long exclAssign; // 未用分配数
+        Long assign; // 已用分配
+        Long exclCount; // 未用总量
+        Long total; // 已用总量
     }
 }

+ 105 - 0
ie-admin/src/main/java/com/ruoyi/web/service/StudentService.java

@@ -0,0 +1,105 @@
+package com.ruoyi.web.service;
+
+import cn.hutool.core.lang.Dict;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.util.DateUtils;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.AjaxResult2;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.domain.DzSubject;
+import com.ruoyi.dz.service.IDzSubjectService;
+import com.ruoyi.enums.ExamineeStatus;
+import com.ruoyi.enums.PaperType;
+import com.ruoyi.learn.domain.LearnExaminee;
+import com.ruoyi.learn.domain.LearnPaper;
+import com.ruoyi.learn.domain.LearnStudent;
+import com.ruoyi.learn.domain.LearnTestStudent;
+import com.ruoyi.learn.mapper.LearnExamineeMapper;
+import com.ruoyi.learn.mapper.LearnPaperMapper;
+import com.ruoyi.learn.mapper.LearnStudentMapper;
+import com.ruoyi.learn.mapper.LearnTestStudentMapper;
+import com.ruoyi.learn.service.ILearnStudentService;
+import com.ruoyi.system.service.ISysUserService;
+import org.apache.commons.compress.utils.Lists;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class StudentService {
+    private final LearnPaperMapper learnPaperMapper;
+    private final LearnTestStudentMapper learnTestStudentMapper;
+    private final ILearnStudentService learnStudentService;
+    private final IDzSubjectService dzSubjectService;
+    private final LearnTeacherService learnTeacherService;
+    private final LearnExamineeMapper learnExamineeMapper;
+
+    public StudentService(LearnPaperMapper learnPaperMapper, LearnTestStudentMapper learnTestStudentMapper, ILearnStudentService learnStudentService, IDzSubjectService dzSubjectService, LearnTeacherService learnTeacherService, LearnExamineeMapper learnExamineeMapper) {
+        this.learnPaperMapper = learnPaperMapper;
+        this.learnTestStudentMapper = learnTestStudentMapper;
+        this.learnStudentService = learnStudentService;
+        this.dzSubjectService = dzSubjectService;
+        this.learnTeacherService = learnTeacherService;
+        this.learnExamineeMapper = learnExamineeMapper;
+    }
+
+    public List<DzSubject> getSubjectList(boolean directed) {
+        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
+        DzSubject sCond = new DzSubject();
+        sCond.setLocations(sysUser.getLocation());
+        sCond.setExamTypes(sysUser.getExamType().name());
+        List<DzSubject> list = dzSubjectService.selectDzSubjectList(sCond);
+        if (!directed) {
+            return list;
+        }
+        LearnStudent learnStudent = learnStudentService.selectLearnStudentByStudentId(SecurityUtils.getUserId());
+        Set<Long> subjectIdSet;
+        if (null == learnStudent || CollectionUtils.isEmpty((subjectIdSet = learnTeacherService.getSubjectIdSet(new Long[]{learnStudent.getMajorPlanId()})))) {
+            return list;
+        }
+        return list.stream().filter(t -> subjectIdSet.contains(t.getSubjectId())).collect(Collectors.toList());
+    }
+
+    public List<Dict> selectStatsForStudent(Long studentId) {
+        Integer undone = 0, done = 0;
+        for(LearnTestStudent ts : learnTestStudentMapper.selectStatsForStudent(studentId)) {
+            if(ExamineeStatus.Init.getVal().equals(ts.getStatus()) || ExamineeStatus.Exam.getVal().equals(ts.getStatus())) {
+                undone += ts.getCount();
+            } else {
+                done += ts.getCount();
+            }
+        }
+        List<Dict> list = Lists.newArrayList();
+        list.add(Dict.create().set("全部", undone + done));
+        list.add(Dict.create().set("未完成", undone));
+        list.add(Dict.create().set("已完成", done));
+        return list;
+    }
+
+    public List<LearnPaper> selectLearnPaperForStudent(Long studentId, Integer status) {
+        return learnPaperMapper.selectLearnPaperForStudent(studentId, status);
+    }
+
+    public List<JSONObject> getSimulateList(Long studentId) {
+        LearnExaminee eCond = new LearnExaminee();
+        eCond.setStudentId(studentId);
+        eCond.setPaperType(PaperType.Simulated.getVal());
+
+        List<JSONObject> list = learnExamineeMapper.selectLearnExamineeList(eCond).stream().map(t -> {
+            JSONObject info = JSONObject.parseObject(t.getPaperInfo());
+            DzSubject dzSubject = dzSubjectService.selectDzSubjectBySubjectId(Long.parseLong(t.getPaperKey()));
+            String name = info.getString("universityName") + "-" + info.getString("majorName");
+            return JSONObject.of("id", t.getExamineeId(), "state", t.getState()
+                    , "subjectName", dzSubject.getSubjectName()
+                    , "name", name
+                    , "date", DateUtils.format(null != t.getEndTime() ? t.getEndTime() : t.getBeginTime(), "yyyy-MM-dd HH:mm")
+                    , "score", t.getScore());
+        }).collect(Collectors.toList());
+
+        return list;
+    }
+}

+ 40 - 4
ie-admin/src/main/java/com/ruoyi/web/service/SysLoginService.java

@@ -4,11 +4,18 @@ import javax.annotation.Resource;
 
 import com.google.common.collect.Maps;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.enums.AccessFromType;
 import com.ruoyi.common.enums.ErrorCodes;
 import com.ruoyi.common.exception.ErrorException;
 import com.ruoyi.common.utils.PhoneUtils;
+import com.ruoyi.dz.domain.DzCards;
+import com.ruoyi.dz.service.IDzCardsService;
+import com.ruoyi.dz.service.impl.DzCardsServiceImpl;
 import com.ruoyi.framework.web.service.TokenService;
 import com.ruoyi.system.service.ShortMessageService;
+import com.ruoyi.web.controller.dz.DzCardsController;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -38,11 +45,13 @@ import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.service.ISysUserService;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
 /**
  * 登录校验方法
- * 
+ *
  * @author ruoyi
  */
 @Component
@@ -56,7 +65,7 @@ public class SysLoginService
 
     @Autowired
     private RedisCache redisCache;
-    
+
     @Autowired
     private ISysUserService userService;
 
@@ -65,6 +74,8 @@ public class SysLoginService
 
     @Autowired
     private ShortMessageService shortMessageService;
+    @Autowired
+    private IDzCardsService cardsService;
 
     /**
      * 登录验证
@@ -75,8 +86,9 @@ public class SysLoginService
      * @param uuid
      * @return
      */
-    public AjaxResult login(String mobile, String username, String password, String code, String uuid) {
+    public AjaxResult login(String mobile, String username, String password, String code, String uuid,String type) {
         if (StringUtils.isNotBlank(mobile)) {
+            //手机验证码登录:{code: "9", uuid: "cc94320c3fce4db5898213b727ac1dc0", mobile: "18774924158", password: "1234"}
             if (!shortMessageService.checkCode(mobile, password)) {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobile, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                 throw new CaptchaException();
@@ -86,9 +98,33 @@ public class SysLoginService
             password = UserConstants.LOGIN_SMS_PASS;
         } else {
             // 验证码校验
+            // 卡密登录json:{code: "0", uuid: "a9eee0b6729f42c4862a57acef8a4bdd", username: "20000025", password: "123456"}
             validateCaptcha(username, code, uuid);
             // 登录前置校验
             loginPreCheck(username, password);
+            if(AccessFromType.isFrontApp(type)||AccessFromType.isH5(type)||AccessFromType.isWechat(type)){
+                //卡密登录时,要将卡的密码转换为用户的密码。手机号的密码与卡的密码分开
+                DzCards card = cardsService.selectDzCardsByCardNo(username);
+                if (null==card){
+                    if(username.length()>6&&username.length()<8){
+                        //老师账号,直接跳过
+
+                    }else if(username.length()>=8){
+                        //卡用户
+                        return AjaxResult.error("卡不存在");
+                    }
+                }
+                if(null!=card&&username.length()>=8){
+                    //卡用户
+                    if (!password.trim().equalsIgnoreCase(card.getPassword())){
+                        return AjaxResult.error("密码错误");
+                    }
+                    String dbPwd = userService.selectPasswordByCardId(card.getCardId());
+                    if (StringUtils.isNotEmpty(dbPwd)){
+                        password = dbPwd;
+                    }
+                }
+            }
         }
         // 用户验证
         Authentication authentication = null;
@@ -170,7 +206,7 @@ public class SysLoginService
 
     /**
      * 校验验证码
-     * 
+     *
      * @param username 用户名
      * @param code 验证码
      * @param uuid 唯一标识

+ 34 - 1
ie-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java

@@ -1,5 +1,6 @@
 package com.ruoyi.web.service;
 
+import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.model.LoginCard;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.enums.UserRegStatus;
@@ -7,8 +8,11 @@ import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.dz.domain.DzCards;
 import com.ruoyi.dz.service.IDzCardsService;
+import com.ruoyi.enums.CardStatus;
 import com.ruoyi.enums.UserTypeEnum;
 import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.learn.domain.LearnStudent;
+import com.ruoyi.learn.mapper.LearnStudentMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import com.ruoyi.common.constant.CacheConstants;
@@ -25,6 +29,8 @@ import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.system.service.ISysUserService;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.text.ParseException;
+
 /**
  * 注册校验方法
  * 
@@ -47,6 +53,8 @@ public class SysRegisterService
 
     @Autowired
     private TokenService tokenService;
+    @Autowired
+    private LearnStudentMapper learnStudentMapper;
 
     /**
      * 注册
@@ -65,10 +73,14 @@ public class SysRegisterService
             if(StringUtils.isNotBlank(username)) { // 卡注册
                 DzCards exist = cardsService.selectDzCardsByCardNo(username);
                 if (StringUtils.isNull(exist)) {
-                    return "卡号不正确";
+                    return "卡号密码不正确";
+                }
+                if(StringUtils.isBlank(password) || !password.equals(exist.getPassword())) {
+                    return "卡号密码不正确";
                 }
                 upCard = new DzCards();
                 upCard.setCardId(exist.getCardId());
+                upCard.setStatus(exist.getStatus());
             } else {
                 username = mobile;
                 password = "123456";
@@ -86,6 +98,7 @@ public class SysRegisterService
             username = exist.getCardNo();
             upCard = new DzCards();
             upCard.setCardId(exist.getCardId());
+            upCard.setStatus(exist.getStatus());
             upUser.setUserId(loginUser.getUserId());
         }
         upUser.setUserName(username);
@@ -131,26 +144,46 @@ public class SysRegisterService
         user.setExamType(register.getExamType());
         user.setEndYear(register.getEndYear());
         user.setScores(register.getScores());
+        user.setSchoolId(register.getSchoolId());
+        user.setClassId(register.getClassId());
 
         if(null == card) { // 手机注册时 无卡
             user.setInviteCode(register.getInviteCode()); //
             user.setRegStatus(UserRegStatus.User);
         } else {
             card.setEndYear(register.getEndYear());
+            try {
+                card.setOutDate(DateUtils.parseDate(register.getEndYear() + "-08-30", "yyyy-MM-dd"));
+            } catch (ParseException e) {
+                throw new RuntimeException("saveInfo 日期错误", e);
+            }
             card.setYear(register.getEndYear() - 3);
             card.setSchoolId(register.getSchoolId());
             card.setClassId(register.getClassId());
 
             if(null == user.getCardId() || !user.getCardId().equals(card.getCardId())) { // 未绑定或换绑时激活卡
+                if(!CardStatus.Paid.getVal().equals(card.getStatus())) {
+                    throw new RuntimeException("无效卡");
+                }
+                user.setEvalCounts("{\"1\":3,\"2\":3,\"3\":3,\"11\":5,\"0\":0}");
                 user.setCardId(card.getCardId());
                 user.setRegStatus(UserRegStatus.Student);
+                card.setStatus(CardStatus.Active.getVal());
                 card.setActiveTime(DateUtils.getNowDate());
             }
         }
         if(null == user.getUserId()) {
             user.setUserType(UserTypeEnum.Card.getVal());
             userService.insertUser(user);
+            LearnStudent upStudent = new LearnStudent();
+            upStudent.setStudentId(user.getUserId());
+            upStudent.setClassId(user.getClassId());
+            learnStudentMapper.insertLearnStudent(upStudent);
         } else {
+            LearnStudent upStudent = new LearnStudent();
+            upStudent.setStudentId(user.getUserId());
+            upStudent.setClassId(user.getClassId());
+            learnStudentMapper.updateLearnStudent(upStudent);
             userService.updateUser(user);
         }
         if(null != card) {

+ 15 - 1
ie-admin/src/main/java/com/ruoyi/web/service/UserDetailsServiceImpl.java

@@ -7,6 +7,9 @@ import com.ruoyi.common.enums.UserRegStatus;
 import com.ruoyi.common.exception.ErrorException;
 import com.ruoyi.common.utils.PhoneUtils;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.dz.domain.DzCards;
+import com.ruoyi.dz.mapper.DzClassesMapper;
+import com.ruoyi.dz.mapper.DzSchoolMapper;
 import com.ruoyi.dz.service.IDzCardsService;
 import com.ruoyi.framework.web.service.SysPasswordService;
 import com.ruoyi.framework.web.service.SysPermissionService;
@@ -47,6 +50,10 @@ public class UserDetailsServiceImpl implements UserDetailsService
 
     @Autowired
     private IDzCardsService dzCardsService;
+    @Autowired
+    private DzSchoolMapper dzSchoolMapper;
+    @Autowired
+    private DzClassesMapper dzClassesMapper;
 
     @Override
     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
@@ -73,7 +80,14 @@ public class UserDetailsServiceImpl implements UserDetailsService
         }
         LoginCard card = new LoginCard();
         if(null != user.getCardId() && UserRegStatus.Student.equals(user.getRegStatus())) {
-            BeanUtils.copyProperties(dzCardsService.selectDzCardsByCardId(user.getCardId()), card, "password");
+            DzCards dzCards = dzCardsService.selectDzCardsByCardId(user.getCardId());
+            BeanUtils.copyProperties(dzCards, card, "password");
+            if(null != dzCards.getSchoolId()) {
+                card.setSchoolName(dzSchoolMapper.selectDzSchoolById(dzCards.getSchoolId()).getName());
+            }
+            if(null != dzCards.getClassId()) {
+                card.setClassName(dzClassesMapper.selectDzClassesByClassId(dzCards.getClassId()).getName());
+            }
         }
         LoginUser loginUser = createLoginUser(user, card);
         if(isPhoneLogin) {

+ 37 - 0
ie-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java

@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.ruoyi.common.enums.ExamType;
 import com.ruoyi.common.enums.UserRegStatus;
 import com.ruoyi.common.utils.dz.SubjectScore;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -131,10 +132,39 @@ public class SysUser extends BaseEntity
     /** 选科ID **/
     private Integer selectSubject;
 
+    @JsonIgnore
     private String directedStudy;
 
+    @JsonIgnore
+    private String evalCounts;
+
     private Long cardId;
 
+
+    /** 校区id */
+    @ApiModelProperty(name = "学校id")
+    private Long schoolId;
+    /** 校区班级ID */
+    @ApiModelProperty(name = "班级ID")
+    private Long classId;
+
+
+    public Long getSchoolId() {
+        return schoolId;
+    }
+
+    public void setSchoolId(Long schoolId) {
+        this.schoolId = schoolId;
+    }
+
+    public Long getClassId() {
+        return classId;
+    }
+
+    public void setClassId(Long classId) {
+        this.classId = classId;
+    }
+
     public SysUser()
     {
 
@@ -449,6 +479,13 @@ public class SysUser extends BaseEntity
         this.cardId = cardId;
     }
 
+    public String getEvalCounts() {
+        return evalCounts;
+    }
+
+    public void setEvalCounts(String evalCounts) {
+        this.evalCounts = evalCounts;
+    }
 
     public String getCode() {
         return String.valueOf(userId);

+ 13 - 0
ie-common/src/main/java/com/ruoyi/common/core/domain/model/LoginCard.java

@@ -1,9 +1,13 @@
 package com.ruoyi.common.core.domain.model;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.util.Date;
+
 @Data
 @ApiModel("用户卡信息")
 public class LoginCard {
@@ -14,10 +18,14 @@ public class LoginCard {
     /** 校区id */
     @ApiModelProperty(name = "学校id")
     private Long schoolId;
+    @ApiModelProperty(name = "学校名称")
+    private String schoolName;
 
     /** 校区班级ID */
     @ApiModelProperty(name = "班级ID")
     private Long classId;
+    @ApiModelProperty(name = "班级名称")
+    private String className;
 
     /** 班级/入学年份 */
     @ApiModelProperty(name = "班级/入学年份")
@@ -26,4 +34,9 @@ public class LoginCard {
     /** 高考年份 */
     @ApiModelProperty(name = "毕业年份")
     private Integer endYear;
+
+    /** 过期时间 */
+    @ApiModelProperty(name = "过期时间")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date outDate;
 }

+ 4 - 0
ie-common/src/main/java/com/ruoyi/common/utils/NumberUtils.java

@@ -31,6 +31,10 @@ public class NumberUtils {
         return true;
     }
 
+    public static boolean isPositive(Long v) {
+        return null != v && v > 0L;
+    }
+
     public static void main(String[] args) {
         System.out.println(getRandom1(8));
         System.out.println(getRandom(8));

+ 12 - 0
ie-common/src/main/java/com/ruoyi/common/utils/StringUtils.java

@@ -3,6 +3,7 @@ package com.ruoyi.common.utils;
 import java.util.*;
 import java.util.stream.Collectors;
 
+import com.google.common.collect.Lists;
 import com.ruoyi.common.core.text.Convert;
 import org.springframework.util.AntPathMatcher;
 import com.ruoyi.common.constant.Constants;
@@ -803,6 +804,17 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
             return false;
         }
     }
+
+    public static List<String> getOptions(String... options) {
+        List<String> optionList = Lists.newArrayList();
+        for (String option : options) {
+            if (StringUtils.isNotBlank(option)) {
+                optionList.add(option);
+            }
+        }
+        return optionList;
+    }
+
     public static void main(String[] args) {
         System.out.println(reverseCourse("物理,化学,生物"));
     }

+ 3 - 0
ie-common/src/main/java/com/ruoyi/common/utils/dz/SubjectScore.java

@@ -1,5 +1,6 @@
 package com.ruoyi.common.utils.dz;
 
+import com.fasterxml.jackson.annotation.JsonValue;
 import com.ruoyi.common.enums.SubjectType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -31,6 +32,8 @@ public class SubjectScore {
     Double english;
     @ApiModelProperty(value = "外语", example = "80")
     Double foreign;
+    @ApiModelProperty(value = "技能", example = "80")
+    Double skill;
 
     /** 物化生历政地 语数英外 **/
     public Double getSelectScore(String name) {

+ 1 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzAgent.java

@@ -49,6 +49,7 @@ public class DzAgent extends TreeEntity
     private String schoolName;
 
     private String username;
+    private String invitationCode;
 
     private SysDept dept;
 

+ 63 - 44
ie-system/src/main/java/com/ruoyi/dz/domain/DzCards.java

@@ -9,7 +9,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
 
 /**
  * 学习卡对象 dz_cards
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -150,52 +150,71 @@ public class DzCards extends BaseEntity
     /** 校区班级ID */
     private String className;
 
-    public void setCardId(Long cardId) 
+    private String nickName;
+    private String phonenumber;
+
+    public String getNickName() {
+        return nickName;
+    }
+
+    public void setNickName(String nickName) {
+        this.nickName = nickName;
+    }
+
+    public String getPhonenumber() {
+        return phonenumber;
+    }
+
+    public void setPhonenumber(String phonenumber) {
+        this.phonenumber = phonenumber;
+    }
+
+    public void setCardId(Long cardId)
     {
         this.cardId = cardId;
     }
 
-    public Long getCardId() 
+    public Long getCardId()
     {
         return cardId;
     }
 
-    public void setCardNo(String cardNo) 
+    public void setCardNo(String cardNo)
     {
         this.cardNo = cardNo;
     }
 
-    public String getCardNo() 
+    public String getCardNo()
     {
         return cardNo;
     }
 
-    public void setPassword(String password) 
+    public void setPassword(String password)
     {
         this.password = password;
     }
 
-    public String getPassword() 
+    public String getPassword()
     {
         return password;
     }
 
-    public void setType(Integer type) 
+    public void setType(Integer type)
     {
         this.type = type;
     }
 
-    public Integer getType() 
+    public Integer getType()
     {
         return type;
     }
 
-    public void setStatus(Integer status) 
+    public void setStatus(Integer status)
     {
         this.status = status;
     }
 
-    public Integer getStatus() 
+    public Integer getStatus()
     {
         return status;
     }
@@ -213,17 +232,17 @@ public class DzCards extends BaseEntity
         this.distributeStatus = distributeStatus;
     }
 
-    public Integer getDistributeStatus() 
+    public Integer getDistributeStatus()
     {
         return distributeStatus;
     }
 
-    public void setTimeStatus(Integer timeStatus) 
+    public void setTimeStatus(Integer timeStatus)
     {
         this.timeStatus = timeStatus;
     }
 
-    public Integer getTimeStatus() 
+    public Integer getTimeStatus()
     {
         return timeStatus;
     }
@@ -238,32 +257,32 @@ public class DzCards extends BaseEntity
         return payStatus;
     }
 
-    public void setIsSettlement(Integer isSettlement) 
+    public void setIsSettlement(Integer isSettlement)
     {
         this.isSettlement = isSettlement;
     }
 
-    public Integer getIsSettlement() 
+    public Integer getIsSettlement()
     {
         return isSettlement;
     }
 
-    public void setDeptId(Long deptId) 
+    public void setDeptId(Long deptId)
     {
         this.deptId = deptId;
     }
 
-    public Long getDeptId() 
+    public Long getDeptId()
     {
         return deptId;
     }
 
-    public void setAgentId(Long agentId) 
+    public void setAgentId(Long agentId)
     {
         this.agentId = agentId;
     }
 
-    public Long getAgentId() 
+    public Long getAgentId()
     {
         return agentId;
     }
@@ -278,32 +297,32 @@ public class DzCards extends BaseEntity
         return leafAgentId;
     }
 
-    public void setCampusId(Long campusId) 
+    public void setCampusId(Long campusId)
     {
         this.campusId = campusId;
     }
 
-    public Long getCampusId() 
+    public Long getCampusId()
     {
         return campusId;
     }
 
-    public void setAssignSchoolId(Long assignSchoolId) 
+    public void setAssignSchoolId(Long assignSchoolId)
     {
         this.assignSchoolId = assignSchoolId;
     }
 
-    public Long getAssignSchoolId() 
+    public Long getAssignSchoolId()
     {
         return assignSchoolId;
     }
 
-    public void setSchoolId(Long schoolId) 
+    public void setSchoolId(Long schoolId)
     {
         this.schoolId = schoolId;
     }
 
-    public Long getSchoolId() 
+    public Long getSchoolId()
     {
         return schoolId;
     }
@@ -338,92 +357,92 @@ public class DzCards extends BaseEntity
         return endYear;
     }
 
-    public void setOpenId(Long openId) 
+    public void setOpenId(Long openId)
     {
         this.openId = openId;
     }
 
-    public Long getOpenId() 
+    public Long getOpenId()
     {
         return openId;
     }
 
-    public void setDistributeTime(Date distributeTime) 
+    public void setDistributeTime(Date distributeTime)
     {
         this.distributeTime = distributeTime;
     }
 
-    public Date getDistributeTime() 
+    public Date getDistributeTime()
     {
         return distributeTime;
     }
 
-    public void setOutDate(Date outDate) 
+    public void setOutDate(Date outDate)
     {
         this.outDate = outDate;
     }
 
-    public Date getOutDate() 
+    public Date getOutDate()
     {
         return outDate;
     }
 
-    public void setOpenTime(Date openTime) 
+    public void setOpenTime(Date openTime)
     {
         this.openTime = openTime;
     }
 
-    public Date getOpenTime() 
+    public Date getOpenTime()
     {
         return openTime;
     }
 
-    public void setPayTime(Date payTime) 
+    public void setPayTime(Date payTime)
     {
         this.payTime = payTime;
     }
 
-    public Date getPayTime() 
+    public Date getPayTime()
     {
         return payTime;
     }
 
-    public void setActiveTime(Date activeTime) 
+    public void setActiveTime(Date activeTime)
     {
         this.activeTime = activeTime;
     }
 
-    public Date getActiveTime() 
+    public Date getActiveTime()
     {
         return activeTime;
     }
 
-    public void setSettlementTime(Date settlementTime) 
+    public void setSettlementTime(Date settlementTime)
     {
         this.settlementTime = settlementTime;
     }
 
-    public Date getSettlementTime() 
+    public Date getSettlementTime()
     {
         return settlementTime;
     }
 
-    public void setRefundTime(Date refundTime) 
+    public void setRefundTime(Date refundTime)
     {
         this.refundTime = refundTime;
     }
 
-    public Date getRefundTime() 
+    public Date getRefundTime()
     {
         return refundTime;
     }
 
-    public void setCloseTime(Date closeTime) 
+    public void setCloseTime(Date closeTime)
     {
         this.closeTime = closeTime;
     }
 
-    public Date getCloseTime() 
+    public Date getCloseTime()
     {
         return closeTime;
     }

+ 16 - 3
ie-system/src/main/java/com/ruoyi/dz/domain/DzCardsOpen.java

@@ -20,6 +20,10 @@ public class DzCardsOpen extends BaseEntity
     /** 标识 */
     private Long id;
 
+    /** 省份 */
+    @Excel(name = "省份")
+    private String location;
+
     /** 代理 */
     @Excel(name = "代理")
     private Long agentId;
@@ -37,8 +41,8 @@ public class DzCardsOpen extends BaseEntity
     @Excel(name = "结束日期", width = 30, dateFormat = "yyyy-MM-dd")
     private Date endDate;
 
-    /** 校 */
-    @Excel(name = "校")
+    /** 校 */
+    @Excel(name = "校")
     private Long schoolId;
 
     /** 发件人 */
@@ -62,7 +66,16 @@ public class DzCardsOpen extends BaseEntity
     /** 校区 */
     private String schoolName;
 
-    public void setId(Long id) 
+
+    public String getLocation() {
+        return location;
+    }
+
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    public void setId(Long id)
     {
         this.id = id;
     }

+ 26 - 6
ie-system/src/main/java/com/ruoyi/dz/domain/DzClasses.java

@@ -20,6 +20,8 @@ public class DzClasses extends BaseEntity
     private Long[] classIds;
     private Long teacherId;
 
+    @Excel(name = "机构")
+    private Long deptId;
     /** 学校 */
     @Excel(name = "学校")
     private Long schoolId;
@@ -36,16 +38,34 @@ public class DzClasses extends BaseEntity
 
     /** 是否线上 */
     @Excel(name = "是否线上")
-    private Long online;
+    private Integer online;
+    //默认班级
+    private Integer isDefault;
 
     /** 状态 */
     @Excel(name = "状态")
-    private Long status;
+    private Integer status;
 
     /** 统计数据 */
     @Excel(name = "统计数据")
     private String stats;
 
+    public Long getDeptId() {
+        return deptId;
+    }
+
+    public void setDeptId(Long deptId) {
+        this.deptId = deptId;
+    }
+
+    public Integer getIsDefault() {
+        return isDefault;
+    }
+
+    public void setIsDefault(Integer isDefault) {
+        this.isDefault = isDefault;
+    }
+
     public Long[] getClassIds() {
         return classIds;
     }
@@ -110,22 +130,22 @@ public class DzClasses extends BaseEntity
         return name;
     }
 
-    public void setOnline(Long online)
+    public void setOnline(Integer online)
     {
         this.online = online;
     }
 
-    public Long getOnline()
+    public Integer getOnline()
     {
         return online;
     }
 
-    public void setStatus(Long status)
+    public void setStatus(Integer status)
     {
         this.status = status;
     }
 
-    public Long getStatus()
+    public Integer getStatus()
     {
         return status;
     }

+ 12 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzSchool.java

@@ -28,6 +28,10 @@ public class DzSchool extends BaseEntity
     @Excel(name = "机构ID")
     private Long deptId;
 
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
     /** 地域 */
     @Excel(name = "地域")
     private String location;
@@ -54,6 +58,14 @@ public class DzSchool extends BaseEntity
 
     private Boolean campus;
 
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
     public String getProCityAreaName() {
         return proCityAreaName;
     }

+ 19 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacher.java

@@ -29,6 +29,8 @@ public class DzTeacher extends BaseEntity
     /** 所在校区 */
     @Excel(name = "所在校区")
     private Long schoolId;
+    //培训校区
+    private Long campusId;
 
     /** 教师姓名 */
     @Excel(name = "教师姓名")
@@ -38,6 +40,23 @@ public class DzTeacher extends BaseEntity
     private String username;
     private String deptName;
     private String schoolName;
+    private String campusName;
+
+    public String getCampusName() {
+        return campusName;
+    }
+
+    public void setCampusName(String campusName) {
+        this.campusName = campusName;
+    }
+
+    public Long getCampusId() {
+        return campusId;
+    }
+
+    public void setCampusId(Long campusId) {
+        this.campusId = campusId;
+    }
 
     public String getDeptName() {
         return deptName;

+ 9 - 8
ie-system/src/main/java/com/ruoyi/dz/mapper/DzAgentMapper.java

@@ -7,25 +7,26 @@ import org.apache.ibatis.annotations.Param;
 
 /**
  * 机构代理Mapper接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface DzAgentMapper 
+public interface DzAgentMapper
 {
     public List<DzAgent> selectDzAgentByAgentIds(@Param("ids")Collection<Long> ids);
 
     /**
      * 查询机构代理
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 机构代理
      */
     public DzAgent selectDzAgentByAgentId(Long agentId);
+    public DzAgent selectDzAgentByUserId(Long userId);
 
     /**
      * 查询机构代理列表
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 机构代理集合
      */
@@ -33,7 +34,7 @@ public interface DzAgentMapper
 
     /**
      * 新增机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
@@ -41,7 +42,7 @@ public interface DzAgentMapper
 
     /**
      * 修改机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
@@ -49,7 +50,7 @@ public interface DzAgentMapper
 
     /**
      * 删除机构代理
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 结果
      */
@@ -57,7 +58,7 @@ public interface DzAgentMapper
 
     /**
      * 批量删除机构代理
-     * 
+     *
      * @param agentIds 需要删除的数据主键集合
      * @return 结果
      */

+ 9 - 9
ie-system/src/main/java/com/ruoyi/dz/mapper/DzClassesMapper.java

@@ -7,18 +7,18 @@ import org.apache.ibatis.annotations.Param;
 
 /**
  * 学生班级Mapper接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-29
  */
-public interface DzClassesMapper 
+public interface DzClassesMapper
 {
     public List<DzClasses> selectClassesByIds(@Param("ids") Collection<Long> ids);
     public List<DzClasses> selectClassesForTeacher(@Param("teacherId") Long teacherId);
-
+    public List<DzClasses> selectDzClassesListBySchoolId(@Param("schoolId") Long schoolId);
     /**
      * 查询学生班级
-     * 
+     *
      * @param classId 学生班级主键
      * @return 学生班级
      */
@@ -26,7 +26,7 @@ public interface DzClassesMapper
 
     /**
      * 查询学生班级列表
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 学生班级集合
      */
@@ -34,7 +34,7 @@ public interface DzClassesMapper
 
     /**
      * 新增学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -42,7 +42,7 @@ public interface DzClassesMapper
 
     /**
      * 修改学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -50,7 +50,7 @@ public interface DzClassesMapper
 
     /**
      * 删除学生班级
-     * 
+     *
      * @param classId 学生班级主键
      * @return 结果
      */
@@ -58,7 +58,7 @@ public interface DzClassesMapper
 
     /**
      * 批量删除学生班级
-     * 
+     *
      * @param classIds 需要删除的数据主键集合
      * @return 结果
      */

+ 2 - 0
ie-system/src/main/java/com/ruoyi/dz/mapper/DzSubjectMapper.java

@@ -11,6 +11,7 @@ import com.ruoyi.dz.domain.DzSubject;
  */
 public interface DzSubjectMapper 
 {
+    public List<DzSubject> selectDzSubjectBySubjectIds(Long[] subjectIds);
     /**
      * 查询科目
      * 
@@ -26,6 +27,7 @@ public interface DzSubjectMapper
      * @return 科目集合
      */
     public List<DzSubject> selectDzSubjectList(DzSubject dzSubject);
+    public List<DzSubject> selectWrongSubjectList(Long studentId);
 
     /**
      * 新增科目

+ 9 - 8
ie-system/src/main/java/com/ruoyi/dz/service/IDzAgentService.java

@@ -5,23 +5,24 @@ import com.ruoyi.dz.domain.DzAgent;
 
 /**
  * 机构代理Service接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface IDzAgentService 
+public interface IDzAgentService
 {
     /**
      * 查询机构代理
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 机构代理
      */
     public DzAgent selectDzAgentByAgentId(Long agentId);
+    public DzAgent selectDzAgentByUserId(Long userId);
 
     /**
      * 查询机构代理列表
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 机构代理集合
      */
@@ -29,7 +30,7 @@ public interface IDzAgentService
 
     /**
      * 新增机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
@@ -37,7 +38,7 @@ public interface IDzAgentService
 
     /**
      * 修改机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
@@ -45,7 +46,7 @@ public interface IDzAgentService
 
     /**
      * 批量删除机构代理
-     * 
+     *
      * @param agentIds 需要删除的机构代理主键集合
      * @return 结果
      */
@@ -53,7 +54,7 @@ public interface IDzAgentService
 
     /**
      * 删除机构代理信息
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 结果
      */

+ 3 - 2
ie-system/src/main/java/com/ruoyi/dz/service/IDzCardsService.java

@@ -86,13 +86,14 @@ public interface IDzCardsService
 
     /**
      * 开卡
-     * @param schoolId
      * @param agentId
+     * @param location
+     * @param schoolId
      * @param beginNo
      * @param endNo
      * @return
      */
-    public Boolean openCard(Long schoolId, Long agentId, String beginNo, String endNo);
+    public Boolean openCard(Long agentId, String location, Long schoolId, String beginNo, String endNo);
 
     /**
      * 申请开卡

+ 9 - 8
ie-system/src/main/java/com/ruoyi/dz/service/IDzClassesService.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzClasses;
 
 /**
  * 学生班级Service接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-29
  */
-public interface IDzClassesService 
+public interface IDzClassesService
 {
     /**
      * 查询学生班级
-     * 
+     *
      * @param classId 学生班级主键
      * @return 学生班级
      */
@@ -21,15 +21,16 @@ public interface IDzClassesService
 
     /**
      * 查询学生班级列表
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 学生班级集合
      */
     public List<DzClasses> selectDzClassesList(DzClasses dzClasses);
+    public List<DzClasses> selectDzClassesListBySchoolId(Long schoolId);
 
     /**
      * 新增学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -37,7 +38,7 @@ public interface IDzClassesService
 
     /**
      * 修改学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -45,7 +46,7 @@ public interface IDzClassesService
 
     /**
      * 批量删除学生班级
-     * 
+     *
      * @param classIds 需要删除的学生班级主键集合
      * @return 结果
      */
@@ -53,7 +54,7 @@ public interface IDzClassesService
 
     /**
      * 删除学生班级信息
-     * 
+     *
      * @param classId 学生班级主键
      * @return 结果
      */

+ 2 - 0
ie-system/src/main/java/com/ruoyi/dz/service/IDzSubjectService.java

@@ -11,6 +11,8 @@ import com.ruoyi.dz.domain.DzSubject;
  */
 public interface IDzSubjectService 
 {
+    List<DzSubject> selectDzSubjectBySubjectIds(Long[] subjectIds);
+
     /**
      * 查询科目
      * 

+ 9 - 9
ie-system/src/main/java/com/ruoyi/dz/service/IDzTeacherService.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzTeacher;
 
 /**
  * 老师Service接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface IDzTeacherService 
+public interface IDzTeacherService
 {
     /**
      * 查询老师
-     * 
+     *
      * @param teacherId 老师主键
      * @return 老师
      */
@@ -21,7 +21,7 @@ public interface IDzTeacherService
 
     /**
      * 查询老师列表
-     * 
+     *
      * @param dzTeacher 老师
      * @return 老师集合
      */
@@ -29,15 +29,15 @@ public interface IDzTeacherService
 
     /**
      * 新增老师
-     * 
+     *
      * @param dzTeacher 老师
      * @return 结果
      */
-    public int insertDzTeacher(DzTeacher dzTeacher);
+    public Long insertDzTeacher(DzTeacher dzTeacher);
 
     /**
      * 修改老师
-     * 
+     *
      * @param dzTeacher 老师
      * @return 结果
      */
@@ -45,7 +45,7 @@ public interface IDzTeacherService
 
     /**
      * 批量删除老师
-     * 
+     *
      * @param teacherIds 需要删除的老师主键集合
      * @return 结果
      */
@@ -53,7 +53,7 @@ public interface IDzTeacherService
 
     /**
      * 删除老师信息
-     * 
+     *
      * @param teacherId 老师主键
      * @return 结果
      */

+ 24 - 9
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzAgentServiceImpl.java

@@ -14,12 +14,12 @@ import com.ruoyi.dz.service.IDzAgentService;
 
 /**
  * 机构代理Service业务层处理
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
 @Service
-public class DzAgentServiceImpl implements IDzAgentService 
+public class DzAgentServiceImpl implements IDzAgentService
 {
     @Autowired
     private DzAgentMapper dzAgentMapper;
@@ -28,7 +28,7 @@ public class DzAgentServiceImpl implements IDzAgentService
 
     /**
      * 查询机构代理
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 机构代理
      */
@@ -38,9 +38,14 @@ public class DzAgentServiceImpl implements IDzAgentService
         return dzAgentMapper.selectDzAgentByAgentId(agentId);
     }
 
+    @Override
+    public DzAgent selectDzAgentByUserId(Long userId) {
+        return dzAgentMapper.selectDzAgentByUserId(userId);
+    }
+
     /**
      * 查询机构代理列表
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 机构代理
      */
@@ -52,21 +57,31 @@ public class DzAgentServiceImpl implements IDzAgentService
 
     /**
      * 新增机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
     @Override
     public int insertDzAgent(DzAgent dzAgent)
     {
+        Long userId = userService.insertRelateUser(UserTypeEnum.Agent, dzAgent.getAgentId(), dzAgent.getDeptId(), dzAgent.getUsername(), dzAgent.getName());
+
+        dzAgent.setUserId(userId);
         int iRet = dzAgentMapper.insertDzAgent(dzAgent);
-        userService.insertRelateUser(UserTypeEnum.Agent, dzAgent.getAgentId(), dzAgent.getDeptId(), dzAgent.getUsername(), dzAgent.getName());
+
+        //将用户登录的username更新为dzAgent.getAgentId()
+        SysUser user = new SysUser();
+        user.setUserId(userId);
+        user.setUserName(String.valueOf(dzAgent.getAgentId()));
+        user.setUserTypeId(dzAgent.getAgentId());
+        userService.updateUserInfo(user);
+
         return iRet;
     }
 
     /**
      * 修改机构代理
-     * 
+     *
      * @param dzAgent 机构代理
      * @return 结果
      */
@@ -78,7 +93,7 @@ public class DzAgentServiceImpl implements IDzAgentService
 
     /**
      * 批量删除机构代理
-     * 
+     *
      * @param agentIds 需要删除的机构代理主键
      * @return 结果
      */
@@ -90,7 +105,7 @@ public class DzAgentServiceImpl implements IDzAgentService
 
     /**
      * 删除机构代理信息
-     * 
+     *
      * @param agentId 机构代理主键
      * @return 结果
      */

+ 48 - 11
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzCardsServiceImpl.java

@@ -19,6 +19,7 @@ import com.ruoyi.dz.domain.*;
 import com.ruoyi.dz.mapper.*;
 import com.ruoyi.enums.*;
 import com.ruoyi.system.mapper.SysDeptMapper;
+import com.ruoyi.system.mapper.SysUserMapper;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -29,12 +30,12 @@ import javax.validation.ValidationException;
 
 /**
  * 学习卡Service业务层处理
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
 @Service
-public class DzCardsServiceImpl implements IDzCardsService 
+public class DzCardsServiceImpl implements IDzCardsService
 {
     @Autowired
     private DzCardsMapper dzCardsMapper;
@@ -48,12 +49,14 @@ public class DzCardsServiceImpl implements IDzCardsService
     private DzSchoolMapper dzSchoolMapper;
     @Autowired
     private DzClassesMapper dzClassesMapper;
+    @Autowired
+    private SysUserMapper userMapper;
 
     private final String format = "%d%08d";
 
     /**
      * 查询学习卡
-     * 
+     *
      * @param cardId 学习卡主键
      * @return 学习卡
      */
@@ -77,7 +80,11 @@ public class DzCardsServiceImpl implements IDzCardsService
         Set<Long> schoolIdSet = Sets.newHashSet();
         Set<Long> deptIdSet = Sets.newHashSet();
         Set<Long> agentIdSet = Sets.newHashSet();
+        Set<Long> cardIdSet = Sets.newHashSet();
         for(DzCards c : list) {
+            if(null != c.getCardId()) {
+                cardIdSet.add(c.getCardId());
+            }
             if(null != c.getClassId()) {
                 classIdSet.add(c.getClassId());
             }
@@ -104,6 +111,8 @@ public class DzCardsServiceImpl implements IDzCardsService
         Map<Long, String> schoolMap = !schoolIdSet.isEmpty() ? dzSchoolMapper.selectDzSchoolListByIds(schoolIdSet).stream().collect(Collectors.toMap(DzSchool::getId, DzSchool::getName)) : Maps.newHashMap();;
         Map<Long, String> agentMap = !agentIdSet.isEmpty() ? dzAgentMapper.selectDzAgentByAgentIds(agentIdSet).stream().collect(Collectors.toMap(DzAgent::getAgentId, DzAgent::getName)) : Maps.newHashMap();
         Map<Long, String> classesMap = !classIdSet.isEmpty() ? dzClassesMapper.selectClassesByIds(classIdSet).stream().collect(Collectors.toMap(DzClasses::getClassId, DzClasses::getName)) : Maps.newHashMap();
+        Map<Long, SysUser> userMap = !cardIdSet.isEmpty() ? userMapper.selectUserByCardIds(cardIdSet).stream() .collect(Collectors.toMap( SysUser::getCardId, user -> user, (existing, replacement) -> existing)) : new HashMap<>();
+
         for(DzCards c : list) {
             c.setClassName(classesMap.get(c.getClassId()));
             c.setAssignSchoolName(schoolMap.get(c.getAssignSchoolId()));
@@ -114,13 +123,17 @@ public class DzCardsServiceImpl implements IDzCardsService
                 String name = agentMap.get(c.getAgentId());
                 c.setAgentName(null != c.getLeafAgentId() && !c.getLeafAgentId().equals(c.getAgentId()) ? agentMap.get(c.getLeafAgentId()) + "(" + name + ")" : name);
             }
+            if (userMap.containsKey(c.getCardId())) {
+                c.setPhonenumber(userMap.get(c.getCardId()).getPhonenumber());
+                c.setNickName(userMap.get(c.getCardId()).getNickName());
+            }
         }
         return list;
     }
 
     /**
      * 查询学习卡列表
-     * 
+     *
      * @param dzCards 学习卡
      * @return 学习卡
      */
@@ -132,7 +145,7 @@ public class DzCardsServiceImpl implements IDzCardsService
 
     /**
      * 新增学习卡
-     * 
+     *
      * @param dzCards 学习卡
      * @return 结果
      */
@@ -145,7 +158,7 @@ public class DzCardsServiceImpl implements IDzCardsService
 
     /**
      * 修改学习卡
-     * 
+     *
      * @param dzCards 学习卡
      * @return 结果
      */
@@ -158,7 +171,7 @@ public class DzCardsServiceImpl implements IDzCardsService
 
     /**
      * 批量删除学习卡
-     * 
+     *
      * @param cardIds 需要删除的学习卡主键
      * @return 结果
      */
@@ -170,7 +183,7 @@ public class DzCardsServiceImpl implements IDzCardsService
 
     /**
      * 删除学习卡信息
-     * 
+     *
      * @param cardId 学习卡主键
      * @return 结果
      */
@@ -221,19 +234,23 @@ public class DzCardsServiceImpl implements IDzCardsService
             dzCards.setAssignExamType(null != examType ? examType.name() : null);
             dzCards.setAssignSchoolId(schoolId);
             dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
+            dzCards.setPayStatus(PayStatus.Paid.getVal()); // TODO 暂时分配即开卡
+            dzCards.setStatus(CardStatus.Paid.getVal());
             dzCardsMapper.updateDzCards(dzCards);
         });
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Boolean openCard(Long schoolId, Long agentId, String beginNo, String endNo) {
+    public Boolean openCard(Long agentId, String location, Long schoolId, String beginNo, String endNo) {
         DzCardsOpen newOpen = new DzCardsOpen();
         newOpen.setAgentId(agentId);
         newOpen.setStartNo(beginNo);
         newOpen.setEndNo(endNo);
         newOpen.setEndDate(DateUtils.addDays(DateUtils.getNowDate(), 14)); // TODO MF 卡默认有效期
         newOpen.setSchoolId(schoolId);
+        newOpen.setLocation(location);
+        newOpen.setSender(SecurityUtils.getLoginUser().getUser().getNickName());
         newOpen.setIsReopen(0);
         newOpen.setStatus(RequestStatus.Accept.getVal());
         dzCardsOpenMapper.insertDzCardsOpen(newOpen);
@@ -245,12 +262,23 @@ public class DzCardsServiceImpl implements IDzCardsService
         if(cards.stream().filter(t -> !t.getPayStatus().equals(PayStatus.UnPay.getVal())).count() > 0) {
             throw new ValidationException("请求打开已开卡: " + beginNo + "-" + endNo);
         }
+        DzAgent dzAgent = dzAgentMapper.selectDzAgentByAgentId(agentId);
         DzCards dzCards = new DzCards();
         cards.stream().forEach(c -> {
             dzCards.setCardId(c.getCardId());
             dzCards.setPayTime(DateUtils.getNowDate());
             dzCards.setPayStatus(PayStatus.Paid.getVal());
             dzCards.setStatus(CardStatus.Paid.getVal());
+            dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
+            dzCards.setAssignLocation(location);
+            dzCards.setAssignSchoolId(schoolId);
+            if(null != dzAgent.getParentId()) {
+                dzCards.setAgentId(dzAgent.getParentId());
+                dzCards.setLeafAgentId(dzAgent.getAgentId());
+            } else {
+                dzCards.setAgentId(dzAgent.getAgentId());
+                dzCards.setLeafAgentId(dzAgent.getAgentId());
+            }
             dzCardsMapper.updateDzCards(dzCards);
         });
         // TODO MF 检查已经使用的或无效的
@@ -262,8 +290,6 @@ public class DzCardsServiceImpl implements IDzCardsService
         SysUser sysUser = SecurityUtils.getLoginUser().getUser();
         if(UserTypeEnum.Agent.getVal().equals(sysUser.getUserType())) {
             dzCardsOpen.setAgentId(sysUser.getUserTypeId());
-        } else {
-            dzCardsOpen.setAgentId(0L);
         }
         dzCardsOpen.setStatus(RequestStatus.Submit.getVal());
         dzCardsOpen.setEndDate(DateUtils.addDays(DateUtils.getNowDate(), 14));
@@ -301,9 +327,20 @@ public class DzCardsServiceImpl implements IDzCardsService
             DzCards dzCards = new DzCards();
             cards.stream().forEach(c -> {
                 dzCards.setCardId(c.getCardId());
+                dzCards.setAssignLocation(exist.getLocation());
+                dzCards.setAssignSchoolId(exist.getSchoolId());
+                DzAgent dzAgent = dzAgentMapper.selectDzAgentByAgentId(exist.getAgentId());
+                if(null != dzAgent.getParentId()) {
+                    dzCards.setAgentId(dzAgent.getParentId());
+                    dzCards.setLeafAgentId(dzAgent.getAgentId());
+                } else {
+                    dzCards.setAgentId(dzAgent.getAgentId());
+                    dzCards.setLeafAgentId(dzAgent.getAgentId());
+                }
                 dzCards.setPayTime(DateUtils.getNowDate());
                 dzCards.setPayStatus(PayStatus.Paid.getVal());
                 dzCards.setStatus(CardStatus.Paid.getVal());
+                dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
                 dzCardsMapper.updateDzCards(dzCards);
             });
         }

+ 14 - 8
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzClassesServiceImpl.java

@@ -10,19 +10,19 @@ import com.ruoyi.dz.service.IDzClassesService;
 
 /**
  * 学生班级Service业务层处理
- * 
+ *
  * @author ruoyi
  * @date 2025-09-29
  */
 @Service
-public class DzClassesServiceImpl implements IDzClassesService 
+public class DzClassesServiceImpl implements IDzClassesService
 {
     @Autowired
     private DzClassesMapper dzClassesMapper;
 
     /**
      * 查询学生班级
-     * 
+     *
      * @param classId 学生班级主键
      * @return 学生班级
      */
@@ -34,7 +34,7 @@ public class DzClassesServiceImpl implements IDzClassesService
 
     /**
      * 查询学生班级列表
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 学生班级
      */
@@ -44,9 +44,15 @@ public class DzClassesServiceImpl implements IDzClassesService
         return dzClassesMapper.selectDzClassesList(dzClasses);
     }
 
+    @Override
+    public List<DzClasses> selectDzClassesListBySchoolId(Long schoolId)
+    {
+        return dzClassesMapper.selectDzClassesListBySchoolId(schoolId);
+    }
+
     /**
      * 新增学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -59,7 +65,7 @@ public class DzClassesServiceImpl implements IDzClassesService
 
     /**
      * 修改学生班级
-     * 
+     *
      * @param dzClasses 学生班级
      * @return 结果
      */
@@ -72,7 +78,7 @@ public class DzClassesServiceImpl implements IDzClassesService
 
     /**
      * 批量删除学生班级
-     * 
+     *
      * @param classIds 需要删除的学生班级主键
      * @return 结果
      */
@@ -84,7 +90,7 @@ public class DzClassesServiceImpl implements IDzClassesService
 
     /**
      * 删除学生班级信息
-     * 
+     *
      * @param classId 学生班级主键
      * @return 结果
      */

+ 13 - 1
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSchoolServiceImpl.java

@@ -2,6 +2,9 @@ package com.ruoyi.dz.service.impl;
 
 import java.util.List;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.enums.UserTypeEnum;
+import com.ruoyi.system.service.ISysUserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.dz.mapper.DzSchoolMapper;
@@ -19,6 +22,8 @@ public class DzSchoolServiceImpl implements IDzSchoolService
 {
     @Autowired
     private DzSchoolMapper dzSchoolMapper;
+    @Autowired
+    private ISysUserService userService;
 
     /**
      * 查询机构校区
@@ -60,7 +65,14 @@ public class DzSchoolServiceImpl implements IDzSchoolService
     public int insertDzSchool(DzSchool dzSchool)
     {
         dzSchool.setCreateTime(DateUtils.getNowDate());
-        return dzSchoolMapper.insertDzSchool(dzSchool);
+        dzSchoolMapper.insertDzSchool(dzSchool);
+        if(null != dzSchool.getDeptId() && dzSchool.getDeptId() > 0) {
+            Long campusId = dzSchool.getId();
+            Long userId = userService.insertRelateUser(UserTypeEnum.School, campusId, dzSchool.getDeptId(), dzSchool.getName(), dzSchool.getName());
+            dzSchool.setUserId(userId);
+            dzSchoolMapper.updateDzSchool(dzSchool);
+        }
+        return dzSchool.getId().intValue();
     }
 
     /**

+ 3 - 0
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSubjectServiceImpl.java

@@ -19,6 +19,9 @@ public class DzSubjectServiceImpl implements IDzSubjectService
     @Autowired
     private DzSubjectMapper dzSubjectMapper;
 
+    public List<DzSubject> selectDzSubjectBySubjectIds(Long[] subjectIds) {
+        return dzSubjectMapper.selectDzSubjectBySubjectIds(subjectIds);
+    }
     /**
      * 查询科目
      * 

+ 31 - 7
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzTeacherServiceImpl.java

@@ -1,11 +1,15 @@
 package com.ruoyi.dz.service.impl;
 
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.dz.domain.DzSchool;
 import com.ruoyi.dz.mapper.DzSchoolMapper;
 import com.ruoyi.enums.UserTypeEnum;
 import com.ruoyi.system.service.ISysUserService;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.dz.mapper.DzTeacherMapper;
@@ -49,7 +53,21 @@ public class DzTeacherServiceImpl implements IDzTeacherService
     @Override
     public List<DzTeacher> selectDzTeacherList(DzTeacher dzTeacher)
     {
-        return dzTeacherMapper.selectDzTeacherList(dzTeacher);
+        List<DzTeacher> list = dzTeacherMapper.selectDzTeacherList(dzTeacher);
+        //填充campusName,得到campusIds
+        List<Long> campusIds = list.stream().map(DzTeacher::getCampusId).collect(Collectors.toList());
+        if (CollectionUtils.isNotEmpty(campusIds)){
+            Map<Long, DzSchool> schoolMap = schoolMapper.selectDzSchoolListByIds(campusIds)
+                    .stream().collect(Collectors.toMap(DzSchool::getId,school -> school,
+                            (existing, replacement) -> existing  // 如果key重复,保留已存在的
+                    ));
+            for (DzTeacher teacher : list) {
+                if (schoolMap.containsKey(teacher.getCampusId())){
+                    teacher.setCampusName(schoolMap.get(teacher.getCampusId()).getName());
+                }
+            }
+        }
+        return list;
     }
 
     /**
@@ -59,18 +77,24 @@ public class DzTeacherServiceImpl implements IDzTeacherService
      * @return 结果
      */
     @Override
-    public int insertDzTeacher(DzTeacher dzTeacher)
+    public Long insertDzTeacher(DzTeacher dzTeacher)
     {
         if (null == dzTeacher.getDeptId()){
-            DzSchool school = schoolMapper.selectDzSchoolById(dzTeacher.getSchoolId());
-            dzTeacher.setDeptId(school.getDeptId());
+            DzSchool school = schoolMapper.selectDzSchoolById(dzTeacher.getCampusId());
+            if (null!=school){
+                dzTeacher.setDeptId(school.getDeptId());
+            }
         }
-        int id = dzTeacherMapper.insertDzTeacher(dzTeacher);
-        Long userId = userService.insertRelateUser(UserTypeEnum.Teacher, dzTeacher.getTeacherId(), dzTeacher.getDeptId(), dzTeacher.getUsername(), dzTeacher.getName());
+        dzTeacherMapper.insertDzTeacher(dzTeacher);//将老师的userId填充到user的userName中
+        Long teacherId = dzTeacher.getTeacherId();
+        Long userId = userService.insertRelateUser(UserTypeEnum.Teacher, dzTeacher.getTeacherId(), dzTeacher.getDeptId(), String.valueOf(teacherId), dzTeacher.getName());
         //反向更新dzTeacher中的userId
         dzTeacher.setUserId(userId);
+        if (StringUtils.isEmpty(dzTeacher.getName())){
+            dzTeacher.setName(String.valueOf(teacherId));
+        }
         dzTeacherMapper.updateDzTeacher(dzTeacher);
-        return id;
+        return teacherId;
     }
 
     /**

+ 13 - 0
ie-system/src/main/java/com/ruoyi/enums/CardAudit.java

@@ -0,0 +1,13 @@
+package com.ruoyi.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum CardAudit {
+    //状态(0:未审核,1:通过,2:拒绝)
+    NoAudit(0), Approved(1), deny(2);
+
+    private final Integer val;
+}

+ 37 - 0
ie-system/src/main/java/com/ruoyi/enums/QuestionType.java

@@ -0,0 +1,37 @@
+package com.ruoyi.enums;
+
+import com.google.common.collect.Maps;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@AllArgsConstructor
+@Getter
+public enum QuestionType {
+    Single(1, "单选题"), Multiple(2, "多选题"), Judgment(3, "判断题"), Fill(4, "填空题"),
+    Subjective(5, "主观题"), Short(6, "简答题"), Essay(7, "问答题"), Analysis(8, "分析题");
+
+    private final Integer val;
+    private final String title;
+
+    private static final Map<String, QuestionType> valMap = Maps.newHashMap();
+
+    public static QuestionType of(Integer vol) {
+        return valMap.get(vol.toString());
+    }
+
+    public static QuestionType of(String vol) {
+        QuestionType type = valMap.get(vol);
+        return null == type ? QuestionType.Subjective : type;
+    }
+
+    static {
+        Arrays.stream(QuestionType.values()).forEach(t -> {
+            valMap.put(t.val.toString(), t);
+            valMap.put(t.title, t);
+            valMap.put(t.name(), t);
+        });
+    }
+}

+ 45 - 1
ie-system/src/main/java/com/ruoyi/enums/UserTypeEnum.java

@@ -1,12 +1,56 @@
 package com.ruoyi.enums;
 
+import com.ruoyi.common.utils.StringUtils;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 @Getter
 @AllArgsConstructor
 public enum UserTypeEnum {
-    Sys("00"), Card("01"), Agent("10"), Teacher("11"), Institution("12"); // 用户类型(00系统01卡10代理11老师12机构)
+    Sys("00"), Card("01"), Agent("10"), Teacher("11"), Institution("12"), School("13"); // 用户类型(00系统01卡10代理11老师12机构)
 
     private final String val;
+
+    public static Boolean isInstitution(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return Institution.val.equalsIgnoreCase(val)||Institution.name().equalsIgnoreCase(val);
+    }
+
+    public static Boolean isTeacher(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return Teacher.val.equalsIgnoreCase(val)||Teacher.name().equalsIgnoreCase(val);
+    }
+
+    public static Boolean isAgent(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return Agent.val.equalsIgnoreCase(val)||Agent.name().equalsIgnoreCase(val);
+    }
+
+    public static Boolean isCard(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return Card.val.equalsIgnoreCase(val)||Card.name().equalsIgnoreCase(val);
+    }
+
+    public static Boolean isSys(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return Sys.val.equalsIgnoreCase(val)||Sys.name().equalsIgnoreCase(val);
+    }
+
+    public static Boolean isSchool(String val){
+        if (StringUtils.isEmpty(val)){
+            return false;
+        }
+        return School.val.equalsIgnoreCase(val) || School.name().equalsIgnoreCase(val);
+    }
+
 }

+ 53 - 3
ie-system/src/main/java/com/ruoyi/learn/domain/AnswerSheet.java

@@ -1,18 +1,26 @@
 package com.ruoyi.learn.domain;
 
+import com.alibaba.fastjson2.JSONObject;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.common.annotation.Excel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.util.Date;
+import java.util.List;
 
 @ApiModel("答题卷")
+@Data
 public class AnswerSheet {
     @ApiModelProperty("考生答题卡")
     Long examineeId;
     @Excel(name = "试卷id")
     private Long paperId;
+    @Excel(name = "试卷id")
+    private Long knowledgeId;
     @ApiModelProperty("测试名称")
     String name;
     @ApiModelProperty("考试开始时间")
@@ -21,18 +29,60 @@ public class AnswerSheet {
     @ApiModelProperty("考试结束时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     Date endTime;
-    @ApiModelProperty("考试时间 单位秒")
-    Long remaining;
     @ApiModelProperty("阅卷类型 1 自阅卷 2 老师阅卷")
     private String scoringType;
     @ApiModelProperty("考场分钟 90, 120, xxx")
     private Long mode;
     @ApiModelProperty("答卷状态 examinee_status")
-    Long state;
+    Integer state;
     @ApiModelProperty("答卷状态名称")
     String stateStr;
     @ApiModelProperty("是否可答题")
     Boolean allowAnswer;
     @ApiModelProperty("是否可阅卷")
     Boolean allowScore;
+
+    private Boolean isDone;
+
+    @ApiModelProperty("题定义, time, score, types")
+    PaperCond paperInfo;
+
+    @ApiModelProperty("做题时长")
+    private Long duration;
+    @ApiModelProperty("总题数")
+    private Integer totalCount;
+    @ApiModelProperty("错误题数")
+    private Integer wrongCount;
+
+    @ApiModelProperty("院校id")
+    private Long collegeId;
+    @ApiModelProperty("院校名称")
+    private String collegeName;
+    @ApiModelProperty("专业id")
+    private Long majorId;
+    @ApiModelProperty("专业名称")
+    private String majorName;
+    @ApiModelProperty("科目ID")
+    private Long subjectId;
+    @ApiModelProperty("科目名称")
+    private String subjectName;
+
+    List<PaperVO.QuestionAnswer> questions;
+
+    JSONObject stats;
+
+    @Data
+    public static class PaperCond {
+        Integer time; // 秒
+        Integer score; // 总分
+        List<PaperCondType> types;
+    }
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class PaperCondType {
+        String type;
+        Integer count;
+        Integer score; // 本类分
+    }
 }

+ 33 - 13
ie-system/src/main/java/com/ruoyi/learn/domain/LearnAnswer.java

@@ -1,5 +1,6 @@
 package com.ruoyi.learn.domain;
 
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -32,7 +33,7 @@ public class LearnAnswer extends BaseEntity
 
     /** 题号 */
     @Excel(name = "题号")
-    private Long seq;
+    private Integer seq;
 
     /** 知识点 */
     @Excel(name = "知识点")
@@ -55,8 +56,8 @@ public class LearnAnswer extends BaseEntity
     private Long count;
 
     /** 状态 1 正确 */
-    @Excel(name = "状态 1 正确")
-    private Long state;
+    @Excel(name = "0 默认 1正确 2错误 3不会")
+    private Integer state;
 
     /** 评级 */
     @Excel(name = "评级")
@@ -64,11 +65,14 @@ public class LearnAnswer extends BaseEntity
 
     /** 评分 */
     @Excel(name = "评分")
-    private Long score;
+    private Integer score;
 
     /** 得分率 */
     @Excel(name = "得分率")
-    private Long scoreRate;
+    private Integer scoreRate;
+
+    private Boolean isMark;
+    private Boolean isNotKnow;
 
     public void setAnswerId(Long answerId) 
     {
@@ -110,12 +114,12 @@ public class LearnAnswer extends BaseEntity
         return questionId;
     }
 
-    public void setSeq(Long seq) 
+    public void setSeq(Integer seq)
     {
         this.seq = seq;
     }
 
-    public Long getSeq() 
+    public Integer getSeq()
     {
         return seq;
     }
@@ -170,12 +174,12 @@ public class LearnAnswer extends BaseEntity
         return count;
     }
 
-    public void setState(Long state) 
+    public void setState(Integer state)
     {
         this.state = state;
     }
 
-    public Long getState() 
+    public Integer getState()
     {
         return state;
     }
@@ -190,26 +194,42 @@ public class LearnAnswer extends BaseEntity
         return scoreLevel;
     }
 
-    public void setScore(Long score) 
+    public void setScore(Integer score)
     {
         this.score = score;
     }
 
-    public Long getScore() 
+    public Integer getScore()
     {
         return score;
     }
 
-    public void setScoreRate(Long scoreRate) 
+    public void setScoreRate(Integer scoreRate)
     {
         this.scoreRate = scoreRate;
     }
 
-    public Long getScoreRate() 
+    public Integer getScoreRate()
     {
         return scoreRate;
     }
 
+    public Boolean getMark() {
+        return isMark;
+    }
+
+    public void setMark(Boolean mark) {
+        isMark = mark;
+    }
+
+    public Boolean getNotKnow() {
+        return isNotKnow;
+    }
+
+    public void setNotKnow(Boolean notKnow) {
+        isNotKnow = notKnow;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 11 - 0
ie-system/src/main/java/com/ruoyi/learn/domain/LearnDirectedKnowledge.java

@@ -65,6 +65,9 @@ public class LearnDirectedKnowledge extends BaseEntity
     @Excel(name = "仿真模拟卷要求")
     private String conditions;
 
+    @Excel(name = "包含模块")
+    private String modules;
+
     public void setId(Long id) 
     {
         this.id = id;
@@ -193,6 +196,14 @@ public class LearnDirectedKnowledge extends BaseEntity
         return conditions;
     }
 
+    public String getModules() {
+        return modules;
+    }
+
+    public void setModules(String modules) {
+        this.modules = modules;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 50 - 7
ie-system/src/main/java/com/ruoyi/learn/domain/LearnExaminee.java

@@ -35,6 +35,12 @@ public class LearnExaminee extends BaseEntity
     @Excel(name = "试卷标识")
     private String paperKey;
 
+    @Excel(name = "试卷信息")
+    private String paperInfo;
+
+    @Excel(name = "做题时长")
+    private Long duration;
+    
     /** 开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
@@ -55,11 +61,11 @@ public class LearnExaminee extends BaseEntity
 
     /** 分值 */
     @Excel(name = "分值")
-    private Long score;
+    private Integer score;
 
     /** 分率 */
     @Excel(name = "分率")
-    private Long scoreRate;
+    private Integer scoreRate;
 
     /** 名次 */
     @Excel(name = "名次")
@@ -69,6 +75,11 @@ public class LearnExaminee extends BaseEntity
     @Excel(name = "年排名次")
     private Long gradeRanking;
 
+    private String stats;
+
+    @Excel(name = "错题总数")
+    private Integer wrongCount;
+
     /** 选择题正确数 */
     @Excel(name = "选择题正确数")
     private Long chooseCount;
@@ -133,6 +144,22 @@ public class LearnExaminee extends BaseEntity
         this.paperKey = paperKey;
     }
 
+    public String getPaperInfo() {
+        return paperInfo;
+    }
+
+    public void setPaperInfo(String paperInfo) {
+        this.paperInfo = paperInfo;
+    }
+
+    public Long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(Long duration) {
+        this.duration = duration;
+    }
+
     public void setBeginTime(Date beginTime)
     {
         this.beginTime = beginTime;
@@ -163,7 +190,23 @@ public class LearnExaminee extends BaseEntity
         return state;
     }
 
-    public void setScoreLevel(String scoreLevel) 
+    public String getStats() {
+        return stats;
+    }
+
+    public void setStats(String stats) {
+        this.stats = stats;
+    }
+
+    public Integer getWrongCount() {
+        return wrongCount;
+    }
+
+    public void setWrongCount(Integer wrongCount) {
+        this.wrongCount = wrongCount;
+    }
+
+    public void setScoreLevel(String scoreLevel)
     {
         this.scoreLevel = scoreLevel;
     }
@@ -173,22 +216,22 @@ public class LearnExaminee extends BaseEntity
         return scoreLevel;
     }
 
-    public void setScore(Long score) 
+    public void setScore(Integer score)
     {
         this.score = score;
     }
 
-    public Long getScore() 
+    public Integer getScore()
     {
         return score;
     }
 
-    public void setScoreRate(Long scoreRate) 
+    public void setScoreRate(Integer scoreRate)
     {
         this.scoreRate = scoreRate;
     }
 
-    public Long getScoreRate() 
+    public Integer getScoreRate()
     {
         return scoreRate;
     }

+ 55 - 23
ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaper.java

@@ -7,7 +7,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
 
 /**
  * 试卷对象 learn_paper
- * 
+ *
  * @author ruoyi
  * @date 2025-09-18
  */
@@ -54,6 +54,10 @@ public class LearnPaper extends BaseEntity
     @Excel(name = "定向key")
     private String directKey;
 
+    /** 试卷信息 */
+    @Excel(name = "试卷信息")
+    private String paperInfo;
+
     /** 标识 */
     @Excel(name = "标识")
     private String tiid;
@@ -78,32 +82,52 @@ public class LearnPaper extends BaseEntity
     @Excel(name = "适用考生")
     private String examineeTypes;
 
-    public void setId(Long id) 
+    private Long examineeId;
+
+    private boolean collect;
+
+    public Long getExamineeId() {
+        return examineeId;
+    }
+
+    public void setExamineeId(Long examineeId) {
+        this.examineeId = examineeId;
+    }
+
+    public boolean isCollect() {
+        return collect;
+    }
+
+    public void setCollect(boolean collect) {
+        this.collect = collect;
+    }
+
+    public void setId(Long id)
     {
         this.id = id;
     }
 
-    public Long getId() 
+    public Long getId()
     {
         return id;
     }
 
-    public void setSubjectId(Long subjectId) 
+    public void setSubjectId(Long subjectId)
     {
         this.subjectId = subjectId;
     }
 
-    public Long getSubjectId() 
+    public Long getSubjectId()
     {
         return subjectId;
     }
 
-    public void setPaperName(String paperName) 
+    public void setPaperName(String paperName)
     {
         this.paperName = paperName;
     }
 
-    public String getPaperName() 
+    public String getPaperName()
     {
         return paperName;
     }
@@ -118,12 +142,12 @@ public class LearnPaper extends BaseEntity
         return year;
     }
 
-    public void setPaperType(String paperType) 
+    public void setPaperType(String paperType)
     {
         this.paperType = paperType;
     }
 
-    public String getPaperType() 
+    public String getPaperType()
     {
         return paperType;
     }
@@ -166,76 +190,84 @@ public class LearnPaper extends BaseEntity
         return paperSource;
     }
 
-    public void setDirectKey(String directKey) 
+    public void setDirectKey(String directKey)
     {
         this.directKey = directKey;
     }
 
-    public String getDirectKey() 
+    public String getDirectKey()
     {
         return directKey;
     }
 
-    public void setTiid(String tiid) 
+    public void setTiid(String tiid)
     {
         this.tiid = tiid;
     }
 
-    public String getTiid() 
+    public String getTiid()
     {
         return tiid;
     }
 
-    public void setOsspath(String osspath) 
+    public void setOsspath(String osspath)
     {
         this.osspath = osspath;
     }
 
-    public String getOsspath() 
+    public String getOsspath()
     {
         return osspath;
     }
 
-    public void setFilename(String filename) 
+    public void setFilename(String filename)
     {
         this.filename = filename;
     }
 
-    public String getFilename() 
+    public String getFilename()
     {
         return filename;
     }
 
-    public void setRelateId(Long relateId) 
+    public void setRelateId(Long relateId)
     {
         this.relateId = relateId;
     }
 
-    public Long getRelateId() 
+    public Long getRelateId()
     {
         return relateId;
     }
 
-    public void setLocations(String locations) 
+    public void setLocations(String locations)
     {
         this.locations = locations;
     }
 
-    public String getLocations() 
+    public String getLocations()
     {
         return locations;
     }
 
-    public void setExamineeTypes(String examineeTypes) 
+    public void setExamineeTypes(String examineeTypes)
     {
         this.examineeTypes = examineeTypes;
     }
 
-    public String getExamineeTypes() 
+    public String getExamineeTypes()
     {
         return examineeTypes;
     }
 
+    public String getPaperInfo() {
+        return paperInfo;
+    }
+
+    public void setPaperInfo(String paperInfo) {
+        this.paperInfo = paperInfo;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 12 - 1
ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaperQuestion.java

@@ -18,6 +18,9 @@ public class LearnPaperQuestion extends BaseEntity
     /** 试卷题id */
     private Long id;
 
+    /** 知识点id */
+    private Long knowledgeId;
+
     /** 试卷id */
     @Excel(name = "试卷id")
     private Long paperId;
@@ -50,7 +53,15 @@ public class LearnPaperQuestion extends BaseEntity
         return id;
     }
 
-    public void setPaperId(Long paperId) 
+    public Long getKnowledgeId() {
+        return knowledgeId;
+    }
+
+    public void setKnowledgeId(Long knowledgeId) {
+        this.knowledgeId = knowledgeId;
+    }
+
+    public void setPaperId(Long paperId)
     {
         this.paperId = paperId;
     }

+ 14 - 1
ie-system/src/main/java/com/ruoyi/learn/domain/LearnPlan.java

@@ -36,6 +36,11 @@ public class LearnPlan extends BaseEntity
     @Excel(name = "是否有效")
     private Integer status;
 
+    /** 开始时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date firstTime;
+
     /** 开始时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd")
@@ -95,7 +100,15 @@ public class LearnPlan extends BaseEntity
         return status;
     }
 
-    public void setBeginTime(Date beginTime) 
+    public Date getFirstTime() {
+        return firstTime;
+    }
+
+    public void setFirstTime(Date firstTime) {
+        this.firstTime = firstTime;
+    }
+
+    public void setBeginTime(Date beginTime)
     {
         this.beginTime = beginTime;
     }

+ 30 - 7
ie-system/src/main/java/com/ruoyi/learn/domain/LearnPlanStudy.java

@@ -45,17 +45,24 @@ public class LearnPlanStudy extends BaseEntity
     @Excel(name = "完成题数")
     private Long questionCount;
 
+    @Excel(name = "完成题数")
+    private Long rightCount;
+
+    /** 完成视频时间 */
+    @Excel(name = "完成视频数量")
+    private Long videoCount;
+
     /** 完成视频时间 */
     @Excel(name = "完成视频时间")
     private Long videoTime;
 
     /** 计划做题数 */
     @Excel(name = "计划做题数")
-    private Long questionPlan;
+    private Integer questionPlan;
 
     /** 计划视频时间 */
     @Excel(name = "计划视频时间")
-    private Long videoPlan;
+    private Integer videoPlan;
 
     public void setId(String id) 
     {
@@ -127,7 +134,23 @@ public class LearnPlanStudy extends BaseEntity
         return questionCount;
     }
 
-    public void setVideoTime(Long videoTime) 
+    public Long getRightCount() {
+        return rightCount;
+    }
+
+    public void setRightCount(Long rightCount) {
+        this.rightCount = rightCount;
+    }
+
+    public Long getVideoCount() {
+        return videoCount;
+    }
+
+    public void setVideoCount(Long videoCount) {
+        this.videoCount = videoCount;
+    }
+
+    public void setVideoTime(Long videoTime)
     {
         this.videoTime = videoTime;
     }
@@ -137,22 +160,22 @@ public class LearnPlanStudy extends BaseEntity
         return videoTime;
     }
 
-    public void setQuestionPlan(Long questionPlan) 
+    public void setQuestionPlan(Integer questionPlan)
     {
         this.questionPlan = questionPlan;
     }
 
-    public Long getQuestionPlan() 
+    public Integer getQuestionPlan()
     {
         return questionPlan;
     }
 
-    public void setVideoPlan(Long videoPlan) 
+    public void setVideoPlan(Integer videoPlan)
     {
         this.videoPlan = videoPlan;
     }
 
-    public Long getVideoPlan() 
+    public Integer getVideoPlan()
     {
         return videoPlan;
     }

+ 109 - 90
ie-system/src/main/java/com/ruoyi/learn/domain/LearnQuestions.java

@@ -8,7 +8,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
 
 /**
  * 试题对象 learn_questions
- * 
+ *
  * @author ruoyi
  * @date 2025-09-18
  */
@@ -149,7 +149,7 @@ public class LearnQuestions extends BaseEntity
 
     /** 得分 */
     @Excel(name = "得分")
-    private BigDecimal score;
+    private Integer score;
 
     /** 选项 */
     @Excel(name = "选项")
@@ -191,442 +191,461 @@ public class LearnQuestions extends BaseEntity
     @Excel(name = "子题类型")
     private String isSubType;
 
-    public void setId(Long id) 
+    private Long userId;
+    boolean collect;
+
+    public boolean isCollect() {
+        return collect;
+    }
+
+    public void setCollect(boolean collect) {
+        this.collect = collect;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public void setId(Long id)
     {
         this.id = id;
     }
 
-    public Long getId() 
+    public Long getId()
     {
         return id;
     }
 
-    public void setTitle(String title) 
+    public void setTitle(String title)
     {
         this.title = title;
     }
 
-    public String getTitle() 
+    public String getTitle()
     {
         return title;
     }
 
-    public void setOptionA(String optionA) 
+    public void setOptionA(String optionA)
     {
         this.optionA = optionA;
     }
 
-    public String getOptionA() 
+    public String getOptionA()
     {
         return optionA;
     }
 
-    public void setOptionB(String optionB) 
+    public void setOptionB(String optionB)
     {
         this.optionB = optionB;
     }
 
-    public String getOptionB() 
+    public String getOptionB()
     {
         return optionB;
     }
 
-    public void setOptionC(String optionC) 
+    public void setOptionC(String optionC)
     {
         this.optionC = optionC;
     }
 
-    public String getOptionC() 
+    public String getOptionC()
     {
         return optionC;
     }
 
-    public void setOptionD(String optionD) 
+    public void setOptionD(String optionD)
     {
         this.optionD = optionD;
     }
 
-    public String getOptionD() 
+    public String getOptionD()
     {
         return optionD;
     }
 
-    public void setOptionE(String optionE) 
+    public void setOptionE(String optionE)
     {
         this.optionE = optionE;
     }
 
-    public String getOptionE() 
+    public String getOptionE()
     {
         return optionE;
     }
 
-    public void setOptionF(String optionF) 
+    public void setOptionF(String optionF)
     {
         this.optionF = optionF;
     }
 
-    public String getOptionF() 
+    public String getOptionF()
     {
         return optionF;
     }
 
-    public void setOptionG(String optionG) 
+    public void setOptionG(String optionG)
     {
         this.optionG = optionG;
     }
 
-    public String getOptionG() 
+    public String getOptionG()
     {
         return optionG;
     }
 
-    public void setAnswer1(String answer1) 
+    public void setAnswer1(String answer1)
     {
         this.answer1 = answer1;
     }
 
-    public String getAnswer1() 
+    public String getAnswer1()
     {
         return answer1;
     }
 
-    public void setAnswer2(String answer2) 
+    public void setAnswer2(String answer2)
     {
         this.answer2 = answer2;
     }
 
-    public String getAnswer2() 
+    public String getAnswer2()
     {
         return answer2;
     }
 
-    public void setQtpye(String qtpye) 
+    public void setQtpye(String qtpye)
     {
         this.qtpye = qtpye;
     }
 
-    public String getQtpye() 
+    public String getQtpye()
     {
         return qtpye;
     }
 
-    public void setSubjectId(Long subjectId) 
+    public void setSubjectId(Long subjectId)
     {
         this.subjectId = subjectId;
     }
 
-    public Long getSubjectId() 
+    public Long getSubjectId()
     {
         return subjectId;
     }
 
-    public void setPaperId(Long paperId) 
+    public void setPaperId(Long paperId)
     {
         this.paperId = paperId;
     }
 
-    public Long getPaperId() 
+    public Long getPaperId()
     {
         return paperId;
     }
 
-    public void setKnowledgeId(Long knowledgeId) 
+    public void setKnowledgeId(Long knowledgeId)
     {
         this.knowledgeId = knowledgeId;
     }
 
-    public Long getKnowledgeId() 
+    public Long getKnowledgeId()
     {
         return knowledgeId;
     }
 
-    public void setDiff(BigDecimal diff) 
+    public void setDiff(BigDecimal diff)
     {
         this.diff = diff;
     }
 
-    public BigDecimal getDiff() 
+    public BigDecimal getDiff()
     {
         return diff;
     }
 
-    public void setSimilarity(Long similarity) 
+    public void setSimilarity(Long similarity)
     {
         this.similarity = similarity;
     }
 
-    public Long getSimilarity() 
+    public Long getSimilarity()
     {
         return similarity;
     }
 
-    public void setParse(String parse) 
+    public void setParse(String parse)
     {
         this.parse = parse;
     }
 
-    public String getParse() 
+    public String getParse()
     {
         return parse;
     }
 
-    public void setKnowId(Long knowId) 
+    public void setKnowId(Long knowId)
     {
         this.knowId = knowId;
     }
 
-    public Long getKnowId() 
+    public Long getKnowId()
     {
         return knowId;
     }
 
-    public void setGradeId(Long gradeId) 
+    public void setGradeId(Long gradeId)
     {
         this.gradeId = gradeId;
     }
 
-    public Long getGradeId() 
+    public Long getGradeId()
     {
         return gradeId;
     }
 
-    public void setKnowledges(String knowledges) 
+    public void setKnowledges(String knowledges)
     {
         this.knowledges = knowledges;
     }
 
-    public String getKnowledges() 
+    public String getKnowledges()
     {
         return knowledges;
     }
 
-    public void setArea(String area) 
+    public void setArea(String area)
     {
         this.area = area;
     }
 
-    public String getArea() 
+    public String getArea()
     {
         return area;
     }
 
-    public void setYear(Long year) 
+    public void setYear(Long year)
     {
         this.year = year;
     }
 
-    public Long getYear() 
+    public Long getYear()
     {
         return year;
     }
 
-    public void setPaperTpye(String paperTpye) 
+    public void setPaperTpye(String paperTpye)
     {
         this.paperTpye = paperTpye;
     }
 
-    public String getPaperTpye() 
+    public String getPaperTpye()
     {
         return paperTpye;
     }
 
-    public void setSource(String source) 
+    public void setSource(String source)
     {
         this.source = source;
     }
 
-    public String getSource() 
+    public String getSource()
     {
         return source;
     }
 
-    public void setFromSite(String fromSite) 
+    public void setFromSite(String fromSite)
     {
         this.fromSite = fromSite;
     }
 
-    public String getFromSite() 
+    public String getFromSite()
     {
         return fromSite;
     }
 
-    public void setIsSub(Integer isSub) 
+    public void setIsSub(Integer isSub)
     {
         this.isSub = isSub;
     }
 
-    public Integer getIsSub() 
+    public Integer getIsSub()
     {
         return isSub;
     }
 
-    public void setIsNormal(Integer isNormal) 
+    public void setIsNormal(Integer isNormal)
     {
         this.isNormal = isNormal;
     }
 
-    public Integer getIsNormal() 
+    public Integer getIsNormal()
     {
         return isNormal;
     }
 
-    public void setIsKonw(Integer isKonw) 
+    public void setIsKonw(Integer isKonw)
     {
         this.isKonw = isKonw;
     }
 
-    public Integer getIsKonw() 
+    public Integer getIsKonw()
     {
         return isKonw;
     }
 
-    public void setTiid(String tiid) 
+    public void setTiid(String tiid)
     {
         this.tiid = tiid;
     }
 
-    public String getTiid() 
+    public String getTiid()
     {
         return tiid;
     }
 
-    public void setMd5(String md5) 
+    public void setMd5(String md5)
     {
         this.md5 = md5;
     }
 
-    public String getMd5() 
+    public String getMd5()
     {
         return md5;
     }
 
-    public void setIsunique(Long isunique) 
+    public void setIsunique(Long isunique)
     {
         this.isunique = isunique;
     }
 
-    public Long getIsunique() 
+    public Long getIsunique()
     {
         return isunique;
     }
 
-    public void setMd52(String md52) 
+    public void setMd52(String md52)
     {
         this.md52 = md52;
     }
 
-    public String getMd52() 
+    public String getMd52()
     {
         return md52;
     }
 
-    public void setScore(BigDecimal score) 
+    public void setScore(Integer score)
     {
         this.score = score;
     }
 
-    public BigDecimal getScore() 
+    public Integer getScore()
     {
         return score;
     }
 
-    public void setOptions(String options) 
+    public void setOptions(String options)
     {
         this.options = options;
     }
 
-    public String getOptions() 
+    public String getOptions()
     {
         return options;
     }
 
-    public void setNumber(Long number) 
+    public void setNumber(Long number)
     {
         this.number = number;
     }
 
-    public Long getNumber() 
+    public Long getNumber()
     {
         return number;
     }
 
-    public void setPaperTypeTitle(String paperTypeTitle) 
+    public void setPaperTypeTitle(String paperTypeTitle)
     {
         this.paperTypeTitle = paperTypeTitle;
     }
 
-    public String getPaperTypeTitle() 
+    public String getPaperTypeTitle()
     {
         return paperTypeTitle;
     }
 
-    public void setOptions0(String options0) 
+    public void setOptions0(String options0)
     {
         this.options0 = options0;
     }
 
-    public String getOptions0() 
+    public String getOptions0()
     {
         return options0;
     }
 
-    public void setTitle0(String title0) 
+    public void setTitle0(String title0)
     {
         this.title0 = title0;
     }
 
-    public String getTitle0() 
+    public String getTitle0()
     {
         return title0;
     }
 
-    public void setTitle1(String title1) 
+    public void setTitle1(String title1)
     {
         this.title1 = title1;
     }
 
-    public String getTitle1() 
+    public String getTitle1()
     {
         return title1;
     }
 
-    public void setParse0(String parse0) 
+    public void setParse0(String parse0)
     {
         this.parse0 = parse0;
     }
 
-    public String getParse0() 
+    public String getParse0()
     {
         return parse0;
     }
 
-    public void setAnswer0(String answer0) 
+    public void setAnswer0(String answer0)
     {
         this.answer0 = answer0;
     }
 
-    public String getAnswer0() 
+    public String getAnswer0()
     {
         return answer0;
     }
 
-    public void setIsUpdate(Long isUpdate) 
+    public void setIsUpdate(Long isUpdate)
     {
         this.isUpdate = isUpdate;
     }
 
-    public Long getIsUpdate() 
+    public Long getIsUpdate()
     {
         return isUpdate;
     }
 
-    public void setIsSubType(String isSubType) 
+    public void setIsSubType(String isSubType)
     {
         this.isSubType = isSubType;
     }
 
-    public String getIsSubType() 
+    public String getIsSubType()
     {
         return isSubType;
     }

+ 13 - 3
ie-system/src/main/java/com/ruoyi/learn/domain/LearnTestStudent.java

@@ -44,7 +44,9 @@ public class LearnTestStudent extends BaseEntity
 
     /** 状态 */
     @Excel(name = "状态")
-    private Long status;
+    private Integer status;
+
+    private Integer count;
 
     public void setId(String id) 
     {
@@ -116,16 +118,24 @@ public class LearnTestStudent extends BaseEntity
         return examineeId;
     }
 
-    public void setStatus(Long status) 
+    public void setStatus(Integer status)
     {
         this.status = status;
     }
 
-    public Long getStatus() 
+    public Integer getStatus()
     {
         return status;
     }
 
+    public Integer getCount() {
+        return count;
+    }
+
+    public void setCount(Integer count) {
+        this.count = count;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 130 - 50
ie-system/src/main/java/com/ruoyi/learn/domain/LearnWrongBook.java

@@ -1,7 +1,10 @@
 package com.ruoyi.learn.domain;
 
 import java.util.Date;
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -9,7 +12,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
 
 /**
  * 学生错题本对象 learn_wrong_book
- * 
+ *
  * @author ruoyi
  * @date 2025-09-25
  */
@@ -50,7 +53,7 @@ public class LearnWrongBook extends BaseEntity
 
     /** 最后参考答案 */
     @Excel(name = "最后参考答案")
-    private String answers;
+    private String answer;
 
     /** 答案1 */
     @Excel(name = "答案1")
@@ -62,11 +65,11 @@ public class LearnWrongBook extends BaseEntity
 
     /** 总分 */
     @Excel(name = "总分")
-    private Long scoreTotal;
+    private Integer scoreTotal;
 
     /** 得分 */
     @Excel(name = "得分")
-    private Long score;
+    private Integer score;
 
     /** 评级 */
     @Excel(name = "评级")
@@ -74,7 +77,7 @@ public class LearnWrongBook extends BaseEntity
 
     /** 得分率 */
     @Excel(name = "得分率")
-    private Long scoreRate;
+    private Integer scoreRate;
 
     /** 错误次数 */
     @Excel(name = "错误次数")
@@ -102,216 +105,293 @@ public class LearnWrongBook extends BaseEntity
     @Excel(name = "是否收藏")
     private Long collect;
 
-    public void setWrongId(Long wrongId) 
+    private String knownledgeName;
+    private String title;
+    private String knowledge;
+    private String parse;
+    @ApiModelProperty("考题类型 question_type")
+    Integer typeId;
+    @ApiModelProperty("考题类型名称")
+    String type;
+    @ApiModelProperty("选项数组")
+    List<String> options;
+    @ApiModelProperty("答案数组")
+    List<String> answers;
+
+    public void setWrongId(Long wrongId)
     {
         this.wrongId = wrongId;
     }
 
-    public Long getWrongId() 
+    public Long getWrongId()
     {
         return wrongId;
     }
 
-    public void setStudentId(Long studentId) 
+    public void setStudentId(Long studentId)
     {
         this.studentId = studentId;
     }
 
-    public Long getStudentId() 
+    public Long getStudentId()
     {
         return studentId;
     }
 
-    public void setQuestionId(Long questionId) 
+    public void setQuestionId(Long questionId)
     {
         this.questionId = questionId;
     }
 
-    public Long getQuestionId() 
+    public Long getQuestionId()
     {
         return questionId;
     }
 
-    public void setSource(String source) 
+    public void setSource(String source)
     {
         this.source = source;
     }
 
-    public String getSource() 
+    public String getSource()
     {
         return source;
     }
 
-    public void setState(Long state) 
+    public void setState(Long state)
     {
         this.state = state;
     }
 
-    public Long getState() 
+    public Long getState()
     {
         return state;
     }
 
-    public void setKnownledgeId(Long knownledgeId) 
+    public void setKnownledgeId(Long knownledgeId)
     {
         this.knownledgeId = knownledgeId;
     }
 
-    public Long getKnownledgeId() 
+    public Long getKnownledgeId()
     {
         return knownledgeId;
     }
 
-    public void setSubjectId(Long subjectId) 
+    public void setSubjectId(Long subjectId)
     {
         this.subjectId = subjectId;
     }
 
-    public Long getSubjectId() 
+    public Long getSubjectId()
     {
         return subjectId;
     }
 
-    public void setPaperId(Long paperId) 
+    public void setPaperId(Long paperId)
     {
         this.paperId = paperId;
     }
 
-    public Long getPaperId() 
+    public Long getPaperId()
     {
         return paperId;
     }
 
-    public void setAnswers(String answers) 
+    public void setAnswer(String answer)
     {
-        this.answers = answers;
+        this.answer = answer;
     }
 
-    public String getAnswers() 
+    public String getAnswer()
     {
-        return answers;
+        return answer;
     }
 
-    public void setAnswer1(String answer1) 
+    public void setAnswer1(String answer1)
     {
         this.answer1 = answer1;
     }
 
-    public String getAnswer1() 
+    public String getAnswer1()
     {
         return answer1;
     }
 
-    public void setAnswer2(String answer2) 
+    public void setAnswer2(String answer2)
     {
         this.answer2 = answer2;
     }
 
-    public String getAnswer2() 
+    public String getAnswer2()
     {
         return answer2;
     }
 
-    public void setScoreTotal(Long scoreTotal) 
+    public void setScoreTotal(Integer scoreTotal)
     {
         this.scoreTotal = scoreTotal;
     }
 
-    public Long getScoreTotal() 
+    public Integer getScoreTotal()
     {
         return scoreTotal;
     }
 
-    public void setScore(Long score) 
+    public void setScore(Integer score)
     {
         this.score = score;
     }
 
-    public Long getScore() 
+    public Integer getScore()
     {
         return score;
     }
 
-    public void setScoreLevel(String scoreLevel) 
+    public void setScoreLevel(String scoreLevel)
     {
         this.scoreLevel = scoreLevel;
     }
 
-    public String getScoreLevel() 
+    public String getScoreLevel()
     {
         return scoreLevel;
     }
 
-    public void setScoreRate(Long scoreRate) 
+    public void setScoreRate(Integer scoreRate)
     {
         this.scoreRate = scoreRate;
     }
 
-    public Long getScoreRate() 
+    public Integer getScoreRate()
     {
         return scoreRate;
     }
 
-    public void setWrongCount(Long wrongCount) 
+    public void setWrongCount(Long wrongCount)
     {
         this.wrongCount = wrongCount;
     }
 
-    public Long getWrongCount() 
+    public Long getWrongCount()
     {
         return wrongCount;
     }
 
-    public void setRightCount(Long rightCount) 
+    public void setRightCount(Long rightCount)
     {
         this.rightCount = rightCount;
     }
 
-    public Long getRightCount() 
+    public Long getRightCount()
     {
         return rightCount;
     }
 
-    public void setTotalCount(Long totalCount) 
+    public void setTotalCount(Long totalCount)
     {
         this.totalCount = totalCount;
     }
 
-    public Long getTotalCount() 
+    public Long getTotalCount()
     {
         return totalCount;
     }
 
-    public void setCreatedTime(Date createdTime) 
+    public void setCreatedTime(Date createdTime)
     {
         this.createdTime = createdTime;
     }
 
-    public Date getCreatedTime() 
+    public Date getCreatedTime()
     {
         return createdTime;
     }
 
-    public void setUpdatedTime(Date updatedTime) 
+    public void setUpdatedTime(Date updatedTime)
     {
         this.updatedTime = updatedTime;
     }
 
-    public Date getUpdatedTime() 
+    public Date getUpdatedTime()
     {
         return updatedTime;
     }
 
-    public void setCollect(Long collect) 
+    public void setCollect(Long collect)
     {
         this.collect = collect;
     }
 
-    public Long getCollect() 
+    public Long getCollect()
     {
         return collect;
     }
 
+    public String getKnownledgeName() {
+        return knownledgeName;
+    }
+
+    public void setKnownledgeName(String knownledgeName) {
+        this.knownledgeName = knownledgeName;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getKnowledge() {
+        return knowledge;
+    }
+
+    public void setKnowledge(String knowledge) {
+        this.knowledge = knowledge;
+    }
+
+    public String getParse() {
+        return parse;
+    }
+
+    public void setParse(String parse) {
+        this.parse = parse;
+    }
+
+    public Integer getTypeId() {
+        return typeId;
+    }
+
+    public void setTypeId(Integer typeId) {
+        this.typeId = typeId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public List<String> getOptions() {
+        return options;
+    }
+
+    public void setOptions(List<String> options) {
+        this.options = options;
+    }
+
+    public List<String> getAnswers() {
+        return answers;
+    }
+
+    public void setAnswers(List<String> answers) {
+        this.answers = answers;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -323,7 +403,7 @@ public class LearnWrongBook extends BaseEntity
             .append("knownledgeId", getKnownledgeId())
             .append("subjectId", getSubjectId())
             .append("paperId", getPaperId())
-            .append("answers", getAnswers())
+            .append("answer", getAnswer())
             .append("answer1", getAnswer1())
             .append("answer2", getAnswer2())
             .append("scoreTotal", getScoreTotal())

+ 78 - 1
ie-system/src/main/java/com/ruoyi/learn/domain/PaperVO.java

@@ -1,9 +1,12 @@
 package com.ruoyi.learn.domain;
 
+import com.ruoyi.enums.QuestionType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import org.apache.commons.collections4.CollectionUtils;
 
+import java.util.Arrays;
 import java.util.List;
 
 @Data
@@ -23,8 +26,82 @@ public class PaperVO {
 
     @Data
     @ApiModel("试题")
-    public static class QuestionSeq extends LearnQuestions {
+    public static class QuestionSeq {
+        private Long id;
+        private Long knowledgeId;
+        private String title;
+        private String answer1;
+        @ApiModelProperty("非标准格式答案或含部分过程说明的答案")
+        private String answer2;
+        private String parse;
         @ApiModelProperty("序号")
         Integer seq;
+        @ApiModelProperty("考题类型 question_type")
+        Integer typeId;
+        @ApiModelProperty("考题类型名称")
+        String type;
+        @ApiModelProperty("选项数组")
+        List<String> options;
+
+        List<QuestionSeq> subQuestions;
+    }
+
+
+    @Data
+    @ApiModel("试题")
+    public static class QuestionAnswer {
+        private Long answerId;
+        @ApiModelProperty("题ID")
+        private Long id;
+        private Long knowledgeId;
+        private String title;
+        private String answer1;
+        @ApiModelProperty("非标准格式答案或含部分过程说明的答案")
+        private String answer2;
+        private String parse;
+        @ApiModelProperty("序号")
+        Integer seq;
+        @ApiModelProperty("考题类型 question_type")
+        Integer typeId;
+
+        @ApiModelProperty("本卷中标记")
+        Boolean isMark;
+        @ApiModelProperty("是否收藏")
+        Boolean isFavorite;
+        @ApiModelProperty("不会做,转State")
+        Boolean isNotKnow;
+        @ApiModelProperty("学生回答")
+        List<String> answers;
+
+        @ApiModelProperty("答题正确状态 0 默认 1正确 2错误 3不会 4 未答")
+        Integer state;
+        @ApiModelProperty("试题标准分")
+        Integer score;
+
+        List<QuestionAnswer> subQuestions;
+
+        public Integer calcState(QuestionAnswer std, boolean isDone) {
+            if(!isDone) {
+                return 0;
+            }
+            if(null != isNotKnow && isNotKnow) {
+                return 3;
+            } else {
+                QuestionType qt = QuestionType.of(this.typeId);
+                if(QuestionType.Single.equals(qt) || QuestionType.Judgment.equals(qt)) {
+                    if(CollectionUtils.isEmpty(answers)) {
+                        return 4;
+                    }
+                    return answers.get(0).equals(std.getAnswer1()) ? 1 : 2;
+                } else if(QuestionType.Multiple.equals(qt)) {
+                    if(CollectionUtils.isEmpty(answers)) {
+                        return 4;
+                    }
+                    List<String> stdAnswers = Arrays.asList(std.getAnswer1().split(","));
+                    return stdAnswers.size() == answers.size() && CollectionUtils.intersection(stdAnswers, answers).size() == answers.size() ? 1 : 2;
+                }
+            }
+            return 0;
+        }
     }
 }

+ 7 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnAnswerMapper.java

@@ -1,6 +1,9 @@
 package com.ruoyi.learn.mapper;
 
 import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.learn.domain.LearnAnswer;
 
 /**
@@ -11,6 +14,10 @@ import com.ruoyi.learn.domain.LearnAnswer;
  */
 public interface LearnAnswerMapper 
 {
+    List<JSONObject> selectKnowledgeStats(Map cond);
+    JSONObject selectQuestionStatsHeader(Map cond);
+    List<JSONObject> selectQuestionStatsDetail(Map cond);
+
     /**
      * 查询答卷答案
      * 

+ 6 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnExamineeMapper.java

@@ -1,7 +1,11 @@
 package com.ruoyi.learn.mapper;
 
+import java.util.Collection;
 import java.util.List;
+
+import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.learn.domain.LearnExaminee;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 答卷Mapper接口
@@ -11,6 +15,8 @@ import com.ruoyi.learn.domain.LearnExaminee;
  */
 public interface LearnExamineeMapper 
 {
+    public List<JSONObject> selectExamRankingStats(@Param("stuScore") Integer stuScore, @Param("paperIds") Collection<Long> paperIds);
+
     /**
      * 查询答卷
      * 

Некоторые файлы не были показаны из-за большого количества измененных файлов