Browse Source

fix(all): 更新bug

Go 4 years ago
parent
commit
6d3177f474
41 changed files with 337 additions and 97 deletions
  1. 7 0
      front/project/admin/app.less
  2. 9 6
      front/project/admin/components/Association/index.js
  3. 1 1
      front/project/admin/routes/course/vsDetail/page.js
  4. 0 7
      front/project/admin/routes/setting/index/index.less
  5. 1 0
      front/project/admin/routes/setting/time/page.js
  6. 6 0
      front/project/admin/routes/show/message/page.js
  7. 1 1
      front/project/h5/routes/page/message/page.js
  8. 2 2
      front/project/h5/routes/product/dataHistory/page.js
  9. 10 12
      front/project/www/components/Login/index.js
  10. 4 3
      front/project/www/components/Tabs/index.js
  11. 1 1
      front/project/www/routes/exercise/main/page.js
  12. 8 1
      front/project/www/routes/my/course/page.js
  13. 2 2
      front/project/www/routes/page/home/index.less
  14. 7 3
      front/project/www/routes/paper/process/base/index.js
  15. 2 2
      front/project/www/routes/paper/process/page.js
  16. 3 3
      front/project/www/routes/paper/question/detail/index.js
  17. 10 0
      front/project/www/routes/paper/question/detail/index.less
  18. 3 2
      front/project/www/routes/paper/question/page.js
  19. 3 3
      front/project/www/routes/paper/report/page.js
  20. 8 0
      front/project/www/routes/question/search/index.less
  21. 10 7
      front/project/www/routes/question/search/page.js
  22. 1 0
      front/project/www/stores/user.js
  23. 12 0
      server/data/src/main/java/com/qxgmat/data/relation/UserQuestionRelationMapper.java
  24. 5 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/QuestionNoRelationMapper.xml
  25. 33 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml
  26. 13 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/AuthController.java
  27. 3 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  28. 2 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java
  29. 2 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  30. 28 12
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  31. 4 3
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/extend/UserExtendDto.java
  32. 21 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/EditPasswordDto.java
  33. 20 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/MessageTemplateDto.java
  34. 10 4
      server/gateway-api/src/main/java/com/qxgmat/service/ManagerService.java
  35. 35 2
      server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java
  36. 19 0
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java
  37. 3 3
      server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java
  38. 6 5
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java
  39. 8 8
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ToolsService.java
  40. 6 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/ExercisePaperService.java
  41. 8 0
      server/tools/src/main/java/com/nuliji/tools/Transform.java

+ 7 - 0
front/project/admin/app.less

@@ -273,4 +273,11 @@ body {
       color: @base_color;
     }
   }
+
+  .ant-upload {
+    img {
+      max-width: 100px;
+      max-height: 100px;
+    }
+  }
 }

+ 9 - 6
front/project/admin/components/Association/index.js

@@ -12,9 +12,12 @@ class Association extends Component {
     super(props);
     this.state = { ids: props.ids, show: !!props.modal, loading: false, err: '' };
     this.questionNos = [];
+    this.bind(this.props);
+  }
 
-    generateSearch(props.field, { mode: 'multiple' }, this, (search) => {
-      return this.searchQuestion(search);
+  bind(props) {
+    generateSearch(props.field || 'questionNoIds', { mode: 'multiple' }, this, (search) => {
+      return this.searchQuestion(props, search);
     }, (row) => {
       return {
         title: row.title,
@@ -79,11 +82,11 @@ class Association extends Component {
     });
   }
 
-  searchQuestion(params) {
+  searchQuestion(props, params) {
     let handler;
-    params.module = this.props.module;
-    params.questionType = this.props.questionType;
-    switch (this.props.module) {
+    params.module = props.module;
+    params.questionType = props.questionType;
+    switch (props.module) {
       case 'sentence':
       case 'exercise':
         // 查找练习题目

+ 1 - 1
front/project/admin/routes/course/vsDetail/page.js

@@ -258,7 +258,7 @@ export default class extends Page {
             return <Col span={7} offset={index % 3 ? 1 : 0}><Card>
               <Button className="delete-button" size="small" onClick={() => {
                 if (row.id) {
-                  Course.delTeacher(row.id).then(() => {
+                  Course.delTeacher({ id: row.id }).then(() => {
                     this.deleteLength('teachers', index);
                   });
                 } else {

+ 0 - 7
front/project/admin/routes/setting/index/index.less

@@ -15,11 +15,4 @@
     right: 10px;
     top: 10px;
   }
-
-  .ant-upload {
-    img {
-      max-width: 100px;
-      max-height: 100px;
-    }
-  }
 }

+ 1 - 0
front/project/admin/routes/setting/time/page.js

@@ -271,6 +271,7 @@ export default class extends Page {
 
   renderExercise() {
     return <TableLayout
+      key={this.state.exercise ? 1 : 0}
       columns={this.exerciseColumns}
       list={this.state.exerciseList}
       pagination={false}

+ 6 - 0
front/project/admin/routes/show/message/page.js

@@ -24,18 +24,22 @@ export default class extends Page {
       key: 'title',
       type: 'input',
       name: '标题',
+      required: true,
     }, {
       key: 'content',
       type: 'textarea',
       name: '内容',
+      required: true,
     }, {
       key: 'link',
       type: 'input',
       name: '链接地址',
+      required: true,
     }, {
       key: 'sendTime',
       type: 'date',
       name: '发送时间',
+      required: true,
     }];
 
     this.columns = [{
@@ -196,6 +200,8 @@ export default class extends Page {
 
   addAction() {
     asyncForm('创建消息', this.itemList, {}, data => {
+      data.messageMethod = 'inside';
+      data.messageCategory = 'custom';
       return System.addMessage(data).then(() => {
         asyncSMessage('添加成功!');
         this.refreshTab('custom');

+ 1 - 1
front/project/h5/routes/page/message/page.js

@@ -56,7 +56,7 @@ export default class extends Page {
           this.readAll();
         }}>
           <Assets name="clean" />
-          全部已读
+          全部已读2
         </div>
         {this.renderList()}
       </div>

+ 2 - 2
front/project/h5/routes/product/dataHistory/page.js

@@ -46,8 +46,8 @@ export default class extends Page {
   }
 
   initData() {
-    My.listData(Object.assign({ order: 'isSubscribe', direction: 'desc' }, this.state.search)).then(result => {
-      this.setTableData(result.list.filter(row => row.isSubscribe), result.total);
+    My.listData(this.state.search).then(result => {
+      this.setTableData(result.list, result.total);
     });
     return this.initListKeys(['history'])
       .then(() => {

+ 10 - 12
front/project/www/components/Login/index.js

@@ -23,7 +23,6 @@ export default class Login extends Component {
   constructor(props) {
     super(props);
     this.validNumber = 0;
-    this.needScan = true;
     this.state = { scanNumber: 0, type: LOGIN_WX, empty: {}, data: { area: MobileArea[0].value } };
     this.validMobileNumber = 0;
     this.validEmailNumber = 0;
@@ -31,11 +30,6 @@ export default class Login extends Component {
       'message',
       event => {
         if (typeof event.data === 'string' && event.data.indexOf('code:') === 0) {
-          if (!this.needScan) {
-            return;
-          }
-          console.log(this.needScan, 0);
-          this.needScan = false;
           const code = event.data.split(':')[1];
           if (this.state.type === LOGIN_WX) {
             this.scanLogin(code);
@@ -61,6 +55,7 @@ export default class Login extends Component {
   }
 
   close() {
+    this.needScan = true;
     User.closeLogin();
   }
 
@@ -123,7 +118,6 @@ export default class Login extends Component {
   scanLogin(code) {
     User.loginWechat(code, true, false)
       .then(result => {
-        this.needScan = false;
         if (result.bindMobile) {
           this.close();
         } else {
@@ -131,10 +125,17 @@ export default class Login extends Component {
         }
       })
       .catch(err => {
-        console.log('scanLoginErr');
-        this.needScan = true;
         if (err.message !== '登录失败') {
           asyncSMessage(err.message, 'error');
+        } else {
+          console.log(this.props.user);
+          if (this.props.user.nickname) {
+            if (this.props.user.bindMobile) {
+              this.close();
+            } else {
+              this.changeType(BIND_PHONE);
+            }
+          }
         }
       });
   }
@@ -142,12 +143,9 @@ export default class Login extends Component {
   scanBind(code) {
     User.loginWechat(code, false, false)
       .then(() => {
-        this.needScan = false;
         this.close();
       })
       .catch(() => {
-        console.log('scanBindErr');
-        this.needScan = true;
         this.changeType(BIND_WX_ERROR);
       });
   }

+ 4 - 3
front/project/www/components/Tabs/index.js

@@ -2,11 +2,11 @@ import React from 'react';
 import { Link } from 'react-router-dom';
 import './index.less';
 
-function getItem(props, item, onChange) {
+function getItem(props, item, onChange, force) {
   const { width, space, active, render } = props;
   return (
     <div
-      onClick={() => active !== item.key && onChange && onChange(item.key)}
+      onClick={() => (force || active !== item.key) && onChange && onChange(item.key)}
       style={{ width: width || '', marginLeft: space || '', marginRight: space || '' }}
       className={`tab ${active === item.key ? 'active' : ''}`}
     >
@@ -25,12 +25,13 @@ function Tabs(props) {
     border,
     space,
     onChange,
+    force,
   } = props;
   return (
     <div className={`tabs ${className} ${type} ${theme} ${size} ${border ? 'border' : ''}`}>
       <div className="tabs-warpper" style={{ marginLeft: space * -1 || '', marginRight: space * -1 || '' }}>
         {tabs.map(item => {
-          return item.path ? <Link to={item.path}>{getItem(props, item)}</Link> : getItem(props, item, onChange);
+          return item.path ? <Link to={item.path}>{getItem(props, item)}</Link> : getItem(props, item, onChange, force);
         })}
       </div>
     </div>

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

@@ -1146,7 +1146,7 @@ export default class extends Page {
             const [first] = struct.children;
             let col = 3;
             if (first && first.type === 'paper') {
-              col = 5;
+              col = 4;
             }
             return (
               <Panel

+ 8 - 1
front/project/www/routes/my/course/page.js

@@ -60,6 +60,12 @@ export default class extends Page {
         row.progressMap[progress.courseNoId] = progress;
       });
     }
+    row.recordMap = {};
+    if (row.records) {
+      row.records.forEach(record => {
+        row.recordMap[record.courseNoId] = record;
+      });
+    }
 
     row.courseNoMap = {};
     row.courseTime = 0;
@@ -67,8 +73,9 @@ export default class extends Page {
       row.courseNos.forEach(no => {
         row.courseNoMap[no.id] = no;
         row.courseTime += no.time;
-        no.paper = row.paperMap[no.id];
+        no.paper = row.paperMap[no.no];
         no.progress = row.progressMap[no.id];
+        no.record = row.recordMap[no.id];
       });
     }
     if (row.currentNo) {

+ 2 - 2
front/project/www/routes/page/home/index.less

@@ -70,7 +70,7 @@
   .block-3 {
     background: #fefefe;
     position: relative;
-    padding-bottom: 400px;
+    padding-bottom: 300px;
 
     .bg {
       .bg-1 {
@@ -479,7 +479,7 @@
   }
 
   .block-4 {
-    padding-top: 140px;
+    padding-top: 20px;
     background-repeat: no-repeat;
     background-position: center;
     background-color: linear-gradient(241deg, rgba(132, 93, 72, 0) 0%, rgba(88, 50, 29, 1) 100%);

+ 7 - 3
front/project/www/routes/paper/process/base/index.js

@@ -1,7 +1,7 @@
 import React, { Component } from 'react';
 import ReactDOM from 'react-dom';
 import './index.less';
-import { Checkbox, Icon as AntDIcon } from 'antd';
+import { Checkbox, Icon as AntDIcon, Tooltip } from 'antd';
 import Assets from '@src/components/Assets';
 import { formatSeconds, formatMinuteSecond, getMap } from '@src/services/Tools';
 import Icon from '../../../../components/Icon';
@@ -460,6 +460,7 @@ export default class extends Component {
   renderExaminationStartDefault() {
     const { paper, flow, setting } = this.props;
     const { disorder = true, order = [], orderIndex } = setting;
+    const tmp = order.filter(row => row);
     return (
       <div className="exercise-start cat">
         <div className="title">Section Ordering</div>
@@ -474,11 +475,14 @@ export default class extends Component {
                   return <div className="block-text" onClick={() => {
                     if (order.indexOf(r.value) >= 0) {
                       // 取消
-                      order[i] = '';
+                      if (tmp.length > 1) {
+                        order[i] = '';
+                      }
                     } else {
                       // 选中
                       order[i] = r.value;
                     }
+                    console.log(order);
                     flow.setSetting({ order });
                   }}>
                     <Checkbox checked={orderIndex === index ? order.indexOf(r.value) >= 0 : false} /> {r.label}{' '}
@@ -537,7 +541,7 @@ export default class extends Component {
         <div className={'layout-body'}>
           <div className="relax">
             <div className="title">
-              Optional Break <Icon name="question" />
+              Optional Break <Tooltip title={'点击「Next」可继续考试'} trigger="click"><Icon name="question" /></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>

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

@@ -106,7 +106,7 @@ export default class extends Page {
       } else {
         this.setState({ scene: 'start' });
         // 模考cat1分钟自动开始
-        if (paper.isAdapt > 1) {
+        if (paper.paperModule === 'examination') {
           this.startWaitTime();
         }
       }
@@ -258,7 +258,7 @@ export default class extends Page {
       this.relaxStage();
       this.accTime = 0;
     } else {
-      this.next();
+      this.relaxToNextStage();
     }
   }
 

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

@@ -625,7 +625,7 @@ export default class extends Component {
   }
 
   renderAWA() {
-    const { userQuestion = { detail: {}, userAnswer: {} } } = this.state;
+    const { userQuestion } = this.props;
     const { showAnswer } = this.state;
     return (<div className="block">
       <div className="block-awa">
@@ -658,10 +658,10 @@ export default class extends Component {
                   单词数:<span className="s">{Number((userQuestion.detail || {}).words || 0)}</span>词
                 </span>
               </div>
-              <div className="content-awa" dangerouslySetInnerHTML={{ __html: userQuestion.userAnswer.awa || '' }} />
+              <div className="content-awa" dangerouslySetInnerHTML={{ __html: (userQuestion.userAnswer || {}).awa || '' }} />
             </div>
           )}
-          {!showAnswer && <div className="show-awa">选择「显示答案」查看自己的作文</div>}
+          {!showAnswer && <div className="show-awa"><div>选择「显示答案」查看自己的作文</div></div>}
         </div>
       </div></div>
     );

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

@@ -265,6 +265,16 @@
             text-align: center;
             line-height: 20px;
             color: #A7A7B7;
+            position: relative;
+
+            >div {
+              left: 0;
+              position: absolute;
+              top: 40%;
+              text-align: center;
+              display: inline-block;
+              width: 100%;
+            }
           }
         }
 

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

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

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

@@ -965,7 +965,7 @@ export default class extends Page {
                     <div className='n'><a href={`/paper/question/${row.id}`} target="_blank">{(row.questionNo || {}).title}</a></div>
                     <div className='desc'><Typography.Paragraph ellipsis={{ rows: 3, expandable: false }}>{row.question.description}</Typography.Paragraph></div>
                   </td>
-                  <td><GIcon name={row.isCorrect ? 'right' : 'error'} noHover /></td>
+                  <td>{row.question.questionType === 'awa' ? '-' : <GIcon name={row.isCorrect ? 'right' : 'error'} noHover />}</td>
                   <td>{row.question.difficult}</td>
                   <td>{formatMinuteSecond(row.userTime)}</td>
                   <td>{row.question.place}</td>
@@ -1096,7 +1096,7 @@ export default class extends Page {
             </div>
           </div>
           <BarChart
-            height={40 * place.length}
+            height={90 + 30 * place.length}
             option={BarOption3(
               ['知识点', '正确率分析', '用时分析'],
               place.map(row => {
@@ -1428,7 +1428,7 @@ export default class extends Page {
         <div className="content">
           <div className="title">知识体系分析</div>
           <BarChart
-            height={40 * Object.keys(subjectDetail.place).length}
+            height={90 + 30 * Object.keys(subjectDetail.place).length}
             option={BarOption3(
               ['知识点', '正确率分析', '用时分析'],
               Object.values(subjectDetail.place).map(row => {

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

@@ -95,6 +95,14 @@
       line-height: 20px;
       height: 50px;
       border-bottom: 1px solid #eee;
+
+      a:hover {
+        color: #4292F0 !important;
+      }
+
+      a:visited {
+        color: #4292F0 !important;
+      }
     }
 
     .search-content {

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

@@ -37,9 +37,7 @@ export default class extends Page {
           key: row.value,
         };
       }),
-      defaultSortMap: {
-        collect_number: 'desc',
-      },
+      defaultSortMap: {},
     };
   }
 
@@ -128,10 +126,12 @@ export default class extends Page {
     let direction = keys.length ? value[keys[index]] : null;
     if (order == null) {
       const [prevOrder] = Object.keys(sortMap);
-      console.log(defaultSortMap[prevOrder]);
       if (!defaultSortMap[prevOrder]) {
-        [order] = Object.keys(defaultSortMap);
-        direction = defaultSortMap[order];
+        const sortKeys = Object.keys(defaultSortMap);
+        if (sortKeys.length > 0) {
+          [order] = sortKeys;
+          direction = defaultSortMap[order];
+        }
       }
     }
     this.search({ order, direction }, false);
@@ -291,6 +291,7 @@ export default class extends Page {
           </div>
           <Tabs
             border
+            force
             type="division"
             theme="theme"
             size="small"
@@ -298,7 +299,9 @@ export default class extends Page {
             width={220}
             active={subject}
             tabs={questionSubjectSelect}
-            onChange={key => this.onRefreshFilter({ subject: key, order: undefined, direction: undefined })}
+            onChange={key => {
+              this.onRefreshFilter({ subject: key, order: undefined, direction: undefined });
+            }}
           />
           <UserAction
             selectList={[

+ 1 - 0
front/project/www/stores/user.js

@@ -238,6 +238,7 @@ export default class UserStore extends BaseStore {
   loginWechat(code, login, auto) {
     return this.apiGet('/auth/wechat_pc', { code, login }).then(result => {
       this.infoHandle(result, auto);
+      console.log('loginWechat');
       return result;
     });
   }

+ 12 - 0
server/data/src/main/java/com/qxgmat/data/relation/UserQuestionRelationMapper.java

@@ -42,6 +42,18 @@ public interface UserQuestionRelationMapper {
             @Param("questionNoIds") Collection questionNoIds
     );
 
+    List<UserQuestion> listByPaper(
+            @Param("userId") Number userId,
+            @Param("paperOrigin") String paperOrigin,
+            @Param("originId") Integer originId
+    );
+
+    List<UserQuestion> listByPapers(
+            @Param("userId") Number userId,
+            @Param("paperOrigin") String paperOrigin,
+            @Param("originIds") Collection originIds
+    );
+
     List<UserRecordStatRelation> stat(
             @Param("userId") Integer userId,
             @Param("startTime") Date startTime,

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

@@ -123,7 +123,11 @@
         find_in_set(#{item}, qn.`module_struct`)
       </foreach>
     </if>
-    ORDER BY #{order} , qn.`module_struct` ASC, qn.`no` ASC
+    ORDER BY
+    <if test="order != null">
+    ${order} ,
+    </if>
+    qn.`module_struct` ASC, qn.`no` ASC
   </select>
 
   <!--

+ 33 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml

@@ -194,6 +194,39 @@
     group by uq.`question_no_id`
   </select>
 
+  <select id="listByPaper" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `user_question` uq
+    left join `user_report` ur on ur.`id`=uq.`report_id`
+    <if test="paperOrigin != null">
+      and ur.`paper_origin` = #{paperOrigin,jdbcType=VARCHAR}
+    </if>
+    <if test="originId != null">
+      and ur.`origin_id` = #{originId,jdbcType=Integer}
+    </if>
+    where
+    ur.`id` > 0 and uq.`user_id` = #{userId,jdbcType=VARCHAR} and uq.`user_time` > 0
+  </select>
+
+  <select id="listByPapers" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `user_question` uq
+    left join `user_report` ur on ur.`id`=uq.`report_id`
+    <if test="paperOrigin != null">
+      and ur.`paper_origin` = #{paperOrigin,jdbcType=VARCHAR}
+    </if>
+    <if test="originIds != null">
+      and ur.`origin_id` in
+      <foreach collection="originIds" item="item" index="index" open="(" close=")" separator=" or ">
+        #{item}
+      </foreach>
+    </if>
+    where
+    ur.`id` > 0 and uq.`user_id` = #{userId,jdbcType=VARCHAR} and uq.`user_time` > 0
+  </select>
+
   <!--
     用户做题记录统计
   -->

+ 13 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/admin/AuthController.java

@@ -2,10 +2,13 @@ package com.qxgmat.controller.admin;
 
 import com.nuliji.tools.Response;
 import com.nuliji.tools.ResponseHelp;
+import com.nuliji.tools.Tools;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.AuthException;
 import com.qxgmat.data.dao.entity.Manager;
+import com.qxgmat.data.dao.entity.User;
 import com.qxgmat.data.relation.entity.ManagerRelation;
+import com.qxgmat.dto.admin.request.EditPasswordDto;
 import com.qxgmat.dto.admin.request.LoginDto;
 import com.qxgmat.dto.admin.response.LoginUserDto;
 import com.qxgmat.help.CaptchaHelp;
@@ -79,6 +82,16 @@ public class AuthController {
         return ResponseHelp.success(true);
     }
 
+    @RequestMapping(value = "/editPassword", method = RequestMethod.POST)
+    @ApiOperation(value = "登出", httpMethod = "POST")
+    public Response<Boolean> editPassword(@RequestBody @Validated EditPasswordDto dto, HttpSession session, HttpServletRequest request) {
+        Manager manager = (Manager) shiroHelp.getLoginManager();
+        Manager entity = Transform.dtoToEntity(dto);
+        entity.setId(manager.getId());
+        managerService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
     @RequestMapping(value = "/refresh", method = RequestMethod.POST)
     @ApiOperation(value = "刷新", httpMethod = "POST")
     public Response<LoginUserDto> refresh(HttpSession session) {

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

@@ -199,6 +199,9 @@ public class UserController {
         time += questionFlowService.studyTime(id, null, null);
         dto.setTotalTime(time);
 
+        Collection<UserService> serviceList = userServiceService.getByUser(id);
+        dto.setServices(Transform.convert(serviceList, UserServiceExtendDto.class));
+
         return ResponseHelp.success(dto);
     }
 

+ 2 - 1
server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java

@@ -686,7 +686,8 @@ public class CourseController {
         List<UserOrderRecord> processList = userOrderRecordList.stream().filter(row-> row.getIsUsed() > 0 && row.getUseEndTime().after(now)).collect(Collectors.toList());
         Collection processIds = Transform.getIds(processList, UserOrderRecord.class, "id");
         Map<Object, Collection<UserPreviewPaperRelation>> previewMap = previewService.groupByRecordId(user.getId(), processIds, 2);
-        Transform.combine(dtos, previewMap, UserCourseProgressDto.class, "productId", "papers", UserPaperBaseExtendDto.class);
+        Map<Object, Collection<BasePaperExtendDto>> basePreviewMap = Transform.convert(previewMap, BasePaperExtendDto.class);
+        Transform.combine(dtos, basePreviewMap, UserCourseProgressDto.class, "id", "papers", UserPaperBaseExtendDto.class);
 
         // 绑定老师
         Collection teacherIds = Transform.getIds(userOrderRecordList, UserOrderRecord.class, "teacherId");

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

@@ -1781,7 +1781,8 @@ public class MyController {
 
         // 获取每个科目的所有作业
         Map<Object, Collection<UserPreviewPaperRelation>> previewMap = previewService.groupByRecordId(user.getId(), recordIds, 1000);
-        Transform.combine(pr, previewMap, UserCourseDetailDto.class, "id", "papers", BasePaperExtendDto.class);
+        Map<Object, Collection<BasePaperExtendDto>> basePreviewMap = Transform.convert(previewMap, BasePaperExtendDto.class);
+        Transform.combine(pr, basePreviewMap, UserCourseDetailDto.class, "id", "papers", BasePaperExtendDto.class);
         for(UserCourseDetailDto dto : pr){
             Collection<UserPreviewPaperRelation> list = previewMap.get(dto.getId());
             if (list == null || list.size() == 0) continue;

+ 28 - 12
server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java

@@ -131,7 +131,7 @@ public class QuestionController {
             @RequestParam(required = false) Integer[] structIds,
             @RequestParam(required = false) String place,
             @RequestParam(required = false) String difficult,
-            @RequestParam(required = false, defaultValue = "id") String order, // collect_number, correct, time, relation_score
+            @RequestParam(required = false) String order, // collect_number, correct, time, relation_score
 //            @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
         User user = (User) shiroHelp.getLoginUser();
@@ -249,14 +249,34 @@ public class QuestionController {
         List<UserExerciseGroupDto> p = new ArrayList<>(three.size());
         for(ExerciseStruct struct : three){
             UserExerciseGroupDto dto = Transform.convert(struct, UserExerciseGroupDto.class);
+
+            // 获取第四层节点
+            List<ExerciseStruct> children = exerciseStructService.children(struct.getId(), 0);
+            List<ExercisePaper> paperList = exercisePaperService.listByLogic(struct.getId(), null, ExerciseLogic.NO, null);
+            Collection paperIds = Transform.getIds(paperList, ExercisePaper.class, "id");
+
             // 获取第三层所有题目,并获取题目统计
-            List<QuestionNo> list = questionNoService.listByStruct(StructModule.EXERCISE, struct.getId());
+            Collection questionNoIds = new ArrayList();
+            for (ExercisePaper paper : paperList){
+                Collections.addAll(questionNoIds, paper.getQuestionNoIds());
+            }
+            List<QuestionNo> list = questionNoService.select(questionNoIds);
+//            if (children == null || children.size() == 0) {
+//                list = questionNoService.listByStruct(StructModule.EXERCISE, struct.getId());
+//            }else{
+//                Integer[] childrenIds = new Integer[children.size()];
+//                int index = 0;
+//                for(ExerciseStruct childrenStruct : children){
+//                    childrenIds[index] = childrenStruct.getId();
+//                    index++;
+//                }
+//                list = questionNoService.listByStruct(StructModule.EXERCISE, childrenIds);
+//            }
             dto.setStat(questionNoService.statPaper(list));
             dto.setQuestionNumber(list.size());
             Map<Object, UserQuestionStat> userQuestionStatMap = null;
             if(user != null){
-                Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
-                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
+                List<UserQuestion> userQuestionList = userQuestionService.listByPaper(user.getId(), PaperOrigin.EXERCISE, paperIds);
                 userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);
 
                 dto.setUserStat(userQuestionService.statQuestion(userQuestionList));
@@ -280,11 +300,8 @@ public class QuestionController {
             }
 
             // 作文、阅读没有第4层
-            // 获取第四层节点
-            List<ExerciseStruct> children = exerciseStructService.children(struct.getId(), 0);
             if (children == null || children.size() == 0){
                 // 以下属的paper作为children
-                List<ExercisePaper> paperList = exercisePaperService.listByLogic(struct.getId(), 0, ExerciseLogic.NO, null);
                 List<UserExerciseGroupExtendDto> childrenDtos = new ArrayList<>(paperList.size());
 
                 Integer start = 0;
@@ -314,7 +331,7 @@ public class QuestionController {
                         if (!flag) minTimes = 0;
                         for(int questionNoId : child.getQuestionNoIds()){
                             UserQuestionStat stat = userQuestionStatMap.get(questionNoId);
-                            if (stat != null && stat.getUserNumber() > minTimes)  userQuestionNumber += 1;
+                            if (stat != null && stat.getUserNumber() >= minTimes)  userQuestionNumber += 1;
                         }
                         extendDto.setUserNumber(userQuestionNumber);
                         extendDto.setMinTimes(minTimes);
@@ -324,15 +341,14 @@ public class QuestionController {
                 }
 
                 if (user != null){
-                    Collection ids = Transform.getIds(paperList, ExercisePaper.class, "id");
-                    List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXERCISE, ids, null);
+                    List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXERCISE, paperIds, null);
                     // 绑定userPaperId,用于关联report
                     Map userPaperMap = Transform.getMap(userPaperList, UserPaper.class, "originId", "id");
                     Transform.combine(childrenDtos, userPaperMap, UserExerciseGroupExtendDto.class, "id", "userPaperId");
 
                     // 获取最后一次作业结果
-                    Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
-                    List<UserReport> reportList = userReportService.listWithLast(paperIds);
+                    Collection userPaperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
+                    List<UserReport> reportList = userReportService.listWithLast(userPaperIds);
                     Transform.combine(childrenDtos, reportList, UserExerciseGroupExtendDto.class, "userPaperId", "report", UserReport.class, "paperId", UserReportExtendDto.class);
                 }
                 dto.setChildren(childrenDtos);

+ 4 - 3
server/gateway-api/src/main/java/com/qxgmat/dto/admin/extend/UserExtendDto.java

@@ -3,6 +3,7 @@ package com.qxgmat.dto.admin.extend;
 import com.nuliji.tools.annotation.Dto;
 import com.qxgmat.data.dao.entity.User;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 @Dto(entity = User.class)
@@ -15,7 +16,7 @@ public class UserExtendDto {
 
     private String email;
 
-    private Integer totalMoney;
+    private BigDecimal totalMoney;
 
     private Integer totalAlert;
 
@@ -51,11 +52,11 @@ public class UserExtendDto {
         this.mobile = mobile;
     }
 
-    public Integer getTotalMoney() {
+    public BigDecimal getTotalMoney() {
         return totalMoney;
     }
 
-    public void setTotalMoney(Integer totalMoney) {
+    public void setTotalMoney(BigDecimal totalMoney) {
         this.totalMoney = totalMoney;
     }
 

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

@@ -0,0 +1,21 @@
+package com.qxgmat.dto.admin.request;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.Manager;
+
+import javax.validation.constraints.NotEmpty;
+
+@Dto(entity = Manager.class)
+public class EditPasswordDto {
+
+    @NotEmpty(message = "密码不能为空!")
+    private String password;
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

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

@@ -15,6 +15,10 @@ public class MessageTemplateDto {
 
     private String link;
 
+    private String messageMethod;
+
+    private String  messageCategory;
+
     private Date sendTime;
 
     private Integer sendStatus;
@@ -66,4 +70,20 @@ public class MessageTemplateDto {
     public void setSendStatus(Integer sendStatus) {
         this.sendStatus = sendStatus;
     }
+
+    public String getMessageMethod() {
+        return messageMethod;
+    }
+
+    public void setMessageMethod(String messageMethod) {
+        this.messageMethod = messageMethod;
+    }
+
+    public String getMessageCategory() {
+        return messageCategory;
+    }
+
+    public void setMessageCategory(String messageCategory) {
+        this.messageCategory = messageCategory;
+    }
 }

+ 10 - 4
server/gateway-api/src/main/java/com/qxgmat/service/ManagerService.java

@@ -58,11 +58,17 @@ public class ManagerService extends AbstractService {
         if(in == null){
             throw new ParameterException("用户不存在");
         }
-        in = getByUserName(manager.getUsername());
-        if (in != null && !in.getId().equals(manager.getId())){
-            throw new ParameterException("用户名已使用");
+        if (manager.getUsername() != null){
+            in = getByUserName(manager.getUsername());
+            if (in != null && !in.getId().equals(manager.getId())){
+                throw new ParameterException("用户名已使用");
+            }
+        }
+
+        if(manager.getPassword() != null){
+            logger.info("{},{}", manager.getPassword(), Tools.stringMD5(Tools.stringMD5(manager.getPassword())));
+            manager.setPassword(Tools.stringMD5(Tools.stringMD5(manager.getPassword())));
         }
-        if(manager.getPassword() != null) manager.setPassword(Tools.stringMD5(Tools.stringMD5(manager.getPassword())));
         int result = update(managerMapper, manager);
         return manager;
     }

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

@@ -9,6 +9,7 @@ import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
 import com.qxgmat.data.constants.enums.module.PaperModule;
+import com.qxgmat.data.constants.enums.module.PaperOrigin;
 import com.qxgmat.data.constants.enums.module.QuestionModule;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.dao.UserQuestionMapper;
@@ -95,8 +96,8 @@ public class UserQuestionService extends AbstractService {
         if(questionIds == null || questionIds.size() == 0) return new ArrayList<>();
         List<UserQuestion> list = userQuestionRelationMapper.listLast(userId, questionIds);
 
-        Collection reportIds = Transform.getIds(list, UserQuestion.class, "id");
-        Transform.replace(list, select(reportIds), UserQuestion.class, "id");
+        Collection userQuestionIds = Transform.getIds(list, UserQuestion.class, "id");
+        Transform.replace(list, select(userQuestionIds), UserQuestion.class, "id");
         return list;
     }
 
@@ -310,6 +311,38 @@ public class UserQuestionService extends AbstractService {
     }
 
     /**
+     * 获取报告的所有做题记录
+     * @param userId
+     * @param origin
+     * @param originId
+     * @return
+     */
+    public List<UserQuestion> listByPaper(Integer userId, PaperOrigin origin, Integer originId){
+        if(originId == null || originId == 0) return new ArrayList<>();
+        List<UserQuestion> list = userQuestionRelationMapper.listByPaper(userId, origin.key, originId);
+
+        Collection userQuestionIds = Transform.getIds(list, UserQuestion.class, "id");
+        Transform.replace(list, select(userQuestionIds), UserQuestion.class, "id");
+        return list;
+    }
+
+    /**
+     * 获取报告的所有做题记录
+     * @param userId
+     * @param origin
+     * @param originIds
+     * @return
+     */
+    public List<UserQuestion> listByPaper(Integer userId, PaperOrigin origin, Collection originIds){
+        if(originIds == null || originIds.size() == 0) return new ArrayList<>();
+        List<UserQuestion> list = userQuestionRelationMapper.listByPapers(userId, origin.key, originIds);
+
+        Collection userQuestionIds = Transform.getIds(list, UserQuestion.class, "id");
+        Transform.replace(list, select(userQuestionIds), UserQuestion.class, "id");
+        return list;
+    }
+
+    /**
      * 获取报告固定题目类型做题记录
      * @return
      */

+ 19 - 0
server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java

@@ -79,6 +79,9 @@ public class ExaminationService extends AbstractService {
     // 出RC阅读题的题号
     final public Integer[] verbalRCPosition = new Integer[]{5, 15, 24, 33};
     final public Integer[] verbalRCNumber = new Integer[]{3,3,3,4};
+    final public Integer[] verbalBaseLevel = new Integer[]{9,9,9,9};
+    // 阶段的最大题目数
+    final public Integer[] verbalNumber = new Integer[]{0, 9, 18, 27, 36};
 
     /**
      * quant考试相关设置
@@ -412,6 +415,22 @@ public class ExaminationService extends AbstractService {
      * @param number
      * @return
      */
+    public Integer verbalStep(Integer number) {
+        Integer step = 0;
+        Integer all = 0;
+        for(Integer n : verbalBaseLevel){
+            all += n;
+            if (all >= number) return step;
+            step += 1;
+        }
+        return step;
+    }
+
+    /**
+     * 通过当前完成的题目数,判断当前阶段
+     * @param number
+     * @return
+     */
     public Integer quantStep(Integer number) {
         Integer step = 0;
         Integer all = 0;

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

@@ -441,11 +441,11 @@ public class OrderFlowService {
             JSONObject vsVsNumber = toolsService.getVsVsNumber(vsNumber);
 
             JSONArray gift = order.getGift();
-            if (askInfo!=null && askInfo.getIntValue("money") > 0){
+            if (askInfo!=null && askInfo.getBigDecimal("money").compareTo(new BigDecimal(0))>0){
                 // 因为存在默认最低值,金额超过限额,才写入赠送
                 JSONObject info = new JSONObject();
                 info.put("key", "ask");
-                info.put("money", askInfo.getIntValue("money"));
+                info.put("money", askInfo.getBigDecimal("money"));
                 info.put("hour", askInfo.getIntValue("hour"));
                 info.put("message", toolsService.askTimeMessage().replace("{}", askInfo.getInteger("hour").toString()));
                 gift.add(info);
@@ -463,7 +463,7 @@ public class OrderFlowService {
                 JSONObject info = new JSONObject();
                 info.put("key", "vs");
                 info.put("from", "videoMoney");
-                info.put("money", vsVideoMoney.getIntValue("money"));
+                info.put("money", vsVideoMoney.getBigDecimal("money"));
                 info.put("value", vsVideoMoney.getIntValue("value"));
                 info.put("message", toolsService.vsVideoMoneyMessage().replace("{}", vsVideoMoney.getInteger("value").toString()));
                 gift.add(info);

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

@@ -986,14 +986,15 @@ public class QuestionFlowService {
         }
         JSONArray steps = verbal.getJSONArray("steps");
         // 判断当前是第几阶段
-        Integer step = (subnumber+1) / examinationService.verbalPre;
+        Integer step = examinationService.verbalStep(subnumber);
+        Integer nextStep = examinationService.verbalStep(subnumber+1);
         Integer questionIndex = 0;
         JSONObject info;
         if (subnumber == 0){
             // 初始化
             info = examinationService.initVerbal(paper.getStructThree());
             steps.add(info);
-        }else if(subnumber % examinationService.verbalPre == 0){
+        }else if(step < nextStep){
             info = steps.getJSONObject(step);
             JSONArray currentIds = info.getJSONArray("ids");
             for(int i = 0; i<currentIds.size(); i++){
@@ -1008,7 +1009,7 @@ public class QuestionFlowService {
             steps.add(info);
         }else{
             info = steps.getJSONObject(step);
-            questionIndex = subnumber % examinationService.verbalPre;
+            questionIndex = subnumber - examinationService.quantNumber[step];
         }
         //获取下一题
         questionNoIds.clear();
@@ -1382,7 +1383,7 @@ public class QuestionFlowService {
                             if (singleResult.size()<=k || singleResult.get(k) == null){
                                 singleResult.set(k, 0);
                             }
-                            if (userSingleList.getBoolean(k)){
+                            if (userSingleList.getBoolean(k) !=null && userSingleList.getBoolean(k)){
                                 singleResult.set(k, singleResult.getIntValue(k)+1);
                             }
                         }
@@ -1402,7 +1403,7 @@ public class QuestionFlowService {
                         if (singleResult.size()<=j || singleResult.get(j) == null){
                             singleResult.set(j, 0);
                         }
-                        if (userSingleList.getBoolean(j)){
+                        if (userSingleList.getBoolean(j) !=null && userSingleList.getBoolean(j)){
                             singleResult.set(j, singleResult.getIntValue(j)+1);
                         }
                     }

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

@@ -489,13 +489,13 @@ public class ToolsService {
 
         JSONArray settings = value.getJSONArray("ask_time");
         if (settings == null) return null;
-        int max = 0;
+        BigDecimal max = new BigDecimal(0);
         int maxIndex = -1;
         for(int i = 0; i < settings.size(); i++){
             JSONObject o = settings.getJSONObject(i);
-            int money = o.getIntValue("money");
-            if(money <= totalMoney.intValue()){
-                if (money > max){
+            BigDecimal money = o.getBigDecimal("money");
+            if(money.compareTo(totalMoney)<0){
+                if (money.compareTo(max)>0){
                     max = money;
                     maxIndex = i;
                 }
@@ -519,13 +519,13 @@ public class ToolsService {
 
         JSONArray settings = value.getJSONArray("vs_video_money");
         if (settings == null) return null;
-        int max = 0;
+        BigDecimal max = new BigDecimal(0);
         int maxIndex = -1;
         for(int i = 0; i < settings.size(); i++){
             JSONObject o = settings.getJSONObject(i);
-            int money = o.getIntValue("money");
-            if(money <= totalMoney.intValue()){
-                if (money > max){
+            BigDecimal money = o.getBigDecimal("money");
+            if(money.compareTo(totalMoney)<0){
+                if (money.compareTo(max)>0){
                     max = money;
                     maxIndex = i;
                 }

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

@@ -58,10 +58,15 @@ public class ExercisePaperService extends AbstractService {
         example.and(
                 example.createCriteria()
                         .andEqualTo("structThree", three)
-                        .andEqualTo("structFour", four)
                         .andEqualTo("logic", logic.key)
                         .andEqualTo("status", 1)
         );
+        if (four != null){
+            example.and(
+                    example.createCriteria()
+                    .andEqualTo("four", four)
+            );
+        }
         if (extend != null){
             example.and(
                     example.createCriteria()

+ 8 - 0
server/tools/src/main/java/com/nuliji/tools/Transform.java

@@ -119,6 +119,14 @@ public class Transform {
         return al;
     }
 
+    public static <S, T> Map<Object, Collection<T>> convert(Map<Object, Collection<S>> source, Class<T> clazz) {
+        Map<Object, Collection<T>> map = new HashMap<>();
+        for (Map.Entry<Object,Collection<S>> entry : source.entrySet()){
+            map.put(entry.getKey(), convert(entry.getValue(), clazz));
+        }
+        return map;
+    }
+
     public static <T> void replace(List<T> targetList, Collection<T> objectList, Class<?> objectClass, String indexProp) throws RuntimeException {
         if(objectList.size() == 0 || targetList.size() == 0) return ;
         Map<Object, T> objectMap = new HashMap<>(objectList.size());