Browse Source

Merge branch 'master' of https://git.proginn.com/zaixianjiaoyu/sourcecode

KaysonCui 5 years ago
parent
commit
6ae72823ba
35 changed files with 1245 additions and 191 deletions
  1. 32 5
      front/project/Constant.js
  2. 12 4
      front/project/admin/routes/setting/rank/page.js
  3. 86 9
      front/project/admin/routes/setting/time/page.js
  4. 7 0
      front/project/admin/routes/user/student/page.js
  5. 16 0
      front/project/admin/stores/system.js
  6. 18 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/QuestionDifficult.java
  7. 5 5
      server/data/src/main/java/com/qxgmat/data/constants/enums/QuestionGroup.java
  8. 2 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/SettingKey.java
  9. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/UserCourseRecordMapper.java
  10. 82 12
      server/data/src/main/java/com/qxgmat/data/dao/entity/Rank.java
  11. 133 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseRecord.java
  12. 7 7
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserQuestion.java
  13. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserReport.java
  14. 7 4
      server/data/src/main/java/com/qxgmat/data/dao/mapping/RankMapper.xml
  15. 18 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseRecordMapper.xml
  16. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserReportMapper.xml
  17. 2 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml
  18. 1 0
      server/data/src/main/resources/mybatis-generator.xml
  19. 3 2
      server/gateway-api/src/main/java/com/qxgmat/Application.java
  20. 17 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  21. 8 0
      server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java
  22. 2 0
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  23. 13 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  24. 20 0
      server/gateway-api/src/main/java/com/qxgmat/dto/request/ExaminationStartDto.java
  25. 3 0
      server/gateway-api/src/main/java/com/qxgmat/service/ExaminationPaperService.java
  26. 0 6
      server/gateway-api/src/main/java/com/qxgmat/service/ExercisePaperService.java
  27. 2 2
      server/gateway-api/src/main/java/com/qxgmat/service/PreviewService.java
  28. 0 6
      server/gateway-api/src/main/java/com/qxgmat/service/SentencePaperService.java
  29. 0 6
      server/gateway-api/src/main/java/com/qxgmat/service/TextbookPaperService.java
  30. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/SubmitQuestion.java
  31. 443 113
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java
  32. 217 4
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ToolsService.java
  33. 17 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/RankService.java
  34. 20 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserServiceService.java
  35. 6 0
      server/gateway-api/src/main/resources/application.yml

+ 32 - 5
front/project/Constant.js

@@ -2,7 +2,7 @@ export const UserUrl = 'http://www.baidu.com/';
 
 export const QuestionDifficult = [{ label: 'easy', value: 'easy' }, { label: 'medium', value: 'medium' }, { label: 'hard', value: 'hard' }];
 
-export const QuestionType = [{ 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: 'AWA/作文', value: 'awa' }];
+export const QuestionType = [{ label: 'SC/语法', value: 'sc', long: 'Sentence Correnction' }, { label: 'RC/阅读', value: 'rc', long: '' }, { label: 'CR/逻辑', value: 'cr' }, { label: 'PS/数学', value: 'ps' }, { label: 'DS/数学', value: 'ds' }, { label: 'IR/综合推理', value: 'ir' }, { label: 'AWA/作文', value: 'awa' }];
 
 export const TextbookType = [{ label: 'PS/数学', value: 'ps' }, { label: 'DS/数学', value: 'ds' }];
 
@@ -36,6 +36,10 @@ export const QuestionRadioDirection = [{ label: '横向', value: 'landscape' },
 
 export const SentenceOption = [{ label: '平行', value: 'parallel' }, { label: '修饰', value: 'embellish' }, { label: '转折', value: 'transition' }, { label: '比较', value: 'compare' }, { label: '因果', value: 'cause' }, { label: '递进', value: 'progressively' }];
 
+export const ExaminationSubject = [{ short: 'V', value: 'verbal', label: '语文', english: 'Verbal' }, { short: 'Q', value: 'quant', label: '数学', english: 'Quant' }, { short: 'IR', value: 'ir', label: '综合推理', english: 'LR' }, { short: 'AWA', value: 'awa', label: '作文', english: 'AWA' }];
+
+export const ExaminationOrder = [{ list: ['awa', 'ir', 'quant', 'verbal'] }, { list: ['quant', 'verbal', 'ir', 'awa'] }, { list: ['verbal', 'quant', 'ir', 'awa'] }];
+
 // const content = {
 //   steps: [{
 //     title: '',
@@ -110,10 +114,33 @@ export const SentenceOption = [{ label: '平行', value: 'parallel' }, { label:
 //   info: { times: '', finishTime: '', userTime: '', time: '', questionNumber: '', userNumber: '', userCorrect: '', totalNumber: '', totalCorrect: '', totalTime: '', correctTime: '', incorrectTime: '' },
 // };
 
-// 模考卷子结果及报告
-// const examinationPaperDetail = {
-//   score: { total: 123, quant: 123, ir: 123, verbial: 123 },
-//
+// 模考题目数:/base/examination/number返回
+// const examinationNumber = { verbial: { number: '36', time: '65' }, ir: { number: '12', time: '30' }, awa: { number: '1', time: '30' }, quant: { number: '31', time: '62' } };
+
+// 模考卷子分数
+// const examinationScore = {
+//   total: 123, totalRank:12, quant: 123, quantRank: 123, verbial: 123, verbialRank: 123, ir: 123, irRank: 123,
+// };
+
+// 模考卷子报告
+// const examinationReportDetail = {
+//   subject: {
+//     [key]: {
+//       key: '',
+//       pace: [{ no: '', time: '', userTime: '' }],
+//       accumulation: [{ no: '', time: '', userTime: '' }],
+//       info: { questionNumber: '', time: '', userNumber: '', userTime: '' },
+//     },
+//   },
+//   type: {
+//     [key]: {
+//       key: '',
+//       place: [{ key: '', userNumber: '', userCorrect: '', userTime: '' }],
+//       difficult: [{ key: '', userNumber: '', userCorrect: '', totalNumber: '', totalCorrect: '' }],
+//       info: { userNumber: '', userTime: '', userCorrect: '', correctTime: '', incorrectTime: '' },
+//     },
+//   },
+//   info: { times: '', finishTime: '', userTime: '', time: '', questionNumber: '', userNumber: '', userCorrect: '' },
 // };
 
 // 每日统计

+ 12 - 4
front/project/admin/routes/setting/rank/page.js

@@ -38,6 +38,14 @@ export default class extends Page {
       key: 'verbalRank',
       type: 'number',
       name: 'V排名',
+    }, {
+      key: 'irScore',
+      type: 'number',
+      name: 'IR分',
+    }, {
+      key: 'irRank',
+      type: 'number',
+      name: 'IR排名',
     }];
 
     this.columns = [{
@@ -53,11 +61,11 @@ export default class extends Page {
       title: 'Q排名',
       dataIndex: 'quantRank',
     }, {
-      title: 'V分',
-      dataIndex: 'verbalScore',
+      title: 'IR分',
+      dataIndex: 'irScore',
     }, {
-      title: 'V排名',
-      dataIndex: 'verbalRank',
+      title: 'IR排名',
+      dataIndex: 'irRank',
     }];
 
     this.actionList = [{

+ 86 - 9
front/project/admin/routes/setting/time/page.js

@@ -38,18 +38,18 @@ export default class extends Page {
       title: '题目数',
       dataIndex: 'number',
       render: (text, result) => {
-        const { examination = [] } = this.state;
-        return <EditTableCell value={(examination[result.id] || {}).number || 0} onChange={(v) => {
-          this.changeMapValue('examination', result.id, 'number', v);
+        const { examination = {} } = this.state;
+        return <EditTableCell value={(examination[result.extend] || {}).number || 0} onChange={(v) => {
+          this.changeMapValue('examination', result.extend, 'number', v);
         }} />;
       },
     }, {
       title: '做题时间',
       dataIndex: 'time',
       render: (text, result) => {
-        const { examination } = this.state;
-        return <EditTableCell value={(examination[result.id] || {}).time || 0} onChange={(v) => {
-          this.changeValue('examination', result.id, 'time', v);
+        const { examination = {} } = this.state;
+        return <EditTableCell value={(examination[result.extend] || {}).time || 0} onChange={(v) => {
+          this.changeMapValue('examination', result.extend, 'time', v);
         }} />;
       },
     }];
@@ -65,7 +65,7 @@ export default class extends Page {
 
   refresh(tab) {
     if (tab === 'exercise') {
-      return this.refreshExercise();
+      return Promise.all([this.refreshExercise(), this.refreshTextbook()]);
     }
     if (tab === 'examination') {
       return this.refreshExamination();
@@ -82,6 +82,24 @@ export default class extends Page {
     });
   }
 
+  refreshSentence() {
+    return System.getSentenceTime().then((result) => {
+      this.setState({ sentence: result || {} });
+
+      const { form } = this.props;
+      form.setFieldsValue(flattenObject(result, 'sentence'));
+    });
+  }
+
+  refreshTextbook() {
+    return System.getSentenceTime().then((result) => {
+      this.setState({ textbook: result || {} });
+
+      const { form } = this.props;
+      form.setFieldsValue(flattenObject(result, 'textbook'));
+    });
+  }
+
   refreshExamination() {
     return System.getExaminationTime().then((result) => {
       this.setState({ examination: result || {} });
@@ -90,7 +108,7 @@ export default class extends Page {
 
   refreshFilter() {
     return System.getFilterTime().then((result) => {
-      this.setState({ filter: result });
+      this.setState({ filter: result || {} });
 
       const { form } = this.props;
       form.setFieldsValue(flattenObject(result, 'filter'));
@@ -137,6 +155,10 @@ export default class extends Page {
     let handler;
     if (tab === 'exercise') {
       handler = this.submitExercise();
+      handler = handler.then(() => {
+        // return this.submitSentence();
+        return this.submitTextbook();
+      });
     }
     if (tab === 'examination') {
       handler = this.submitExamination();
@@ -154,6 +176,16 @@ export default class extends Page {
     return System.setExerciseTime(exercise);
   }
 
+  submitSentence() {
+    const { sentence } = this.state;
+    return System.setSentenceTime(sentence);
+  }
+
+  submitTextbook() {
+    const { textbook } = this.state;
+    return System.setTextbookTime(textbook);
+  }
+
   submitExamination() {
     const { examination } = this.state;
     return System.setExaminationTime(examination);
@@ -185,6 +217,48 @@ export default class extends Page {
     />;
   }
 
+  renderSentence() {
+    const { getFieldDecorator } = this.props.form;
+    return <Form>
+      <Row>
+        <Col span={12}>
+          <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='每题预估时间'>
+            {getFieldDecorator('sentence.time', {
+              rules: [
+                { required: true, message: '输入每题预估时间' },
+              ],
+            })(
+              <InputNumber placeholder='请输入每题预估时间' addonAfter="s" onChange={(value) => {
+                this.changeValue('sentence', 'time', value);
+              }} style={{ width: '200px' }} />,
+            )}
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>;
+  }
+
+  renderTextbook() {
+    const { getFieldDecorator } = this.props.form;
+    return <Form>
+      <Row>
+        <Col span={12}>
+          <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='每题预估时间'>
+            {getFieldDecorator('sentence.time', {
+              rules: [
+                { required: true, message: '输入每题预估时间' },
+              ],
+            })(
+              <InputNumber placeholder='请输入每题预估时间' addonAfter="s" onChange={(value) => {
+                this.changeValue('sentence', 'time', value);
+              }} style={{ width: '200px' }} />,
+            )}
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>;
+  }
+
   renderFilterTime() {
     const { getFieldDecorator } = this.props.form;
     return <Form>
@@ -222,7 +296,7 @@ export default class extends Page {
   renderExamination() {
     return <TableLayout
       columns={this.examinationColumns}
-      list={this.state.exerciseList}
+      list={this.state.examinationList}
       pagination={false}
       loading={this.props.core.loading}
     />;
@@ -235,7 +309,10 @@ export default class extends Page {
       this.refresh(value);
     }}>
       <Tabs.TabPane tab="预估做题时间" key="exercise">
+        <h1>练习设置</h1>
         {this.renderExercise()}
+        <h1>机经设置</h1>
+        {this.renderTextbook()}
       </Tabs.TabPane>
       <Tabs.TabPane tab="数据剔除时间" key="filter">
         {this.renderFilterTime()}

+ 7 - 0
front/project/admin/routes/user/student/page.js

@@ -62,6 +62,13 @@ export default class extends Page {
             return `${formatDate(row.startTime)} - ${formatDate(row.expireTime)}`;
           }).join(<br />);
         },
+      },
+      {
+        title: '创建时间',
+        dataIndex: 'createTime',
+        render: (text) => {
+          return formatDate(text);
+        },
       }, {
         title: '操作',
         dataIndex: 'handler',

+ 16 - 0
front/project/admin/stores/system.js

@@ -61,6 +61,22 @@ export default class SystemStore extends BaseStore {
     return this.apiPut('/setting/exercise_time', params);
   }
 
+  getSentenceTime() {
+    return this.apiGet('/setting/sentence_time');
+  }
+
+  setSentenceTime(params) {
+    return this.apiPut('/setting/sentence_time', params);
+  }
+
+  getTextbookTime() {
+    return this.apiGet('/setting/textbook_time');
+  }
+
+  setTextbookTime(params) {
+    return this.apiPut('/setting/textbook_time', params);
+  }
+
   getExaminationTime() {
     return this.apiGet('/setting/examination_time');
   }

+ 18 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/QuestionDifficult.java

@@ -13,4 +13,22 @@ public enum QuestionDifficult {
         this.key = key;
         this.title = title;
     }
+
+    public static QuestionDifficult ValueOf(String name){
+        if (name == null) return null;
+        return QuestionDifficult.valueOf(name.toUpperCase());
+    }
+
+    public static int GetScore(QuestionDifficult difficult){
+        switch(difficult){
+            case EASY:
+                return 550;
+            case MEDIUM:
+                return 650;
+            case HARD:
+                return 750;
+            default:
+                return 0;
+        }
+    }
 }

+ 5 - 5
server/data/src/main/java/com/qxgmat/data/constants/enums/QuestionGroup.java

@@ -3,7 +3,7 @@ package com.qxgmat.data.constants.enums;
 /**
  * Created by gaojie on 2017/11/19.
  */
-public enum QuestionGroup {
+public enum QuestionSubject {
     VERBAL("verbal", "语文"),
     QUANT("quant", "数学"),
     IR("ir", "综合推理"),
@@ -13,14 +13,14 @@ public enum QuestionGroup {
 
     public String key;
     public String title;
-    private QuestionGroup(String key, String title){
+    private QuestionSubject(String key, String title){
         this.key = key;
         this.title = title;
     }
 
-    public static QuestionGroup ValueOf(String name){
+    public static QuestionSubject ValueOf(String name){
         if (name == null) return null;
-        return QuestionGroup.valueOf(name.toUpperCase());
+        return QuestionSubject.valueOf(name.toUpperCase());
     }
 
     /**
@@ -28,7 +28,7 @@ public enum QuestionGroup {
      * @param type
      * @return
      */
-    public static QuestionGroup FromType(QuestionType type){
+    public static QuestionSubject FromType(QuestionType type){
         switch(type){
             case SC:
             case RC:

+ 2 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/SettingKey.java

@@ -18,6 +18,8 @@ public enum SettingKey {
     EXERCISE_PAPER_STATUS("exercise_paper_status"), // 练习自动组卷状态:process进度,finish时间
     PREPARE_INFO("prepare_info"), // 备考统计信息
     SENTENCE_TIME("sentence_time"), // 长难句题目时间
+    TEXTBOOK_TIME("textbook_time"), // 机经题目时间
+    EXAMINATION_SCORE("examination_score"), // 模考分数计算
 
     TIPS("tips"); // 页面提示信息
 

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

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

+ 82 - 12
server/data/src/main/java/com/qxgmat/data/dao/entity/Rank.java

@@ -20,7 +20,7 @@ public class Rank implements Serializable {
      * 总排行
      */
     @Column(name = "`total_rank`")
-    private String totalRank;
+    private Integer totalRank;
 
     /**
      * quant分数
@@ -32,7 +32,7 @@ public class Rank implements Serializable {
      * quant排行
      */
     @Column(name = "`quant_rank`")
-    private String quantRank;
+    private Integer quantRank;
 
     /**
      * verbal分数
@@ -44,7 +44,19 @@ public class Rank implements Serializable {
      * verbal排行
      */
     @Column(name = "`verbal_rank`")
-    private String verbalRank;
+    private Integer verbalRank;
+
+    /**
+     * ir分数
+     */
+    @Column(name = "`ir_score`")
+    private Integer irScore;
+
+    /**
+     * ir排行
+     */
+    @Column(name = "`ir_rank`")
+    private Integer irRank;
 
     private static final long serialVersionUID = 1L;
 
@@ -85,7 +97,7 @@ public class Rank implements Serializable {
      *
      * @return total_rank - 总排行
      */
-    public String getTotalRank() {
+    public Integer getTotalRank() {
         return totalRank;
     }
 
@@ -94,7 +106,7 @@ public class Rank implements Serializable {
      *
      * @param totalRank 总排行
      */
-    public void setTotalRank(String totalRank) {
+    public void setTotalRank(Integer totalRank) {
         this.totalRank = totalRank;
     }
 
@@ -121,7 +133,7 @@ public class Rank implements Serializable {
      *
      * @return quant_rank - quant排行
      */
-    public String getQuantRank() {
+    public Integer getQuantRank() {
         return quantRank;
     }
 
@@ -130,7 +142,7 @@ public class Rank implements Serializable {
      *
      * @param quantRank quant排行
      */
-    public void setQuantRank(String quantRank) {
+    public void setQuantRank(Integer quantRank) {
         this.quantRank = quantRank;
     }
 
@@ -157,7 +169,7 @@ public class Rank implements Serializable {
      *
      * @return verbal_rank - verbal排行
      */
-    public String getVerbalRank() {
+    public Integer getVerbalRank() {
         return verbalRank;
     }
 
@@ -166,10 +178,46 @@ public class Rank implements Serializable {
      *
      * @param verbalRank verbal排行
      */
-    public void setVerbalRank(String verbalRank) {
+    public void setVerbalRank(Integer verbalRank) {
         this.verbalRank = verbalRank;
     }
 
+    /**
+     * 获取ir分数
+     *
+     * @return ir_score - ir分数
+     */
+    public Integer getIrScore() {
+        return irScore;
+    }
+
+    /**
+     * 设置ir分数
+     *
+     * @param irScore ir分数
+     */
+    public void setIrScore(Integer irScore) {
+        this.irScore = irScore;
+    }
+
+    /**
+     * 获取ir排行
+     *
+     * @return ir_rank - ir排行
+     */
+    public Integer getIrRank() {
+        return irRank;
+    }
+
+    /**
+     * 设置ir排行
+     *
+     * @param irRank ir排行
+     */
+    public void setIrRank(Integer irRank) {
+        this.irRank = irRank;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -183,6 +231,8 @@ public class Rank implements Serializable {
         sb.append(", quantRank=").append(quantRank);
         sb.append(", verbalScore=").append(verbalScore);
         sb.append(", verbalRank=").append(verbalRank);
+        sb.append(", irScore=").append(irScore);
+        sb.append(", irRank=").append(irRank);
         sb.append("]");
         return sb.toString();
     }
@@ -221,7 +271,7 @@ public class Rank implements Serializable {
          *
          * @param totalRank 总排行
          */
-        public Builder totalRank(String totalRank) {
+        public Builder totalRank(Integer totalRank) {
             obj.setTotalRank(totalRank);
             return this;
         }
@@ -241,7 +291,7 @@ public class Rank implements Serializable {
          *
          * @param quantRank quant排行
          */
-        public Builder quantRank(String quantRank) {
+        public Builder quantRank(Integer quantRank) {
             obj.setQuantRank(quantRank);
             return this;
         }
@@ -261,11 +311,31 @@ public class Rank implements Serializable {
          *
          * @param verbalRank verbal排行
          */
-        public Builder verbalRank(String verbalRank) {
+        public Builder verbalRank(Integer verbalRank) {
             obj.setVerbalRank(verbalRank);
             return this;
         }
 
+        /**
+         * 设置ir分数
+         *
+         * @param irScore ir分数
+         */
+        public Builder irScore(Integer irScore) {
+            obj.setIrScore(irScore);
+            return this;
+        }
+
+        /**
+         * 设置ir排行
+         *
+         * @param irRank ir排行
+         */
+        public Builder irRank(Integer irRank) {
+            obj.setIrRank(irRank);
+            return this;
+        }
+
         public Rank build() {
             return this.obj;
         }

+ 133 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseRecord.java

@@ -0,0 +1,133 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import javax.persistence.*;
+
+@Table(name = "user_course_record")
+public class UserCourseRecord implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 用户id
+     */
+    @Column(name = "`user_id`")
+    private Integer userId;
+
+    /**
+     * 课程id
+     */
+    @Column(name = "`course_id`")
+    private Integer courseId;
+
+    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 course_id - 课程id
+     */
+    public Integer getCourseId() {
+        return courseId;
+    }
+
+    /**
+     * 设置课程id
+     *
+     * @param courseId 课程id
+     */
+    public void setCourseId(Integer courseId) {
+        this.courseId = courseId;
+    }
+
+    @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(", courseId=").append(courseId);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static UserCourseRecord.Builder builder() {
+        return new UserCourseRecord.Builder();
+    }
+
+    public static class Builder {
+        private UserCourseRecord obj;
+
+        public Builder() {
+            this.obj = new UserCourseRecord();
+        }
+
+        /**
+         * @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 courseId 课程id
+         */
+        public Builder courseId(Integer courseId) {
+            obj.setCourseId(courseId);
+            return this;
+        }
+
+        public UserCourseRecord build() {
+            return this.obj;
+        }
+    }
+}

+ 7 - 7
server/data/src/main/java/com/qxgmat/data/dao/entity/UserQuestion.java

@@ -67,7 +67,7 @@ public class UserQuestion implements Serializable {
     private Integer isCorrect;
 
     /**
-     * 详细信息:长难句逻辑、结构
+     * 详细信息:长难句逻辑、结构,模考题分数
      */
     @Column(name = "`detail`")
     private JSONObject detail;
@@ -254,18 +254,18 @@ public class UserQuestion implements Serializable {
     }
 
     /**
-     * 获取详细信息:长难句逻辑、结构
+     * 获取详细信息:长难句逻辑、结构,模考题分数
      *
-     * @return detail - 详细信息:长难句逻辑、结构
+     * @return detail - 详细信息:长难句逻辑、结构,模考题分数
      */
     public JSONObject getDetail() {
         return detail;
     }
 
     /**
-     * 设置详细信息:长难句逻辑、结构
+     * 设置详细信息:长难句逻辑、结构,模考题分数
      *
-     * @param detail 详细信息:长难句逻辑、结构
+     * @param detail 详细信息:长难句逻辑、结构,模考题分数
      */
     public void setDetail(JSONObject detail) {
         this.detail = detail;
@@ -417,9 +417,9 @@ public class UserQuestion implements Serializable {
         }
 
         /**
-         * 设置详细信息:长难句逻辑、结构
+         * 设置详细信息:长难句逻辑、结构,模考题分数
          *
-         * @param detail 详细信息:长难句逻辑、结构
+         * @param detail 详细信息:长难句逻辑、结构,模考题分数
          */
         public Builder detail(JSONObject detail) {
             obj.setDetail(detail);

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

@@ -82,6 +82,12 @@ public class UserReport implements Serializable {
     private JSONObject setting;
 
     /**
+     * 考分:模考分值,json
+     */
+    @Column(name = "`score`")
+    private JSONObject score;
+
+    /**
      * 详细信息: json
      */
     @Column(name = "`detail`")
@@ -322,6 +328,24 @@ public class UserReport implements Serializable {
     }
 
     /**
+     * 获取考分:模考分值,json
+     *
+     * @return score - 考分:模考分值,json
+     */
+    public JSONObject getScore() {
+        return score;
+    }
+
+    /**
+     * 设置考分:模考分值,json
+     *
+     * @param score 考分:模考分值,json
+     */
+    public void setScore(JSONObject score) {
+        this.score = score;
+    }
+
+    /**
      * 获取详细信息: json
      *
      * @return detail - 详细信息: json
@@ -386,6 +410,7 @@ public class UserReport implements Serializable {
         sb.append(", userCorrect=").append(userCorrect);
         sb.append(", finishTime=").append(finishTime);
         sb.append(", setting=").append(setting);
+        sb.append(", score=").append(score);
         sb.append(", detail=").append(detail);
         sb.append(", createTime=").append(createTime);
         sb.append(", updateTime=").append(updateTime);
@@ -531,6 +556,16 @@ public class UserReport implements Serializable {
         }
 
         /**
+         * 设置考分:模考分值,json
+         *
+         * @param score 考分:模考分值,json
+         */
+        public Builder score(JSONObject score) {
+            obj.setScore(score);
+            return this;
+        }
+
+        /**
          * 设置详细信息: json
          *
          * @param detail 详细信息: json

+ 7 - 4
server/data/src/main/java/com/qxgmat/data/dao/mapping/RankMapper.xml

@@ -7,16 +7,19 @@
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="total_score" jdbcType="INTEGER" property="totalScore" />
-    <result column="total_rank" jdbcType="VARCHAR" property="totalRank" />
+    <result column="total_rank" jdbcType="INTEGER" property="totalRank" />
     <result column="quant_score" jdbcType="INTEGER" property="quantScore" />
-    <result column="quant_rank" jdbcType="VARCHAR" property="quantRank" />
+    <result column="quant_rank" jdbcType="INTEGER" property="quantRank" />
     <result column="verbal_score" jdbcType="INTEGER" property="verbalScore" />
-    <result column="verbal_rank" jdbcType="VARCHAR" property="verbalRank" />
+    <result column="verbal_rank" jdbcType="INTEGER" property="verbalRank" />
+    <result column="ir_score" jdbcType="INTEGER" property="irScore" />
+    <result column="ir_rank" jdbcType="INTEGER" property="irRank" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `total_score`, `total_rank`, `quant_score`, `quant_rank`, `verbal_score`, `verbal_rank`
+    `id`, `total_score`, `total_rank`, `quant_score`, `quant_rank`, `verbal_score`, `verbal_rank`, 
+    `ir_score`, `ir_rank`
   </sql>
 </mapper>

+ 18 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseRecordMapper.xml

@@ -0,0 +1,18 @@
+<?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.UserCourseRecordMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.UserCourseRecord">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="user_id" jdbcType="INTEGER" property="userId" />
+    <result column="course_id" jdbcType="INTEGER" property="courseId" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `id`, `user_id`, `course_id`
+  </sql>
+</mapper>

+ 3 - 2
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserReportMapper.xml

@@ -18,6 +18,7 @@
     <result column="user_correct" jdbcType="INTEGER" property="userCorrect" />
     <result column="finish_time" jdbcType="TIMESTAMP" property="finishTime" />
     <result column="setting" jdbcType="VARCHAR" property="setting" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
+    <result column="score" jdbcType="VARCHAR" property="score" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="detail" jdbcType="VARCHAR" property="detail" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
@@ -27,7 +28,7 @@
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `paper_id`, `paper_module`, `module_id`, `question_no_ids`, `question_number`, 
-    `time`, `user_number`, `user_time`, `user_correct`, `finish_time`, `setting`, `detail`, 
-    `create_time`, `update_time`
+    `time`, `user_number`, `user_time`, `user_correct`, `finish_time`, `setting`, `score`, 
+    `detail`, `create_time`, `update_time`
   </sql>
 </mapper>

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

@@ -62,7 +62,8 @@
     </if>
     where uc.`id` != null
     <if test="keyword != null">
-      and (u.`mobile` like #{keywordLike,jdbcType=VARCHAR} or u.`id` like #{keywordLike,jdbcType=VARCHAR})
+      and (u.`mobile` like #{keywordLike,jdbcType=VARCHAR}
+        or u.`id` like #{keywordLike,jdbcType=VARCHAR})
     </if>
   </select>
 </mapper>

+ 1 - 0
server/data/src/main/resources/mybatis-generator.xml

@@ -144,6 +144,7 @@
             <columnOverride column="detail" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
             <columnOverride column="question_no_ids" javaType="Integer[]" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.IntegerArrayWithJsonHandler"/>
             <columnOverride column="detail" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
+            <columnOverride column="score" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
             <columnOverride column="setting" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
         </table>
         <table schema="qianxing" tableName="user_question" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false" delimitAllColumns="true">

+ 3 - 2
server/gateway-api/src/main/java/com/qxgmat/Application.java

@@ -27,7 +27,8 @@ public class Application {
 }
 
 // 每日数据统计
-// 模考报告生成
 // 服务权限验证
 // 预习作业添加模式:用于主动发送用户
-// 课程与预习作业关系,分册进度
+// 课程与预习作业关系,分册进度
+// 作文练习,单题成卷
+// 模考做题次数,reset

+ 17 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java

@@ -165,6 +165,23 @@ public class SettingController {
         return ResponseHelp.success(entity.getValue());
     }
 
+    @RequestMapping(value = "/textbook_time", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改机经时间设置", httpMethod = "PUT")
+    private Response<Boolean> editTextbookTime(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.TEXTBOOK_TIME);
+        entity.setValue(dto);
+        settingService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/textbook_time", method = RequestMethod.GET)
+    @ApiOperation(value = "获取机经时间配置", httpMethod = "GET")
+    private Response<JSONObject> getTextbookTime(){
+        Setting entity = settingService.getByKey(SettingKey.TEXTBOOK_TIME);
+
+        return ResponseHelp.success(entity.getValue());
+    }
+
     @RequestMapping(value = "/tips", method = RequestMethod.PUT)
     @ApiOperation(value = "修改结构说明", httpMethod = "PUT")
     private Response<Boolean> editTips(@RequestBody @Validated JSONObject dto){

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

@@ -110,4 +110,12 @@ public class BaseController {
         List<ExaminationStruct> p = examinationStructService.children(id, children);
         return ResponseHelp.success(p);
     }
+
+    @RequestMapping(value = "/examination/number", method = RequestMethod.GET)
+    @ApiOperation(value = "模考题目数", notes = "获取模考题目数", httpMethod = "GET")
+    public Response<JSONObject> examinationNumber()  {
+        Setting entity = settingService.getByKey(SettingKey.EXAMINATION_TIME);
+
+        return ResponseHelp.success(entity.getValue());
+    }
 }

+ 2 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java

@@ -280,6 +280,7 @@ public class MyController {
     public Response<Boolean> bindCollect(@RequestBody @Validated UserQuestionIdsDto dto)  {
         User user = (User) shiroHelp.getLoginUser();
 
+        // todo 绑定数据
         return ResponseHelp.success(true);
     }
 
@@ -328,6 +329,7 @@ public class MyController {
     public Response<Boolean> bindError(@RequestBody @Validated UserErrorBindDto dto)  {
         User user = (User) shiroHelp.getLoginUser();
 
+        // todo 绑定数据
         return ResponseHelp.success(true);
     }
 

+ 13 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java

@@ -217,13 +217,25 @@ public class QuestionController {
         return ResponseHelp.success(userQuestionDetailDtos);
     }
 
+    @RequestMapping(value = "/examination/start", method = RequestMethod.POST)
+    @ApiOperation(value = "开始: 模考", notes = "提交考试设置", httpMethod = "POST")
+    public Response<UserReportBaseDto> startExamination(@RequestBody @Validated ExaminationStartDto dto)  {
+        User user = (User) shiroHelp.getLoginUser();
+        JSONObject setting = new JSONObject();
+        setting.put("disorder", dto.getDisorder());
+        setting.put("order", dto.getOrder());
+        UserReport report = questionFlowService.start(user.getId(), PaperModule.EXAMINATION, dto.getPaperId(), setting);
+
+        return ResponseHelp.success(Transform.convert(report, UserReportBaseDto.class));
+    }
+
     @RequestMapping(value = "/exercise/start", method = RequestMethod.POST)
     @ApiOperation(value = "开始: 练习", notes = "提交考试设置", httpMethod = "POST")
     public Response<UserReportBaseDto> startExercise(@RequestBody @Validated ExerciseStartDto dto)  {
         User user = (User) shiroHelp.getLoginUser();
         JSONObject setting = new JSONObject();
         setting.put("disorder", dto.getDisorder());
-        UserReport report = questionFlowService.start(user.getId(), PaperModule.PREVIEW, dto.getPaperId(), setting);
+        UserReport report = questionFlowService.start(user.getId(), PaperModule.EXERCISE, dto.getPaperId(), setting);
 
         return ResponseHelp.success(Transform.convert(report, UserReportBaseDto.class));
     }

+ 20 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/request/ExaminationStartDto.java

@@ -1,6 +1,10 @@
 package com.qxgmat.dto.request;
 
+import com.alibaba.fastjson.JSONArray;
+
 public class ExaminationStartDto {
+    private Boolean disorder;
+    private JSONArray order;
     private Integer paperId;
 
     public Integer getPaperId() {
@@ -10,4 +14,20 @@ public class ExaminationStartDto {
     public void setPaperId(Integer paperId) {
         this.paperId = paperId;
     }
+
+    public Boolean getDisorder() {
+        return disorder;
+    }
+
+    public void setDisorder(Boolean disorder) {
+        this.disorder = disorder;
+    }
+
+    public JSONArray getOrder() {
+        return order;
+    }
+
+    public void setOrder(JSONArray order) {
+        this.order = order;
+    }
 }

+ 3 - 0
server/gateway-api/src/main/java/com/qxgmat/service/ExaminationPaperService.java

@@ -30,6 +30,9 @@ public class ExaminationPaperService extends AbstractService {
     @Resource
     private QuestionService questionService;
 
+    public void initUserPaper(UserPaper userPaper, Integer id){
+    }
+
     /**
      * 根据第三层获取paper
      * @param id

+ 0 - 6
server/gateway-api/src/main/java/com/qxgmat/service/ExercisePaperService.java

@@ -56,12 +56,6 @@ public class ExercisePaperService extends AbstractService {
     @Resource
     private UserReportService userReportService;
 
-    public void initUserPaper(UserPaper userPaper, Integer id){
-        ExercisePaper paper = get(id);
-        userPaper.setTitle(paper.getTitle());
-        userPaper.setQuestionNoIds(paper.getQuestionNoIds());
-    }
-
     /**
      * 获取考点分组所有可用考点信息
      * @return

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

@@ -181,7 +181,7 @@ public class PreviewService extends AbstractService {
 
         // 获取考题时间
         List<QuestionNoRelation> relationList = questionNoService.listWithRelationByIds(previewPaper.getQuestionNoIds());
-        Integer time = toolsService.computerTime(relationList);
+        Integer time = toolsService.computerTime(relationList.toArray(new QuestionNoRelation[0]));
         for(Integer userId : userIds){
             userPaperService.add(UserPaper.builder()
                     .paperModule(PaperModule.PREVIEW.key)
@@ -217,7 +217,7 @@ public class PreviewService extends AbstractService {
 
         // 获取考题时间
         List<QuestionNoRelation> relationList = questionNoService.listWithRelationByIds(previewPaper.getQuestionNoIds());
-        Integer time = toolsService.computerTime(relationList);
+        Integer time = toolsService.computerTime(relationList.toArray(new QuestionNoRelation[0]));
         for(Integer userId : newList){
             if (!editList.contains(userId)){
                 // 添加

+ 0 - 6
server/gateway-api/src/main/java/com/qxgmat/service/SentencePaperService.java

@@ -46,12 +46,6 @@ public class SentencePaperService extends AbstractService {
     @Resource
     private SentenceQuestionService sentenceQuestionService;
 
-    public void initUserPaper(UserPaper userPaper, Integer id){
-        SentencePaper paper = get(id);
-        userPaper.setTitle(paper.getTitle());
-        userPaper.setQuestionNoIds(paper.getQuestionNoIds());
-        userPaper.setTime(0);
-    }
     /**
      * 添加长难句题目:加入题库,关联题目,并关联长难句paper
      * @param relation

+ 0 - 6
server/gateway-api/src/main/java/com/qxgmat/service/TextbookPaperService.java

@@ -42,12 +42,6 @@ public class TextbookPaperService extends AbstractService {
     @Resource
     private TextbookQuestionService textbookQuestionService;
 
-    public void initUserPaper(UserPaper userPaper, Integer id){
-        TextbookPaper paper = get(id);
-        userPaper.setTitle(paper.getTitle());
-        userPaper.setQuestionNoIds(paper.getQuestionNoIds());
-        userPaper.setTime(0);
-    }
     /**
      * 添加长难句题目:加入题库,关联题目,并关联长难句paper
      * @param relation

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

@@ -9,5 +9,5 @@ import com.qxgmat.data.dao.entity.UserReport;
  */
 @FunctionalInterface
 public interface SubmitQuestion {
-    Boolean callback(UserQuestion question);
+    Boolean callback(UserQuestion question, UserReport report);
 }

+ 443 - 113
server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java

@@ -4,16 +4,11 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
-import com.qxgmat.data.constants.enums.QuestionContentType;
-import com.qxgmat.data.constants.enums.QuestionType;
-import com.qxgmat.data.constants.enums.SettingKey;
+import com.qxgmat.data.constants.enums.*;
 import com.qxgmat.data.constants.enums.module.PaperModule;
 import com.qxgmat.data.constants.enums.module.QuestionModule;
 import com.qxgmat.data.dao.entity.*;
-import com.qxgmat.data.relation.entity.QuestionNoRelation;
-import com.qxgmat.data.relation.entity.SentenceQuestionRelation;
-import com.qxgmat.data.relation.entity.UserReportLimitRelation;
-import com.qxgmat.data.relation.entity.UserReportRelation;
+import com.qxgmat.data.relation.entity.*;
 import com.qxgmat.service.*;
 import com.qxgmat.service.annotation.*;
 import com.qxgmat.service.inline.*;
@@ -88,63 +83,92 @@ public class QuestionFlowService {
     private Map<PaperModule, StatReport> finishCallback = new HashMap<>();
 
     public QuestionFlowService(){
-        initPaperCallback.put(PaperModule.EXERCISE, (paper, id)->{
-           exercisePaperService.initUserPaper(paper, id);
+        initPaperCallback.put(PaperModule.EXERCISE, (userPaper, id)->{
+            ExercisePaper paper = exercisePaperService.get(id);
+            userPaper.setTitle(paper.getTitle());
+            userPaper.setQuestionNoIds(paper.getQuestionNoIds());
             // 获取考题时间
-            List<QuestionNoRelation> relationList = questionNoService.listWithRelationByIds(paper.getQuestionNoIds());
-            Integer time = toolsService.computerTime(relationList);
-            paper.setTime(time);
+            List<QuestionNoRelation> relationList = questionNoService.listWithRelationByIds(userPaper.getQuestionNoIds());
+            Integer time = toolsService.computerTime(relationList.toArray(new QuestionNoRelation[0]));
+            userPaper.setTime(time);
         });
-        initPaperCallback.put(PaperModule.PREVIEW, (paper, id)->{
+        initPaperCallback.put(PaperModule.PREVIEW, (userPaper, id)->{
             // 后台主动修改,无需变更
         });
-        initPaperCallback.put(PaperModule.ERROR, (paper, id)->{
+        initPaperCallback.put(PaperModule.ERROR, (userPaper, id)->{
             // 用户主动添加,无需变更
         });
-        initPaperCallback.put(PaperModule.COLLECT, (paper, id)->{
+        initPaperCallback.put(PaperModule.COLLECT, (userPaper, id)->{
             // 用户主动添加,无需变更
         });
-        initPaperCallback.put(PaperModule.SENTENCE, (paper, id)->{
-            sentencePaperService.initUserPaper(paper, id);
+        initPaperCallback.put(PaperModule.SENTENCE, (userPaper, id)->{
+            SentencePaper paper = sentencePaperService.get(id);
+            userPaper.setTitle(paper.getTitle());
+            userPaper.setQuestionNoIds(paper.getQuestionNoIds());
+            userPaper.setQuestionNumber(paper.getQuestionNumber());
+            // 获取考题时间
+            List<SentenceQuestionRelation> relationList = sentenceQuestionService.listWithRelationByIds(userPaper.getQuestionNoIds());
+            Integer time = toolsService.computerTime(relationList.toArray(new SentenceQuestionRelation[0]));
+            userPaper.setTime(time);
+        });
+        initPaperCallback.put(PaperModule.TEXTBOOK, (userPaper, id)->{
+            TextbookPaper paper = textbookPaperService.get(id);
+            userPaper.setTitle(paper.getTitle());
+            userPaper.setQuestionNoIds(paper.getQuestionNoIds());
+            userPaper.setQuestionNumber(paper.getQuestionNumber());
+            // 获取考题时间
+            List<TextbookQuestionRelation> relationList = textbookQuestionService.listWithRelationByIds(userPaper.getQuestionNoIds());
+            Integer time = toolsService.computerTime(relationList.toArray(new TextbookQuestionRelation[0]));
+            userPaper.setTime(time);
         });
-        initPaperCallback.put(PaperModule.TEXTBOOK, (paper, id)->{
-            textbookPaperService.initUserPaper(paper, id);
+        initPaperCallback.put(PaperModule.EXAMINATION, (userPaper, id)->{
+            ExaminationPaper paper = examinationPaperService.get(id);
+            userPaper.setTitle(paper.getTitle());
         });
 
         initReportCallback.put(PaperModule.EXERCISE, (report, paper)->{
             JSONObject setting = report.getSetting();
             if (setting.getBoolean("disorder")){
                 // 随机试题
-                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
+//                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
             }
         });
         initReportCallback.put(PaperModule.PREVIEW, (report, paper)->{
             JSONObject setting = report.getSetting();
             if (setting.getBoolean("disorder")){
                 // 随机试题
-                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
+//                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
             }
         });
         initReportCallback.put(PaperModule.COLLECT, (report, paper)->{
             JSONObject setting = report.getSetting();
             if (setting.getBoolean("disorder")){
                 // 随机试题
-                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
+//                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
             }
         });
         initReportCallback.put(PaperModule.ERROR, (report, paper)->{
             JSONObject setting = report.getSetting();
             if (setting.getBoolean("disorder")){
                 // 随机试题
-                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
+//                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
             }
         });
-        initReportCallback.put(PaperModule.SENTENCE, (paper, report)->{
+        initReportCallback.put(PaperModule.SENTENCE, (report, paper)->{
             // 无特殊设置
         });
-        initReportCallback.put(PaperModule.TEXTBOOK, (paper, report)->{
+        initReportCallback.put(PaperModule.TEXTBOOK, (report, paper)->{
             // 无特殊设置
         });
+        initReportCallback.put(PaperModule.EXAMINATION, (report, paper)->{
+            JSONObject setting = report.getSetting();
+            if (setting.getBoolean("disorder")){
+                // 随机试题选项
+//                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
+            }
+            JSONArray order = setting.getJSONArray("order");
+            toolsService.examinationReportInit(report, order);
+        });
 
         nextCallback.put(PaperModule.EXERCISE, (question, report, lastQuestion)->{
             Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
@@ -177,71 +201,52 @@ public class QuestionFlowService {
         nextCallback.put(PaperModule.SENTENCE, (question, report, lastQuestion)->{
             Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
             if (questionNoId == 0) return false;
-            SentenceQuestion relation = sentenceQuestionService.get(questionNoId);
+            SentenceQuestionRelation relation = sentenceQuestionService.relation(sentenceQuestionService.get(questionNoId));
             question.setQuestionNoId(relation.getId());
             question.setQuestionId(relation.getQuestionId());
-            question.setTime(0);
+            Integer time = toolsService.computerTime(relation);
+            question.setTime(time);
             return true;
         });
         nextCallback.put(PaperModule.TEXTBOOK, (question, report, lastQuestion)->{
             Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
             if (questionNoId == 0) return false;
-            SentenceQuestion relation = sentenceQuestionService.get(questionNoId);
+            TextbookQuestionRelation relation = textbookQuestionService.relation(textbookQuestionService.get(questionNoId));
+            question.setQuestionNoId(relation.getId());
+            question.setQuestionId(relation.getQuestionId());
+            Integer time = toolsService.computerTime(relation);
+            question.setTime(time);
+            return true;
+        });
+        nextCallback.put(PaperModule.EXAMINATION, (question, report, lastQuestion)->{
+            JSONObject setting = report.getSetting();
+            JSONArray order = setting.getJSONArray("order");
+            // 判断数量是否已经完成
+            // 根据设置出题
+            QuestionNoRelation relation = null;
+            // todo 适应性难度判断
+            ExaminationPaper paper = examinationPaperService.get(report.getModuleId());
+            if (paper.getIsAdapt() > 0){
+
+            }else{
+
+            }
             question.setQuestionNoId(relation.getId());
             question.setQuestionId(relation.getQuestionId());
-            question.setTime(0);
+            Integer time = toolsService.computerTime(relation);
+            question.setTime(time);
             return true;
         });
 
-        submitCallback.put(QuestionModule.BASE, (userQuestion)->{
+        submitCallback.put(QuestionModule.BASE, (userQuestion, userReport)->{
             // 判断答题情况
             JSONObject userAnswer = userQuestion.getUserAnswer();
             Question question = questionService.get(userQuestion.getQuestionId());
             JSONObject answer = question.getAnswer();
-
-            String type = question.getContent().getString("type");
-            QuestionContentType contentType = QuestionContentType.ValueOf(type);
-            JSONArray userQuestions = userAnswer.getJSONArray("questions");
-            JSONArray questions = answer.getJSONArray("questions");
-            for(int i = 0; i< questions.size(); i++){
-                JSONObject userOne = userQuestions.getJSONObject(i);
-                JSONObject one = questions.getJSONObject(i);
-                switch(contentType){
-                    case DOUBLE:
-                        JSONArray userDoubleList = userOne.getJSONArray("double");
-                        JSONArray doubleList = one.getJSONArray("double");
-//                        if(JSONObject.toJSON(userDoubleList) != JSONObject.toJSON(doubleList)){
-//                            return false;
-//                        }
-
-                        for(int j = 0; j < doubleList.size(); j++){
-                            JSONArray singleList = doubleList.getJSONArray(j);
-                            JSONArray userSingleList = userDoubleList.getJSONArray(i);
-                            for (int k = 0; k < singleList.size(); k++){
-                                if (userSingleList.getBoolean(k) != singleList.getBoolean(k)){
-                                    return false;
-                                }
-                            }
-                        }
-                        break;
-                    case SINGLE:
-                    case INLINE:
-                    default:
-                        JSONArray userSingleList = userOne.getJSONArray("single");
-                        JSONArray singleList = one.getJSONArray("single");
-//                        if(JSONObject.toJSON(userSingleList) != JSONObject.toJSON(singleList)){
-//                            return false;
-//                        }
-                        for(int j = 0; j < singleList.size(); j++){
-                            if (userSingleList.getBoolean(j) != singleList.getBoolean(j)){
-                                return false;
-                            }
-                        }
-                }
-            }
-            return true;
+            boolean result = this.baseAnswer(userAnswer, answer, question);
+            return result;
         });
-        submitCallback.put(QuestionModule.SENTENCE, (userQuestion)->{
+        submitCallback.put(QuestionModule.SENTENCE, (userQuestion, userReport)->{
             // 判断答题情况
             JSONObject userAnswer = userQuestion.getUserAnswer();
             Question question = questionService.get(userQuestion.getQuestionId());
@@ -270,6 +275,13 @@ public class QuestionFlowService {
             // 主谓宾判断正确,答题正确
             return correct;
         });
+        submitCallback.put(QuestionModule.TEXTBOOK, (userQuestion, userReport)->{
+            // 判断答题情况
+            JSONObject userAnswer = userQuestion.getUserAnswer();
+            Question question = questionService.get(userQuestion.getQuestionId());
+            JSONObject answer = question.getAnswer();
+            return this.baseAnswer(userAnswer, answer, question);
+        });
 
         submitAfterCallback.put(QuestionModule.BASE, (userQuestion, question)->{
             // 作文不统计
@@ -350,7 +362,7 @@ public class QuestionFlowService {
                 }
             }
             questionService.edit(Question.builder().id(question.getId())
-                    .answer(answerDistributed)
+                    .answerDistributed(answerDistributed)
                     .build());
 
         });
@@ -375,11 +387,11 @@ public class QuestionFlowService {
         finishCallback.put(PaperModule.SENTENCE, (report, questionList)->{
             report.setDetail(this.statSentenceReport(report, questionList));
         });
-        finishCallback.put(PaperModule.EXAMINATION, (report, questionList)->{
-
-        });
         finishCallback.put(PaperModule.TEXTBOOK, (report, questionList)->{
-
+            report.setDetail(this.statTextbookReport(report, questionList));
+        });
+        finishCallback.put(PaperModule.EXAMINATION, (report, questionList)->{
+            this.statExaminationReport(report, questionList);
         });
     }
 
@@ -484,7 +496,7 @@ public class QuestionFlowService {
         // 根据report模块获取试题所属模块
         QuestionModule module = QuestionModule.WithPaper(PaperModule.ValueOf(userReport.getPaperModule()));
         SubmitQuestion callback = submitCallback.get(module);
-        Boolean result = callback.callback(userQuestion);
+        Boolean result = callback.callback(userQuestion, userReport);
         userQuestion.setIsCorrect(result ? 1 : 0);
         userQuestionService.edit(userQuestion);
 
@@ -616,6 +628,57 @@ public class QuestionFlowService {
     }
 
     /**
+     * 基本题型的答案判断
+     * @param userAnswer
+     * @param answer
+     * @param question
+     * @return
+     */
+    private Boolean baseAnswer(JSONObject userAnswer, JSONObject answer, Question question){
+        String type = question.getContent().getString("type");
+        QuestionContentType contentType = QuestionContentType.ValueOf(type);
+        JSONArray userQuestions = userAnswer.getJSONArray("questions");
+        JSONArray questions = answer.getJSONArray("questions");
+        for(int i = 0; i< questions.size(); i++){
+            JSONObject userOne = userQuestions.getJSONObject(i);
+            JSONObject one = questions.getJSONObject(i);
+            switch(contentType){
+                case DOUBLE:
+                    JSONArray userDoubleList = userOne.getJSONArray("double");
+                    JSONArray doubleList = one.getJSONArray("double");
+//                        if(JSONObject.toJSON(userDoubleList) != JSONObject.toJSON(doubleList)){
+//                            return false;
+//                        }
+
+                    for(int j = 0; j < doubleList.size(); j++){
+                        JSONArray singleList = doubleList.getJSONArray(j);
+                        JSONArray userSingleList = userDoubleList.getJSONArray(i);
+                        for (int k = 0; k < singleList.size(); k++){
+                            if (userSingleList.getBoolean(k) != singleList.getBoolean(k)){
+                                return false;
+                            }
+                        }
+                    }
+                    break;
+                case SINGLE:
+                case INLINE:
+                default:
+                    JSONArray userSingleList = userOne.getJSONArray("single");
+                    JSONArray singleList = one.getJSONArray("single");
+//                        if(JSONObject.toJSON(userSingleList) != JSONObject.toJSON(singleList)){
+//                            return false;
+//                        }
+                    for(int j = 0; j < singleList.size(); j++){
+                        if (userSingleList.getBoolean(j) != singleList.getBoolean(j)){
+                            return false;
+                        }
+                    }
+            }
+        }
+        return true;
+    }
+
+    /**
      * 长难句单个选项答案判断
      * @param userAnswer
      * @param answer
@@ -649,60 +712,146 @@ public class QuestionFlowService {
         // report
         JSONObject detail = new JSONObject();
         JSONArray pace = new JSONArray();
-        for (UserQuestion userQuestion : questionList){
+        JSONObject difficultMap  = new JSONObject();
+        JSONObject placeMap = new JSONObject();
+
+        for (UserQuestion userQuestion:questionList){
+            QuestionNoRelation relation = relationMap.get(userQuestion.getQuestionNoId());
+
             // 每题用时
             JSONObject one = new JSONObject();
             one.put("time", userQuestion.getTime());
             one.put("userTime", userQuestion.getUserTime());
             one.put("no", userQuestion.getNo());
             pace.add(one);
-        }
-        detail.put("pace", pace);
 
-        JSONObject difficultMap  = new JSONObject();
-        for (UserQuestion userQuestion:questionList){
+            // 考点用时,以及正确度
+            String placeKey = relation.getQuestion().getPlace();
+            JSONObject place = placeMap.getJSONObject(placeKey);
+            if (place == null){
+                place = new JSONObject();
+                place.put("key", placeKey);
+                place.put("userNumber", 1);
+                place.put("userCorrect", userQuestion.getIsCorrect());
+                place.put("userTime", userQuestion.getUserTime());
+                placeMap.put(placeKey, place);
+            }else{
+                place.put("userNumber", place.getInteger("userNumber") + 1);
+                place.put("userCorrect", place.getInteger("userCorrect") + userQuestion.getIsCorrect());
+                place.put("userTime", place.getInteger("userTime") + userQuestion.getUserTime());
+            }
+
             // 难度正确度
-            QuestionNoRelation relation = relationMap.get(userQuestion.getQuestionNoId());
-            String d = relation.getQuestion().getDifficult();
-            JSONObject t = difficultMap.getJSONObject(d);
-            if (t == null){
-                t = new JSONObject();
-                t.put("key", d);
-                t.put("userNumber", 1);
-                t.put("userCorrect", userQuestion.getIsCorrect());
-                t.put("totalNumber", relation.getTotalNumber());
-                t.put("totalCorrect", relation.getTotalCorrect());
-                difficultMap.put(d, t);
+            String difficultKey = relation.getQuestion().getDifficult();
+            JSONObject difficult = difficultMap.getJSONObject(difficultKey);
+            if (difficult == null){
+                difficult = new JSONObject();
+                difficult.put("key", difficultKey);
+                difficult.put("userNumber", 1);
+                difficult.put("userCorrect", userQuestion.getIsCorrect());
+                difficult.put("totalNumber", relation.getTotalNumber());
+                difficult.put("totalCorrect", relation.getTotalCorrect());
+                difficultMap.put(difficultKey, difficult);
             }else{
-                t.put("userNumber", t.getInteger("userNumber") + 1);
-                t.put("userCorrect", t.getInteger("userCorrect") + userQuestion.getIsCorrect());
-                t.put("totalNumber", t.getInteger("totalNumber") + relation.getTotalNumber());
-                t.put("totalCorrect", t.getInteger("totalCorrect") + relation.getTotalCorrect());
+                difficult.put("userNumber", difficult.getInteger("userNumber") + 1);
+                difficult.put("userCorrect", difficult.getInteger("userCorrect") + userQuestion.getIsCorrect());
+                difficult.put("totalNumber", difficult.getInteger("totalNumber") + relation.getTotalNumber());
+                difficult.put("totalCorrect", difficult.getInteger("totalCorrect") + relation.getTotalCorrect());
             }
         }
+        detail.put("pace", pace);
         JSONArray difficult = new JSONArray();
         difficult.addAll(difficultMap.values());
         detail.put("difficult", difficult);
+        JSONArray place = new JSONArray();
+        place.addAll(placeMap.values());
+        detail.put("place", place);
+
+        JSONObject limit = new JSONObject();
+        // 限时统计考试正确度
+        UserReportLimitRelation relation = userReportService.statLimit(report.getPaperModule(), report.getModuleId());
+        limit.put("userNumber", relation.getUserNumber());
+        limit.put("userCorrect", relation.getUserCorrect());
+        detail.put("limit", limit);
+
+        JSONObject info = new JSONObject();
+        // 基本信息
+        info.put("times", paper.getTimes() + 1); // paper还未计数
+        info.put("finishTime", report.getFinishTime());
+        info.put("userTime", report.getUserTime());
+        info.put("time", report.getTime());
+        info.put("questionNumber", report.getQuestionNumber());
+        info.put("userNumber", report.getUserNumber());
+        info.put("userCorrect", report.getUserCorrect());
+        Integer totalNumber = 0;
+        Integer totalCorrect = 0;
+        Integer totalTime = 0;
+        for (QuestionNoRelation questionNoRelation : relationMap.values()){
+            totalNumber += questionNoRelation.getTotalNumber();
+            totalCorrect += questionNoRelation.getTotalCorrect();
+            totalTime += questionNoRelation.getTotalTime();
+        }
+        Integer correctTime = 0;
+        Integer incorrectTime = 0;
+        for (UserQuestion userQuestion : questionList){
+            if (userQuestion.getIsCorrect() > 0){
+                correctTime += userQuestion.getUserTime();
+            }else{
+                incorrectTime += userQuestion.getUserTime();
+            }
+        }
 
+        info.put("totalNumber", totalNumber);
+        info.put("totalCorrect", totalCorrect);
+        info.put("totalTime", totalTime);
+        info.put("correctTime", correctTime);
+        info.put("incorrectTime", incorrectTime);
+        detail.put("info", info);
+        return detail;
+    }
+
+    /**
+     * 根据机经报告格式,统计信息:移除练习中的难度分析
+     * @param report
+     * @param questionList
+     * @return
+     */
+    private JSONObject statTextbookReport(UserReport report, List<UserQuestion> questionList){
+        UserPaper paper = userPaperService.get(report.getPaperId());
+        Collection questionNoIds = Transform.getIds(questionList, UserQuestion.class, "questionNoId");
+        Map<Number, TextbookQuestionRelation> relationMap = textbookQuestionService.mapWithRelationByIds((Integer[])questionNoIds.toArray());
+
+        // report
+        JSONObject detail = new JSONObject();
+        JSONArray pace = new JSONArray();
         JSONObject placeMap = new JSONObject();
         for (UserQuestion userQuestion:questionList){
+            TextbookQuestionRelation relation = relationMap.get(userQuestion.getQuestionNoId());
+
+            // 每题用时
+            JSONObject one = new JSONObject();
+            one.put("time", userQuestion.getTime());
+            one.put("userTime", userQuestion.getUserTime());
+            one.put("no", userQuestion.getNo());
+            pace.add(one);
+
             // 考点用时,以及正确度
-            QuestionNoRelation relation = relationMap.get(userQuestion.getQuestionNoId());
-            String d = relation.getQuestion().getPlace();
-            JSONObject t = placeMap.getJSONObject(d);
-            if (t == null){
-                t = new JSONObject();
-                t.put("key", d);
-                t.put("userNumber", 1);
-                t.put("userCorrect", userQuestion.getIsCorrect());
-                t.put("userTime", userQuestion.getUserTime());
-                placeMap.put(d, t);
+            String placeKey = relation.getQuestion().getPlace();
+            JSONObject place = placeMap.getJSONObject(placeKey);
+            if (place == null){
+                place = new JSONObject();
+                place.put("key", placeKey);
+                place.put("userNumber", 1);
+                place.put("userCorrect", userQuestion.getIsCorrect());
+                place.put("userTime", userQuestion.getUserTime());
+                placeMap.put(placeKey, place);
             }else{
-                t.put("userNumber", t.getInteger("userNumber") + 1);
-                t.put("userCorrect", t.getInteger("userCorrect") + userQuestion.getIsCorrect());
-                t.put("userTime", t.getInteger("userTime") + userQuestion.getUserTime());
+                place.put("userNumber", place.getInteger("userNumber") + 1);
+                place.put("userCorrect", place.getInteger("userCorrect") + userQuestion.getIsCorrect());
+                place.put("userTime", place.getInteger("userTime") + userQuestion.getUserTime());
             }
         }
+        detail.put("pace", pace);
         JSONArray place = new JSONArray();
         place.addAll(placeMap.values());
         detail.put("place", place);
@@ -726,7 +875,7 @@ public class QuestionFlowService {
         Integer totalNumber = 0;
         Integer totalCorrect = 0;
         Integer totalTime = 0;
-        for (QuestionNoRelation questionNoRelation : relationMap.values()){
+        for (TextbookQuestionRelation questionNoRelation : relationMap.values()){
             totalNumber += questionNoRelation.getTotalNumber();
             totalCorrect += questionNoRelation.getTotalCorrect();
             totalTime += questionNoRelation.getTotalTime();
@@ -821,4 +970,185 @@ public class QuestionFlowService {
         detail.put("info", info);
         return detail;
     }
+
+    /**
+     * 根据模考报告格式,统计信息
+     * @param report
+     * @param questionList
+     * @return
+     */
+    private void statExaminationReport(UserReport report, List<UserQuestion> questionList){
+        UserPaper paper = userPaperService.get(report.getPaperId());
+        Collection questionNoIds = Transform.getIds(questionList, UserQuestion.class, "questionNoId");
+        Map<Number, QuestionNoRelation> relationMap = questionNoService.mapWithRelationByIds((Integer[])questionNoIds.toArray());
+
+        // report
+        JSONObject detail = new JSONObject();
+        JSONObject score = new JSONObject();
+
+        // 成绩单
+        JSONObject subjectMap = new JSONObject();
+        JSONObject typeMap = new JSONObject();
+        JSONObject tempMap = new JSONObject();
+
+        for (UserQuestion userQuestion : questionList){
+            QuestionNoRelation relation = relationMap.get(userQuestion.getQuestionNoId());
+            QuestionType questionType = QuestionType.ValueOf(relation.getQuestion().getQuestionType());
+            QuestionSubject questionSubject = QuestionSubject.FromType(questionType);
+            JSONObject type = typeMap.getJSONObject(questionType.key);
+            JSONObject temp = tempMap.getJSONObject(questionSubject.key);
+            JSONObject subject = subjectMap.getJSONObject(questionSubject.key);
+
+            // 归类
+            if (type == null || type.isEmpty()){
+                type = new JSONObject();
+                type.put("key", questionType.key);
+                JSONObject initTypeInfo = new JSONObject();
+                // 初始化题型基础信息
+                initTypeInfo.put("userNumber", 0);
+                initTypeInfo.put("userTime", 0);
+                initTypeInfo.put("userCorrect", 0);
+                initTypeInfo.put("correctTime", 0);
+                initTypeInfo.put("incorrectTime", 0);
+                initTypeInfo.put("diffCorrect", 0f);
+                initTypeInfo.put("diffIncorrect", 0f);
+                type.put("info", initTypeInfo);
+                temp = new JSONObject();
+                temp.put("place", new JSONObject());
+                temp.put("difficult", new JSONObject());
+                tempMap.put(questionSubject.key, temp);
+            }
+            if (subject == null || subject.isEmpty()){
+                subject = new JSONObject();
+                subject.put("key", questionSubject.key);
+                subject.put("pace", new JSONArray());
+                JSONObject initSubjectInfo = new JSONObject();
+                JSONObject subjectBase = toolsService.examinationSubjectInit(questionSubject);
+                // 初始化学科基础信息
+                initSubjectInfo.put("questionNumber", subjectBase.getIntValue("number"));
+                initSubjectInfo.put("time", subjectBase.getIntValue("time"));
+                initSubjectInfo.put("userNumber", 0);
+                initSubjectInfo.put("userTime", 0);
+                initSubjectInfo.put("difficultScore", 0);
+                subject.put("info", initSubjectInfo);
+                subjectMap.put(questionSubject.key, subject);
+            }
+            JSONArray pace = subject.getJSONArray("pace");
+            JSONObject placeMap = temp.getJSONObject("place");
+            JSONObject difficultMap = temp.getJSONObject("difficult");
+            JSONObject typeInfo = type.getJSONObject("info");
+            JSONObject subjectInfo = type.getJSONObject("info");
+
+            // 每题用时
+            JSONObject one = new JSONObject();
+            one.put("time", userQuestion.getTime());
+            one.put("userTime", userQuestion.getUserTime());
+            one.put("no", userQuestion.getNo());
+            pace.add(one);
+
+            // 考点用时,以及正确度
+            String placeKey = relation.getQuestion().getPlace();
+            JSONObject place = placeMap.getJSONObject(placeKey);
+            if (place == null){
+                place = new JSONObject();
+                place.put("key", placeKey);
+                place.put("userNumber", 1);
+                place.put("userCorrect", userQuestion.getIsCorrect());
+                place.put("userTime", userQuestion.getUserTime());
+                placeMap.put(placeKey, place);
+            }else{
+                place.put("userNumber", place.getInteger("userNumber") + 1);
+                place.put("userCorrect", place.getInteger("userCorrect") + userQuestion.getIsCorrect());
+                place.put("userTime", place.getInteger("userTime") + userQuestion.getUserTime());
+            }
+
+            // 难度正确度
+            String difficultKey = relation.getQuestion().getDifficult();
+            JSONObject difficult = difficultMap.getJSONObject(difficultKey);
+            if (difficult == null){
+                difficult = new JSONObject();
+                difficult.put("key", difficultKey);
+                difficult.put("userNumber", 1);
+                difficult.put("userCorrect", userQuestion.getIsCorrect());
+                difficult.put("totalNumber", relation.getTotalNumber());
+                difficult.put("totalCorrect", relation.getTotalCorrect());
+                difficultMap.put(difficultKey, difficult);
+            }else{
+                difficult.put("userNumber", difficult.getInteger("userNumber") + 1);
+                difficult.put("userCorrect", difficult.getInteger("userCorrect") + userQuestion.getIsCorrect());
+                difficult.put("totalNumber", difficult.getInteger("totalNumber") + relation.getTotalNumber());
+                difficult.put("totalCorrect", difficult.getInteger("totalCorrect") + relation.getTotalCorrect());
+            }
+
+            // 基础数据
+            typeInfo.put("userNumber", typeInfo.getIntValue("userNumber")+1);
+            typeInfo.put("userTime", typeInfo.getIntValue("userTime")+userQuestion.getUserTime());
+            typeInfo.put("userCorrect", typeInfo.getIntValue("userCorrect")+userQuestion.getIsCorrect());
+            if (userQuestion.getIsCorrect() > 0){
+                typeInfo.put("correctTime", typeInfo.getIntValue("correctTime")+userQuestion.getUserTime());
+            }else{
+                typeInfo.put("incorrectTime", typeInfo.getIntValue("incorrectTime")+userQuestion.getUserTime());
+            }
+
+            subjectInfo.put("userNumber", subjectInfo.getIntValue("userNumber")+1);
+            subjectInfo.put("userTime", subjectInfo.getIntValue("userTime")+userQuestion.getUserTime());
+
+            // 题型难度分计算
+            QuestionDifficult questionDifficult = QuestionDifficult.ValueOf(difficultKey);
+            Integer difficultScore = QuestionDifficult.GetScore(questionDifficult);
+
+            if (userQuestion.getIsCorrect() > 0){
+                typeInfo.put("diffCorrect", typeInfo.getFloatValue("diffCorrect") + toolsService.diffScore(relation.getTotalNumber(), relation.getTotalCorrect(), difficultScore));
+            }else{
+                typeInfo.put("diffIncorrect", typeInfo.getFloatValue("diffIncorrect") + toolsService.diffScore(relation.getTotalNumber(), relation.getTotalCorrect(), difficultScore));
+            }
+
+            subjectInfo.put("difficultScore", subjectInfo.getIntValue("difficultScore")+difficultScore);
+        }
+
+        // 计算难度平均值
+        for (String key : typeMap.keySet()) {
+            JSONObject type = typeMap.getJSONObject(key);
+            JSONObject typeInfo = type.getJSONObject("info");
+            typeInfo.put("avgDiffCorrect", toolsService.avgDiffScore(typeInfo.getFloat("diffCorrect"), typeInfo.getIntValue("userCorrect")));
+            typeInfo.put("avgDiffIncorrect", toolsService.avgDiffScore(typeInfo.getFloat("diffIncorrect"), typeInfo.getIntValue("userNumber") - typeInfo.getIntValue("userCorrect")));
+        }
+
+        // 学科得分计算
+        JSONObject quantSubject = subjectMap.getJSONObject(QuestionSubject.QUANT.key);
+        JSONObject quantInfo = quantSubject.getJSONObject("info");
+        Integer quantScore = toolsService.quantScore(quantInfo.getIntValue("number"), quantInfo.getIntValue("userNumber"), quantInfo.getInteger("difficultScore"), quantInfo.getInteger("userCorrect"));
+        JSONObject verbalSubject = subjectMap.getJSONObject(QuestionSubject.VERBAL.key);
+        JSONObject verbalInfo = verbalSubject.getJSONObject("info");
+        Integer verbalScore = toolsService.verbalScore(verbalInfo.getIntValue("number"), verbalInfo.getIntValue("userNumber"), verbalInfo.getInteger("difficultScore"), verbalInfo.getInteger("userCorrect"));
+        JSONObject irSubject = subjectMap.getJSONObject(QuestionSubject.IR.key);
+        JSONObject irInfo = irSubject.getJSONObject("info");
+        Integer irScore = toolsService.irScore(irInfo.getIntValue("number"), irInfo.getIntValue("userNumber"), irInfo.getInteger("difficultScore"), irInfo.getInteger("userCorrect"));
+
+        Rank rank = toolsService.totalScore(quantScore, verbalScore);
+        score.put("total", rank.getTotalScore());
+        score.put("totalRank", rank.getTotalRank());
+        score.put("quant", quantScore);
+        score.put("quantRank", rank.getQuantRank());
+        score.put("verbal", verbalScore);
+        score.put("ir", irScore);
+        score.put("irRank", rank.getIrRank());
+
+        detail.put("subject", subjectMap);
+        detail.put("type", typeMap);
+
+        JSONObject info = new JSONObject();
+        // 基本信息
+        info.put("times", paper.getTimes() + 1); // paper还未计数
+        info.put("finishTime", report.getFinishTime());
+        info.put("userTime", report.getUserTime());
+        info.put("time", report.getTime());
+        info.put("questionNumber", report.getQuestionNumber());
+        info.put("userNumber", report.getUserNumber());
+        info.put("userCorrect", report.getUserCorrect());
+        detail.put("info", info);
+
+        report.setDetail(detail);
+        report.setScore(score);
+    }
 }

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

@@ -1,12 +1,16 @@
 package com.qxgmat.service.extend;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.qxgmat.data.constants.enums.QuestionSubject;
 import com.qxgmat.data.constants.enums.SettingKey;
-import com.qxgmat.data.dao.entity.QuestionNo;
-import com.qxgmat.data.dao.entity.Setting;
-import com.qxgmat.data.dao.entity.UserQuestion;
+import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.relation.entity.QuestionNoRelation;
+import com.qxgmat.data.relation.entity.SentenceQuestionRelation;
+import com.qxgmat.data.relation.entity.TextbookQuestionRelation;
+import com.qxgmat.service.inline.RankService;
 import com.qxgmat.service.inline.SettingService;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -15,16 +19,31 @@ import java.util.List;
 @Service
 public class ToolsService {
 
+    @Value("${examination.verbalA}")
+    private Integer verbalA;
+
+    @Value("${examination.verbalB}")
+    private Integer verbalB;
+
+    @Value("${examination.quantC}")
+    private Integer quantC;
+
+    @Value("${examination.quantD}")
+    private Integer quantD;
+
     @Resource
     private SettingService settingService;
 
+    @Resource
+    private RankService rankService;
+
     /**
      * 根据练习时间设置计算考题考试时间
      *      setting: {struct: {difficult: ""}}
      * @param relationList
      * @return
      */
-    public Integer computerTime(List<QuestionNoRelation> relationList){
+    public Integer computerTime(QuestionNoRelation[] relationList){
         Setting setting = settingService.getByKey(SettingKey.EXERCISE_TIME);
         JSONObject value = setting.getValue();
 
@@ -69,6 +88,68 @@ public class ToolsService {
     }
 
     /**
+     * 根据练习时间设置获取单个考题时间
+     *      setting: {struct: {difficult: ""}}
+     * @param relation
+     * @return
+     */
+    public Integer computerTime(TextbookQuestionRelation relation){
+        Setting setting = settingService.getByKey(SettingKey.TEXTBOOK_TIME);
+        JSONObject value = setting.getValue();
+
+        String t = value.getString("time");
+        if (t == null || t.isEmpty()) return 0;
+        return Integer.valueOf(t);
+    }
+
+    /**
+     * 根据练习时间设置计算考题考试时间
+     *      setting: {struct: {difficult: ""}}
+     * @param relationList
+     * @return
+     */
+    public Integer computerTime(TextbookQuestionRelation[] relationList){
+        Setting setting = settingService.getByKey(SettingKey.TEXTBOOK_TIME);
+        JSONObject value = setting.getValue();
+
+        String t = value.getString("time");
+        if (t == null || t.isEmpty()) return 0;
+        return Integer.valueOf(t) * relationList.length;
+    }
+
+    /**
+     * 根据长难句时间设置获取单个考题时间
+     *      setting: {time: ""}
+     * @param relation
+     * @return
+     */
+    public Integer computerTime(SentenceQuestionRelation relation){
+        // todo 按人工还是手动
+        Setting setting = settingService.getByKey(SettingKey.SENTENCE_TIME);
+        JSONObject value = setting.getValue();
+
+        String t = value.getString("time");
+        if (t == null || t.isEmpty()) return 0;
+        return Integer.valueOf(t);
+    }
+
+    /**
+     * 根据长难句时间设置获取考题考试时间
+     *      setting: {time: ""}
+     * @param relationList
+     * @return
+     */
+    public Integer computerTime(SentenceQuestionRelation[] relationList){
+        // todo 按人工还是手动
+        Setting setting = settingService.getByKey(SettingKey.SENTENCE_TIME);
+        JSONObject value = setting.getValue();
+
+        String t = value.getString("time");
+        if (t == null || t.isEmpty()) return 0;
+        return Integer.valueOf(t) * relationList.length;
+    }
+
+    /**
      * 根据后台设置剔除时间,判断试题统计情况
      * @param question
      * @return
@@ -80,4 +161,136 @@ public class ToolsService {
         if (value.getInteger("max") < question.getTime()) return false;
         return  true;
     }
+
+    /**
+     * 根据考试设置,得到做题时间,做题数量
+     *      setting: {subject: { time: "", number: "" }}
+     * @param order
+     */
+    public void examinationReportInit(UserReport userReport, JSONArray order){
+        Setting setting = settingService.getByKey(SettingKey.EXAMINATION_TIME);
+        JSONObject value = setting.getValue();
+        Integer number = 0;
+        Integer time = 0;
+        for(String subject : order.toJavaList(String.class)){
+            JSONObject one = value.getJSONObject(subject);
+            if(one!= null && !one.isEmpty()){
+                number += one.getInteger("number");
+                time += one.getInteger("time");
+            }
+        }
+        userReport.setQuestionNumber(number);
+        userReport.setTime(time);
+    }
+
+    /**
+     * 获取模考学科数据:
+     *      { time: '', number: '' }
+     * @param subject
+     * @return
+     */
+    public JSONObject examinationSubjectInit(QuestionSubject subject){
+        Setting setting = settingService.getByKey(SettingKey.EXAMINATION_TIME);
+        JSONObject value = setting.getValue();
+        return value.getJSONObject(subject.key);
+    }
+
+    /**
+     * Quant分数计算:系数c * Quant部分平均正确率 + 系数d * Quant部分题目平均难度
+     * @param number
+     * @param userNumber
+     * @param difficultScore
+     * @param correctNumber
+     * @return
+     */
+    public Integer quantScore(Integer number, Integer userNumber, Integer difficultScore, Integer correctNumber){
+        return quantC * correctNumber / number + quantD * difficultScore / userNumber;
+    }
+
+    /**
+     * Verbal分数计算:系数a * Verbal部分题目平均正确率 + 系数b * Verbal部分题目平均难度
+     * @param number
+     * @param userNumber
+     * @param difficultScore
+     * @param correctNumber
+     * @return
+     */
+    public Integer verbalScore(Integer number, Integer userNumber, Integer difficultScore, Integer correctNumber){
+        return verbalA * correctNumber / number + verbalB * difficultScore / userNumber;
+    }
+
+    /**
+     * IR分数计算:最高8分
+     * @param number
+     * @param userNumber
+     * @param difficultScore
+     * @param correctNumber
+     * @return
+     */
+    public int irScore(Integer number, Integer userNumber, Integer difficultScore, Integer correctNumber){
+        switch(correctNumber){
+            case 0:
+            case 1:
+                return 1;
+            case 2:
+                return 2;
+            case 3:
+            case 4:
+                return 3;
+            case 5:
+                return 4;
+            case 6:
+                return 5;
+            case 7:
+                return 6;
+            case 8:
+                return 7;
+            case 9:
+            default:
+                return 8;
+        }
+    }
+
+    /**
+     * avg diff correct=[(550*0.3+550*0.5+550*0.4)+(650*0.6+650*0.8)+(750*0.9)]➗(3+2+1)
+     * @param totalNumber
+     * @param totalCorrect
+     * @param difficultScore
+     * @return
+     */
+    public float diffScore(Integer totalNumber, Integer totalCorrect, Integer difficultScore){
+        Setting setting = settingService.getByKey(SettingKey.EXAMINATION_SCORE);
+        JSONObject value = setting.getValue();
+        if (value.getBooleanValue("difficult")){
+            return difficultScore * (1- (float)totalCorrect / totalNumber);
+        }else{
+            return difficultScore;
+        }
+    }
+
+    /**
+     * 当这个结果低于500的时候统一显示为500,
+     * 高于500时,是多少就显示多少,且必须为10的倍数,722=720,754=750,756=760
+     * @param totalDiffScore
+     * @param number
+     * @return
+     */
+    public int avgDiffScore(float totalDiffScore, Integer number){
+        int avg = (int)(totalDiffScore / number);
+        if (avg < 500){
+            return 500;
+        }
+        return (int) (Math.floor((int)(avg / 10)) * 10);
+    }
+
+    /**
+     * 得出V小分和Q小分后根据总分-小分excel对应表(我方提供)算出最后考试的总分
+     * @param quantScore
+     * @param verbalScore
+     * @return
+     */
+    public Rank totalScore(Integer quantScore, Integer verbalScore){
+        Rank rank = rankService.search(quantScore, verbalScore);
+        return rank != null ? rank : new Rank();
+    }
 }

+ 17 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/RankService.java

@@ -61,6 +61,23 @@ public class RankService extends AbstractService {
         return one(rankMapper, example);
     }
 
+    /**
+     * 计算考分
+     * @param quantScore
+     * @param verbalScore
+     * @return
+     */
+    public Rank search(Number quantScore, Number verbalScore){
+        Example example = new Example(Rank.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("verbalScore", verbalScore)
+                        .andEqualTo("quantScore", quantScore)
+        );
+
+        return one(rankMapper, example);
+    }
+
     public Rank add(Rank rank){
         int result = insert(rankMapper, rank);
         rank = one(rankMapper, rank.getId());

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

@@ -5,6 +5,7 @@ import com.nuliji.tools.AbstractService;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
+import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.dao.UserServiceMapper;
 import com.qxgmat.data.dao.entity.Pay;
 import com.qxgmat.data.dao.entity.UserPay;
@@ -23,7 +24,6 @@ public class UserServiceService extends AbstractService {
     @Resource
     private UserServiceMapper userServiceMapper;
 
-
     // 开通服务
     public boolean used(UserPay pay){
         add(UserService.builder().userId(pay.getUserId()).service(pay.getModuleExtend()).build());
@@ -31,6 +31,25 @@ public class UserServiceService extends AbstractService {
     }
 
     /**
+     * 判断是否有权限
+     * @param userId
+     * @param key
+     * @return
+     */
+    public boolean HasService(Integer userId, ServiceKey key){
+        Example example = new Example(UserService.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("userId", userId)
+                        .andEqualTo("service", key.key)
+                        .andGreaterThanOrEqualTo("startTime", new Date())
+                        .andLessThan("expireTime", new Date())
+        );
+        UserService service = one(userServiceMapper, example);
+        return service != null;
+    }
+
+    /**
      * 合并用户信息,将old转移至new
      * @param oldUserId
      * @param newUserId

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

@@ -74,3 +74,9 @@ self:
 paper:
   sentenceLength: 20
   textbookLength: 31
+
+examination:
+  verbalA: 1
+  verbalB: 2
+  quantC: 1
+  quantD: 2