Browse Source

fix(all): 修复bug

Go 4 years ago
parent
commit
8f3dd0d005
93 changed files with 1142 additions and 353 deletions
  1. 2 0
      front/project/Constant.js
  2. 7 2
      front/project/admin/routes/course/detail/page.js
  3. 20 2
      front/project/admin/routes/course/package/page.js
  4. 42 30
      front/project/admin/routes/setting/index/page.js
  5. 2 0
      front/project/admin/routes/student/askCourse/page.js
  6. 10 5
      front/project/admin/routes/student/study/page.js
  7. 0 1
      front/project/admin/routes/student/studyDetail/page.js
  8. 1 0
      front/project/admin/routes/subject/exercise/page.js
  9. 0 5
      front/project/admin/routes/textbook/topic/page.js
  10. 1 1
      front/project/www/components/Card/index.less
  11. 3 1
      front/project/www/components/Icon/index.js
  12. 1 0
      front/project/www/components/ListTable/index.less
  13. 1 1
      front/project/www/components/Note/index.js
  14. 4 0
      front/project/www/components/Other/index.less
  15. 7 6
      front/project/www/components/OtherModal/index.js
  16. 9 0
      front/project/www/components/Video/index.js
  17. 8 0
      front/project/www/components/Video/index.less
  18. 7 3
      front/project/www/routes/course/answer/page.js
  19. 43 19
      front/project/www/routes/course/detail/page.js
  20. 4 0
      front/project/www/routes/course/main/index.less
  21. 30 7
      front/project/www/routes/course/note/page.js
  22. 6 1
      front/project/www/routes/course/online/page.js
  23. 4 5
      front/project/www/routes/course/vs/page.js
  24. 5 5
      front/project/www/routes/examination/list/page.js
  25. 4 0
      front/project/www/routes/exercise/main/page.js
  26. 0 1
      front/project/www/routes/my/collect/page.js
  27. 81 36
      front/project/www/routes/my/course/page.js
  28. 1 0
      front/project/www/routes/my/error/page.js
  29. 1 0
      front/project/www/routes/my/main/page.js
  30. 16 9
      front/project/www/routes/my/report/page.js
  31. 19 1
      front/project/www/routes/page/cart/page.js
  32. 4 0
      front/project/www/routes/page/ready/index.less
  33. 2 0
      front/project/www/routes/page/ready/page.js
  34. 20 6
      front/project/www/routes/paper/process/base/index.js
  35. 29 19
      front/project/www/routes/paper/process/page.js
  36. 3 3
      front/project/www/routes/paper/question/detail/index.js
  37. 4 4
      front/project/www/routes/paper/question/detail/index.less
  38. 1 1
      front/project/www/routes/paper/question/page.js
  39. 1 0
      front/project/www/routes/paper/report/index.less
  40. 31 22
      front/project/www/routes/paper/report/page.js
  41. 3 3
      front/project/www/routes/question/search/page.js
  42. 8 0
      front/project/www/routes/question/searchHistory/index.less
  43. 2 1
      front/project/www/routes/question/searchHistory/page.js
  44. 2 2
      front/project/www/stores/course.js
  45. 14 2
      front/project/www/stores/my.js
  46. 8 0
      front/project/www/stores/order.js
  47. 2 2
      front/project/www/stores/question.js
  48. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/UserCourseDataSubscribeMapper.java
  49. 160 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseDataSubscribe.java
  50. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrderCheckout.java
  51. 19 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseDataSubscribeMapper.xml
  52. 3 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderCheckoutMapper.xml
  53. 2 1
      server/data/src/main/java/com/qxgmat/data/relation/ExaminationPaperRelationMapper.java
  54. 19 0
      server/data/src/main/java/com/qxgmat/data/relation/UserNoteCourseRelationMapper.java
  55. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml
  56. 39 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteCourseRelationMapper.xml
  57. 2 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml
  58. 6 6
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml
  59. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml
  60. 1 0
      server/data/src/main/resources/db/migration/V12__update_user_order_checkout.sql
  61. 3 5
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/CourseController.java
  62. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  63. 3 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  64. 36 5
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  65. 8 0
      server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java
  66. 10 9
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  67. 0 1
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/UserAskCourseDto.java
  68. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/UserAskCourseListDto.java
  69. 20 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserCourseAppointmentExtendDto.java
  70. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserOrderRecordExtendDto.java
  71. 27 0
      server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordSelectDto.java
  72. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserCourseDetailDto.java
  73. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserPaperDto.java
  74. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserQuestionBaseDto.java
  75. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/UserCollectExperienceService.java
  76. 16 22
      server/gateway-api/src/main/java/com/qxgmat/service/UserNoteCourseService.java
  77. 9 6
      server/gateway-api/src/main/java/com/qxgmat/service/UserPaperService.java
  78. 16 0
      server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java
  79. 12 4
      server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java
  80. 8 4
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java
  81. 1 0
      server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java
  82. 103 48
      server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java
  83. 32 15
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java
  84. 5 5
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ToolsService.java
  85. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/CoursePackageService.java
  86. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/CourseService.java
  87. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/ExercisePaperService.java
  88. 1 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserAskCourseService.java
  89. 1 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseAppointmentCommentService.java
  90. 2 2
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseProgressService.java
  91. 4 4
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserPaperQuestionService.java
  92. 1 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserReportService.java
  93. 1 0
      server/gateway-api/src/main/resources/application.yml

+ 2 - 0
front/project/Constant.js

@@ -17,6 +17,8 @@ export const QuestionDifficult = [{ label: 'Easy', value: 'easy', sort: 2 }, { l
 
 export const QuestionType = [{ label: '语法SC', value: 'sc', long: 'Sentence Correnction' }, { label: '阅读RC', value: 'rc', long: 'Reading Comprehension' }, { label: '逻辑CR', value: 'cr', long: 'Critical Reasoning' }, { label: '数学PS', value: 'ps', long: 'Problem solving' }, { label: '数学DS', value: 'ds', long: 'Data Sufficiency' }, { label: '综合推理IR', value: 'ir', long: 'Integrated Reasoning' }, { label: '作文AWA', value: 'awa', long: 'Analytical Writing Assessment' }];
 
+export const SubjectType = [{ label: '语文', value: 'verbal', long: 'Verbal' }, { label: '数学', value: 'quant', long: 'Quantitative' }, { label: '综合推理IR', value: 'ir', long: 'Integrated Reasoning' }, { label: '作文AWA', value: 'awa', long: 'Analytical Writing Assessment' }];
+
 export const ExaminationQuestionType = [{ label: '语法SC', value: 'sc', long: 'Sentence Correnction' }, { label: '阅读RC', value: 'rc', long: 'Reading Comprehension' }, { label: '逻辑CR', value: 'cr', long: 'Critical Reasoning' }, { label: '数学PS', value: 'ps', long: 'Problem solving' }, { label: '数学DS', value: 'ds', long: 'Data Sufficiency' }];
 
 export const FeedbackQuestionType = [{ label: '语法SC', value: 'sc' }, { label: '阅读RC', value: 'rc' }, { label: '逻辑CR', value: 'cr' }, { label: '数学PS', value: 'ps' }, { label: '数学DS', value: 'ds' }, { label: '综合推理IR', value: 'ir' }, { label: '长难句', value: 'sentence' }];

+ 7 - 2
front/project/admin/routes/course/detail/page.js

@@ -309,6 +309,7 @@ export default class extends Page {
           {getFieldDecorator('title', {
             rules: [
               { required: true, message: '请输入课程名称' },
+              { max: 10, message: '课程名称过长' },
             ],
           })(
             <Input placeholder='请输入课程名称' />,
@@ -469,7 +470,7 @@ export default class extends Page {
     return <Block>
       <Form>
         {getFieldDecorator('id')(<input hidden />)}
-        {exercise && <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='学科'>
+        {exercise && data && <Form.Item key={data.structId} labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='学科'>
           {getFieldDecorator('structId', structIdConfig)(
             <TreeSelect treeData={exercise} />,
           )}
@@ -497,7 +498,7 @@ export default class extends Page {
   }
 
   renderTime() {
-    const { time, timerange } = this.state;
+    const { time, timerange = [] } = this.state;
     if (!time) return null;
     return <Block>
       <h1>课时管理</h1>
@@ -521,6 +522,10 @@ export default class extends Page {
         <Col span={1}>
           <Form.Item>
             <Button onClick={() => {
+              if (timerange.length < 1) {
+                asyncSMessage('选择时间段', 'warn');
+                return;
+              }
               Course.addTime({ courseId: this.params.id, startTime: timerange[0], endTime: timerange[1] }).then(() => {
                 this.refreshTime();
                 this.setState({ timerange: null });

+ 20 - 2
front/project/admin/routes/course/package/page.js

@@ -17,12 +17,13 @@ export default class extends Page {
   constructor(props) {
     super(props);
     this.exerciseMap = {};
+    this.courseList = [];
     this.actionList = [{
       key: 'add',
       type: 'primary',
       name: '创建',
     }];
-
+    this.itemF = null;
     this.itemList = [{
       key: 'id',
       type: 'hidden',
@@ -35,6 +36,9 @@ export default class extends Page {
       type: 'select',
       select: [],
       name: '套餐学科',
+      onChange: (value) => {
+        this.refreshCourse(value);
+      },
     }, {
       key: 'description',
       type: 'input',
@@ -138,10 +142,11 @@ export default class extends Page {
       this.setState({ exercise: formatTreeData(list, 'id', 'title', 'parentId') });
     });
     Course.list({ excludeVs: true, excludeOnline: true }).then((result) => {
-      this.itemList[5].select = result.list.map(row => {
+      this.courseList = result.list.map(row => {
         row.value = row.id;
         return row;
       });
+      this.setState({ load: false });
     });
   }
 
@@ -161,6 +166,9 @@ export default class extends Page {
         asyncSMessage('添加成功!');
         this.refresh();
       });
+    }).then((form) => {
+      this.itemF = form;
+      this.itemList[5].select = [];
     });
   }
 
@@ -171,7 +179,17 @@ export default class extends Page {
         asyncSMessage('编辑成功!');
         this.refresh();
       });
+    }).then((form) => {
+      this.itemF = form;
+      this.refreshCourse(row.structId);
+    });
+  }
+
+  refreshCourse(structId) {
+    this.itemList[5].select = this.courseList.filter(row => {
+      return (row.parentStructId || row.structId) === structId;
     });
+    this.itemF.setState({ load: true });
   }
 
   special(row, isSpecial) {

+ 42 - 30
front/project/admin/routes/setting/index/page.js

@@ -48,43 +48,55 @@ export default class extends Page {
   }
 
   submit() {
-    this.submitIndex();
-    this.submitBase();
+    Promise.resolve()
+      .then(() => {
+        return this.submitIndex();
+      }).then(() => {
+        return this.submitBase();
+      }).then(() => {
+        asyncSMessage('保存成功');
+      });
   }
 
   submitIndex() {
-    const { form } = this.props;
-    form.validateFields(['index'], (err) => {
-      if (!err) {
-        // const { indexInfo } = this.state;
-        const { index } = form.getFieldsValue();
-        index.course = index.course.filter(row => row);
-        index.activity = index.activity.filter(row => row);
-        index.evaluation = index.evaluation.filter(row => row);
-        System.setIndex(index)
-          .then(() => {
-            this.setState({ indexInfo: index });
-            asyncSMessage('保存成功');
-          }).catch((e) => {
-            form.setFields(formatFormError(index, e.result));
-          });
-      }
+    return new Promise((resolve, reject) => {
+      const { form } = this.props;
+      form.validateFields(['index'], (err) => {
+        if (!err) {
+          // const { indexInfo } = this.state;
+          const { index } = form.getFieldsValue();
+          index.course = index.course.filter(row => row);
+          index.activity = index.activity.filter(row => row);
+          index.evaluation = index.evaluation.filter(row => row);
+          System.setIndex(index)
+            .then(() => {
+              this.setState({ indexInfo: index });
+              resolve();
+            }).catch((e) => {
+              form.setFields(formatFormError(index, e.result));
+              reject();
+            });
+        }
+      });
     });
   }
 
   submitBase() {
-    const { form } = this.props;
-    form.validateFields(['base'], (err) => {
-      if (!err) {
-        const { base } = form.getFieldsValue();
-        System.setBase(base)
-          .then(() => {
-            this.setState({ base });
-            asyncSMessage('保存成功');
-          }).catch((e) => {
-            form.setFields(formatFormError(base, e.result));
-          });
-      }
+    return new Promise((resolve, reject) => {
+      const { form } = this.props;
+      form.validateFields(['base'], (err) => {
+        if (!err) {
+          const { base } = form.getFieldsValue();
+          System.setBase(base)
+            .then(() => {
+              this.setState({ base });
+              resolve();
+            }).catch((e) => {
+              form.setFields(formatFormError(base, e.result));
+              reject();
+            });
+        }
+      });
     });
   }
 

+ 2 - 0
front/project/admin/routes/student/askCourse/page.js

@@ -53,6 +53,7 @@ export default class extends Page {
       allowClear: true,
       name: '展示状态',
       select: SwitchSelect,
+      number: true,
     }, {
       key: 'userId',
       type: 'select',
@@ -174,6 +175,7 @@ export default class extends Page {
         row.value = row.id;
         return row;
       });
+      this.setState({ load: false });
     });
   }
 

+ 10 - 5
front/project/admin/routes/student/study/page.js

@@ -7,7 +7,7 @@ import FilterLayout from '@src/layouts/FilterLayout';
 // import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
 import { getMap, formatTreeData, formatDate, bindSearch } from '@src/services/Tools';
-import { asyncSMessage } from '@src/services/AsyncTools';
+import { asyncSMessage, asyncDelConfirm } from '@src/services/AsyncTools';
 import { CourseModule, CourseStatus } from '../../../../Constant';
 import { Course } from '../../../stores/course';
 import { Exercise } from '../../../stores/exercise';
@@ -15,7 +15,7 @@ import { User } from '../../../stores/user';
 
 const CourseModuleMap = getMap(CourseModule, 'value', 'label');
 const CourseStatusMap = getMap(CourseStatus, 'value', 'label');
-
+CourseStatusMap[3] = '已停用';
 export default class extends Page {
   constructor(props) {
     super(props);
@@ -100,6 +100,9 @@ export default class extends Page {
             status = 2;
           }
         }
+        if (record.isStop) {
+          status = 3;
+        }
         return CourseStatusMap[status] || status;
       },
     }, {
@@ -163,9 +166,11 @@ export default class extends Page {
   }
 
   stopAction(id) {
-    return Course.editStudy({ id, isStop: 1 }).then(() => {
-      asyncSMessage('停用成功!');
-      this.refresh();
+    asyncDelConfirm('停用确认', '是否停用选中记录?', () => {
+      return User.stopRecord({ id }).then(() => {
+        asyncSMessage('停用成功!');
+        this.refresh();
+      });
     });
   }
 

+ 0 - 1
front/project/admin/routes/student/studyDetail/page.js

@@ -199,7 +199,6 @@ export default class extends Page {
     data.id = id;
     return User.editCourseAppointment(data).then(() => {
       this.refreshVs();
-      this.cleanInfo();
     });
   }
 

+ 1 - 0
front/project/admin/routes/subject/exercise/page.js

@@ -181,6 +181,7 @@ export default class extends Page {
                   // data.associationContent = data.associationContent.map(row => row.id);
                   return Question.edit(data).then(() => {
                     asyncSMessage('修改成功!');
+                    this.refresh();
                   });
                 });
             }}>题源联想</a>

+ 0 - 5
front/project/admin/routes/textbook/topic/page.js

@@ -81,11 +81,6 @@ export default class extends Page {
           {(
             <Link to={`/textbook/topic/detail/${record.id}`}>编辑</Link>
           )}
-          {(
-            <a onClick={() => {
-              this.deleteAction(record);
-            }}>删除</a>
-          )}
         </div>;
       },
     }];

+ 1 - 1
front/project/www/components/Card/index.less

@@ -278,7 +278,7 @@
 
           .progress {
             display: inline-block;
-            width: 160px;
+            width: 140px;
           }
 
           .action {

+ 3 - 1
front/project/www/components/Icon/index.js

@@ -2,11 +2,13 @@ import React from 'react';
 import './index.less';
 
 function GIcon(props) {
-  const { className, active, name, noHover, onClick, children } = props;
+  const { className, active, name, noHover, onClick, children, onHover } = props;
   return (
     <div
       className={`icon ${className || ''} ${name} ${active ? 'active' : ''} ${noHover ? 'no' : ''}`}
       onClick={() => onClick && onClick()}
+      onMouseEnter={() => onHover && onHover(true)}
+      onMouseLeave={() => onHover && onHover(false)}
     >
       {children}
     </div>

+ 1 - 0
front/project/www/components/ListTable/index.less

@@ -23,6 +23,7 @@
   .filter {
     height: 68px;
     line-height: 68px;
+    padding: 0;
     padding-left: 44px;
     border-bottom: 1px solid @line_color;
 

+ 1 - 1
front/project/www/components/Note/index.js

@@ -21,7 +21,7 @@ export default class Note extends Component {
           {data.reply && <div className="reply">{data.reply}</div>}
           {data.content}
         </div>
-        <a hidden={!data.file} className="link t-s-12">
+        <a hidden={!data.file} href={data.file} target="_blank" className="link t-s-12">
           {data.name}
         </a>
       </div>

+ 4 - 0
front/project/www/components/Other/index.less

@@ -164,6 +164,10 @@
     .slider-frame {
       padding: 40px 0 !important;
     }
+
+    .slider-list::after {
+      clear: both;
+    }
   }
 
   .item {

+ 7 - 6
front/project/www/components/OtherModal/index.js

@@ -966,12 +966,12 @@ export class FeedbackErrorDataModal extends Component {
 export class AskCourseModal extends Component {
   constructor(props) {
     super(props);
-    this.state = { data: { position: [], content: '', originContent: '' } };
+    this.state = { data: { position: '', content: '', originContent: '' } };
   }
 
   componentWillReceiveProps(nextProps) {
     if (nextProps.show && nextProps.defaultData) {
-      this.setState({ data: Object.assign({}, nextProps.defaultData, this.state.data) });
+      this.setState({ data: Object.assign({}, nextProps.defaultData) });
     }
   }
 
@@ -993,7 +993,7 @@ export class AskCourseModal extends Component {
     }
     return My.addCourseAsk(course.id, courseNo.id, data.position, data.originContent, data.content).then(
       () => {
-        this.setState({ data: { position: [], content: '', originContent: '' } });
+        this.setState({ data: { position: '', content: '', originContent: '' } });
         if (onConfirm) onConfirm();
       },
     );
@@ -1001,7 +1001,7 @@ export class AskCourseModal extends Component {
 
   onCancel() {
     const { onCancel } = this.props;
-    this.setState({ data: { position: [], content: '', originContent: '' } });
+    this.setState({ data: { position: '', content: '', originContent: '' } });
     if (onCancel) onCancel();
   }
 
@@ -1048,6 +1048,7 @@ export class AskCourseModal extends Component {
           className="b-c-1 w-10 p-10"
           rows={4}
           placeholder={'老师会在n小时内回答你的问题。'}
+          empty={empty.content}
           onChange={e => {
             this.changeData('content', e.target.value);
           }}
@@ -1080,14 +1081,14 @@ export class CourseNoteModal extends Component {
   }
 
   onConfirm() {
-    const { course, onConfirm } = this.props;
+    const { course, onConfirm, courseNos } = this.props;
     const { data } = this.state;
     if (!data.content) {
       this.setState({ empty: { content: !data.content } });
       return Promise.reject();
     }
     if (!data.courseNoId) {
-      data.courseNoId = this.state.courseNos[0].id;
+      data.courseNoId = courseNos[0].id;
     }
     return My.updateCourseNote(course.id, data.courseNoId, data.content).then(() => {
       this.setState({ data: { content: '' } });

+ 9 - 0
front/project/www/components/Video/index.js

@@ -72,6 +72,7 @@ export default class Video extends Component {
         ],
         width: this.props.width,
         height: this.props.height,
+        fluid: true,
       },
       () => {
         this.ready = true;
@@ -100,6 +101,9 @@ export default class Video extends Component {
     this.timeInterval = setInterval(() => {
       const { onTimeUpdate } = this.props;
       if (onTimeUpdate) onTimeUpdate(this.player.currentTime());
+      if (this.player.currentTime() === this.player.duration()) {
+        this.onPause();
+      }
       this.setState({ progress: this.player.currentTime() * 100 / this.player.duration() });
     }, 1000);
   }
@@ -193,6 +197,11 @@ export default class Video extends Component {
           return playing ? this.onPause() : this.onPlay();
         }}>
           <video
+            onContextMenu={(e) => {
+              e.preventDefault();
+              return false;
+            }}
+            controlslist="nodownload"
             ref={node => {
               this.videoNode = node;
             }}

+ 8 - 0
front/project/www/components/Video/index.less

@@ -6,6 +6,14 @@
   overflow: hidden;
   background: #3A3A3AFF;
 
+  video::-webkit-media-controls-enclosure {
+    overflow: hidden;
+  }
+
+  video::-webkit-media-controls-panel {
+    width: calc(100% + 30px);
+  }
+
   .video-wrapper {
 
     .vjs-loading-spinner {

+ 7 - 3
front/project/www/routes/course/answer/page.js

@@ -66,13 +66,12 @@ export default class extends Page {
 
     const { tab } = this.state;
     switch (tab) {
-      case 'special':
-        this.refreshSpecial();
-        break;
       case 'my':
         this.refreshMy();
         break;
+      case 'special':
       default:
+        this.refreshSpecial();
         break;
     }
   }
@@ -127,6 +126,11 @@ export default class extends Page {
     this.initData();
   }
 
+  onChangePage(page) {
+    this.search({ page }, false);
+    this.initData();
+  }
+
   onAction() { }
 
   delAsk(id) {

+ 43 - 19
front/project/www/routes/course/detail/page.js

@@ -80,7 +80,10 @@ export default class extends Page {
                   tip="Restart"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.restart('preview', paper.id);
+                      Question.restart(paper.paper.id)
+                        .then(() => {
+                          this.refreshPaper();
+                        });
                     });
                   }}
                 />
@@ -92,7 +95,7 @@ export default class extends Page {
                   tip="Report"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.reportLink('preview', paper);
+                      Question.reportLink(paper);
                     });
                   }}
                 />
@@ -150,6 +153,7 @@ export default class extends Page {
   }
 
   init() {
+    this.time = 0;
     Main.dataStruct().then(result => {
       const dataStructSelect = result.map(row => {
         return {
@@ -165,6 +169,11 @@ export default class extends Page {
     });
   }
 
+  outPage() {
+    const { item } = this.state;
+    this.updateProgress(item.id, this.lastSecond, item.time);
+  }
+
   formatRecord(row) {
     row.paperMap = {};
     if (row.papers) {
@@ -226,6 +235,10 @@ export default class extends Page {
       this.comments = result.list;
       this.setState({ comments: result.list });
     });
+    Main.listFaq({ page: 1, size: 100, channel: 'course-video', position: id }).then(result => {
+      this.faqs = result.list;
+      this.setState({ faqs: result.list });
+    });
   }
 
   refreshAsk(position) {
@@ -251,6 +264,18 @@ export default class extends Page {
       });
   }
 
+  refreshPaper() {
+    const { id } = this.params;
+    const { data } = this.state;
+    if (!data.have) return;
+    My.listCoursePaper({ courseId: id, page: 1, size: data.courseNos.length })
+      .then((result) => {
+        data.papers = result.list;
+        this.formatRecord(data);
+        this.setState({ data });
+      });
+  }
+
   onChangeRightTab(rightTab) {
     this.setState({ rightTab });
   }
@@ -277,7 +302,8 @@ export default class extends Page {
           key: `${start}`,
         });
         start += 5;
-        end = Math.min(start + 5, max);
+        end = start + 5;
+        // end = Math.min(start + 5, max);
       }
     }
     // 切换播放,记录进度
@@ -311,6 +337,14 @@ export default class extends Page {
     this.showWater(second);
   }
 
+  pauseVideo(second) {
+    const { item } = this.state;
+    this.updateProgress(item.id, second, item.time);
+    // 停止计时
+    this.lastTime = null;
+    this.showWater(second);
+  }
+
   showWater(second, extend) {
     const { data, water = {} } = this.state;
     let { waters = [] } = data;
@@ -357,16 +391,6 @@ export default class extends Page {
     }, 1);
   }
 
-  pauseVideo(second) {
-    // 停止计时
-    const now = new Date();
-    if (this.lastTime != null) {
-      this.time += (now.getTime() - this.lastTime.getTime()) / 1000;
-    }
-    this.lastTime = null;
-    this.showWater(second);
-  }
-
   next() {
     const { data, item } = this.state;
     if (data.courseNos.length === item.no) {
@@ -376,12 +400,12 @@ export default class extends Page {
   }
 
   onVideoAction(key) {
-    const { rightTab, showTab, showAsk, showNote, item = {} } = this.state;
+    const { rightTab, showTab, showAsk, showNote, item = {}, position } = this.state;
     switch (key) {
       case 'ask':
-        return this.setState({ showAsk: !showAsk });
+        return this.setState({ showAsk: !showAsk, ask: { position: `${position}` } });
       case 'note':
-        return this.setState({ showNote: !showNote, note: this.noteMap ? this.noteMap[item.id] || {} : {} });
+        return this.setState({ showNote: !showNote, note: this.noteMap ? this.noteMap[item.id] || { courseNoId: item.id } : { courseNoId: item.id } });
       case 'answer':
         return this.setState({ showTab: rightTab === '1' ? !showTab : true, rightTab: '1' });
       case 'list':
@@ -398,7 +422,7 @@ export default class extends Page {
     this.time += (now.getTime() - this.lastTime.getTime()) / 1000;
     this.lastTime = now;
     const progress = formatPercent(currentTime, totalTime);
-    if (record || this.time > 600) {
+    if (record || this.time > 300 || progress === 100) {
       // 最长5分钟记录一次
       Course.noProgress(id, courseNoId, progress, this.time, courseNoId);
       this.time = 0;
@@ -453,7 +477,7 @@ export default class extends Page {
                 立即购买
               </Button>}
               {!data.have && <Button theme="default" radius size="lager" disabled={data.add || add} onClick={() => this.add()}>
-                <Assets name="add" />
+                <Assets name={data.add || add ? 'add_disabled' : 'add'} />
               </Button>}
               {data.have && <Button className="m-r-1" radius size="lager" onClick={() => linkTo('/my/course')}>
                 我的课程
@@ -463,7 +487,7 @@ export default class extends Page {
           <div className="t-2 m-b-1">授课老师:{data.teacher}</div>
           <div className={'detail'}>
             <div className="left">
-              {data.have && <div hidden={(paper.paper && paper.paper.finishTimes > 0)} className="left-top">
+              {data.have && paper.id && <div hidden={(paper.paper && paper.paper.finishTimes > 0)} className="left-top">
                 <span className="d-i-b m-r-1">预习作业</span>
                 <span className="d-i-b m-r-2">
                   <ProgressText width={480} size="small" progress={paper.report ? formatPercent(paper.report.userNumber, paper.report.questionNumber) : 0} />

+ 4 - 0
front/project/www/routes/course/main/index.less

@@ -26,6 +26,7 @@
         top: 290px;
         left: 50%;
         transform: translateX(-50%);
+
         .button {
           margin: 0 20px;
           font-weight: 600;
@@ -77,6 +78,7 @@
           cursor: pointer;
           border-bottom: 1px solid transparent;
         }
+
         .name:hover {
           border-bottom: 1px solid #fff;
         }
@@ -118,6 +120,8 @@
     }
 
     .list {
+      padding-bottom: 20px;
+
       .item {
         width: 380px;
         background: rgba(255, 255, 255, 1);

+ 30 - 7
front/project/www/routes/course/note/page.js

@@ -39,6 +39,9 @@ export default class extends Page {
       selectList: [],
       allChecked: false,
       showDetail: false,
+      defaultSortMap: {
+
+      },
     };
   }
 
@@ -54,13 +57,21 @@ export default class extends Page {
   initData() {
     const { id } = this.params;
     const data = Object.assign(this.state, this.state.search);
+    data.filterMap = this.state.search;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
+    } else if (data.direction == null || data.direction === '') {
+      data.sortMap = Object.assign({}, this.state.defaultSortMap);
     }
-    data.filterMap = this.state.search;
     this.setState(data);
 
-    My.listCourseNote(Object.assign({ courseId: id }, this.state.search))
+    My.listCourseNote(Object.assign({ courseId: id }, this.state.search, {
+      order: Object.keys(data.sortMap)
+        .map(key => {
+          return `${key} ${data.sortMap[key]}`;
+        })
+        .join(','),
+    }))
       .then(result => {
         result.list = result.list.map(row => {
           row.key = row.courseNoId;
@@ -82,10 +93,22 @@ export default class extends Page {
 
   onSort(value) {
     const keys = Object.keys(value);
-    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
-    const { sortMap } = this.state;
+    const { sortMap, defaultSortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
-    this.search({ order: keys.length && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    let order = keys.length && value[keys[index]] ? keys[index] : null;
+    let direction = keys.length ? value[keys[index]] : null;
+    if (order == null) {
+      const [prevOrder] = Object.keys(sortMap);
+      if (!defaultSortMap[prevOrder]) {
+        const k = Object.keys(defaultSortMap);
+        if (k.length > 0) {
+          [order] = Object.keys(defaultSortMap);
+          direction = defaultSortMap[order];
+        }
+      }
+    }
+    this.search({ order, direction }, false);
+    this.initData();
   }
 
   onChangePage(page) {
@@ -217,8 +240,8 @@ export default class extends Page {
               { title: '导出', key: 'export', tag: 'vip', disabled: !info.vip },
             ]}
             sortList={[
-              { right: true, label: '课时', key: 'courseNoId', fixed: true },
-              { right: true, label: '更新时间', key: 'updateTime', fixed: true },
+              { right: true, label: '课时', key: 'no', fixed: true },
+              { right: true, label: '更新时间', key: 'update_time', fixed: true },
             ]}
             sortMap={sortMap}
             filterMap={filterMap}

+ 6 - 1
front/project/www/routes/course/online/page.js

@@ -114,6 +114,9 @@ export default class extends Page {
   onFilter(key, value) {
     const { filterMap } = this.state;
     filterMap[key] = value;
+    if (key === 'parentStructId') {
+      delete filterMap.structId;
+    }
     this.search(filterMap, false);
     this.initData();
   }
@@ -194,7 +197,9 @@ export default class extends Page {
       <Filter
         filter={filterMap}
         list={filterList}
-        onFilter={(key, value) => this.onFilter(key, value)}
+        onFilter={(key, value) => {
+          this.onFilter(key, value);
+        }}
       />,
       <div className="tab-1-list">
         {list.map((data) => {

+ 4 - 5
front/project/www/routes/course/vs/page.js

@@ -74,11 +74,10 @@ export default class extends Page {
       });
     }
     this.setState({ key, data: item, faqs: this.faqs, comments: this.commentMap[key], teachers: this.teacherMap[key] });
-    this.changeNumber(item.minNumber || 1);
+    this.changeNumber(item, item.minNumber || 1);
   }
 
-  changeNumber(number) {
-    const { data } = this.state;
+  changeNumber(data, number) {
     let price = data.price * number;
     const days = data.expirePreDays / 10 * number;
     let max = 0;
@@ -213,12 +212,12 @@ export default class extends Page {
                   <Icon
                     className="up"
                     type="caret-up"
-                    onClick={() => number < data.maxNumber && this.changeNumber(number + 1)}
+                    onClick={() => number < data.maxNumber && this.changeNumber(data, number + 1)}
                   />
                   <Icon
                     className="down"
                     type="caret-down"
-                    onClick={() => number !== 1 && number > data.minNumber && this.changeNumber(number - 1)}
+                    onClick={() => number !== 1 && number > data.minNumber && this.changeNumber(data, number - 1)}
                   />
                 </div>
               </div>

+ 5 - 5
front/project/www/routes/examination/list/page.js

@@ -28,7 +28,7 @@ export default class extends Page {
             <div className="table-row" style={{ paddingRight: 40 }}>
               <div className="night f-s-16">
                 {record.title}
-                {record.paper && record.paper.paperNo > 0 ? String.fromCharCode(64 + record.paper.paperNo) : ''}
+                {record.paper && record.paper.paperNo > 1 ? String.fromCharCode(64 + record.paper.paperNo - 1) : ''}
               </div>
               <div>
                 <ProgressText progress={progress} size="small" />
@@ -55,7 +55,7 @@ export default class extends Page {
           return [
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report && record.report.score ? `${record.report.score.totalScore}分${record.report.score.totalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.totalScore || 0}分${record.report.score.totalRank || 0}th` : '-分-th'}
               </div>
               <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.totalScore / record.totalTimes) : '-'}分</div>
             </div>,
@@ -78,7 +78,7 @@ export default class extends Page {
           return [
             <div className="table-row">
               <div className="night f-s-16 f-w-b">
-                {record.report && record.report.score ? `${record.report.score.verbalScore}分${record.report.score.verbalRank}th` : '-分-th'}
+                {record.report && record.report.score ? `${record.report.score.verbalScore || 0}分${record.report.score.verbalRank || 0}th` : '-分-th'}
               </div>
               <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.verbalScore / record.totalTimes) : '-'}分</div>
             </div>,
@@ -100,7 +100,7 @@ export default class extends Page {
         render: record => {
           return [<div className="table-row">
             <div className="night f-s-16 f-w-b">
-              {record.report && record.report.score ? `${record.report.score.quantScore}分${record.report.score.quantRank}th` : '-分-th'}
+              {record.report && record.report.score ? `${record.report.score.quantScore || 0}分${record.report.score.quantRank || 0}th` : '-分-th'}
             </div>
             <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.quantScore / record.totalTimes) : '-'}分</div>
           </div>,
@@ -122,7 +122,7 @@ export default class extends Page {
         render: record => {
           return [<div className="table-row">
             <div className="night f-s-16 f-w-b">
-              {record.report && record.report.score ? `${record.report.score.irScore}分${record.report.score.irRank}th` : '-分-th'}
+              {record.report && record.report.score ? `${record.report.score.irScore || 0}分${record.report.score.irRank || 0}th` : '-分-th'}
             </div>
             <div className="f-s-12">全站: {record.totalTimes > 0 ? Math.round(record.irScore / record.totalTimes) : '-'}分</div>
           </div>,

+ 4 - 0
front/project/www/routes/exercise/main/page.js

@@ -466,6 +466,10 @@ export default class extends Page {
       // 排序:sc,rc,cr
       result = result.map(row => {
         row.sort = CourseSorted[row.course.extend] || 10;
+        row.papers = (row.papers || []).filter(r => {
+          if (r.report && r.report.isFinish) return false;
+          return true;
+        });
         return row;
       });
       result.sort((a, b) => {

+ 0 - 1
front/project/www/routes/my/collect/page.js

@@ -249,7 +249,6 @@ export default class extends Page {
     let direction = keys.length ? value[keys[index]] : null;
     if (order == null) {
       const [prevOrder] = Object.keys(sortMap);
-      console.log(prevOrder, defaultSortMap[prevOrder]);
       if (!defaultSortMap[prevOrder]) {
         [order] = Object.keys(defaultSortMap);
         direction = defaultSortMap[order];

+ 81 - 36
front/project/www/routes/my/course/page.js

@@ -204,6 +204,18 @@ export default class extends Page {
       });
   }
 
+  refreshPaper(recordId) {
+    const { list } = this.state;
+    const [record] = list.filter(row => row.id === recordId);
+    if (!record) return Promise.reject(new Error('记录错误'));
+    return My.listCoursePaper({ courseId: record.productId, page: 1, size: record.courseNos.length })
+      .then((result) => {
+        record.papers = result.list;
+        this.formatRecord(record);
+        this.setState({ list });
+      });
+  }
+
   suspend() {
     const { suspend } = this.state;
     My.suspendCourse(suspend.id)
@@ -275,7 +287,7 @@ export default class extends Page {
 
   uploadNote(file) {
     const { note = {} } = this.state;
-    Common.uploadImage(file).then(result => {
+    Common.upload(file).then(result => {
       note.file = result.url;
       note.name = file.name;
       this.setState({ note });
@@ -284,7 +296,7 @@ export default class extends Page {
 
   uploadSupply(file) {
     const { supply = {} } = this.state;
-    Common.uploadImage(file).then(result => {
+    Common.upload(file).then(result => {
       supply.file = result.url;
       supply.name = file.name;
       this.setState({ supply });
@@ -558,7 +570,7 @@ export default class extends Page {
           show={showFinish}
           onConfirm={() => this.setState({ showFinish: false })}
         />
-        <CourseNoteModal getContainer={() => document.body} show={showCourseNote} defaultData={note} course={data.course} courseNos={data.courseNos} noteMap={data.noteMap} onConfirm={() => {
+        <CourseNoteModal getContainer={() => document.body} show={showCourseNote} defaultData={note} course={data.course} courseNos={data.courseNos || []} noteMap={data.noteMap || {}} onConfirm={() => {
           this.setState({ showCourseNote: false });
           this.refreshNote();
         }} onCancel={() => this.setState({ showCourseNote: false })} />
@@ -576,6 +588,9 @@ export default class extends Page {
           refreshDetail={recordId => {
             this.refreshDetail(recordId);
           }}
+          refreshPaper={() => {
+            this.refreshPaper(item.id);
+          }}
           onRestore={() => {
             this.setState({ showRestore: true, restore: item });
           }}
@@ -655,6 +670,7 @@ class CourseOnline extends Component {
       {
         title: '学习内容',
         key: 'title',
+        width: 250,
         render: (text, record) => {
           return <a href={`/course/detail/${record.courseId}?no=${record.no}`} target="_blank">课时 {record.no}: {text}</a>;
         },
@@ -702,7 +718,10 @@ class CourseOnline extends Component {
                   tip="Restart"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.restart('preview', paper);
+                      Question.restart(paper.paper.id)
+                        .then(() => {
+                          this.refreshPaper(record);
+                        });
                     });
                   }}
                 />
@@ -714,7 +733,7 @@ class CourseOnline extends Component {
                   tip="Report"
                   onClick={() => {
                     User.needLogin().then(() => {
-                      Question.reportLink('preview', paper);
+                      Question.reportLink(paper);
                     });
                   }}
                 />
@@ -779,6 +798,11 @@ class CourseOnline extends Component {
     onNote(record);
   }
 
+  refreshPaper() {
+    const { refreshPaper } = this.props;
+    refreshPaper();
+  }
+
   render() {
     const { data = {} } = this.props;
     switch (data.status) {
@@ -839,32 +863,32 @@ class CourseOnline extends Component {
             </div>
           </div>
           <div className="right">
-            <div className="item" onClick={() => {
-              openLink(`/course/detail/${data.course.id}?no=${data.currentNo}`);
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/detail/${data.course.id}?no=${data.currentNo || 1}`);
             }}>
-              <GIcon name="speed-block" active noHover />
+              <GIcon name="speed-block" active />
               <div className="text">
                 <span>{data.currentNo}</span>/{(data.courseNos || []).length}
               </div>
             </div>
-            <div className="item" onClick={() => {
+            <div className="item c-p" onClick={() => {
               openLink(`/course/answer/${data.course.id}`);
             }}>
-              <GIcon name="question-block" active noHover />
+              <GIcon name="question-block" active />
               <div className="text">
                 <span>{data.answerNumber}</span>/{data.askNumber}
               </div>
             </div>
-            <div className="item" onClick={() => onTime()}>
-              <GIcon name="clockin-block" active noHover />
+            <div className="item c-p" onClick={() => onTime()}>
+              <GIcon name="clockin-block" active />
               <div className="text">
                 <span>{formatSeconds(data.totalTime)}</span> {data.previewProgress || 0}%
               </div>
             </div>
-            <div className="item" onClick={() => {
+            <div className="item c-p" onClick={() => {
               openLink(`/course/note/${data.course.id}`);
             }}>
-              <GIcon name="note-block" active noHover />
+              <GIcon name="note-block" active />
               <div className="text">{data.noteNumber}</div>
             </div>
           </div>
@@ -954,20 +978,24 @@ class CourseOnline extends Component {
             </div>
           </div>
           <div className="right">
-            <div className="item">
-              <GIcon name="question-block" active noHover />
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/answer/${data.course.id}`);
+            }}>
+              <GIcon name="question-block" active />
               <div className="text">
                 <span>{data.answerNumber}</span>/{data.askNumber}
               </div>
             </div>
-            <div className="item" onClick={() => onTime()}>
-              <GIcon name="clockin-block" active noHover />
+            <div className="item c-p" onClick={() => onTime()}>
+              <GIcon name="clockin-block" active />
               <div className="text">
                 <span>{formatSeconds(data.totalTime)}</span> {data.previewProgress || 0}%
               </div>
             </div>
-            <div className="item">
-              <GIcon name="note-block" active noHover />
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/note/${data.course.id}`);
+            }}>
+              <GIcon name="note-block" active />
               <div className="text">{data.noteNumber}</div>
             </div>
             {data.courseAward > 0 && (
@@ -977,7 +1005,7 @@ class CourseOnline extends Component {
               </div>
             )}
           </div>
-          <div className="open">
+          <div className="open c-p">
             <GIcon name={open ? 'up' : 'down'} onClick={() => this.setState({ open: !open })} />
           </div>
         </div>
@@ -1029,30 +1057,36 @@ class CourseOnline extends Component {
             </div>
           </div>
           <div className="right">
-            <div className="item">
-              <GIcon name="speed-block" noHover />
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/detail/${data.course.id}?no=${data.currentNo || 1}`);
+            }}>
+              <GIcon name="speed-block" />
               <div className="text">
                 <span>{data.currentNo}</span>/{(data.courseNos || []).length}
               </div>
             </div>
-            <div className="item">
-              <GIcon name="question-block" active noHover />
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/answer/${data.course.id}`);
+            }}>
+              <GIcon name="question-block" active />
               <div className="text">
                 <span>{data.answerNumber}</span>/{data.askNumber}
               </div>
             </div>
-            <div className="item" onClick={() => onTime()}>
-              <GIcon name="clockin-block" active noHover />
+            <div className="item c-p" onClick={() => onTime()}>
+              <GIcon name="clockin-block" active />
               <div className="text">
                 <span>{formatSeconds(data.totalTime)}</span> {data.previewProgress || 0}%
               </div>
             </div>
-            <div className="item">
-              <GIcon name="note-block" active noHover />
+            <div className="item c-p" onClick={() => {
+              openLink(`/course/note/${data.course.id}`);
+            }}>
+              <GIcon name="note-block" active />
               <div className="text">{data.noteNumber}</div>
             </div>
           </div>
-          <div className="open">
+          <div className="open c-p">
             <GIcon name={open ? 'up' : 'down'} onClick={() => this.setState({ open: !open })} />
           </div>
         </div>
@@ -1272,10 +1306,18 @@ class CourseVs extends Component {
       showTips:
         props.data.commentTips === 0 &&
         (props.data.appointments.length === Math.ceil(props.data.number / 2) ||
-          props.data.appointments.length === props.data.number),
+          props.data.appointments.length === props.data.number) && props.data.number > 1,
     };
   }
 
+  open(recordId) {
+    Order.useRecord(recordId).then(() => {
+      this.props.refreshDetail(recordId);
+    }).catch((err) => {
+      asyncSMessage(err.message, 'error');
+    });
+  }
+
   closeCommentTips() {
     My.courseCommentTips(this.props.data.id).then(() => {
       this.setState({ showTips: false });
@@ -1299,7 +1341,7 @@ class CourseVs extends Component {
   }
 
   renderIng() {
-    const { data, onComment, closeCommentTips } = this.props;
+    const { data, onComment, onSuspend, closeCommentTips } = this.props;
     const { tab, showTips, open } = this.state;
     return (
       <div className="education-item ing">
@@ -1317,6 +1359,8 @@ class CourseVs extends Component {
                 const { key } = value;
                 if (key === 'comment') {
                   onComment();
+                } else if (key === 'suspend') {
+                  onSuspend();
                 }
               }}
             />
@@ -1684,12 +1728,13 @@ class TimeLineItem extends Component {
           case 'not':
             return (
               <FileUpload
-                onUpload={file => {
-                  return Common.upload({ file }).then(result => onUploadQuestion(appointment, result.url, file.name));
-                }}
+                onUpload={({ file }) => {
+                  return Common.upload(file).then(result => onUploadQuestion(appointment, result.url, file.name));
+                }
+                }
               >
                 <span className="link">点此上传</span>
-              </FileUpload>
+              </FileUpload >
             );
           default:
             return (

+ 1 - 0
front/project/www/routes/my/error/page.js

@@ -398,6 +398,7 @@ export default class extends Page {
   remove(report) {
     My.removeError(report.id)
       .then(() => {
+        this.clearErrorReport();
         this.refresh();
       });
   }

+ 1 - 0
front/project/www/routes/my/main/page.js

@@ -205,6 +205,7 @@ export default class extends Page {
   }
 
   initData() {
+    User.refreshToken();
     // 获取学习数据
     My.getStudyTotal().then((total) => {
       total.categorys = total.categorys.map((row, index) => {

+ 16 - 9
front/project/www/routes/my/report/page.js

@@ -224,7 +224,14 @@ export default class extends Page {
     ];
 
     this.examinationColumns = [
-      { key: 'title', title: '模考名称', fixSort: true },
+      {
+        key: 'title',
+        title: '模考名称',
+        fixSort: true,
+        render: (text, record) => {
+          return text + (record.paper && record.paper.paperNo > 1 ? String.fromCharCode(64 + record.paper.paperNo - 1) : '');
+        },
+      },
       {
         key: 'latest_time',
         title: '做题时间',
@@ -277,7 +284,7 @@ export default class extends Page {
           const { reports } = record;
           const report = childIndex === -1 ? reports[0] : record;
           if (!report) return null;
-          if (!report.qxCat) {
+          if (record.isAdapt === 0) {
             return (
               <div className="f-s-12">
                 仅CAT模考
@@ -289,7 +296,7 @@ export default class extends Page {
           if (!report.score) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{report.score.totalScore}</div>
+              <div className="t-2 t-s-12">{report.score.totalScore || 0}</div>
               <div className="t-6 t-s-12">
                 {record.qxCat === 1
                   ? Math.round(record.origin.totalScore / record.origin.totalTimes)
@@ -306,7 +313,7 @@ export default class extends Page {
           const { reports } = record;
           const report = childIndex === -1 ? reports[0] : record;
           if (!report) return null;
-          if (!report.qxCat) {
+          if (record.isAdapt === 0) {
             return (
               <div className="f-s-12">
                 {formatPercent(report.setting.real ? report.setting.real.verbal : report.setting.number.verbal, this.nums.verbal.number, false)}
@@ -316,7 +323,7 @@ export default class extends Page {
           if (!report.score) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{report.score.verbalScore}</div>
+              <div className="t-2 t-s-12">{report.score.verbalScore || 0}</div>
               <div className="t-6 t-s-12">
                 {record.qxCat === 1
                   ? Math.round(record.origin.verbalScore / record.origin.totalTimes)
@@ -333,7 +340,7 @@ export default class extends Page {
           const { reports } = record;
           const report = childIndex === -1 ? reports[0] : record;
           if (!report) return null;
-          if (!report.qxCat) {
+          if (record.isAdapt === 0) {
             return (
               <div className="f-s-12">{formatPercent(report.setting.real ? report.setting.real.quant : report.setting.number.quant, false)}</div>
             );
@@ -341,7 +348,7 @@ export default class extends Page {
           if (!report.score) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{report.score.quantScore}</div>
+              <div className="t-2 t-s-12">{report.score.quantScore || 0}</div>
               <div className="t-6 t-s-12">
                 {record.qxCat === 1
                   ? Math.round(record.origin.quantScore / record.origin.totalTimes)
@@ -358,13 +365,13 @@ export default class extends Page {
           const { reports } = record;
           const report = childIndex === -1 ? reports[0] : record;
           if (!report) return null;
-          if (!report.qxCat) {
+          if (record.isAdapt === 0) {
             return <div className="f-s-12">{formatPercent(report.setting.real ? report.setting.real.ir : report.setting.number.ir, this.nums.ir.number, false)}</div>;
           }
           if (!report.score) return null;
           return (
             <div className="sub">
-              <div className="t-2 t-s-12">{report.score.irScore}</div>
+              <div className="t-2 t-s-12">{report.score.irScore || 0}</div>
               <div className="t-6 t-s-12">
                 {record.qxCat === 1
                   ? Math.round(record.origin.irScore / record.origin.totalTimes)

+ 19 - 1
front/project/www/routes/page/cart/page.js

@@ -37,7 +37,9 @@ export default class extends Page {
     Order.allCheckout()
       .then(result => {
         User.formatOrder(result);
-        this.setState({ order: result, list: result.checkouts });
+        const selectList = result.checkouts.filter(row => row.isSelect).map(row => row.id);
+        const allChecked = selectList.length === result.checkouts.length && selectList.length > 0;
+        this.setState({ order: result, list: result.checkouts, selectList, allChecked });
       });
   }
 
@@ -57,6 +59,18 @@ export default class extends Page {
       });
     }
     this.setState({ selectList, allChecked: checked });
+    Promise.all(list.map(row => {
+      if (row.isSelect && checked) {
+        return Promise.resolve();
+      }
+      if (!row.isSelect && !checked) {
+        return Promise.resolve();
+      }
+      return Order.selectCheckout(row.id, checked);
+    }))
+      .then(() => {
+        this.refresh();
+      });
   }
 
   onSelect(key, checked) {
@@ -67,6 +81,9 @@ export default class extends Page {
       selectList.splice(selectList.indexOf(key), 1);
     }
     this.setState({ selectList, allChecked: false });
+    Order.selectCheckout(key, checked).then(() => {
+      this.refresh();
+    });
   }
 
   onDelete(list) {
@@ -97,6 +114,7 @@ export default class extends Page {
       .then(result => {
         return User.needPay(result)
           .then(() => {
+            this.refresh();
             linkTo(`/order/${result.id}`);
           })
           .catch(() => {

+ 4 - 0
front/project/www/routes/page/ready/index.less

@@ -146,4 +146,8 @@
 
     }
   }
+
+  .ant-affix {
+    width: 250px !important;
+  }
 }

+ 2 - 0
front/project/www/routes/page/ready/page.js

@@ -299,6 +299,7 @@ export default class extends Page {
 
   changePage(key) {
     const item = this.categoryMap[key];
+    const top = true;
     if (item.isRoom) {
       this.refreshRoom();
     } else if (item.isData) {
@@ -311,6 +312,7 @@ export default class extends Page {
       this.refreshList(item);
     }
     this.setState({ current: key, open: true });
+    if (top) this.scrollTo(0);
   }
 
   refreshRead() {

+ 20 - 6
front/project/www/routes/paper/process/base/index.js

@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
 import './index.less';
 import { Checkbox, Icon as AntDIcon, Tooltip } from 'antd';
 import Assets from '@src/components/Assets';
-import { formatSeconds, formatMinuteSecond, getMap } from '@src/services/Tools';
+import { formatSeconds, formatMinuteSecond } from '@src/services/Tools';
 import Icon from '../../../../components/Icon';
 import Button from '../../../../components/Button';
 import Navigation from '../../../../components/Navigation';
@@ -12,10 +12,18 @@ import Calculator from '../../../../components/Calculator';
 import AnswerSelect from '../../../../components/AnswerSelect';
 import AnswerTable from '../../../../components/AnswerTable';
 import Editor from '../../../../components/Editor';
-import { QuestionType, ExaminationOrder } from '../../../../../Constant';
-
-const QuestionTypeMap = getMap(QuestionType, 'value');
+import { ExaminationOrder } from '../../../../../Constant';
 
+// const QuestionTypeMap = getMap(QuestionType, 'value');
+const SubjectTypeMap = {
+  sc: 'Verbal',
+  cr: 'Verbal',
+  rc: 'Verbal',
+  ds: 'Quantitative',
+  ps: 'Quantitative',
+  awa: 'Analytical Writing Assessment',
+  ir: 'Integrated Reasoning',
+};
 export default class extends Component {
   constructor(props) {
     super(props);
@@ -258,7 +266,7 @@ export default class extends Component {
     return (
       <div className="layout">
         <div className="fixed">
-          {QuestionTypeMap[question.questionType].long}
+          {SubjectTypeMap[question.questionType]}
           {question.questionType === 'ir' && (
             <Assets
               className="calculator-icon"
@@ -512,6 +520,7 @@ export default class extends Component {
 
   renderRelax() {
     const { paper, stageTime, flow, showTime } = this.props;
+    const { showT } = this.state;
     return (
       <div className="layout">
         <div className="layout-header">
@@ -541,7 +550,12 @@ export default class extends Component {
         <div className={'layout-body'}>
           <div className="relax">
             <div className="title">
-              Optional Break <Tooltip title={'点击「Next」可继续考试'} trigger="click"><Icon name="question" /></Tooltip>
+              <span>Optional Break</span>
+              <Tooltip visible={showT} title={'点击「Next」可继续考试'}>
+                <Icon name="question" onHover={(value) => {
+                  this.setState({ showT: value });
+                }} />
+              </Tooltip>
             </div>
             <div className="time" dangerouslySetInnerHTML={{ __html: formatMinuteSecond(stageTime).split(':').map(row => row.replace(/([0-9])/g, '<div class="block">$1</div>')).join('<div class="div">:</div>') }} />
           </div>

+ 29 - 19
front/project/www/routes/paper/process/page.js

@@ -30,8 +30,6 @@ export default class extends Page {
 
     this.baseRef = null;
     this.sentenceRef = null;
-
-    this.accTime = 0;
   }
 
   outPage() {
@@ -132,7 +130,7 @@ export default class extends Page {
       // 开始统计做题进度
       if (report.paperModule === 'examination') {
         const { order } = setting;
-        this.initStage(order[0], 0, 0);
+        this.initStage(order[0], 0);
       }
       this.totalQuestionTime();
       return this.next();
@@ -148,8 +146,8 @@ export default class extends Page {
         this.setState({ report, scene: 'questionn' });
         // 更新当前做题进度
         if (report.paperModule === 'examination') {
-          const { stage, time, number } = report.setting;
-          this.initStage(stage, time[stage], number[stage]);
+          const { stage, time } = report.setting;
+          this.initStage(stage, time[stage]);
         }
         this.totalQuestionTime(report.userTime || 0);
         return this.next();
@@ -165,7 +163,7 @@ export default class extends Page {
     return Question.next(report.id)
       .then(userQuestion => {
         const questionSetting = {};
-        if (setting.disorder && userQuestion.questionType !== 'ds') {
+        if (setting.disorder && userQuestion.questionType !== 'ds' && userQuestion.questionType !== 'sentence') {
           const { questions } = userQuestion.question.content;
           if (questions) {
             // 乱序显示选项
@@ -177,9 +175,13 @@ export default class extends Page {
             });
           }
         }
+        if (report.paperModule === 'examination') {
+          this.updateStage(userQuestion.stage);
+        }
         this.setState({
           userQuestion,
           question: userQuestion.question,
+          questionNo: userQuestion.questionNo,
           questionSetting,
           scene: 'question',
         });
@@ -254,11 +256,19 @@ export default class extends Page {
   }
 
   stageToRelax() {
-    if (this.accTime > 55 * 60) {
-      this.relaxStage();
-      this.accTime = 0;
-    } else {
+    const { report } = this.state;
+    // 进入下一阶段
+    const { order } = report.setting;
+    if (this.stage === 'awa') {
       this.relaxToNextStage();
+    } else if (this.stage === 'ir') {
+      if (order.indexOf(this.stage) === 1) {
+        this.relaxStage();
+      } else {
+        this.relaxToNextStage();
+      }
+    } else {
+      this.relaxStage();
     }
   }
 
@@ -366,8 +376,6 @@ export default class extends Page {
     this.stageProcess = this.stages[this.stage];
     this.stageQuestionTime(0);
     this.setState({ totalNumber: this.stageProcess.number });
-
-    this.accTime += this.stageProcess.time;
   }
 
   relaxStage() {
@@ -385,8 +393,12 @@ export default class extends Page {
     this.stageTime = time;
     this.stageQuestionTime(time);
     this.setState({ totalNumber: this.stageProcess.number });
+  }
 
-    this.accTime += this.stageProcess.time;
+  updateStage(stage) {
+    this.stage = stage;
+    this.stageProcess = this.stages[stage];
+    this.setState({ totalNumber: this.stageProcess.number });
   }
 
   toggleFullscreen() {
@@ -421,13 +433,11 @@ export default class extends Page {
   }
 
   renderDetail() {
-    const { scene, paper, userQuestion } = this.state;
+    const { scene, paper, userQuestion, questionNo = {} } = this.state;
     if (!paper.id || !scene) return null;
-    switch (paper.paperModule) {
-      case 'sentence':
-        return <Sentence ref={(ref) => { this.sentenceRef = ref; }} key={userQuestion.id} {...this.state} flow={this} mode='process' />;
-      default:
-        return <Base ref={(ref) => { this.baseRef = ref; }} key={userQuestion.id} {...this.state} flow={this} mode='process' />;
+    if (paper.paperModule === 'sentence' || questionNo.module === 'sentence') {
+      return <Sentence ref={(ref) => { this.sentenceRef = ref; }} key={userQuestion.id} {...this.state} flow={this} mode='process' />;
     }
+    return <Base ref={(ref) => { this.baseRef = ref; }} key={userQuestion.id} {...this.state} flow={this} mode='process' />;
   }
 }

+ 3 - 3
front/project/www/routes/paper/question/detail/index.js

@@ -238,8 +238,8 @@ export default class extends Component {
   }
 
   renderDetail() {
-    const { paper = {} } = this.props;
-    switch (paper.paperModule) {
+    const { question = {} } = this.props;
+    switch (question.questionType) {
       case 'sentence':
         return <Sentence {...this.props} {...this.state} flow={this} scene="answer" mode="question" />;
       default:
@@ -661,7 +661,7 @@ export default class extends Component {
               <div className="content-awa" dangerouslySetInnerHTML={{ __html: (userQuestion.userAnswer || {}).awa || '' }} />
             </div>
           )}
-          {!showAnswer && <div className="show-awa"><div>选择「显示答案」查看自己的作文</div></div>}
+          {!showAnswer && <div className="show-awa"><div>打开「显示答案」查看作文</div></div>}
         </div>
       </div></div>
     );

+ 4 - 4
front/project/www/routes/paper/question/detail/index.less

@@ -259,9 +259,9 @@
 
           .show-awa {
             font-size: 12px;
-            width: 100%;
-            height: 100%;
-            margin: 50% 0;
+            // width: 100%;
+            // height: 100%;
+            // margin: 50% 0;
             text-align: center;
             line-height: 20px;
             color: #A7A7B7;
@@ -270,7 +270,7 @@
             >div {
               left: 0;
               position: absolute;
-              top: 40%;
+              top: 80px;
               text-align: center;
               display: inline-block;
               width: 100%;

+ 1 - 1
front/project/www/routes/paper/question/page.js

@@ -56,7 +56,7 @@ export default class extends Page {
       if (!question.answerDistributed) question.answerDistributed = { questions: [] };
       if (!userQuestion.userAnswer) userQuestion.userAnswer = { questions: [] };
       if (!userQuestion.userAnswer.questions) userQuestion.userAnswer.questions = [];
-      if ((report.setting || {}).disorder && questionType !== 'ds' && questionType !== 'awa') {
+      if ((report.setting || {}).disorder && questionType !== 'ds' && questionType !== 'awa' && questionType !== 'sentence') {
         const { content } = question;
         // 还原做题顺序
         content.questions.forEach((q, i) => {

+ 1 - 0
front/project/www/routes/paper/report/index.less

@@ -218,6 +218,7 @@
   }
 }
 
+.examination.question,
 .exercise.question,
 .sentence.question {
   .header {

+ 31 - 22
front/project/www/routes/paper/report/page.js

@@ -195,7 +195,7 @@ function lineOption1(title, data, legend, color, yMax = 300) {
     yAxis: {
       type: 'value',
       min: 0,
-      max: yMax,
+      max: Number(yMax),
       axisLabel: { color: '#686872' },
       axisLine: { lineStyle: { color: '#D1D6DF' } },
     },
@@ -524,7 +524,7 @@ export default class extends Page {
 
   initData() {
     const { id } = this.params;
-    const { info = '' } = this.state.search;
+    const { info = '', subject } = this.state.search;
     Question.detailReport(id).then(result => {
       switch (result.paperModule) {
         case 'sentence':
@@ -548,7 +548,7 @@ export default class extends Page {
         switch (info) {
           case 'question':
             // 题目回顾列表
-            Question.questionReport(id).then(result => {
+            Question.questionReport({ userReportId: id, stage: subject }).then(result => {
               switch (report.paperModule) {
                 case 'sentence':
                   result = result.map((row) => {
@@ -573,6 +573,18 @@ export default class extends Page {
                   });
                   this.refreshExercise(result);
                   break;
+                case 'examination':
+                  result = result.map((row) => {
+                    row.correct = row.isCorrect ? 0 : 1;
+                    row.difficult = QuestionDifficultSort[row.question.difficult];
+                    row.place = row.question.place;
+
+                    row.note = row.note ? 1 : 0;
+                    row.collect = row.collect ? 1 : 0;
+                    return row;
+                  });
+                  this.refreshExamination(result);
+                  break;
                 default:
               }
               this.setState({ list: result });
@@ -679,15 +691,18 @@ export default class extends Page {
         return this.renderSentence();
       case 'textbook':
         if (info === 'question') {
-          return this.renderExerciseQuestion();
+          return this.renderBaseQuestion('exercise');
         }
         return this.renderTextbook();
       case 'exercise':
         if (info === 'question') {
-          return this.renderExerciseQuestion();
+          return this.renderQuestion('exercise');
         }
         return this.renderExercise();
       case 'examination':
+        if (info === 'question') {
+          return this.renderBaseQuestion('examination');
+        }
         return this.renderExamination();
       default:
         return <div />;
@@ -918,9 +933,9 @@ export default class extends Page {
     </div>;
   }
 
-  renderExerciseQuestion() {
+  renderBaseQuestion(type) {
     const { report, list, order, showNote, note = {}, questionNo = {} } = this.state;
-    return <div className='exercise question'>
+    return <div className={`${type} question`}>
       <div className='header'>
         <div className='content'>
           <div className='title'>题目回顾</div>
@@ -1096,7 +1111,7 @@ export default class extends Page {
             </div>
           </div>
           <BarChart
-            height={90 + 30 * place.length}
+            height={90 + 50 * place.length}
             option={BarOption3(
               ['知识点', '正确率分析', '用时分析'],
               place.map(row => {
@@ -1137,10 +1152,6 @@ export default class extends Page {
     </div>;
   }
 
-  renderTextbookQuestion() {
-    return this.renderExerciseQuestion();
-  }
-
   renderExaminationDetail() {
     const { report = {}, tab } = this.state;
     const { detail = {} } = report;
@@ -1271,15 +1282,15 @@ export default class extends Page {
                 '累计用时情况',
                 (function (sd) {
                   let userTime = 0;
-                  let time = 0;
-                  return sd.pace.map(row => {
+                  // let time = 0;
+                  return sd.pace.map((row, index) => {
                     userTime += row.userTime;
-                    time += row.time;
-                    return [`${row.no}`, userTime, time];
+                    // time += row.time;
+                    return [`${index + 1}`, userTime];
                   });
                 }(subjectDetail)),
-                ['我的', '建议'],
-                ['#A3A8BF', '#7AA7DC'],
+                ['我的'],
+                ['#A3A8BF'],
                 subjectDetail.info.time,
               )}
             />
@@ -1428,7 +1439,7 @@ export default class extends Page {
         <div className="content">
           <div className="title">知识体系分析</div>
           <BarChart
-            height={90 + 30 * Object.keys(subjectDetail.place).length}
+            height={90 + 50 * Object.keys(subjectDetail.place).length}
             option={BarOption3(
               ['知识点', '正确率分析', '用时分析'],
               Object.values(subjectDetail.place).map(row => {
@@ -1473,9 +1484,7 @@ export default class extends Page {
                 <td>{row.ignore || !info.cat ? '--' : score[`${row.value}Score`]}</td>
                 <td>{row.ignore || !info.cat ? '--' : score[`${row.value}Rank`]}</td>
                 <td><Button size="small" radius onClick={() => {
-                  Question.getDetailByNo(report.id, 1, row.value).then((r) => {
-                    openLink(`/paper/question/${r.id}`);
-                  });
+                  linkTo(`/paper/report/${report.id}?info=question&subject=${row.value}`);
                 }}>回顾</Button></td></tr>;
             })}
             <tr>

+ 3 - 3
front/project/www/routes/question/search/page.js

@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import './index.less';
 import { Icon, Typography } from 'antd';
-import { Link } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import { getMap, formatPercent, formatSeconds } from '@src/services/Tools';
@@ -285,9 +285,9 @@ export default class extends Page {
       <div className="filter-layout">
         <div className="content">
           <div style={{ right: 0, top: 0 }} className="p-a" hidden={!login}>
-            <Link to="/question/search/history">
+            <a href="/question/search/history" target="_blank">
               <Assets name="history_time" className="m-r-5" /> 浏览历史 >
-            </Link>
+            </a>
           </div>
           <Tabs
             border

+ 8 - 0
front/project/www/routes/question/searchHistory/index.less

@@ -37,6 +37,14 @@
           line-height: 12px;
           padding: 6px 20px;
         }
+
+        a:hover {
+          color: #4292F0 !important;
+        }
+
+        a:visited {
+          color: #4292F0 !important;
+        }
       }
     }
   }

+ 2 - 1
front/project/www/routes/question/searchHistory/page.js

@@ -1,5 +1,6 @@
 import React, { Component } from 'react';
 import './index.less';
+import { Typography } from 'antd';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import { getMap, formatDate } from '@src/services/Tools';
@@ -90,7 +91,7 @@ class LogItem extends Component {
       <div className="log-item">
         <span className="t-6 m-r-1 tag">{QuestionTypeMap[data.question.questionType]}</span>
         <a className="t-1 m-r-2 f-w-b" href={`/question/detail/${data.questionNo.id}`} target="_blank" onClick={() => onClick()}>{data.questionNo.title}</a>
-        <span className="p-l-1 t-2">{data.question.description}</span>
+        <span className="p-l-1 t-2"><Typography.Paragraph ellipsis={{ rows: 3, expandable: false }}><div className="search-content" dangerouslySetInnerHTML={{ __html: data.question.description }} /></Typography.Paragraph></span>
       </div>
     );
   }

+ 2 - 2
front/project/www/stores/course.js

@@ -20,8 +20,8 @@ export default class CourseStore extends BaseStore {
     return this.apiGet('/course/detail', { courseId });
   }
 
-  listAsk({ keyword, courseId, courseNoId, position, order, direction }) {
-    return this.apiGet('/course/ask/list', { keyword, courseId, courseNoId, position, order, direction });
+  listAsk({ keyword, courseId, courseNoId, position, order, direction, page, size }) {
+    return this.apiGet('/course/ask/list', { keyword, courseId, courseNoId, position, order, direction, page, size });
   }
 
   askView(id) {

+ 14 - 2
front/project/www/stores/my.js

@@ -303,6 +303,18 @@ export default class MyStore extends BaseStore {
   }
 
   /**
+   * 获取笔记列表
+   * @param {*} page
+   * @param {*} size
+   * @param {*} startTime
+   * @param {*} endTime
+   * @param {*} order
+   */
+  listCoursePaper({ courseId, page, size }) {
+    return this.apiGet('/my/paper/course/list', { courseId, page, size });
+  }
+
+  /**
    * 获取报告列表
    * @param {*} origin
    * @param {*} structId
@@ -359,8 +371,8 @@ export default class MyStore extends BaseStore {
     return this.apiDel('/my/ask/course/delete', { id });
   }
 
-  listCourseAsk({ keyword, courseId, courseNoId, answerStatus, order, direction }) {
-    return this.apiGet('/my/ask/course/list', { keyword, courseId, courseNoId, answerStatus, order, direction });
+  listCourseAsk({ keyword, courseId, courseNoId, answerStatus, order, direction, page, size }) {
+    return this.apiGet('/my/ask/course/list', { keyword, courseId, courseNoId, answerStatus, order, direction, page, size });
   }
 
   /**

+ 8 - 0
front/project/www/stores/order.js

@@ -25,6 +25,14 @@ export default class OrderStore extends BaseStore {
       });
   }
 
+  selectCheckout(id, select) {
+    return this.apiPut('/order/checkout/select', { id, select })
+      .then(result => {
+        this.setState({ number: result });
+        return result;
+      });
+  }
+
   removeCheckout(id) {
     return this.apiDel('/order/checkout/delete', { id })
       .then(result => {

+ 2 - 2
front/project/www/stores/question.js

@@ -185,8 +185,8 @@ export default class QuestionStore extends BaseStore {
    * 获取做题题目记录
    * @param {*} userReportId
    */
-  questionReport(userReportId) {
-    return this.apiGet('/question/report/question', { userReportId });
+  questionReport({ userReportId, stage }) {
+    return this.apiGet('/question/report/question', { userReportId, stage });
   }
 
   /**

+ 7 - 0
server/data/src/main/java/com/qxgmat/data/dao/UserCourseDataSubscribeMapper.java

@@ -0,0 +1,7 @@
+package com.qxgmat.data.dao;
+
+import com.nuliji.tools.mybatis.Mapper;
+import com.qxgmat.data.dao.entity.UserCourseDataSubscribe;
+
+public interface UserCourseDataSubscribeMapper extends Mapper<UserCourseDataSubscribe> {
+}

+ 160 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseDataSubscribe.java

@@ -0,0 +1,160 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "user_course_data_subscribe")
+public class UserCourseDataSubscribe implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 用户id
+     */
+    @Column(name = "`user_id`")
+    private Integer userId;
+
+    /**
+     * 资料id
+     */
+    @Column(name = "`data_id`")
+    private Integer dataId;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取用户id
+     *
+     * @return user_id - 用户id
+     */
+    public Integer getUserId() {
+        return userId;
+    }
+
+    /**
+     * 设置用户id
+     *
+     * @param userId 用户id
+     */
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * 获取资料id
+     *
+     * @return data_id - 资料id
+     */
+    public Integer getDataId() {
+        return dataId;
+    }
+
+    /**
+     * 设置资料id
+     *
+     * @param dataId 资料id
+     */
+    public void setDataId(Integer dataId) {
+        this.dataId = dataId;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", userId=").append(userId);
+        sb.append(", dataId=").append(dataId);
+        sb.append(", createTime=").append(createTime);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static UserCourseDataSubscribe.Builder builder() {
+        return new UserCourseDataSubscribe.Builder();
+    }
+
+    public static class Builder {
+        private UserCourseDataSubscribe obj;
+
+        public Builder() {
+            this.obj = new UserCourseDataSubscribe();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置用户id
+         *
+         * @param userId 用户id
+         */
+        public Builder userId(Integer userId) {
+            obj.setUserId(userId);
+            return this;
+        }
+
+        /**
+         * 设置资料id
+         *
+         * @param dataId 资料id
+         */
+        public Builder dataId(Integer dataId) {
+            obj.setDataId(dataId);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        public UserCourseDataSubscribe build() {
+            return this.obj;
+        }
+    }
+}

+ 35 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrderCheckout.java

@@ -61,6 +61,12 @@ public class UserOrderCheckout implements Serializable {
     private Integer number;
 
     /**
+     * 选中
+     */
+    @Column(name = "`is_select`")
+    private Integer isSelect;
+
+    /**
      * 购买金额
      */
     @Column(name = "`money`")
@@ -242,6 +248,24 @@ public class UserOrderCheckout implements Serializable {
     }
 
     /**
+     * 获取选中
+     *
+     * @return is_select - 选中
+     */
+    public Integer getIsSelect() {
+        return isSelect;
+    }
+
+    /**
+     * 设置选中
+     *
+     * @param isSelect 选中
+     */
+    public void setIsSelect(Integer isSelect) {
+        this.isSelect = isSelect;
+    }
+
+    /**
      * 获取购买金额
      *
      * @return money - 购买金额
@@ -334,6 +358,7 @@ public class UserOrderCheckout implements Serializable {
         sb.append(", service=").append(service);
         sb.append(", param=").append(param);
         sb.append(", number=").append(number);
+        sb.append(", isSelect=").append(isSelect);
         sb.append(", money=").append(money);
         sb.append(", originMoney=").append(originMoney);
         sb.append(", createTime=").append(createTime);
@@ -443,6 +468,16 @@ public class UserOrderCheckout implements Serializable {
         }
 
         /**
+         * 设置选中
+         *
+         * @param isSelect 选中
+         */
+        public Builder isSelect(Integer isSelect) {
+            obj.setIsSelect(isSelect);
+            return this;
+        }
+
+        /**
          * 设置购买金额
          *
          * @param money 购买金额

+ 19 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseDataSubscribeMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.qxgmat.data.dao.UserCourseDataSubscribeMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.UserCourseDataSubscribe">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="user_id" jdbcType="INTEGER" property="userId" />
+    <result column="data_id" jdbcType="INTEGER" property="dataId" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `id`, `user_id`, `data_id`, `create_time`
+  </sql>
+</mapper>

+ 3 - 1
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderCheckoutMapper.xml

@@ -14,6 +14,7 @@
     <result column="service" jdbcType="VARCHAR" property="service" />
     <result column="param" jdbcType="VARCHAR" property="param" />
     <result column="number" jdbcType="INTEGER" property="number" />
+    <result column="is_select" jdbcType="INTEGER" property="isSelect" />
     <result column="money" jdbcType="DECIMAL" property="money" />
     <result column="origin_money" jdbcType="DECIMAL" property="originMoney" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -25,6 +26,7 @@
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `order_id`, `parent_id`, `product_type`, `product_id`, `service`, 
-    `param`, `number`, `money`, `origin_money`, `create_time`, `expire_days`, `use_expire_days`
+    `param`, `number`, `is_select`, `money`, `origin_money`, `create_time`, `expire_days`, 
+    `use_expire_days`
   </sql>
 </mapper>

+ 2 - 1
server/data/src/main/java/com/qxgmat/data/relation/ExaminationPaperRelationMapper.java

@@ -1,5 +1,6 @@
 package com.qxgmat.data.relation;
 
+import com.qxgmat.data.dao.entity.ExaminationPaper;
 import com.qxgmat.data.dao.entity.ExercisePaper;
 import org.apache.ibatis.annotations.Param;
 
@@ -19,7 +20,7 @@ public interface ExaminationPaperRelationMapper {
             @Param("second") Boolean second
     );
 
-    List<ExercisePaper> listWithUser(
+    List<ExaminationPaper> listWithUser(
             @Param("structId") Number structId,
             @Param("userId") Number userId,
             @Param("qxCat") Integer qxCat,

+ 19 - 0
server/data/src/main/java/com/qxgmat/data/relation/UserNoteCourseRelationMapper.java

@@ -0,0 +1,19 @@
+package com.qxgmat.data.relation;
+
+import com.qxgmat.data.dao.entity.UserNoteQuestion;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface UserNoteCourseRelationMapper {
+    List<UserNoteQuestion> listByCourse(
+            @Param("userId") Number userId,
+            @Param("keyword") String keyword,
+            @Param("courseId") Integer courseId,
+            String order
+    );
+}

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml

@@ -11,7 +11,7 @@
     <!--
       WARNING - @mbg.generated
     -->
-    DISTINCT(uaq.`id`)
+    uaq.`id`
   </sql>
 
   <!--修改问答排序-->

+ 39 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteCourseRelationMapper.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.qxgmat.data.relation.UserNoteCourseRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.UserNoteCourse">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    unc.`id` as `id`
+  </sql>
+
+  <select id="listByCourse" resultMap="IdMap">
+    <if test="keyword != null">
+      <bind name="keywordLike" value="'%' + keyword + '%'" />
+    </if>
+    select
+    <include refid="Id_Column_List" />,
+    unc.`update_time` as `update_time`,
+    cn.`no` as `no`
+    from `user_note_course` unc
+    left join `course_no` cn on cn.`id` = unc.`course_no_id`
+    where
+    cn.`id` > 0 and unc.`user_id` = #{userId,jdbcType=VARCHAR}
+    <if test="keyword != null">
+      and (unc.`content` like #{keywordLike,jdbcType=VARCHAR})
+    </if>
+    <if test="courseId != null">
+      and unc.`course_id`= #{courseId,jdbcType=INTEGER}
+    </if>
+    <if test="order != null">
+      order by ${order}
+    </if>
+  </select>
+</mapper>

+ 2 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml

@@ -41,6 +41,7 @@
     <foreach collection="ids" item="item" index="index" open="(" close=")" separator=",">
       #{item}
     </foreach>
+    group by uor.`time_id`
   </select>
 
   <!--
@@ -160,7 +161,7 @@
       and ((uor.`is_suspend` = 1 and uor.`restore_time` is not null) or (uor.`is_suspend` = 0))
     </if>
     <if test="gtUseEndTime != null">
-      and ((uor.`is_suspend` = 1 and uor.`restore_time` is null) or (uor.`is_suspend` = 0 and uor.`use_end_time` &gt; #{gtUseEndTime,jdbcType=INTEGER}) or uor.`use_end_time` is null)
+      and (uor.`use_end_time` &gt; #{gtUseEndTime,jdbcType=INTEGER} or uor.`use_end_time` is null)
     </if>
     order by ${order} ${direction}
   </select>

+ 6 - 6
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml

@@ -41,8 +41,8 @@
     if(up.`question_number`>0,ur.`user_time` / ur.`user_number`,0) as `time`,
     if(up.`question_number`>0,ur.`user_number` / ur.`question_number`,0) as `progress`
     from `user_paper` up
-    left join `user_report` ur on ur.`id`= up.`latest_report_id`
-    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
+    left join `user_report` ur on ur.`id`= up.`latest_report_id` and ur.user_number > 0 and ur.is_finish > 0
+    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` > 0
     <if test="paperOrigin != null">
     and up.`paper_origin` = #{paperOrigin,jdbcType=VARCHAR}
     </if>
@@ -71,7 +71,7 @@
     if(ur.`question_number`>0,ur.`user_time` / ur.`user_number`,0) as `time`,
     if(ur.`question_number`>0,ur.`user_number` / ur.`question_number`,0) as `progress`
     from `user_paper` up
-    left join `user_report` ur on ur.`id`= up.`latest_report_id` and ur.user_number > 0
+    left join `user_report` ur on ur.`id`= up.`latest_report_id` and ur.user_number > 0 and ur.is_finish > 0
     left join `exercise_paper` ep on up.`paper_origin` = 'exercise' and ep.`id` = up.`origin_id`
     <if test="questionTypes != null">
       and
@@ -111,7 +111,7 @@
         pp.`question_type` = #{item}
       </foreach>
     </if>
-    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
+    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` > 0
     <if test="keyword != null">
       and (ep.`title` like #{keywordLike,jdbcType=VARCHAR}
       or pa.`title` like #{keywordLike,jdbcType=VARCHAR}
@@ -151,7 +151,7 @@
     ur.`user_time` / ur.`user_number` as `time`,
     ur.`user_number` / ur.`question_number` as `progress`
     from `user_paper` up
-    left join `user_report` ur on ur.`id`= up.`latest_report_id` and ur.user_number > 0
+    left join `user_report` ur on ur.`id`= up.`latest_report_id` and ur.user_number > 0 and ur.is_finish > 0
     left join `examination_paper` ep on up.`paper_origin` = 'examination' and ep.`id` = up.`origin_id`
     <if test="structIds != null and structIds.length>0">
       and (ep.`struct_two` in
@@ -171,7 +171,7 @@
       <if test="year != null">
         and tp.`year` = #{year,jdbcType=VARCHAR}
       </if>
-    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` is not null
+    where up.`user_id` = #{userId,jdbcType=VARCHAR} and ur.`id` > 0
     <if test="keyword != null">
       and (ep.`title` like #{keywordLike,jdbcType=VARCHAR}
       or tp.`title` like #{keywordLike,jdbcType=VARCHAR})

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml

@@ -28,7 +28,7 @@
     <trim prefix="set" suffixOverrides=",">
       `total_money`=`total_money`+#{money, jdbcType=DECIMAL},
     </trim>
-    WHERE `id` = #{id, jdbcType=VARCHAR}
+    WHERE `id` = #{id, jdbcType=INTEGER}
   </update>
 
   <!--

+ 1 - 0
server/data/src/main/resources/db/migration/V12__update_user_order_checkout.sql

@@ -0,0 +1 @@
+ALTER TABLE user_order_checkout add column is_select tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '选中' AFTER number;

+ 3 - 5
server/gateway-api/src/main/java/com/qxgmat/controller/admin/CourseController.java

@@ -628,9 +628,9 @@ public class CourseController {
         Transform.combine(pr, userList, CourseStudentOnlineListDto.class, "userId", "user", User.class, "id", UserExtendDto.class);
 
         // 绑定时间段
-        Collection timeIds = Transform.getIds(p, UserOrderRecord.class, "no");
+        Collection timeIds = Transform.getIds(p, UserOrderRecord.class, "timeId");
         List<CourseTime> timeList = courseTimeService.select(timeIds);
-        Transform.combine(pr, timeList, CourseStudentOnlineListDto.class, "no", "time", CourseTime.class, "id", CourseTimeExtendDto.class);
+        Transform.combine(pr, timeList, CourseStudentOnlineListDto.class, "timeId", "time", CourseTime.class, "id", CourseTimeExtendDto.class);
 
         return ResponseHelp.success(pr, page, size, p.getTotal());
     }
@@ -753,11 +753,9 @@ public class CourseController {
         return ResponseHelp.success(pr, page, size, p.getTotal());
     }
 
-
-
     @RequestMapping(value = "/ask/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "修改提问信息", httpMethod = "PUT")
-    public Response<Boolean> edit(@RequestBody @Validated UserAskCourseDto dto, HttpServletRequest request) {
+    public Response<Boolean> editAsk(@RequestBody @Validated UserAskCourseDto dto, HttpServletRequest request) {
         UserAskCourse entity = Transform.dtoToEntity(dto);
         UserAskCourse in = userAskCourseService.get(entity.getId());
         // 调整回答

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java

@@ -921,7 +921,7 @@ public class UserController {
         List<UserOrder> orderList = userOrderService.select(orderIds);
         Map orderMap = Transform.getMap(orderList, UserOrder.class, "id");
 
-        String columnNames[]={"申请时间","用户昵称","用户ID","用户手机号","开票金额","开票内容", "发票抬头", "纳税人识别号", "收件邮箱"};//列名
+        String columnNames[]={"申请时间","用户昵称","用户ID","用户手机号","开票金额","开票内容", "发票抬头", "纳税人识别号", "收件邮箱"};//列名
         String filename = "发票";
         //生成一个Excel文件
         // 创建excel工作簿

+ 3 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java

@@ -40,9 +40,11 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import javax.validation.Validator;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Created by GaoJie on 2017/10/31.
@@ -335,7 +337,7 @@ public class AuthController {
         List<UserOrderRecord> recordList = userOrderRecordService.listWithCourse(1, 1000, user.getId(), null, true, false, null, null);
         Collection recordIds = Transform.getIds(recordList, UserOrderRecord.class, "id");
         List<UserPreviewPaperRelation> relationList = previewService.listByRecordId(user.getId(), recordIds, 2);
-        dto.setPreviewNumber(relationList.size());
+        dto.setPreviewNumber((int)relationList.stream().filter((p)-> p.getReport() == null || (p.getReport().getIsFinish()==null || p.getReport().getIsFinish()==0)).count());
         return dto;
     }
 }

+ 36 - 5
server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java

@@ -133,6 +133,9 @@ public class MyController {
     private ExaminationPaperService examinationPaperService;
 
     @Autowired
+    private ExaminationStructService examinationStructService;
+
+    @Autowired
     private FaqService faqService;
 
     @Autowired
@@ -605,7 +608,7 @@ public class MyController {
         for(UserCourseRecord record:userCourseRecordList){
             courseTime += record.getUserTime();
             courseNumber += 1;
-            UserCourseResultExtendDto d = Transform.convert(record, UserCourseRecordExtendDto.class);
+            UserCourseResultExtendDto d = Transform.convert(record, UserCourseResultExtendDto.class);
             d.setExtend(((Course)courseMap.get(record.getCourseId())).getExtend());
             d.setTitle(((CourseNo)courseNoMap.get(record.getCourseNoId())).getTitle());
             d.setNo(((CourseNo)courseNoMap.get(record.getCourseNoId())).getNo());
@@ -1277,15 +1280,32 @@ public class MyController {
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) String keyword,
             @RequestParam(required = false) Integer courseId,
-            @RequestParam(required = false, defaultValue = "") String order, // update_time, no
-            @RequestParam(required = false, defaultValue = "desc") String direction,
+            @RequestParam(required = false, defaultValue = "id") String order, // update_time, no
             HttpSession session)  {
         User user = (User) shiroHelp.getLoginUser();
-        Page<UserNoteCourse> p = userNoteCourseService.listByCourse(page, size, keyword, user.getId(), courseId, order, DirectionStatus.ValueOf(direction));
+        Page<UserNoteCourse> p = userNoteCourseService.listByCourse(page, size, keyword, user.getId(), courseId, order);
 
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
 
+    @RequestMapping(value = "/paper/course/list", method = RequestMethod.GET)
+    @ApiOperation(value = "获取课程预习作业", notes = "获取作业列表", httpMethod = "GET")
+    public Response<PageMessage<BasePaperExtendDto>> listPaperCourse(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = false) Integer courseId,
+            HttpSession session)  {
+        User user = (User) shiroHelp.getLoginUser();
+        Page<BasePaperExtendDto> dto = new Page<>();
+        UserCourse userCourse = userCourseService.getCourse(user.getId(), courseId);
+        if (userCourse != null) {
+            Collection<UserPreviewPaperRelation> previewList = previewService.getByRecordId(user.getId(), userCourse.getRecordId(), 1000);
+
+            dto.addAll(Transform.convert(previewList, BasePaperExtendDto.class));
+        }
+        return ResponseHelp.success(dto, page, size, dto.getTotal());
+    }
+
     @RequestMapping(value = "/report/list", method = RequestMethod.GET)
     @ApiOperation(value = "获取报告列表", notes = "获取报告列表", httpMethod = "GET")
     public Response<PageMessage<UserPaperDto>> listReport(
@@ -1323,6 +1343,15 @@ public class MyController {
                     libraryId = 0;
                 }
             }
+            if (structIds != null && structIds.length == 1){
+                List<ExaminationStruct> two = examinationStructService.children(structIds[0], 0);
+                structIds = new Integer[two.size()];
+                int index = 0;
+                for(ExaminationStruct struct : two){
+                    structIds[index] = struct.getId();
+                    index++;
+                }
+            }
             p = userPaperService.listExamination(page, size, user.getId(), keyword, structIds, libraryId, year, Tools.baseDate(startTime), Tools.baseDate(endTime), order != null ? order.replace("|", " ") : null);
         }else{
             throw new ParameterException("参数逻辑错误");
@@ -1768,6 +1797,8 @@ public class MyController {
         Map<Object, Collection<UserCourseProgress>> progressMap = userCourseProgressService.groupByRecordId(recordIds);
         Transform.combine(pr, progressMap, UserCourseDetailDto.class, "id", "progress", UserCourseProgressExtendDto.class);
         Map<Object, Collection<UserCourseRecord>> recordMap = userCourseRecordService.groupByRecordId(recordIds);
+        Transform.combine(pr, recordMap, UserCourseDetailDto.class, "id", "records", UserCourseRecordExtendDto.class);
+
         for(UserCourseDetailDto dto : pr){
             dto.setTotalDays(courseExtendService.computeCourseDay(map.get(dto.getId())));
             Collection<CourseNo> courseNos = courseNoMap.get(dto.getProductId());
@@ -1833,7 +1864,7 @@ public class MyController {
 
         // vs预约comment
         Map<Object, Collection<UserCourseAppointmentComment>> commentMap = userCourseAppointmentCommentService.groupByRecordId(recordIds);
-        Transform.combine(pr, commentMap, UserCourseDetailDto.class, "id", "appointments", UserCourseAppointmentComment.class);
+        Transform.combine(pr, commentMap, UserCourseDetailDto.class, "id", "comments", UserCourseAppointmentComment.class);
 
 
         return ResponseHelp.success(pr, page, size, p.getTotal());

+ 8 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java

@@ -118,6 +118,14 @@ public class OrderController {
         return ResponseHelp.success(number);
     }
 
+    @RequestMapping(value = "/checkout/select", method = RequestMethod.PUT)
+    @ApiOperation(value = "选中购物车", notes = "选中购物车", httpMethod = "DELETE")
+    public Response<Integer> selectCheckout(@RequestBody @Validated RecordSelectDto dto, HttpServletRequest request) throws Exception {
+        User user = (User) shiroHelp.getLoginUser();
+        int number = orderFlowService.selectCheckout(user.getId(), dto.getId(), dto.getSelect());
+        return ResponseHelp.success(number);
+    }
+
     @RequestMapping(value = "/checkout/delete", method = RequestMethod.DELETE)
     @ApiOperation(value = "删除购物车", notes = "删除购物车", httpMethod = "DELETE")
     public Response<Integer> deleteCheckout(@RequestParam int id, HttpServletRequest request) throws Exception {

+ 10 - 9
server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java

@@ -139,8 +139,8 @@ public class QuestionController {
         // 过滤千行cat的数据
 //        List<ExaminationStruct> structs = examinationStructService.main();
 //        ExaminationStruct qxCatStruct = structs.stream().filter((row)-> row.getExtend().equals(ServiceKey.QX_CAT.key)).findFirst().get();
-
-        Page<QuestionNoRelation> p = questionNoService.searchStemFulltext(page, size, keyword, questionTypes, module, structIds, place, difficult, null, order);
+        ExaminationStruct struct = examinationService.getQxCat();
+        Page<QuestionNoRelation> p = questionNoService.searchStemFulltext(page, size, keyword, questionTypes, module, structIds, place, difficult, struct.getId(), order);
         List<QuestionNoDetailDto> pr = Transform.convert(p, QuestionNoDetailDto.class);
 
         if(user!= null){
@@ -172,8 +172,8 @@ public class QuestionController {
         // 过滤千行cat的数据
 //        List<ExaminationStruct> structs = examinationStructService.main();
 //        ExaminationStruct qxCatStruct = structs.stream().filter((row)-> row.getExtend().equals(ServiceKey.QX_CAT.key)).findFirst().get();
-
-        Page<QuestionNo> p = questionNoService.searchNoFulltext(page, size, keyword, module, null);
+        ExaminationStruct struct = examinationService.getQxCat();
+        Page<QuestionNo> p = questionNoService.searchNoFulltext(page, size, keyword, module, struct.getId());
         List<QuestionNoDto> pr = Transform.convert(p, QuestionNoDto.class);
 
         return ResponseHelp.success(pr, page, size, p.getTotal());
@@ -276,7 +276,7 @@ public class QuestionController {
             dto.setQuestionNumber(list.size());
             Map<Object, UserQuestionStat> userQuestionStatMap = null;
             if(user != null){
-                List<UserQuestion> userQuestionList = userQuestionService.listByPaper(user.getId(), PaperOrigin.EXERCISE, paperIds);
+                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
                 userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);
 
                 dto.setUserStat(userQuestionService.statQuestion(userQuestionList));
@@ -459,7 +459,7 @@ public class QuestionController {
             dto.setStartTime(userService!=null ? userService.getStartTime() : null);
             dto.setExpireTime(userService != null ? userService.getExpireTime() : null);
 
-            dto.setCanReset(examinationService.isFinishCat(user.getId()));
+            dto.setCanReset(examinationService.isFinishQxCat(user.getId()));
         }
 
         return ResponseHelp.success(dto);
@@ -567,7 +567,7 @@ public class QuestionController {
                 // 获取上一遍模考成绩
                 UserService userService = userServiceService.getServiceBase(user.getId(), serviceKey);
                 if (userService.getIsReset() > 0){
-                    List<UserPaper> prevPaperList = userPaperService.listWithCat(user.getId(), ids, user.getQxCat() - 1);
+                    List<UserPaper> prevPaperList = userPaperService.listWithCat(user.getId(), ids, user.getQxCat()-1);
                     Transform.combine(pr, prevPaperList, UserExaminationPaperDto.class, "id", "prevPaper", UserPaper.class, "originId", UserPaperBaseExtendDto.class);
                     // 绑定userPaperId,用于关联report
                     Map prevUserPaperMap = Transform.getMap(prevPaperList, UserPaper.class, "originId", "id");
@@ -728,13 +728,14 @@ public class QuestionController {
     @RequestMapping(value = "/report/question", method = RequestMethod.GET)
     @ApiOperation(value = "获取做题记录", notes = "获取做题记录", httpMethod = "GET")
     public Response<List<UserQuestionBaseDto>> detailReportQuestion(
-            @RequestParam(required = true) Integer userReportId
+            @RequestParam(required = true) Integer userReportId,
+            @RequestParam(required = false) String stage
     )  {
         User user = (User) shiroHelp.getLoginUser();
         if (user == null) {
             throw new AuthException("请先登录");
         }
-        List<UserQuestion> p = questionFlowService.listByReport(user.getId(), userReportId);
+        List<UserQuestion> p = questionFlowService.listByReport(user.getId(), userReportId, stage);
         List<UserQuestionBaseDto> pr = Transform.convert(p, UserQuestionBaseDto.class);
 
         Collection questionIds = Transform.getIds(p, UserQuestion.class, "questionId");

+ 0 - 1
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/UserAskCourseDto.java

@@ -12,7 +12,6 @@ public class UserAskCourseDto {
     @NotNull(message = "提问id不允许为空!")
     private Integer id;
 
-    @NotEmpty(message = "回答不允许为空")
     private String answer;
 
     private String content;

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/UserAskCourseListDto.java

@@ -24,6 +24,8 @@ public class UserAskCourseListDto {
 
     private UserExtendDto user;
 
+    private Integer managerId;
+
     private ManagerExtendDto manager;
 
     private Integer courseId;
@@ -181,4 +183,12 @@ public class UserAskCourseListDto {
     public void setCreateTime(Date createTime) {
         this.createTime = createTime;
     }
+
+    public Integer getManagerId() {
+        return managerId;
+    }
+
+    public void setManagerId(Integer managerId) {
+        this.managerId = managerId;
+    }
 }

+ 20 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserCourseAppointmentExtendDto.java

@@ -34,6 +34,10 @@ public class UserCourseAppointmentExtendDto {
 
     private JSONArray noteList;
 
+    private String questionFile;
+
+    private String questionFileName;
+
     public Integer getId() {
         return id;
     }
@@ -137,4 +141,20 @@ public class UserCourseAppointmentExtendDto {
     public void setNoteList(JSONArray noteList) {
         this.noteList = noteList;
     }
+
+    public String getQuestionFile() {
+        return questionFile;
+    }
+
+    public void setQuestionFile(String questionFile) {
+        this.questionFile = questionFile;
+    }
+
+    public String getQuestionFileName() {
+        return questionFileName;
+    }
+
+    public void setQuestionFileName(String questionFileName) {
+        this.questionFileName = questionFileName;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserOrderRecordExtendDto.java

@@ -26,6 +26,8 @@ public class UserOrderRecordExtendDto {
 
     private Integer number;
 
+    private Integer isSelect;
+
     private BigDecimal originMoney;
 
     private BigDecimal money;
@@ -247,4 +249,12 @@ public class UserOrderRecordExtendDto {
     public void setSource(String source) {
         this.source = source;
     }
+
+    public Integer getIsSelect() {
+        return isSelect;
+    }
+
+    public void setIsSelect(Integer isSelect) {
+        this.isSelect = isSelect;
+    }
 }

+ 27 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordSelectDto.java

@@ -0,0 +1,27 @@
+package com.qxgmat.dto.request;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+
+@Dto(entity = UserOrderCheckout.class)
+public class RecordSelectDto {
+    private Integer id;
+
+    private Boolean select;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Boolean getSelect() {
+        return select;
+    }
+
+    public void setSelect(Boolean select) {
+        this.select = select;
+    }
+}

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserCourseDetailDto.java

@@ -73,6 +73,8 @@ public class UserCourseDetailDto {
 
     private Collection<UserCourseProgressExtendDto> progress;
 
+    private Collection<UserCourseRecordExtendDto> records;
+
     private Collection<BasePaperExtendDto> papers;
 
     private Collection<UserCourseAppointmentComment> comments;
@@ -340,4 +342,12 @@ public class UserCourseDetailDto {
     public void setCctalkName(String cctalkName) {
         this.cctalkName = cctalkName;
     }
+
+    public Collection<UserCourseRecordExtendDto> getRecords() {
+        return records;
+    }
+
+    public void setRecords(Collection<UserCourseRecordExtendDto> records) {
+        this.records = records;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserPaperDto.java

@@ -27,6 +27,8 @@ public class UserPaperDto {
 
     private Integer qxCat;
 
+    private Integer isAdapt;
+
     private Integer[] questionNoIds;
 
     private List<String> questionTypes;
@@ -162,4 +164,12 @@ public class UserPaperDto {
     public void setOriginId(Integer originId) {
         this.originId = originId;
     }
+
+    public Integer getIsAdapt() {
+        return isAdapt;
+    }
+
+    public void setIsAdapt(Integer isAdapt) {
+        this.isAdapt = isAdapt;
+    }
 }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserQuestionBaseDto.java

@@ -15,6 +15,8 @@ public class UserQuestionBaseDto {
 
     private Integer no;
 
+    private String stage;
+
     private Integer stageNo;
 
     private String questionModule;
@@ -170,4 +172,12 @@ public class UserQuestionBaseDto {
     public void setStageNo(Integer stageNo) {
         this.stageNo = stageNo;
     }
+
+    public String getStage() {
+        return stage;
+    }
+
+    public void setStage(String stage) {
+        this.stage = stage;
+    }
 }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/service/UserCollectExperienceService.java

@@ -57,7 +57,7 @@ public class UserCollectExperienceService extends AbstractService {
         example.and(
                 example.createCriteria()
                         .andEqualTo("userId", userId)
-                        .andEqualTo("experienceId", ids)
+                        .andIn("experienceId", ids)
         );
         return select(userCollectExperienceMapper, example);
     }

+ 16 - 22
server/gateway-api/src/main/java/com/qxgmat/service/UserNoteCourseService.java

@@ -2,12 +2,14 @@ package com.qxgmat.service;
 
 import com.github.pagehelper.Page;
 import com.nuliji.tools.AbstractService;
+import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.dao.UserNoteCourseMapper;
 import com.qxgmat.data.dao.entity.UserNoteCourse;
+import com.qxgmat.data.relation.UserNoteCourseRelationMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -23,28 +25,20 @@ public class UserNoteCourseService extends AbstractService {
     @Resource
     private UserNoteCourseMapper userNoteCourseMapper;
 
-    public Page<UserNoteCourse> listByCourse(int page, int size, String keyword, Integer userId, Integer courseId, String order, DirectionStatus direction){
-        Example example = new Example(UserNoteCourse.class);
-        example.and(
-                example.createCriteria()
-                        .andEqualTo("userId", userId)
-                        .andEqualTo("courseId", courseId)
-        );
-        if (keyword != null)
-            example.and(
-                    example.createCriteria()
-                            .orLike("content", "%"+keyword+"%")
-            );
-        if(order == null || order.isEmpty()) order = "id";
-        switch(direction){
-            case ASC:
-                example.orderBy(order).asc();
-                break;
-            case DESC:
-            default:
-                example.orderBy(order).desc();
-        }
-        return select(userNoteCourseMapper, example, page, size);
+    @Resource
+    private UserNoteCourseRelationMapper userNoteCourseRelationMapper;
+
+    public Page<UserNoteCourse> listByCourse(int page, int size, String keyword, Integer userId, Integer courseId, String order){
+        Page<UserNoteCourse> p = page(()->{
+            userNoteCourseRelationMapper.listByCourse(userId, keyword, courseId, order);
+        }, page, size);
+
+        Collection ids = Transform.getIds(p, UserNoteCourse.class, "id");
+
+        // 获取详细数据
+        List<UserNoteCourse> list = select(ids);
+        Transform.replace(p, list, UserNoteCourse.class, "id");
+        return p;
     }
 
     public List<UserNoteCourse> listByCourse(Number courseId){

+ 9 - 6
server/gateway-api/src/main/java/com/qxgmat/service/UserPaperService.java

@@ -183,10 +183,10 @@ public class UserPaperService extends AbstractService {
      * 获取用户cat做题记录
      * @param userId
      * @param ids
-     * @param no
+     * @param qxCat
      * @return
      */
-    public List<UserPaper> listWithCat(Integer userId, Collection ids, Integer no){
+    public List<UserPaper> listWithCat(Integer userId, Collection ids, Integer qxCat){
         if (ids == null || ids.size() == 0) return new ArrayList<>();
         Example example = new Example(UserPaper.class);
         example.and(
@@ -194,7 +194,7 @@ public class UserPaperService extends AbstractService {
                         .andEqualTo("userId", userId)
                         .andEqualTo("paperOrigin", PaperOrigin.EXAMINATION.key)
                         .andIn("originId", ids)
-                        .andEqualTo("paperNo", no)
+                        .andEqualTo("qxCat", qxCat)
         );
         return select(userPaperMapper, example);
     }
@@ -206,7 +206,7 @@ public class UserPaperService extends AbstractService {
      * @param originId
      * @return
      */
-    public UserPaper getByPaper(Integer userId, PaperOrigin origin, Integer originId){
+    public UserPaper getByPaper(Integer userId, PaperOrigin origin, Integer originId, boolean isQxCat, Integer qxCat){
         // 查找对应的paper是否有,没有则添加
         Example example = new Example(UserPaper.class);
         UserPaper paper;
@@ -229,8 +229,11 @@ public class UserPaperService extends AbstractService {
                             .andEqualTo("paperOrigin", origin.key)
                             .andEqualTo("originId", originId)
             );
-            // 如果是千行cat,则获取最后一次
-            example.orderBy("paperNo").desc();
+            // 如果是千行cat,则获取对应的qxCat序号
+            if (isQxCat){
+                example.createCriteria()
+                        .andEqualTo("qxCat", qxCat);
+            }
             paper = one(userPaperMapper, example);
         }
 

+ 16 - 0
server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java

@@ -300,12 +300,28 @@ public class UserQuestionService extends AbstractService {
      * @return
      */
     public List<UserQuestion> listByReport(Integer userId, Integer userReportId){
+        return listByReport(userId, userReportId, null);
+    }
+
+    /**
+     * 获取报告的所有做题记录
+     * @param userId
+     * @param userReportId
+     * @return
+     */
+    public List<UserQuestion> listByReport(Integer userId, Integer userReportId, String stage){
         Example example = new Example(UserQuestion.class);
         example.and(
                 example.createCriteria()
                         .andEqualTo("userId", userId)
                         .andEqualTo("reportId", userReportId)
         );
+        if (stage != null){
+            example.and(
+                    example.createCriteria()
+                        .andEqualTo("stage", stage)
+            );
+        }
         example.orderBy("id").asc();
         return select(userQuestionMapper, example);
     }

+ 12 - 4
server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java

@@ -209,9 +209,15 @@ public class CourseExtendService {
             // 如果进度过半,下2次课时
             UserCourseProgress progress = (UserCourseProgress)progressMap.get(no.getId());
             if(progress == null) continue;
-            if (min != 0 && min > no.getNo()) continue;
-            min = no.getNo();
+            if (progress.getProgress() > 50){
+                if (min > no.getNo()+1) continue;
+                min = no.getNo()+1;
+            }else{
+                if (min > no.getNo()) continue;
+                min = no.getNo();
+            }
         }
+        if (min > courseNoList.size()) min = courseNoList.size();
         return min;
     }
 
@@ -247,11 +253,13 @@ public class CourseExtendService {
                 suspend = (int)(Tools.day(record.getRestoreTime()).getTime() - Tools.day(record.getSuspendTime()).getTime())/86400000;
             }
         }
+        int day = 0;
         if (record.getUseEndTime().before(new Date())){
-            return (int)(Tools.day(record.getUseEndTime()).getTime() - Tools.day(record.getUseStartTime()).getTime())/86400000 - suspend;
+            day = (int)(Tools.day(record.getUseEndTime()).getTime() - Tools.day(record.getUseStartTime()).getTime())/86400000 - suspend;
         }else{
-            return (int)(Tools.day(new Date()).getTime() - Tools.day(record.getUseStartTime()).getTime())/86400000 - suspend;
+            day = (int)(Tools.day(new Date()).getTime() - Tools.day(record.getUseStartTime()).getTime())/86400000 - suspend;
         }
+        return day+1;
 //        Date min = null;
 //        Date max = null;
 //        for(UserCourseRecord userCourseRecord:userCourseRecords){

+ 8 - 4
server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java

@@ -21,6 +21,7 @@ import com.qxgmat.data.relation.UserReportRelationMapper;
 import com.qxgmat.data.relation.entity.QuestionDifficultRelation;
 import com.qxgmat.data.relation.entity.QuestionNoRelation;
 import com.qxgmat.service.UserPaperService;
+import com.qxgmat.service.UserServiceService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.inline.*;
 import org.springframework.stereotype.Service;
@@ -61,6 +62,9 @@ public class ExaminationService extends AbstractService {
     private UserReportService userReportService;
 
     @Resource
+    private UserServiceService userServiceService;
+
+    @Resource
     private UsersService usersService;
 
     @Resource
@@ -238,9 +242,9 @@ public class ExaminationService extends AbstractService {
      * @param userId
      * @return
      */
-    public boolean isFinishCat(Integer userId){
+    public boolean isFinishQxCat(Integer userId){
         User user = usersService.get(userId);
-        ExaminationStruct struct = getCat();
+        ExaminationStruct struct = getQxCat();
         List<ExaminationPaper> paperList = examinationPaperService.listByTwo(struct.getId());
         Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
         List<UserPaper> userPaperList = userPaperService.listWithCat(userId, ids, user.getQxCat());
@@ -265,7 +269,7 @@ public class ExaminationService extends AbstractService {
     @Transactional
     public Boolean resetCat(Integer userId, boolean force){
         User user = usersService.get(userId);
-        ExaminationStruct struct = getCat();
+        ExaminationStruct struct = getQxCat();
         List<ExaminationPaper> paperList = examinationPaperService.listByTwo(struct.getId());
         Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
         List<UserPaper> userPaperList = userPaperService.listWithCat(userId, ids, user.getQxCat());
@@ -287,7 +291,7 @@ public class ExaminationService extends AbstractService {
         return userPaperService.reset(paperIds, userId);
     }
 
-    public ExaminationStruct getCat(){
+    public ExaminationStruct getQxCat(){
         List<ExaminationStruct> list = examinationStructService.main();
         for (ExaminationStruct struct : list){
             if (struct.getLevel() == 1) continue;

+ 1 - 0
server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java

@@ -495,6 +495,7 @@ public class MessageExtendService {
         for(UserOrderRecord gift: gifts){
             Map<String, String> giftMap = new HashMap<>();
             ServiceKey key = ServiceKey.ValueOf(gift.getService());
+            if (key == null) continue;
             giftMap.put("title", key.title);
             giftMap.put("expireDays", String.valueOf(gift.getExpireDays()));
             giftMap.put("useExpireDays", String.valueOf(gift.getUseExpireDays()));

+ 103 - 48
server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java

@@ -97,6 +97,8 @@ public class OrderFlowService {
 
     private Map<ProductType, InitRecord> initRecordCallback = new HashMap<>();
 
+    private Map<ProductType, InitRecord> initRecordFinishCallback = new HashMap<>();
+
     private Map<ProductType, UseRecord> useRecordCallback = new HashMap<>();
 
     private Map<ProductType, StopRecord> stopRecordCallback = new HashMap<>();
@@ -505,7 +507,60 @@ public class OrderFlowService {
             CoursePackage coursePackage = coursePackageService.get(record.getProductId());
 
             // 记录销售数量
-            coursePackageService.accumulation(coursePackage.getId(), 1);
+            coursePackageService.accumulation(record.getProductId(), 1);
+
+            return record;
+        }));
+        initRecordCallback.put(ProductType.DATA, ((order, record)->{
+            // 资料无需开启,也没有有效期
+            record.setIsUsed(1);
+            record.setIsSubscribe(1);
+            Date time = new Date();
+            record.setUseTime(time);
+
+            // 电子资料自动订阅
+            UserDataSubscribe subscribe = userDataSubscribeService.getByData(record.getUserId(), record.getProductId());
+
+            if (subscribe == null){
+                userDataSubscribeService.add(UserDataSubscribe.builder()
+                        .userId(record.getUserId())
+                        .dataId(record.getProductId())
+                        .build());
+            }
+
+            // 记录销售数量
+            courseDataService.accumulation(record.getProductId(), 1, 0);
+            return record;
+        }));
+        initRecordCallback.put(ProductType.SERVICE, ((order, record)->{
+            if(record.getService().equals(ServiceKey.VIP.key)){
+                // VIP直接开通
+                record = useRecordCallback.get(ProductType.SERVICE).callback(record);
+                record.setProductType(ProductType.SERVICE.key);
+            }else{
+                ServiceKey serviceKey = ServiceKey.ValueOf(record.getService());
+                Date startTime = new Date();
+                Date endTime = Tools.addDate(startTime, record.getExpireDays());
+                record.setStartTime(startTime);
+                record.setEndTime(endTime);
+                // 如果使用半价优惠券,则扣除
+                if (serviceKey == ServiceKey.TEXTBOOK){
+                    User user = usersService.get(record.getUserId());
+                    if (user.getTextbookHalf() == 0) {
+                        // todo 支付存在问题
+                    }else if (record.getSource().equals(RecordSource.GIFT_COURSE.key)){
+                        // 礼物不考虑
+                    }else{
+                        usersService.edit(User.builder().id(user.getId()).textbookHalf(user.getTextbookHalf() - 1).build());
+                    }
+                }
+            }
+            return record;
+        }));
+
+        initRecordFinishCallback.put(ProductType.COURSE_PACKAGE, ((order, record)->{
+            // 处理套餐赠送
+            CoursePackage coursePackage = coursePackageService.get(record.getProductId());
 
             InitRecord callback = initRecordCallback.get(ProductType.SERVICE);
             JSONObject gift = coursePackage.getGift();
@@ -515,6 +570,7 @@ public class OrderFlowService {
                 UserOrderRecord giftRecord = UserOrderRecord.builder()
                         .orderId(order.getId())
                         .userId(order.getUserId())
+                        .parentId(record.getId())
                         .productType(ProductType.SERVICE.key)
                         .service(ServiceKey.QX_CAT.key)
                         .source(RecordSource.GIFT_COURSE.key)
@@ -530,6 +586,7 @@ public class OrderFlowService {
                 UserOrderRecord giftRecord = UserOrderRecord.builder()
                         .orderId(order.getId())
                         .userId(order.getUserId())
+                        .parentId(record.getId())
                         .productType(ProductType.SERVICE.key)
                         .service(ServiceKey.TEXTBOOK.key)
                         .source(RecordSource.GIFT_COURSE.key)
@@ -546,6 +603,7 @@ public class OrderFlowService {
                 UserOrderRecord giftRecord = UserOrderRecord.builder()
                         .orderId(order.getId())
                         .userId(order.getUserId())
+                        .parentId(record.getId())
                         .productType(ProductType.SERVICE.key)
                         .service(ServiceKey.VIP.key)
                         .param(vipKey.key)
@@ -557,52 +615,6 @@ public class OrderFlowService {
             }
             return record;
         }));
-        initRecordCallback.put(ProductType.DATA, ((order, record)->{
-            // 资料无需开启,也没有有效期
-            record.setIsUsed(1);
-            record.setIsSubscribe(1);
-            Date time = new Date();
-            record.setUseTime(time);
-
-            // 电子资料自动订阅
-            UserDataSubscribe subscribe = userDataSubscribeService.getByData(record.getUserId(), record.getProductId());
-
-            if (subscribe == null){
-                userDataSubscribeService.add(UserDataSubscribe.builder()
-                        .userId(record.getUserId())
-                        .dataId(record.getProductId())
-                        .build());
-            }
-
-            // 记录销售数量
-            courseDataService.accumulation(record.getProductId(), 1, 0);
-            return record;
-        }));
-        initRecordCallback.put(ProductType.SERVICE, ((order, record)->{
-            if(record.getService().equals(ServiceKey.VIP.key)){
-                // VIP直接开通
-                record = useRecordCallback.get(ProductType.SERVICE).callback(record);
-                record.setProductType(ProductType.SERVICE.key);
-            }else{
-                ServiceKey serviceKey = ServiceKey.ValueOf(record.getService());
-                Date startTime = new Date();
-                Date endTime = Tools.addDate(startTime, record.getExpireDays());
-                record.setStartTime(startTime);
-                record.setEndTime(endTime);
-                // 如果使用半价优惠券,则扣除
-                if (serviceKey == ServiceKey.TEXTBOOK){
-                    User user = usersService.get(record.getUserId());
-                    if (user.getTextbookHalf() == 0) {
-                        // todo 支付存在问题
-                    }else if (record.getSource().equals(RecordSource.GIFT_COURSE.key)){
-                        // 礼物不考虑
-                    }else{
-                        usersService.edit(User.builder().id(user.getId()).textbookHalf(user.getTextbookHalf() - 1).build());
-                    }
-                }
-            }
-            return record;
-        }));
 
         useRecordCallback.put(ProductType.COURSE, (record->{
             User user = usersService.get(record.getUserId());
@@ -687,6 +699,9 @@ public class OrderFlowService {
                         .expireTime(endTime)
                         .build();
                 userService = userServiceService.add(userService);
+                if (serviceKey == ServiceKey.QX_CAT){
+                    examinationService.resetCat(record.getUserId(), true);
+                }
             }else{
                 if (serviceKey == ServiceKey.QX_CAT){
                     // cat逻辑独立
@@ -694,7 +709,7 @@ public class OrderFlowService {
                         // 有效期内
                         if (userService.getIsReset() == 0) throw new ParameterException("已开通当前服务");
                         // 第二次还未做完
-                        if (userService.getIsReset() == 1 && examinationService.isFinishCat(record.getUserId()))  throw new ParameterException("已开通当前服务");
+                        if (userService.getIsReset() == 1 && examinationService.isFinishQxCat(record.getUserId()))  throw new ParameterException("已开通当前服务");
                     }
                     // 重置
                     examinationService.resetCat(record.getUserId(), true);
@@ -804,6 +819,11 @@ public class OrderFlowService {
             record = userOrderRecordService.add(record);
             // 建立新旧id关系
             pMap.put(checkout.getId(), record.getId());
+
+            InitRecord finishCallback = initRecordFinishCallback.get(ProductType.ValueOf(record.getProductType()));
+            if (finishCallback != null){
+                callback.callback(userOrder, record);
+            }
         }
 
         for(UserOrderCheckout checkout : cList){
@@ -814,6 +834,11 @@ public class OrderFlowService {
             InitRecord callback = initRecordCallback.get(ProductType.ValueOf(record.getProductType()));
             callback.callback(userOrder, record);
             userOrderRecordService.add(record);
+
+            InitRecord finishCallback = initRecordFinishCallback.get(ProductType.ValueOf(record.getProductType()));
+            if (finishCallback != null){
+                callback.callback(userOrder, record);
+            }
         }
 
         JSONArray gift = userOrder.getGift();
@@ -823,6 +848,7 @@ public class OrderFlowService {
                 if(info.get("key").equals("vs")){
                     // 添加赠送课时
                     Integer courseId = info.getInteger("courseId");
+                    Course course = courseService.get(courseId);
                     if(courseId != null){
                         UserOrderRecord record = UserOrderRecord.builder()
                                 .orderId(userOrder.getId())
@@ -833,6 +859,8 @@ public class OrderFlowService {
                                 .productId(courseId)
                                 .number(info.getIntValue("number"))
                                 .source(RecordSource.GIFT_COURSE.key)
+                                .expireDays(course.getExpireDays())
+                                .useExpireDays(course.getExpirePreDays() / 10)
                                 .build();
 
                         InitRecord callback = initRecordCallback.get(ProductType.ValueOf(record.getProductType()));
@@ -867,6 +895,8 @@ public class OrderFlowService {
      */
     @Transactional
     public int addCheckout(Integer userId, UserOrderCheckout checkout){
+        // 默认选中
+        checkout.setIsSelect(1);
         List<UserOrderCheckout> list = userOrderCheckoutService.allByUser(userId, 0);
         InitCheckout callback = initCheckoutCallback.get(ProductType.ValueOf(checkout.getProductType()));
         checkout = callback.callback(checkout, list);
@@ -906,7 +936,30 @@ public class OrderFlowService {
         return userOrderCheckoutService.allByUserBase(userId, 0).size();
     }
 
+    /**
+     * 选中购物车
+     * @param userId
+     * @param checkoutId
+     * @return
+     */
+    @Transactional
+    public int selectCheckout(Integer userId, Integer checkoutId, Boolean select){
+        UserOrderCheckout in = userOrderCheckoutService.get(checkoutId);
+        if (in == null){
+            throw new ParameterException("记录不存在");
+        }
+        if (!in.getUserId().equals(userId)){
+            throw new ParameterException("记录不存在");
+        }
+        if (in.getParentId() != null && in.getParentId() > 0){
+            // 如果能删除子项目,要处理删除父项目,转移其他子项目
+            throw new ParameterException("套餐内,不允许单独选中");
+        }
+        userOrderCheckoutService.edit(UserOrderCheckout.builder().id(checkoutId).isSelect(select ? 1 : 0).build());
 
+        // 计算购物车数量
+        return userOrderCheckoutService.allByUserBase(userId, 0).size();
+    }
 
     /**
      * 删除购物车
@@ -950,6 +1003,8 @@ public class OrderFlowService {
      * @return
      */
     public UserOrder preOrderWithCheckout(Integer userId, List<UserOrderCheckout> list){
+        // 计算选中
+        list = list.stream().filter((checkout)->checkout.getIsSelect() > 0).collect(Collectors.toList());
         UserOrder order = UserOrder.builder()
                 .userId(userId)
                 .originMoney(BigDecimal.valueOf(0))

+ 32 - 15
server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java

@@ -174,6 +174,7 @@ public class QuestionFlowService {
 
             userPaper.setQuestionNoIds(paper.getQuestionNoIds());
             userPaper.setQuestionNumber(paper.getQuestionNoIds().length);
+            userPaper.setPaperModule(paper.getPaperModule());
             // 根据papermodule得到考试时长
             QuestionModule module = QuestionModule.WithPaper(PaperModule.ValueOf(paper.getPaperModule()));
             InitPaper callback = makePaperCallback.get(module);
@@ -222,9 +223,9 @@ public class QuestionFlowService {
             ServiceKey serviceKey = ServiceKey.ValueOf(examinationStruct.getExtend());
             if (serviceKey == ServiceKey.QX_CAT) {
                 User user = usersService.get(userPaper.getUserId());
-                userPaper.setPaperNo(user.getQxCat());
                 UserService userService = userServiceService.getService(user.getId(), serviceKey);
-                userPaper.setQxCat(userService.getIsReset() + 1);
+                userPaper.setPaperNo(userService.getIsReset() + 1);
+                userPaper.setQxCat(user.getQxCat());
             }
             return userPaper;
         });
@@ -283,9 +284,9 @@ public class QuestionFlowService {
                 UserPaper newPaper = userPaperService.newByPaper(userPaper.getUserId(), PaperOrigin.ValueOf(userPaper.getPaperOrigin()), PaperModule.ValueOf(userPaper.getPaperModule()), userPaper.getOriginId());
                 newPaper.setIsAdapt(userPaper.getIsAdapt());
                 newPaper.setTitle(userPaper.getTitle());
-                newPaper.setPaperNo(user.getQxCat());
                 UserService userService = userServiceService.getService(user.getId(), serviceKey);
-                newPaper.setQxCat(userService.getIsReset() + 1);
+                newPaper.setPaperNo(userService.getIsReset() + 1);
+                newPaper.setQxCat(user.getQxCat());
             }
             return userPaper;
         });
@@ -337,14 +338,14 @@ public class QuestionFlowService {
         submitCallback.put(QuestionModule.BASE, (userQuestion, userReport)->{
             // 判断答题情况
             JSONObject userAnswer = userQuestion.getUserAnswer();
-            Question question = questionService.get(userQuestion.getQuestionId());
             // 作文统计字数
-            if (question.getQuestionType().equals(QuestionType.AWA.key)){
+            if (userQuestion.getQuestionType().equals(QuestionType.AWA.key)){
                 JSONObject detail = new JSONObject();
                 if (userAnswer != null){
                     String awa = userAnswer.getString("awa");
                     String base = awa.replaceAll("/<[^>]+>/g", "")
-                            .replaceAll("/[,.+-:;']+/g", "");
+                            .replaceAll("/[,.+-:;'\"]+/g", " ")
+                            .replaceAll("/&nbsp/g", " ");
                     detail.put("words", base.split(" ").length);
                 }else{
                     detail.put("words", 0);
@@ -352,6 +353,7 @@ public class QuestionFlowService {
                 userQuestion.setDetail(detail);
                 return false;
             }
+            Question question = questionService.get(userQuestion.getQuestionId());
             JSONObject answer = question.getAnswer();
             return (boolean) this.baseAnswer(userAnswer, answer, question);
         });
@@ -416,6 +418,15 @@ public class QuestionFlowService {
         });
 
         finishCallback.put(PaperModule.EXERCISE, (report, questionList)->{
+            if (questionList.size() > 0){
+                // 长难句练习卷
+                UserQuestion userQuestion = questionList.get(0);
+                if (userQuestion.getQuestionType().equals(QuestionType.SENTENCE.key)){
+                    StatReport callback = finishCallback.get(PaperModule.SENTENCE);
+                    callback.callback(report, questionList);
+                    return;
+                }
+            }
             report.setDetail(this.statExerciseReport(report, questionList));
         });
         finishCallback.put(PaperModule.SENTENCE, (report, questionList)->{
@@ -521,7 +532,13 @@ public class QuestionFlowService {
     @Transactional
     public UserPaper paper(Integer userId, PaperOrigin origin, Integer paperId){
         PaperModule module = PaperModule.WithOrigin(origin);
-        UserPaper paper = userPaperService.getByPaper(userId, origin, paperId);
+        User user = usersService.get(userId);
+        boolean isQxCat = false;
+        if (origin == PaperOrigin.EXAMINATION){
+            ExaminationPaper p = examinationPaperService.get(paperId);
+            isQxCat = examinationService.isQxCat(p);
+        }
+        UserPaper paper = userPaperService.getByPaper(userId, origin, paperId, isQxCat, user.getQxCat());
         if (paper == null){
             paper = userPaperService.newByPaper(userId, origin, module, paperId);
             // 先判断来源初始化,如果没有,则根据模块初始化
@@ -764,7 +781,7 @@ public class QuestionFlowService {
             throw new ParameterException("试卷不存在");
         }
 
-        if (userReport.getIsFinish() > 0){
+        if (userReport.getIsFinish() != null && userReport.getIsFinish() > 0){
             return true;
         }
         userReport.setFinishTime(new Date());
@@ -800,7 +817,7 @@ public class QuestionFlowService {
         UserReport userReport = userReportService.getLastByPaper(userId, userPaperId);
 
         // Finish定义提交完成时间,包含restart,理解为endTime
-        if (userReport.getIsFinish() == 0){
+        if (userReport.getIsFinish() ==null || userReport.getIsFinish() == 0){
             userReport.setFinishTime(new Date());
             userReport.setIsFinish(1);
 
@@ -1009,7 +1026,7 @@ public class QuestionFlowService {
             steps.add(info);
         }else{
             info = steps.getJSONObject(step);
-            questionIndex = subnumber - examinationService.quantNumber[step];
+            questionIndex = subnumber - examinationService.verbalNumber[step];
         }
         //获取下一题
         questionNoIds.clear();
@@ -1144,8 +1161,8 @@ public class QuestionFlowService {
      * @param userId
      * @return
      */
-    public List<UserQuestion> listByReport(Integer userId,Integer userReportId){
-        List<UserQuestion> userQuestionList = userQuestionService.listByReport(userId, userReportId);
+    public List<UserQuestion> listByReport(Integer userId,Integer userReportId, String stage){
+        List<UserQuestion> userQuestionList = userQuestionService.listByReport(userId, userReportId, stage);
         return userQuestionList;
     }
 
@@ -1914,8 +1931,8 @@ public class QuestionFlowService {
         }
 
         // 学科得分计算
-        Integer quantScore = 0;
-        Integer verbalScore = 0;
+        Long quantScore = 0l;
+        Long verbalScore = 0l;
         Integer irScore = 0;
         JSONObject quantSubject = subjectMap.getJSONObject(QuestionSubject.QUANT.key);
         if (quantSubject != null){

+ 5 - 5
server/gateway-api/src/main/java/com/qxgmat/service/extend/ToolsService.java

@@ -253,8 +253,8 @@ public class ToolsService {
      * @param correctNumber
      * @return
      */
-    public Integer quantScore(Integer number, Integer userNumber, float difficultScore, Integer correctNumber){
-        int score = (int)Math.round(17 + 19.45 * correctNumber / number + 0.72 * difficultScore / userNumber);
+    public long quantScore(Integer number, Integer userNumber, float difficultScore, Integer correctNumber){
+        long score = Math.round(17 + 19.45 * correctNumber / number + 0.72 * difficultScore / userNumber);
         if (score > scoreMax) return scoreMax;
         if (score < scoreMin) return scoreMin;
         return score;
@@ -279,8 +279,8 @@ public class ToolsService {
      * @param correctNumber
      * @return
      */
-    public Integer verbalScore(Integer number, Integer userNumber, float difficultScore, Integer correctNumber){
-        int score = (int)Math.round(23.59 + 29.6 * correctNumber / number + 1.72 * difficultScore / userNumber);
+    public long verbalScore(Integer number, Integer userNumber, float difficultScore, Integer correctNumber){
+        long score = Math.round(23.59 + 29.6 * correctNumber / number + 1.72 * difficultScore / userNumber);
         if (score > scoreMax) return scoreMax;
         if (score < scoreMin) return scoreMin;
         return score;
@@ -360,7 +360,7 @@ public class ToolsService {
      * @param verbalScore
      * @return
      */
-    public Rank totalScore(Integer quantScore, Integer verbalScore){
+    public Rank totalScore(Number quantScore, Number verbalScore){
         Rank rank = rankService.search(quantScore, verbalScore);
         return rank != null ? rank : new Rank();
     }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/service/inline/CoursePackageService.java

@@ -96,7 +96,7 @@ public class CoursePackageService extends AbstractService {
         if (ids==null || ids.size() == 0) return null;
         List<CoursePackage> all = select(coursePackageMapper);
         for(CoursePackage p : all){
-            if(ids.containsAll(Arrays.stream(p.getCourseIds()).collect(Collectors.toList()))){
+            if(p.getCourseIds() != null && p.getCourseIds().length > 0 && ids.containsAll(Arrays.stream(p.getCourseIds()).collect(Collectors.toList()))){
                 return p;
             }
         }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/service/inline/CourseService.java

@@ -56,7 +56,7 @@ public class CourseService extends AbstractService {
                             .andNotEqualTo("courseModule", CourseModule.VS.key)
             );
         }
-        if (excludeVs != null) {
+        if (excludeOnline != null) {
             example.and(
                     example.createCriteria()
                             .andNotEqualTo("courseModule", CourseModule.ONLINE.key)

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/service/inline/ExercisePaperService.java

@@ -64,7 +64,7 @@ public class ExercisePaperService extends AbstractService {
         if (four != null){
             example.and(
                     example.createCriteria()
-                    .andEqualTo("four", four)
+                    .andEqualTo("structFour", four)
             );
         }
         if (extend != null){

+ 1 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserAskCourseService.java

@@ -240,6 +240,7 @@ public class UserAskCourseService extends AbstractService {
      * @return
      */
     public List<UserAskCourseStatRelation> statGroupCourse(Collection courseIds, Integer showStatus){
+        if (courseIds == null || courseIds.size() == 0) return new ArrayList<>();
         return userAskCourseRelationMapper.statGroupCourse(courseIds, showStatus);
     }
 

+ 1 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseAppointmentCommentService.java

@@ -48,6 +48,7 @@ public class UserCourseAppointmentCommentService extends AbstractService {
                 example.createCriteria()
                         .andIn("recordId", recordIds)
         );
+        example.orderBy("id").asc();
         List<UserCourseAppointmentComment> nos =  select(userCourseAppointmentCommentMapper, example);
         Collection<UserCourseAppointmentComment> list;
         for(UserCourseAppointmentComment no : nos){

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseProgressService.java

@@ -47,8 +47,8 @@ public class UserCourseProgressService extends AbstractService {
             if (in.getProgress() <= progress) {
                 in.setProgress(progress);
                 entity = edit(in);
-            }else if(in.getProgress() == 100){
-                in.setProgress(progress);
+            }else if(in.getProgress() >= 100){
+                in.setProgress(100);
                 in.setTimes(in.getTimes() + 1);
                 entity = edit(in);
                 return entity;

+ 4 - 4
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserPaperQuestionService.java

@@ -45,10 +45,10 @@ public class UserPaperQuestionService extends AbstractService {
     @Transactional
     public void addRemoveError(Integer userId, List<QuestionNoRelation> questionNoList){
         for(QuestionNoRelation questionNo : questionNoList){
-            if (questionNo.getQuestion().getQuestionType().equals(QuestionType.AWA.key)){
-                // 作文不算错题
-                continue;
-            }
+//            if (questionNo.getQuestion().getQuestionType().equals(QuestionType.AWA.key)){
+//                // 作文不算错题
+//                continue;
+//            }
             QuestionModule questionModule = QuestionModule.WithQuestionNo(QuestionNoModule.ValueOf(questionNo.getModule()));
             UserPaperQuestion paperQuestion = UserPaperQuestion.builder()
                     .userId(userId)

+ 1 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserReportService.java

@@ -85,6 +85,7 @@ public class UserReportService extends AbstractService {
                 example.createCriteria()
                         .andIn("paperId", paperIds)
                         .andGreaterThan("userNumber", 0)
+                        .andGreaterThan("isFinish", 0)
         );
         example.orderBy("id").desc();
         List<UserReport> userClassList = select(userReportMapper, example);

+ 1 - 0
server/gateway-api/src/main/resources/application.yml

@@ -1,5 +1,6 @@
 server:
   connection-timeout: 30000000 #设置链接超时时间为30秒
+  maxHttpHeaderSize: 10240000
 
 spring:
   profiles: