Browse Source

fix(al): 修正bug

Go 4 years ago
parent
commit
f2a98fa1de
55 changed files with 480 additions and 138 deletions
  1. 1 0
      front/project/admin/routes/course/package/page.js
  2. 3 3
      front/project/admin/routes/show/comment/page.js
  3. 12 0
      front/project/h5/routes/page/bind/page.js
  4. 1 1
      front/project/h5/routes/page/id/page.js
  5. 4 3
      front/project/www/app.js
  6. 1 1
      front/project/www/components/AnswerList/index.js
  7. 1 1
      front/project/www/components/Header/index.js
  8. 4 4
      front/project/www/components/Invite/index.js
  9. 3 0
      front/project/www/components/Login/index.js
  10. 2 2
      front/project/www/components/Other/index.js
  11. 2 2
      front/project/www/components/Other/index.less
  12. 7 4
      front/project/www/components/OtherModal/index.js
  13. 1 1
      front/project/www/components/ProgressText/index.less
  14. 1 0
      front/project/www/components/UserTable/index.js
  15. 1 1
      front/project/www/routes/course/detail/page.js
  16. 5 5
      front/project/www/routes/course/vs/page.js
  17. 15 5
      front/project/www/routes/my/collect/page.js
  18. 2 2
      front/project/www/routes/my/course/index.less
  19. 24 7
      front/project/www/routes/my/error/page.js
  20. 3 3
      front/project/www/routes/my/index.js
  21. 3 4
      front/project/www/routes/my/main/page.js
  22. 6 2
      front/project/www/routes/my/message/page.js
  23. 14 4
      front/project/www/routes/my/note/page.js
  24. 15 5
      front/project/www/routes/my/report/page.js
  25. 1 1
      front/project/www/routes/page/export/page.js
  26. 2 1
      front/project/www/routes/question/search/page.js
  27. 17 0
      front/project/www/stores/user.js
  28. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/UserDataSubscribeMapper.java
  29. 160 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserDataSubscribe.java
  30. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserQuestion.java
  31. 19 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserDataSubscribeMapper.xml
  32. 2 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserQuestionMapper.xml
  33. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserAskQuestionRelationMapper.xml
  34. 8 8
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCollectQuestionRelationMapper.xml
  35. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserNoteQuestionRelationMapper.xml
  36. 1 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml
  37. 8 8
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserPaperRelationMapper.xml
  38. 8 6
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml
  39. 1 0
      server/data/src/main/resources/db/migration/V11__update_user_question.sql
  40. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/CourseController.java
  41. 2 1
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/TextbookController.java
  42. 5 5
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  43. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  44. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/TextbookController.java
  45. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserReportBaseDto.java
  46. 8 9
      server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java
  47. 4 1
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java
  48. 26 20
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java
  49. 4 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/CourseNoService.java
  50. 7 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/QuestionService.java
  51. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookPaperService.java
  52. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookQuestionService.java
  53. 2 2
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderRecordService.java
  54. 2 2
      server/gateway-api/src/main/java/com/qxgmat/util/shiro/OauthRealm.java
  55. 2 2
      server/gateway-api/src/main/java/com/qxgmat/util/shiro/UserRealm.java

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

@@ -43,6 +43,7 @@ export default class extends Page {
       key: 'price',
       type: 'number',
       name: '套餐价格',
+      min: 0,
     }, {
       key: 'courseIds',
       type: 'multiple',

+ 3 - 3
front/project/admin/routes/show/comment/page.js

@@ -191,7 +191,7 @@ export default class extends Page {
 
   initData() {
     if (!this.state.search.order) {
-      this.state.search.order = 'sort';
+      this.state.search.order = 'updateTime';
       this.state.search.direction = 'desc';
     }
     System.listComment(Object.assign({ isSpecial: true }, this.state.search)).then(result => {
@@ -209,8 +209,8 @@ export default class extends Page {
       this.setState({ mode: 'order', search });
     } else {
       search.size = 20;
-      search.order = null;
-      search.direction = null;
+      search.order = 'updateTime';
+      search.direction = 'desc';
       this.setState({ mode: null, search });
     }
     this.initData();

+ 12 - 0
front/project/h5/routes/page/bind/page.js

@@ -7,11 +7,13 @@ import Input, { SelectInput, VerificationInput } from '../../../components/Input
 import Button from '../../../components/Button';
 import { User } from '../../../stores/user';
 import { Common } from '../../../stores/common';
+import { checkEmail, checkMobile } from '../../../../../src/services/Tools';
 
 export default class extends Page {
   initState() {
     return {
       data: { mobile: '', area: MobileArea[0].value, email: '' },
+      empty: { mobile: '' },
     };
   }
 
@@ -33,6 +35,11 @@ export default class extends Page {
     const { data } = this.state;
     const { area, mobile } = data;
     if (!area || !mobile) return;
+    if (!checkMobile(mobile)) {
+      this.setState({ emailError: '请输入正确的手机号' });
+      return;
+    }
+    this.setState({ emailError: null });
     this.validMobileNumber += 1;
     const number = this.validMobileNumber;
     User.validWechat(area, mobile)
@@ -56,6 +63,11 @@ export default class extends Page {
     const { data } = this.state;
     const { email } = data;
     if (!email) return;
+    if (!checkEmail(email)) {
+      this.setState({ emailError: '请输入正确的邮箱' });
+      return;
+    }
+    this.setState({ emailError: null });
     this.validEmailNumber += 1;
     const number = this.validEmailNumber;
     User.validEmail(email)

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

@@ -7,7 +7,7 @@ export default class extends Page {
   init() {
     const { id } = this.params;
     User.originInviteCode(id);
-    replaceLink('/bind');
+    replaceLink('/login');
   }
 
   renderView() {

+ 4 - 3
front/project/www/app.js

@@ -4,7 +4,7 @@ import zhCN from 'antd/lib/locale-provider/zh_CN';
 import './app.less';
 import Header from './components/Header';
 import Login from './components/Login';
-import './components/OtherModal';
+import { InviteModal } from './components/OtherModal';
 import './components/Other';
 import './components/VipRenew';
 import './components/UserAction';
@@ -28,14 +28,15 @@ export default class extends Component {
             {children}
             <Login {...this.props} />
             <PayModal {...this.props} />
+            <InviteModal {...this.props} />
           </div>
         ) : (<div className={`${config.tab || ''}`} id="page">
           <Header tabs={project.tabs} active={config.tab} {...this.props} />
           {children}
           <Login {...this.props} />
           <PayModal {...this.props} />
-        </div>
-        )}
+          <InviteModal {...this.props} />
+        </div>)}
       </LocaleProvider>
     );
   }

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

@@ -36,7 +36,7 @@ export default class AnswerList extends Component {
             <div className={`item ${getKey(type, index, selected, answer)} ${show ? 'show' : ''}`}>
               <div className="icon" />
               <div className="text">{item}</div>
-              {show && <div className="total">{formatPercent(distributed[index], count, false)}用户选择该选项</div>}
+              {show && <div className="total">{formatPercent(distributed[type][index], count, false)}用户选择该选项</div>}
             </div>
           );
         })}

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

@@ -58,7 +58,7 @@ function Header(props) {
                     </div>
                     {info.textbook && <div className="t-s-12 nowrap">最近更新:{formatDate(info.textbook, 'YYYY-MM-DD')}</div>}
                   </div>
-                  <div className={`item ${inviteStatus ? 'more-message' : ''}`}>
+                  <div className={`item ${inviteStatus ? 'more-message' : ''}`} onClick={() => User.needInvite()}>
                     <div className="t-1">
                       <Assets name={inviteStatus ? 'yaoqing_more' : 'yaoqing'} svg />
                       邀请好友

+ 4 - 4
front/project/www/components/Invite/index.js

@@ -13,14 +13,15 @@ import { PcUrl, H5Url } from '../../../Constant';
 export default class Invite extends Component {
   constructor(props) {
     super(props);
-    this.state = { data: this.props.data || {}, qr: Main.qrCode(`${H5Url}/id/${(this.props.data || {}).inviteCode}`) };
+    const data = Object.assign({}, this.props.user.info);
+    this.state = { data, qr: Main.qrCode(`${H5Url}/id/${data.inviteCode}`) };
   }
 
   changeData(field, value) {
     let { data } = this.state;
     data = data || {};
     data[field] = value;
-    this.setState({ data, error: null });
+    this.setState({ data, error: null, success: false });
   }
 
   send() {
@@ -34,8 +35,7 @@ export default class Invite extends Component {
   }
 
   render() {
-    const { data } = this.props;
-    const { success, qr } = this.state;
+    const { success, qr, data } = this.state;
     return (
       <div className="invite-block">
         <div className="title-block">

+ 3 - 0
front/project/www/components/Login/index.js

@@ -34,6 +34,7 @@ export default class Login extends Component {
           if (!this.needScan) {
             return;
           }
+          console.log(this.needScan, 0);
           this.needScan = false;
           const code = event.data.split(':')[1];
           if (this.state.type === LOGIN_WX) {
@@ -130,6 +131,7 @@ export default class Login extends Component {
         }
       })
       .catch(err => {
+        console.log('scanLoginErr');
         this.needScan = true;
         if (err.message !== '登录失败') {
           asyncSMessage(err.message, 'error');
@@ -144,6 +146,7 @@ export default class Login extends Component {
         this.close();
       })
       .catch(() => {
+        console.log('scanBindErr');
         this.needScan = true;
         this.changeType(BIND_WX_ERROR);
       });

+ 2 - 2
front/project/www/components/Other/index.js

@@ -44,7 +44,7 @@ export class CommentFalls extends Component {
                 <div className="item-header">
                   <Assets className="avatar" src={row.user ? row.user.avatar : row.avatar} />
                   <div className="name">{row.user ? row.user.nickname : row.nickname}</div>
-                  <div className="date">{formatDate(row.updateTime, 'YYYY年MM月DD日')}</div>
+                  <div className="date">{formatDate(row.updateTime || row.createTime, 'YYYY年MM月DD日')}</div>
                 </div>
                 <div className="item-body">{row.content}</div>
               </div>
@@ -99,7 +99,7 @@ export class AnswerCarousel extends Component {
               {list.map(item => {
                 return (
                   <div className="item">
-                    <div className="item-block">
+                    <div className="item-block item-title">
                       <Assets name="question" />
                       {item.content}
                     </div>

+ 2 - 2
front/project/www/components/Other/index.less

@@ -168,7 +168,7 @@
 
   .item {
     width: 354px;
-    height: 155px;
+    min-height: 155px;
     background: rgba(255, 255, 255, 1);
     box-shadow: 0px 5px 10px 0px rgba(156, 183, 223, 0.14), 0px 15px 28px 0px rgba(0, 0, 0, 0.01);
     border-radius: 4px;
@@ -178,7 +178,7 @@
 
     .item-block {
       position: relative;
-      height: 40px;
+      min-height: 40px;
       text-align: left;
       font-size: 12px;
       line-height: 16px;

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

@@ -666,16 +666,19 @@ export class EditAvatar extends Component {
 export class InviteModal extends Component {
   constructor(props) {
     super(props);
-    this.state = { data: {} };
+    this.state = {};
   }
 
   render() {
-    const { show, onClose, data } = this.props;
+    const { needInvite } = this.props.user;
+    if (!needInvite) return [];
     return (
-      <Modal className="invite-modal" show={show} width={630} title="邀请好友" onClose={onClose}>
+      <Modal className="invite-modal" show={needInvite} width={630} title="邀请好友" onClose={() => {
+        User.closeInvite();
+      }}>
         <div className="invite-modal-wrapper">
           <div className="tip">每邀请一位小伙伴加入“千行GMAT”, 您的VIP权限会延长7天,可累加 无上限!赶紧行动吧~</div>
-          <Invite data={data} />
+          <Invite user={this.props.user} />
         </div>
       </Modal>
     );

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

@@ -10,7 +10,7 @@
   }
 
   .t {
-    margin-left: 10px;
+    margin-left: 5px;
     min-width: 40px;
     text-align: right;
   }

+ 1 - 0
front/project/www/components/UserTable/index.js

@@ -53,6 +53,7 @@ export default class UserTable extends Component {
                     <th
                       className={`${item.className || ''} ${i === 0 && select ? 'check' : ''}`}
                       width={item.width}
+                      style={item.style}
                       align={item.align}
                     >
                       {item.title}

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

@@ -519,7 +519,7 @@ export default class extends Page {
               <div className="tab-body">{this[`renderRightTab${rightTab}`]()}</div>
             </div>
           </div>
-          {data.have && <UserTable columns={this.columns} list={courseNos} />}
+          {data.have && <UserTable columns={this.columns} data={courseNos} />}
         </div>
         <div hidden={data.have} className="bottom">
           <div className="content">{this.renderTab()}</div>

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

@@ -97,7 +97,7 @@ export default class extends Page {
 
   buy() {
     const { data } = this.state;
-    Order.speedPay({ productType: 'course', productId: data.id }).then(result => {
+    Order.speedPay({ productType: 'course', productId: data.id, number: 1 }).then(result => {
       User.needPay(result).then(() => {
         linkTo('/my/course');
       });
@@ -106,7 +106,7 @@ export default class extends Page {
 
   add() {
     const { data } = this.state;
-    Order.addCheckout({ productType: 'course', productId: data.id }).then(() => {
+    Order.addCheckout({ productType: 'course', productId: data.id, number: 1 }).then(() => {
       this.setState({ add: true });
     });
   }
@@ -161,7 +161,7 @@ export default class extends Page {
               const course = this.courseVsMap[t.value] || {};
               return (
                 <div className={`item ${key === t.value ? 'active' : ''}`} onClick={() => this.onChangeItem(t.value)}>
-                  <Assets src={t.cover} />
+                  <Assets name={t.cover} />
                   <div className="t-1 t-s-20 f-w-b">{course.title}</div>
                   <div className="t-2">{course.comment}</div>
                 </div>
@@ -170,7 +170,7 @@ export default class extends Page {
           </div>
           <div className="item-detail">
             <div className="left">
-              <Assets name={data.cover} />
+              <Assets src={data.cover} />
             </div>
             <div className="right">
               <div className="t-1 t-s-16 m-b-2" dangerouslySetInnerHTML={{ __html: data.serviceContent }} />
@@ -230,7 +230,7 @@ export default class extends Page {
                   立即购买
                 </Button>}
                 {!data.have && <Button theme="default" disabled={this.state.add || data.add} radius size="lager" onClick={() => this.add()}>
-                  <Assets name="add" />
+                  <Assets name={data.add || this.state.add ? 'add_disabled' : 'add'} />
                 </Button>}
               </div>
             </div>

+ 15 - 5
front/project/www/routes/my/collect/page.js

@@ -43,13 +43,14 @@ export default class extends Page {
     return {
       tab: 'question',
       module: 'exercise',
-      timerange: 'today',
+      timerange: 'all',
       filterMap: {},
       sortMap: {},
       list: [],
       selectList: [],
       allChecked: false,
       showDetail: false,
+      defaultSortMap: { collect_time: 'desc' },
     };
   }
 
@@ -135,7 +136,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = { collect_time: 'desc' };
+      data.sortMap = this.state.defaultSortMap;
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -238,10 +239,19 @@ export default class extends Page {
 
   onSort(value) {
     const keys = Object.keys(value);
-    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
-    const { sortMap } = this.state;
+    const { sortMap, defaultSortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
-    this.search({ order: keys.length && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    let order = keys.length && value[keys[index]] ? keys[index] : null;
+    let direction = keys.length ? value[keys[index]] : null;
+    if (order == null) {
+      const [prevOrder] = Object.keys(sortMap);
+      console.log(defaultSortMap[prevOrder]);
+      if (!defaultSortMap[prevOrder]) {
+        [order] = Object.keys(defaultSortMap);
+        direction = defaultSortMap[order];
+      }
+    }
+    this.search({ order, direction }, false);
     this.initData();
   }
 

+ 2 - 2
front/project/www/routes/my/course/index.less

@@ -138,12 +138,12 @@
         .right {
           flex: 1;
           padding-top: 30px;
-          padding-left: 50px;
+          padding-left: 20px;
 
           .item {
             text-align: center;
             display: inline-block;
-            padding: 0 25px;
+            width: 100px;
 
             .assets {}
 

+ 24 - 7
front/project/www/routes/my/error/page.js

@@ -47,6 +47,7 @@ export default class extends Page {
       data: [{}, {}],
       selectList: [],
       allChecked: false,
+      defaultSortMap: { latest_time: 'desc' },
     };
   }
 
@@ -56,6 +57,7 @@ export default class extends Page {
         key: 'question_type',
         title: '题型',
         width: 115,
+        style: { whiteSpace: 'nowrap' },
         render: (text, record) => {
           return QuestionTypeMap[record.questionType];
         },
@@ -66,6 +68,7 @@ export default class extends Page {
         title: '题目ID',
         width: 100,
         fixSort: true,
+        style: { whiteSpace: 'nowrap' },
         render: (text, record) => {
           return <a href={`/paper/question/${record.id}`} target="_blank">{text}</a>;
         },
@@ -75,7 +78,7 @@ export default class extends Page {
         title: '内容',
         width: 185,
         render: (text) => {
-          return <div style={{ maxHeight: '80px', overflow: 'hidden' }}>{text}</div>;
+          return <div style={{ maxHeight: '80px', overflow: 'hidden' }}>{(text || '').replace(/&nbsp;/g, '')}</div>;
         },
       },
       {
@@ -83,6 +86,7 @@ export default class extends Page {
         title: '耗时',
         width: 100,
         sort: true,
+        style: { whiteSpace: 'nowrap' },
         render: (text, record) => {
           const user = Math.floor(record.stat ? record.stat.userTime / record.stat.userNumber : 0);
           const all = Math.floor(record.questionNo.totalNumber ? record.questionNo.totalTime / record.questionNo.totalNumber : 0);
@@ -97,6 +101,7 @@ export default class extends Page {
         title: '错误率',
         width: 95,
         sort: true,
+        style: { whiteSpace: 'nowrap' },
         render: (text, record) => {
           return <div className="sub">
             <div className="t-2 t-s-12">{record.stat ? formatPercent(record.stat.userNumber - record.stat.userCorrect, record.stat.userNumber, false) : '-'}</div>
@@ -109,6 +114,7 @@ export default class extends Page {
         title: '最近做题',
         width: 105,
         sort: true,
+        style: { whiteSpace: 'nowrap' },
         render: (text) => {
           return <div className="sub">
             <div className="t-2 t-s-12">{text.split(' ')[0]}</div>
@@ -119,8 +125,10 @@ export default class extends Page {
       {
         key: '',
         title: '',
+        width: 70,
+        style: { whiteSpace: 'nowrap' },
         render: (text, record, index) => {
-          return <div><GIcon name="star" active={record.collect} className="m-r-5" onClick={() => this.toggleCollect(index)} /><GIcon name="note" active={record.note} onClick={() => this.note(index)} /></div>;
+          return <div style={{ whiteSpace: 'nowrap' }}><GIcon name="star" active={record.collect} className="m-r-5" onClick={() => this.toggleCollect(index)} /><GIcon name="note" active={record.note} onClick={() => this.note(index)} /></div>;
         },
       },
     ];
@@ -133,7 +141,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = { latest_time: 'desc' };
+      data.sortMap = this.state.defaultSortMap;
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -148,7 +156,7 @@ export default class extends Page {
     const [startTime, endTime] = timeRange(data.timerange);
     refreshQuestionType(this, data.subject, data.questionType, {
       all: true,
-      needSentence: true,
+      needSentence: data.tab !== 'examination',
       allSubject: true,
     }).then(({ questionTypes, selectSentence }) => {
       return refreshStruct(this, questionTypes, data.tab, data.one, data.two, {
@@ -245,10 +253,19 @@ export default class extends Page {
 
   onSort(value) {
     const keys = Object.keys(value);
-    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
-    const { sortMap } = this.state;
+    const { sortMap, defaultSortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
-    this.search({ order: keys.length && value[keys[index]] && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    let order = keys.length && value[keys[index]] ? keys[index] : null;
+    let direction = keys.length ? value[keys[index]] : null;
+    if (order == null) {
+      const [prevOrder] = Object.keys(sortMap);
+      console.log(defaultSortMap[prevOrder]);
+      if (!defaultSortMap[prevOrder]) {
+        [order] = Object.keys(defaultSortMap);
+        direction = defaultSortMap[order];
+      }
+    }
+    this.search({ order, direction }, false);
     this.initData();
   }
 

+ 3 - 3
front/project/www/routes/my/index.js

@@ -171,7 +171,7 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
             twoSelectPlaceholder = null;
           } else if (twoSelectMap[one].length === 0 || !twoSelectMap[one]) {
             twoSelectMap[one] = [{
-              title: '',
+              title: '全部',
               key: '',
             }];
           }
@@ -279,7 +279,7 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
             twoSelectPlaceholder = null;
           } else if (twoSelectMap[one].length === 0 || !twoSelectMap[one]) {
             twoSelectMap[one] = [{
-              title: '',
+              title: '全部',
               key: '',
             }];
           }
@@ -296,7 +296,7 @@ export function refreshStruct(compontent, questionTypes, module, one, two, { all
           oneSelectPlaceholder = null;
           twoSelectPlaceholder = null;
         }
-        compontent.setState({ oneSelect, twoSelectMap });
+        compontent.setState({ oneSelect, twoSelectMap, oneSelectPlaceholder, twoSelectPlaceholder });
         return {
           structIds,
           latest,

+ 3 - 4
front/project/www/routes/my/main/page.js

@@ -15,7 +15,7 @@ import { Icon as GIcon } from '../../../components/Icon';
 import UserTable from '../../../components/UserTable';
 import Examination from '../../../components/Examination';
 import menu from '../index';
-import { BindPhone, BindEmail, EditInfo, RealAuth, EditAvatar, InviteModal } from '../../../components/OtherModal';
+import { BindPhone, BindEmail, EditInfo, RealAuth, EditAvatar } from '../../../components/OtherModal';
 import VipRenew from '../../../components/VipRenew';
 import { My } from '../../../stores/my';
 import { Main } from '../../../stores/main';
@@ -536,7 +536,7 @@ export default class extends Page {
 
   renderInfo() {
     const { info = {} } = this.props.user;
-    const { showExamination, showPhone, showEmail, showEdit, showReal, showAvatar, showInvite, showVip } = this.state;
+    const { showExamination, showPhone, showEmail, showEdit, showReal, showAvatar, showVip } = this.state;
     return (
       <div className="info-layout">
         <div className="body">
@@ -570,7 +570,7 @@ export default class extends Page {
                 radius
                 size="small"
                 onClick={() => {
-                  this.setState({ showInvite: true });
+                  User.needInvite();
                 }}
               >
                 邀请
@@ -657,7 +657,6 @@ export default class extends Page {
           onConfirm={file => this.setState({ showAvatar: false, showEdit: true, avatarFile: file, avatarImage: null })}
           onCancel={() => this.setState({ showAvatar: false, showEdit: true, avatarImage: null })}
         />
-        <InviteModal show={showInvite} data={info} onClose={() => this.setState({ showInvite: false })} />
         <VipRenew
           show={showVip}
           data={info}

+ 6 - 2
front/project/www/routes/my/message/page.js

@@ -22,11 +22,15 @@ const columns = [
     render: (text, row) => {
       return <div>
         {!row.isRead ? <span className='dot'>{text}</span> : text}
-        {row.content && <div className='ws-p'>{row.content}{row.link && <a className='m-l-5' href={row.link} target="_blank">{row.linkTitle || '查看详情'}</a>}{row.linkSecond && <a className='m-l-5' href={row.linkSecond} target="_blank">{row.linkSecondTitle || '查看详情'}</a>}</div>}
+        {row.content && <div className='ws-pl'>{row.content}{row.link && <a className='m-l-5' href={row.link} target="_blank">{row.linkTitle || '查看详情'}</a>}{row.linkSecond && <a className='m-l-5' href={row.linkSecond} target="_blank">{row.linkSecondTitle || '查看详情'}</a>}</div>}
       </div>;
     },
   },
-  { title: '类型', key: 'type', width: 120 },
+  {
+    title: '类型',
+    key: 'type',
+    width: 120,
+  },
   {
     title: '发送时间',
     key: 'date',

+ 14 - 4
front/project/www/routes/my/note/page.js

@@ -103,6 +103,7 @@ export default class extends Page {
       allChecked: false,
       tab: 'exercise',
       timerange: 'all',
+      defaultSortMap: { update_time: 'desc' },
     };
   }
 
@@ -112,7 +113,7 @@ export default class extends Page {
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
     } else if (data.order !== null && data.order !== '') {
-      data.sortMap = { update_time: 'desc' };
+      data.sortMap = this.state.defaultSortMap;
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -199,10 +200,19 @@ export default class extends Page {
 
   onSort(value) {
     const keys = Object.keys(value);
-    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
-    const { sortMap } = this.state;
+    const { sortMap, defaultSortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
-    this.search({ order: keys.length && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    let order = keys.length && value[keys[index]] ? keys[index] : null;
+    let direction = keys.length ? value[keys[index]] : null;
+    if (order == null) {
+      const [prevOrder] = Object.keys(sortMap);
+      console.log(defaultSortMap[prevOrder]);
+      if (!defaultSortMap[prevOrder]) {
+        [order] = Object.keys(defaultSortMap);
+        direction = defaultSortMap[order];
+      }
+    }
+    this.search({ order, direction }, false);
     this.initData();
   }
 

+ 15 - 5
front/project/www/routes/my/report/page.js

@@ -96,6 +96,7 @@ export default class extends Page {
       list: [], // { children: [{}, {}], title: 'channgnanjue' }, {}, {}, {}
       selectList: [],
       allChecked: false,
+      defaultSortMap: { latest_time: 'desc' },
     };
   }
 
@@ -501,8 +502,8 @@ export default class extends Page {
     data.filterMap = this.state.search;
     if (data.order) {
       data.sortMap = { [data.order]: data.direction };
-    } else if (data.order !== '') {
-      data.sortMap = { latest_time: 'desc' };
+    } else if (data.order !== null && data.order !== '') {
+      data.sortMap = this.state.defaultSortMap;
     }
     if (data.timerange) {
       data.filterMap.timerange = data.timerange;
@@ -618,10 +619,19 @@ export default class extends Page {
 
   onSort(value) {
     const keys = Object.keys(value);
-    // this.search({ order: keys.length ? keys.join('|') : null, direction: keys.length ? Object.values(value).join('|') : null });
-    const { sortMap } = this.state;
+    const { sortMap, defaultSortMap } = this.state;
     const index = keys.length > 1 && sortMap[keys[0]] ? 1 : 0;
-    this.search({ order: keys.length && value[keys[index]] ? keys[index] : null, direction: keys.length ? value[keys[index]] : null }, false);
+    let order = keys.length && value[keys[index]] ? keys[index] : null;
+    let direction = keys.length ? value[keys[index]] : null;
+    if (order == null) {
+      const [prevOrder] = Object.keys(sortMap);
+      console.log(defaultSortMap[prevOrder]);
+      if (!defaultSortMap[prevOrder]) {
+        [order] = Object.keys(defaultSortMap);
+        direction = defaultSortMap[order];
+      }
+    }
+    this.search({ order, direction }, false);
     this.initData();
   }
 

+ 1 - 1
front/project/www/routes/page/export/page.js

@@ -231,7 +231,7 @@ class BaseDetail extends Component {
       <div className="detail-item-block">
         <div className="title-item">答案</div>
         <div className="t-1 t-s-16 m-b-2">
-          {userAnswer && <span className="m-r-2">我的答案 {AnswerIndex[answer.questions[0].single.indexOf(true)]}</span>}
+          {userAnswer && <span className="m-r-2">我的答案 {AnswerIndex[userAnswer.questions[0].single.indexOf(true)]}</span>}
           <span>正确答案 {AnswerIndex[answer.questions[0].single.indexOf(true)]}</span>
         </div>
         {this.renderNote('题目', note.questionContent)}

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

@@ -81,7 +81,8 @@ export default class extends Page {
           result.list = result.list.map(row => {
             if (list.length > 0 && row.question.description) {
               for (let i = 0; i < list.length; i += 1) {
-                row.question.description = row.question.description.split(list[i]).join(`<span>${list[i]}</span>`);
+                const reg = new RegExp(`(${list[i]})`, 'ig');
+                row.question.description = row.question.description.replace(reg, '<span>$1</span>');
               }
             }
             return row;

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

@@ -109,6 +109,14 @@ export default class UserStore extends BaseStore {
     });
   }
 
+  needInvite() {
+    return new Promise((resolve, reject) => {
+      this.successCB = resolve;
+      this.failCB = reject;
+      this.setState({ needInvite: true });
+    });
+  }
+
   formatCheckout(record) {
     formatCheckout(record);
   }
@@ -126,6 +134,15 @@ export default class UserStore extends BaseStore {
     this.failCB = null;
   }
 
+  closeInvite(err) {
+    this.setState({ needInvite: false });
+    if (err) {
+      if (this.failCB) this.failCB();
+    } else if (this.successCB) this.successCB();
+    this.successCB = null;
+    this.failCB = null;
+  }
+
   needLogin() {
     if (this.state.login) {
       return Promise.resolve();

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

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

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

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

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

@@ -102,6 +102,12 @@ public class UserQuestion implements Serializable {
     @Column(name = "`detail`")
     private JSONObject detail;
 
+    /**
+     * 自动创建
+     */
+    @Column(name = "`is_auto`")
+    private Integer isAuto;
+
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -392,6 +398,24 @@ public class UserQuestion implements Serializable {
     }
 
     /**
+     * 获取自动创建
+     *
+     * @return is_auto - 自动创建
+     */
+    public Integer getIsAuto() {
+        return isAuto;
+    }
+
+    /**
+     * 设置自动创建
+     *
+     * @param isAuto 自动创建
+     */
+    public void setIsAuto(Integer isAuto) {
+        this.isAuto = isAuto;
+    }
+
+    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -427,6 +451,7 @@ public class UserQuestion implements Serializable {
         sb.append(", isCorrect=").append(isCorrect);
         sb.append(", setting=").append(setting);
         sb.append(", detail=").append(detail);
+        sb.append(", isAuto=").append(isAuto);
         sb.append(", createTime=").append(createTime);
         sb.append("]");
         return sb.toString();
@@ -602,6 +627,16 @@ public class UserQuestion implements Serializable {
         }
 
         /**
+         * 设置自动创建
+         *
+         * @param isAuto 自动创建
+         */
+        public Builder isAuto(Integer isAuto) {
+            obj.setIsAuto(isAuto);
+            return this;
+        }
+
+        /**
          * @param createTime
          */
         public Builder createTime(Date createTime) {

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

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

+ 2 - 1
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserQuestionMapper.xml

@@ -21,6 +21,7 @@
     <result column="is_correct" jdbcType="INTEGER" property="isCorrect" />
     <result column="setting" jdbcType="VARCHAR" property="setting" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="detail" jdbcType="VARCHAR" property="detail" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
+    <result column="is_auto" jdbcType="INTEGER" property="isAuto" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
   <sql id="Base_Column_List">
@@ -29,6 +30,6 @@
     -->
     `id`, `user_id`, `report_id`, `question_module`, `question_type`, `question_id`, 
     `question_no_id`, `no`, `stage`, `stage_no`, `time`, `user_time`, `user_answer`, 
-    `is_correct`, `setting`, `detail`, `create_time`
+    `is_correct`, `setting`, `detail`, `is_auto`, `create_time`
   </sql>
 </mapper>

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

@@ -40,7 +40,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'exercise' and qn.`delete_time` is null
       and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -94,7 +94,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = uaq.`question_no_id` and qn.`module` = 'examination' and qn.`delete_time` is null
       and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

+ 8 - 8
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCollectQuestionRelationMapper.xml

@@ -22,10 +22,10 @@
     max(uq.`create_time`) as `latest_time`,
     max(ucq.`create_time`) as `collect_time`,
     max(q.`update_time`) as `update_time`,
-    sum(uq.`is_correct`) / count(uq.id) as `correct`,
+    sum(uq.`is_correct`) * 100 / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
-    max(uq.`no`) as `no`,
+    max(qn.`no`) as `no`,
     max(ur.`origin_id`) as `pid`
     from `user_collect_question` ucq
     left join `user_question` uq on uq.`question_id` = ucq.`question_id` and uq.`user_id`=#{userId,jdbcType=VARCHAR}
@@ -38,7 +38,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'exercise' and qn.`delete_time` is null
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -79,12 +79,12 @@
     </if>
     select max(ucq.`id`) as `id`,
     max(uq.`create_time`) as `latest_time`,
-    ucq.`create_time` as `collect_time`,
-    q.`update_time` as `update_time`,
-    sum(uq.`is_correct`) / count(uq.id) as `correct`,
+    max(ucq.`create_time`) as `collect_time`,
+    max(q.`update_time`) as `update_time`,
+    sum(uq.`is_correct`) * 100 / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
-    max(uq.`no`) as `no`,
+    max(qn.`no`) as `no`,
     max(ur.`origin_id`) as `pid`
     from `user_collect_question` ucq
     left join `user_question` uq on uq.`question_id` = ucq.`question_id` and uq.`user_id`=#{userId,jdbcType=VARCHAR}
@@ -97,7 +97,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = ucq.`question_no_id` and qn.`module` = 'examination' and qn.`delete_time` is null
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

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

@@ -30,7 +30,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = unq.`question_no_id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = unq.`question_no_id` and qn.`module` = 'exercise' and qn.`delete_time` is null
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and

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

@@ -89,7 +89,7 @@
         #{item, jdbcType=VARCHAR}
       </foreach>
     </if>
-    left join `course_teacher` on ct.`id` = uor.`teacher_id`
+    left join `course_teacher` ct on ct.`id` = uor.`teacher_id`
     <if test="structId != null">
       and (c.`struct_id` = #{structId,jdbcType=VARCHAR} or c.`parent_struct_id` = #{structId,jdbcType=VARCHAR})
     </if>
@@ -105,7 +105,6 @@
     <if test="userId != null">
       and uor.`user_id` = #{userId,jdbcType=VARCHAR}
     </if>
-    group by
     order by ${order} ${direction}
   </select>
 

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

@@ -64,10 +64,10 @@
     select
     DISTINCT(up.`id`),
     up.`title` as `title`,
-    up.`latest_time` as `latest_time`,
-    if(up.`question_number`>0,ur.`user_correct` / up.`question_number`,0) as `correct`,
-    if(up.`question_number`>0,ur.`user_time` / up.`question_number`,0) as `time`,
-    if(up.`question_number`>0,ur.`user_number` / up.`question_number`,0) as `progress`
+    ur.`update_time` as `latest_time`,
+    if(ur.`question_number`>0,ur.`user_correct` / ur.`user_number`,0) as `correct`,
+    if(ur.`question_number`>0,ur.`user_time` / ur.`user_number`,0) as `time`,
+    if(ur.`question_number`>0,ur.`user_number` / ur.`question_number`,0) as `progress`
     from `user_paper` up
     left join `user_report` ur on ur.`id`= up.`latest_report_id`
     left join `exercise_paper` ep on up.`paper_origin` = 'exercise' and ep.`id` = up.`origin_id`
@@ -142,10 +142,10 @@
     select
     DISTINCT(up.`id`),
     up.`title` as `title`,
-    up.`latest_time` as `latest_time`,
-    ur.`user_correct` / up.`question_number` as `correct`,
-    ur.`user_time` / up.`question_number` as `time`,
-    ur.`user_number` / up.`question_number` as `progress`
+    ur.`update_time` as `latest_time`,
+    ur.`user_correct` / ur.`user_number` as `correct`,
+    ur.`user_time` / ur.`user_number` as `time`,
+    ur.`user_number` / ur.`question_number` as `progress`
     from `user_paper` up
     left join `user_report` ur on ur.`id`= up.`latest_report_id`
     left join `examination_paper` ep on up.`paper_origin` = 'examination' and ep.`id` = up.`origin_id`

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

@@ -33,10 +33,10 @@
     </if>
     select max(uq.`id`) as `id`,
     max(uq.`create_time`) as `latest_time`,
-    sum(uq.`is_correct`) / count(uq.id) as `correct`,
+    sum(uq.`is_correct`) * 100 / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
-    max(uq.`no`) as `no`,
+    max(qn.`no`) as `no`,
     max(ur.`origin_id`) as `pid`
     from `user_question` uq
     left join `user_report` ur on ur.`id`=uq.`report_id`
@@ -48,7 +48,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'exercise'
+    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'exercise' and qn.`delete_time` is null
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -99,6 +99,7 @@
       and uq.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
     </if>
     group by uq.`question_module`, uq.`question_no_id`, q.`question_type`
+    having sum(uq.`is_correct`) &lt; count(uq.`id`)
     <if test="order != null">
       order by ${order}
     </if>
@@ -110,10 +111,10 @@
     </if>
     select max(uq.`id`) as `id`,
     max(uq.`create_time`) as `latest_time`,
-    sum(uq.`is_correct`) / count(uq.id) as `correct`,
+    sum(uq.`is_correct`) * 100 / count(uq.id) as `correct`,
     sum(uq.`user_time`) / count(uq.id) as `time`,
     if(q.`question_type`='sc', 1, if(q.`question_type`='rc', 2, if(q.`question_type`='cr', 3,if(q.`question_type`='ds', 4, if(q.`question_type`='ps', 5,if(q.`question_type`='ir', 6, 7)))))) as `question_type`,
-    max(uq.`no`) as `no`,
+    max(qn.`no`) as `no`,
     max(ur.`origin_id`) as `pid`
     from `user_question` uq
     left join `user_report` ur on ur.`id`=uq.`report_id`
@@ -125,7 +126,7 @@
         q.`question_type` = #{item}
       </foreach>
     </if>
-    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'examination'
+    left join `question_no` qn on qn.`id` = uq.`question_no_id` and qn.`module` = 'examination' and qn.`delete_time` is null
     and (q.`question_module` = 'base')
     <if test="structIds != null">
       and
@@ -171,6 +172,7 @@
       and uq.`create_time` &lt; #{endTime,jdbcType=TIMESTAMP}
     </if>
     group by uq.`question_module`, uq.`question_no_id`, q.`question_type`
+    having sum(uq.`is_correct`) &lt; count(uq.`id`)
     <if test="order != null">
       order by ${order}
     </if>

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

@@ -0,0 +1 @@
+ALTER TABLE user_question add column is_auto tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '自动创建' AFTER detail;

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

@@ -466,7 +466,7 @@ public class CourseController {
         courseNoService.deleteNo(id);
         // 统计课时数
         CourseNoStatRelation relation = courseNoService.statCourse(in.getCourseId());
-        courseService.edit(Course.builder().id(in.getCourseId()).noNumber(relation.getNumber()).time(relation.getNumber()).build());
+        courseService.edit(Course.builder().id(in.getCourseId()).noNumber(relation.getNumber()).time(relation.getTime()).build());
         // 删除对应预习作业关系
         previewPaperService.removeCourseNo(in.getCourseId(), in.getId());
         previewAssignService.removeCourseNo(in.getCourseId(), in.getId());

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

@@ -158,7 +158,8 @@ public class TextbookController {
     @RequestMapping(value = "/library/add", method = RequestMethod.POST)
     @ApiOperation(value = "发布新换库信息", httpMethod = "POST")
     public Response<Boolean> addLibrary(@RequestBody @Validated TextbookLibraryStartDto dto, HttpServletRequest request) {
-        TextbookLibrary entity = Transform.dtoToEntity(dto);
+        TextbookLibrary entity = new TextbookLibrary();
+        entity.setStartDate(Tools.baseDate(dto.getStartDate()));
         textbookService.replace(entity);
         // 发送机经到用户邮箱
         asyncTask.postTextbookLibrary();

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

@@ -789,7 +789,7 @@ public class MyController {
             dto.setTotalTime(stat.getTotalTime());
 
             Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
-            List<UserQuestion> userQuestionList = userQuestionService.listByQuestionWithTime(user.getId(), QuestionModule.BASE, questionNoIds, Tools.baseDate(startTime), Tools.baseDate(endTime));
+            List<UserQuestion> userQuestionList = userQuestionService.listByQuestionWithTime(user.getId(), questionNoIds, Tools.baseDate(startTime), Tools.baseDate(endTime));
             Map userQuestionMap = Transform.getMap(userQuestionList, UserQuestion.class, "questionNoId");
             dto.setUserQuestion(userQuestionMap.size());
 
@@ -1015,9 +1015,9 @@ public class MyController {
         Transform.combine(pr, questionNoList, UserCollectQuestionInfoDto.class, "questionNoId", "questionNo", QuestionNo.class, "id", QuestionNoExtendDto.class);
 
         // 绑定题目统计
-        List<UserQuestion> userQuestionList = userQuestionService.listByQuestion(user.getId(), questionIds);
+        List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
         Map<Object, UserQuestionStat> stats = userQuestionService.statQuestionMap(userQuestionList);
-        Transform.combine(pr, stats, UserCollectQuestionInfoDto.class, "questionId", "stat");
+        Transform.combine(pr, stats, UserCollectQuestionInfoDto.class, "questionNoId", "stat");
 
         // 最近做题
         List<UserQuestion> lastList = userQuestionService.listWithLast(user.getId(), questionNoIds);
@@ -1086,9 +1086,9 @@ public class MyController {
         Transform.combine(pr, questionNoList, UserQuestionErrorInfoDto.class, "questionNoId", "questionNo", QuestionNo.class, "id", QuestionNoExtendDto.class);
 
         // 绑定题目统计
-        List<UserQuestion> userQuestionList = userQuestionService.listByQuestion(user.getId(), questionIds);
+        List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
         Map<Object, UserQuestionStat> stats = userQuestionService.statQuestionMap(userQuestionList);
-        Transform.combine(pr, stats, UserQuestionErrorInfoDto.class, "questionId", "stat");
+        Transform.combine(pr, stats, UserQuestionErrorInfoDto.class, "questionNoId", "stat");
 
         // 最近做题
         List<UserQuestion> lastList = userQuestionService.listWithLast(user.getId(), questionNoIds);

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

@@ -256,7 +256,7 @@ public class QuestionController {
             Map<Object, UserQuestionStat> userQuestionStatMap = null;
             if(user != null){
                 Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
-                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), QuestionModule.BASE, questionNoIds);
+                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
                 userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);
 
                 dto.setUserStat(userQuestionService.statQuestion(userQuestionList));

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

@@ -150,7 +150,7 @@ public class TextbookController
                     UserOrderRecord record = userOrderRecordService.getUnUseService(user.getId(), ServiceKey.TEXTBOOK);
                     dto.setUnUseRecord(Transform.convert(record, UserServiceRecordExtendDto.class));
                 }
-                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), QuestionModule.TEXTBOOK, questionNoIds);
+                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
                 userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);
 
                 dto.setUserStat(userQuestionService.statQuestion(userQuestionList));

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

@@ -29,6 +29,8 @@ public class UserReportBaseDto {
 
     private Integer userNumber;
 
+    private Integer userCorrect;
+
     private Integer isFinish;
 
     private JSONObject setting;
@@ -148,4 +150,12 @@ public class UserReportBaseDto {
     public void setTimes(Integer times) {
         this.times = times;
     }
+
+    public Integer getUserCorrect() {
+        return userCorrect;
+    }
+
+    public void setUserCorrect(Integer userCorrect) {
+        this.userCorrect = userCorrect;
+    }
 }

+ 8 - 9
server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java

@@ -132,25 +132,23 @@ public class UserQuestionService extends AbstractService {
         return select(userQuestionMapper, example);
     }
 
-    public List<UserQuestion> listByQuestionNo(Integer userId, QuestionModule questionModule, Collection questionNoIds){
+    public List<UserQuestion> listByQuestionNo(Integer userId, Collection questionNoIds){
         if (questionNoIds == null || questionNoIds.size() == 0) return new ArrayList<>();
         Example example = new Example(UserQuestion.class);
         example.and(
                 example.createCriteria()
                         .andEqualTo("userId", userId)
-                        .andEqualTo("questionModule", questionModule.key)
                         .andIn("questionNoId", questionNoIds)
         );
         return select(userQuestionMapper, example);
     }
 
-    public List<UserQuestion> listByQuestionWithTime(Integer userId, QuestionModule questionModule, Collection questionNoIds, Date startTime, Date endTime){
+    public List<UserQuestion> listByQuestionWithTime(Integer userId, Collection questionNoIds, Date startTime, Date endTime){
         if (questionNoIds == null || questionNoIds.size() == 0) return new ArrayList<>();
         Example example = new Example(UserQuestion.class);
         example.and(
                 example.createCriteria()
                         .andEqualTo("userId", userId)
-                        .andEqualTo("questionModule", questionModule.key)
                         .andIn("questionNoId", questionNoIds)
                         .andGreaterThanOrEqualTo("createTime", startTime)
                         .andLessThan("createTime", endTime)
@@ -170,14 +168,14 @@ public class UserQuestionService extends AbstractService {
             if (userQuestion.getUserTime()<=0){
                 continue;
             }
-            if (!map.containsKey(userQuestion.getQuestionId())){
-                map.put(userQuestion.getQuestionId(), new ArrayList<>());
+            if (!map.containsKey(userQuestion.getQuestionNoId())){
+                map.put(userQuestion.getQuestionNoId(), new ArrayList<>());
             }
-            List<UserQuestion> list = map.get(userQuestion.getQuestionId());
+            List<UserQuestion> list = map.get(userQuestion.getQuestionNoId());
             list.add(userQuestion);
         }
-        for(Integer questionId : map.keySet()){
-            relationMap.put(questionId, statQuestion(map.get(questionId)));
+        for(Integer questionNoId : map.keySet()){
+            relationMap.put(questionNoId, statQuestion(map.get(questionNoId)));
         }
 
         return relationMap;
@@ -220,6 +218,7 @@ public class UserQuestionService extends AbstractService {
                 example.createCriteria()
                         .andEqualTo("userId", userId)
                         .andEqualTo("reportId", userReportId)
+                        .andEqualTo("isAuto", 0)
         );
         example.orderBy("id").desc();
         return one(userQuestionMapper, example);

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

@@ -217,7 +217,10 @@ public class UsersService extends AbstractService {
             if (inviteCode != null && !inviteCode.isEmpty()){
                 User origin = getByInviteCode(inviteCode);
                 user.setOriginId(origin.getId());
-                edit(User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).inviteLatestTime(new Date()).build());
+                User tmp = User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).inviteLatestTime(new Date()).build();
+                edit(tmp);
+                origin.setInviteNumber(tmp.getInviteNumber());
+                origin.setInviteLatestTime(tmp.getInviteLatestTime());
                 // 邀请奖励
                 orderFlowService.giveInvite(origin);
             }

+ 26 - 20
server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java

@@ -619,7 +619,7 @@ public class QuestionFlowService {
     public UserQuestion next(Integer userId, Integer userReportId){
         UserQuestion userQuestion = userQuestionService.getLastByReport(userId, userReportId);
         // 查找未完成的questionId
-        if (userQuestion==null || userQuestion.getUserTime() >0){
+        if (userQuestion==null || userQuestion.getUserTime() >0 || userQuestion.getIsAuto() == 1){
             // 创建新的question
             UserReport userReport = userReportService.get(userReportId);
             if (!userReport.getUserId().equals(userId)){
@@ -718,12 +718,14 @@ public class QuestionFlowService {
         UserQuestion question;
         if(lastQuestion != null && lastQuestion.getUserTime() == 0){
             lastQuestion.setUserTime(totalTime - useTime);
+            lastQuestion.setIsAuto(1);
             this.submit(userId, lastQuestion);
         }
         // 判断未完成题目数,自动生成
         question = userQuestionService.newByReport(userReport, lastQuestion);
         while(number.getIntValue(stage) < totalNumber){
             examinationCompute(paper, userReport, lastQuestion, question);
+            question.setIsAuto(1);
             question = userQuestionService.add(question);
             lastQuestion = question;
             question = userQuestionService.newByReport(userReport, lastQuestion);
@@ -908,26 +910,30 @@ public class QuestionFlowService {
         Integer subnumber =  number.getIntValue(stage);
         Integer surplus = totalNumber - subnumber;
         QuestionSubject subject = QuestionSubject.ValueOf(stage);
-        List<String> questionTypes = QuestionType.SpecialFromSubject(subject);
-        List<UserQuestion> userQuestionList = userQuestionService.listByReportAndType(report.getUserId(), report.getId(), questionTypes);
-        Collection ids = Transform.getIds(userQuestionList, UserQuestion.class, "questionNoId");
         // 根据设置出题
         Integer questionNoId = 0;
-        if (paper.getIsAdapt() > 0 && QuestionSubject.SupportAdapt(subject)){
-            switch(subject){
-                case VERBAL:
-                    questionNoId = verbalCompute(paper, setting, subnumber, ids, userQuestionList);
-                    break;
-                case QUANT:
-                    questionNoId = quantCompute(paper, setting, subnumber, ids, userQuestionList);
-                    break;
-                default:
-                    throw new ParameterException("模考出题流程错误:"+subject.key+"不支持适应性判断");
+        if (paper.getIsAdapt() > 0){
+            List<String> questionTypes = QuestionType.SpecialFromSubject(subject);
+            List<UserQuestion> userQuestionList = userQuestionService.listByReportAndType(report.getUserId(), report.getId(), questionTypes);
+            Collection ids = Transform.getIds(userQuestionList, UserQuestion.class, "questionNoId");
+
+            if (QuestionSubject.SupportAdapt(subject)){
+                switch(subject){
+                    case VERBAL:
+                        questionNoId = verbalCompute(paper, setting, subnumber, ids, userQuestionList);
+                        break;
+                    case QUANT:
+                        questionNoId = quantCompute(paper, setting, subnumber, ids, userQuestionList);
+                        break;
+                    default:
+                        throw new ParameterException("模考出题流程错误:"+subject.key+"不支持适应性判断");
+                }
+            }else{
+                questionNoId = randomCompute(subject, paper, questionTypes, setting, subnumber, ids, userQuestionList);
             }
-        }else if (paper.getIsAdapt() > 0){
-            questionNoId = randomCompute(subject, paper, questionTypes, setting, subnumber, ids, userQuestionList);
         }else{
-            questionNoId = fixOrderCompute(subject, paper, questionTypes, setting, subnumber, ids, userQuestionList);
+            UserQuestion userQuestion = userQuestionService.getLastByReport(report.getUserId(), report.getId());
+            questionNoId = fixOrderCompute(subject, paper, userQuestion);
         }
         if (questionNoId ==null || questionNoId == 0) {
             throw new ParameterException("模考出题流程错误:题目生成错误");
@@ -1094,11 +1100,11 @@ public class QuestionFlowService {
         return questionNoService.randomExamination(paper.getStructThree(), targetTypes, ids);
     }
 
-    public Integer fixOrderCompute(QuestionSubject subject, ExaminationPaper paper, List<String> questionTypes, JSONObject setting, Integer subnumber, Collection ids, List<UserQuestion> subQuestionList){
+    public Integer fixOrderCompute(QuestionSubject subject, ExaminationPaper paper, UserQuestion lastQuestion){
         List<String> targetTypes = QuestionType.FromSubject(subject);
         Integer prevNo = null;
-        if (subQuestionList != null && subQuestionList.size() > 0){
-            Integer questionNoId = subQuestionList.get(subQuestionList.size() - 1).getQuestionNoId();
+        if (lastQuestion != null){
+            Integer questionNoId = lastQuestion.getQuestionNoId();
             QuestionNo questionNo = questionNoService.get(questionNoId);
             prevNo = questionNo.getNo();
         }

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

@@ -77,7 +77,10 @@ public class CourseNoService extends AbstractService {
         if (relation != null && relation.size() > 0){
             return relation.get(0);
         }else{
-            return null;
+            CourseNoStatRelation r = new CourseNoStatRelation();
+            r.setNumber(0);
+            r.setTime(0);
+            return r;
         }
     }
 

+ 7 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/QuestionService.java

@@ -79,12 +79,19 @@ public class QuestionService extends AbstractService {
         Map<Integer, List<Question>> relationMap = new HashMap<>();
         List<Integer> ids = new ArrayList<>();
         for(Integer[] questionIds : questionIdsMap.values()){
+            if (questionIds == null){
+                continue;
+            }
             ids.addAll(Arrays.stream(questionIds).collect(Collectors.toList()));
         }
         List<Question> questionList = select(ids);
         Map questionMap = Transform.getMap(questionList, Question.class, "id");
         for(Integer k: questionIdsMap.keySet()){
             List<Question> l = new ArrayList<>();
+            Integer[] questionIds = questionIdsMap.get(k);
+            if (questionIds == null){
+                continue;
+            }
             for (Integer questionId : questionIdsMap.get(k)){
                 l.add((Question)questionMap.get(questionId));
             }

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

@@ -56,7 +56,7 @@ public class TextbookPaperService extends AbstractService {
      * @param prefixTitle
      */
     public void updateTitle(Integer libraryId, String prefixTitle, Integer length){
-        Example example = new Example(TextbookLibrary.class);
+        Example example = new Example(TextbookPaper.class);
         example.and(
                 example.createCriteria()
                 .andEqualTo("libraryId", libraryId)

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

@@ -86,7 +86,7 @@ public class TextbookQuestionService extends AbstractService {
      * @param prefixTitle
      */
     public void updateTitle(Integer libraryId, String prefixTitle) {
-        Example example = new Example(TextbookLibrary.class);
+        Example example = new Example(TextbookQuestion.class);
         example.and(
                 example.createCriteria()
                         .andEqualTo("libraryId", libraryId)

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

@@ -115,7 +115,7 @@ public class UserOrderRecordService extends AbstractService {
         example.and(
                 example.createCriteria()
                         .andEqualTo("productType", ProductType.COURSE.key)
-                        .andEqualTo("isUse", 0)
+                        .andEqualTo("isUsed", 0)
                         .andGreaterThanOrEqualTo("endTime", startTime)
                         .andLessThanOrEqualTo("endTime", endTime)
         );
@@ -152,7 +152,7 @@ public class UserOrderRecordService extends AbstractService {
                 example.createCriteria()
                         .andEqualTo("productType", ProductType.SERVICE.key)
                         .andEqualTo("service", serviceKey.key)
-                        .andEqualTo("isUse", 0)
+                        .andEqualTo("isUsed", 0)
                         .andGreaterThanOrEqualTo("endTime", startTime)
                         .andLessThanOrEqualTo("endTime", endTime)
         );

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/util/shiro/OauthRealm.java

@@ -33,8 +33,8 @@ public class OauthRealm extends AuthorizingRealm {
         String platform = new String(token.getPassword());
 
         User user = usersService.Oauth(null, code, platform, userInfo);
-        if (user.getIsFrozen() > 0){
-            throw new UnknownAccountException("账号异常,请练习管理员!");
+        if (user != null && user.getIsFrozen()!=null && user.getIsFrozen() > 0){
+            throw new UnknownAccountException("账号异常,请联系管理员!");
         }
         return new SimpleAuthenticationInfo(user, platform, getName());
     }

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/util/shiro/UserRealm.java

@@ -56,8 +56,8 @@ public class UserRealm extends AuthorizingRealm {
         if (user == null || user.getId() <= 0) {
             throw new UnknownAccountException("用户不存在!");
         }
-        if (user.getIsFrozen() > 0){
-            throw new UnknownAccountException("账号异常,请练习管理员!");
+        if (user.getIsFrozen() != null && user.getIsFrozen() > 0){
+            throw new UnknownAccountException("账号异常,请联系管理员!");
         }
 //        if(!usersService.equalsPassword(user, password)){
 //            throw new IncorrectCredentialsException("用户名或密码错误!");