Browse Source

Merge branch 'master' of www.gitinn.com:zaixianjiaoyu/sourcecode

KaysonCui 5 years ago
parent
commit
146760bdf4
40 changed files with 630 additions and 50 deletions
  1. 15 0
      front/project/admin/routes/textbook/library/page.js
  2. 5 9
      front/project/www/components/UserTable/index.js
  3. 1 1
      front/project/www/index.js
  4. 9 3
      front/project/www/routes/course/note/page.js
  5. 9 3
      front/project/www/routes/my/collect/page.js
  6. 9 3
      front/project/www/routes/my/error/page.js
  7. 1 1
      front/project/www/routes/my/message/page.js
  8. 73 12
      front/project/www/routes/my/note/page.js
  9. 9 3
      front/project/www/routes/page/cart/page.js
  10. 11 0
      front/project/www/routes/page/export/index.js
  11. 3 0
      front/project/www/routes/page/export/index.less
  12. 3 1
      front/project/www/routes/page/index.js
  13. 11 0
      front/project/www/routes/page/ready/index.js
  14. 3 0
      front/project/www/routes/page/ready/index.less
  15. 3 1
      front/project/www/routes/question/index.js
  16. 11 0
      front/project/www/routes/question/search/index.js
  17. 3 0
      front/project/www/routes/question/search/index.less
  18. 11 0
      front/project/www/routes/question/searchHistory/index.js
  19. 3 0
      front/project/www/routes/question/searchHistory/index.less
  20. 5 1
      front/project/www/routes/textbook/index.js
  21. 11 0
      front/project/www/routes/textbook/main/index.js
  22. 3 0
      front/project/www/routes/textbook/main/index.less
  23. 11 0
      front/project/www/routes/textbook/topic/index.js
  24. 3 0
      front/project/www/routes/textbook/topic/index.less
  25. 11 0
      front/project/www/routes/textbook/topicDetail/index.js
  26. 3 0
      front/project/www/routes/textbook/topicDetail/index.less
  27. 11 0
      front/project/www/routes/textbook/year/index.js
  28. 3 0
      front/project/www/routes/textbook/year/index.less
  29. 11 3
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java
  30. 12 3
      server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java
  31. 105 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibraryHistory.java
  32. 140 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserMessage.java
  33. 5 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryHistoryMapper.xml
  34. 6 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMessageMapper.xml
  35. 4 0
      server/data/src/main/resources/db/migration/V1__init_table.sql
  36. 5 0
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  37. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  38. 30 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/TextbookLibraryHistoryDto.java
  39. 1 1
      server/gateway-api/src/main/java/com/qxgmat/help/SmsHelp.java
  40. 56 1
      server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java

+ 15 - 0
front/project/admin/routes/textbook/library/page.js

@@ -196,6 +196,11 @@ export default class extends Page {
               <Input.TextArea placeholder='更新日志' />,
             )}
           </Form.Item>}
+          {postSwitch.quant && <Form.Item >
+            {getFieldDecorator('post.quantDescription')(
+              <Input placeholder='更新简介:站内信邮箱使用' />,
+            )}
+          </Form.Item>}
           {postSwitch.quant && <Form.Item>
             {getFieldDecorator('post.quant')(
               <Upload
@@ -227,6 +232,11 @@ export default class extends Page {
               <Input.TextArea placeholder='更新日志' />,
             )}
           </Form.Item>}
+          {postSwitch.rc && <Form.Item >
+            {getFieldDecorator('post.rcDescription')(
+              <Input placeholder='更新简介:站内信邮箱使用' />,
+            )}
+          </Form.Item>}
           {postSwitch.rc && <Form.Item>
             {getFieldDecorator('post.rc')(
               <Upload
@@ -258,6 +268,11 @@ export default class extends Page {
               <Input.TextArea placeholder='更新日志' />,
             )}
           </Form.Item>}
+          {postSwitch.ir && <Form.Item >
+            {getFieldDecorator('post.irDescription')(
+              <Input placeholder='更新简介:站内信邮箱使用' />,
+            )}
+          </Form.Item>}
           {postSwitch.ir && <Form.Item>
             {getFieldDecorator('post.ir')(
               <Upload

+ 5 - 9
front/project/www/components/UserTable/index.js

@@ -19,7 +19,7 @@ export default class UserTable extends Component {
     } else {
       selectList.splice(selectList.indexOf(key), 1);
     }
-    if (this.props.onSelect) this.props.onSelect(selectList);
+    if (this.props.onSelect) this.props.onSelect(selectList, key, checked);
   }
 
   render() {
@@ -58,18 +58,14 @@ export default class UserTable extends Component {
                       {item.fixSort &&
                         (sortMap[item.key] ? (
                           <Icon active name="arrow-down" onClick={() => this.onSort(item.key, '')} />
-                        ) : (
-                          <Icon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />
-                        ))}
+                        ) : (<Icon name="arrow-up" onClick={() => this.onSort(item.key, 'desc')} />))}
                       {item.sort &&
                         (sortMap[item.key] ? (
                           <Assets
                             name={sortMap[item.key] === 'asc' ? 'seqencing2_up_select' : 'seqencing2_down_select'}
                             onClick={() => this.onSort(item.key, sortMap[item.key] === 'asc' ? 'desc' : '')}
                           />
-                        ) : (
-                          <Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />
-                        ))}
+                        ) : (<Assets name="seqencing2_normal" onClick={() => this.onSort(item.key, 'asc')} />))}
                     </th>
                   );
                 })}
@@ -109,7 +105,7 @@ export default class UserTable extends Component {
   }
 
   renderTd(row, checked, item, index, childIndex, columnIndex) {
-    const { select, rowKey } = this.props;
+    const { select, rowKey = 'key' } = this.props;
     return (
       <td
         className={`${item.className || ''} ${columnIndex === 0 && select ? 'check' : ''}`}
@@ -117,7 +113,7 @@ export default class UserTable extends Component {
         align={item.align}
       >
         {childIndex === -1 && columnIndex === 0 && select && (
-          <CheckboxItem theme="white" checked={checked} onClick={value => this.onSelect(value, row[rowKey])} />
+          <CheckboxItem theme="white" checked={checked} onClick={value => this.onSelect(value, row[rowKey], row, rowKey)} />
         )}
         {item.render ? item.render(row[item.key], row, index, childIndex) : row[item.key]}
       </td>

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

@@ -11,7 +11,7 @@ export default {
     { key: 'ready', name: 'GetReady', path: '/ready' },
     { key: 'exercise', name: '练习', path: '/exercise' },
     { key: 'examination', name: 'CAT模考', path: '/examination' },
-    { key: 'questions', name: '题库', path: '/questions' },
+    { key: 'question', name: '题库', path: '/question' },
     { key: 'textbook', name: '换库机经', path: '/textbook' },
     { key: 'course', name: '课堂', path: '/course' },
   ],

+ 9 - 3
front/project/www/routes/course/note/page.js

@@ -93,19 +93,25 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const selectList = [];
+    const { selectList } = this.state;
+    const { list = [] } = this.state;
     if (checked) {
-      const { list = [] } = this.state;
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+    } else {
+      list.forEach(item => {
+        const index = selectList.indexOf(item.key);
+        if (index < 0) return;
+        selectList.splice(index, 1);
+      });
     }
     this.setState({ selectList, allChecked: checked });
   }
 
   onSelect(selectList) {
-    this.setState({ selectList });
+    this.setState({ selectList, allCheckbox: false });
   }
 
   onAction(key) {

+ 9 - 3
front/project/www/routes/my/collect/page.js

@@ -204,19 +204,25 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const selectList = [];
+    const { selectList } = this.state;
+    const { list = [] } = this.state;
     if (checked) {
-      const { list = [] } = this.state;
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+    } else {
+      list.forEach(item => {
+        const index = selectList.indexOf(item.key);
+        if (index < 0) return;
+        selectList.splice(index, 1);
+      });
     }
     this.setState({ selectList, allChecked: checked });
   }
 
   onSelect(selectList) {
-    this.setState({ selectList });
+    this.setState({ selectList, allCheckbox: false });
   }
 
   onAction(key) {

+ 9 - 3
front/project/www/routes/my/error/page.js

@@ -190,19 +190,25 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const selectList = [];
+    const { selectList } = this.state;
+    const { list = [] } = this.state;
     if (checked) {
-      const { list = [] } = this.state;
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+    } else {
+      list.forEach(item => {
+        const index = selectList.indexOf(item.key);
+        if (index < 0) return;
+        selectList.splice(index, 1);
+      });
     }
     this.setState({ selectList, allChecked: checked });
   }
 
   onSelect(selectList) {
-    this.setState({ selectList });
+    this.setState({ selectList, allCheckbox: false });
   }
 
   onAction(key) {

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

@@ -21,7 +21,7 @@ const columns = [
     render: (text, row) => {
       return <div>
         {!row.isRead ? <span className='dot'>{text}</span> : text}
-        {row.content && <div className=''>{row.content}{row.link && <a className='m-l-5' href={row.link} target="_blank">查看详情</a>}</div>}
+        {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>}
       </div>;
     },
   },

+ 73 - 12
front/project/www/routes/my/note/page.js

@@ -182,23 +182,83 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const selectList = [];
+    const { selectList, contentSelectList } = this.state;
+    const { list = [] } = this.state;
     if (checked) {
-      const { list = [] } = this.state;
       list.forEach(item => {
-        if (selectList.indexOf(item.key) >= 0) return;
-        selectList.push(item.key);
+        if (selectList.indexOf(item.key) < 0) {
+          selectList.push(item.key);
+        }
+
+        AskTarget.forEach((r) => {
+          if (!item[`${r.value}Content`]) return;
+          if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
+            contentSelectList.push(`${item.key}|${r.value}`);
+          }
+        });
+      });
+    } else {
+      list.forEach(item => {
+        const index = selectList.indexOf(item.key);
+        if (index >= 0) {
+          selectList.splice(index, 1);
+        }
+
+        AskTarget.forEach((r) => {
+          if (!item[`${r.value}Content`]) return;
+          const i = contentSelectList.indexOf(`${item.key}|${r.value}`);
+          if (i >= 0) {
+            contentSelectList.splice(i, 1);
+          }
+        });
       });
     }
-    this.setState({ selectList, allChecked: checked });
+    this.setState({ selectList, contentSelectList, allChecked: checked });
   }
 
-  onSelect(selectList) {
-    this.setState({ selectList });
+  onSelect(selectList, key, checked) {
+    const { contentSelectList, list = [] } = this.state;
+    if (checked) {
+      const [item] = list.filter(row => row.key === key);
+      if (item) {
+        // 选中下面所有
+        AskTarget.forEach((r) => {
+          if (!item[`${r.value}Content`]) return;
+          if (contentSelectList.indexOf(`${item.key}|${r.value}`) < 0) {
+            contentSelectList.push(`${item.key}|${r.value}`);
+          }
+        });
+      }
+    } else {
+      const [item] = list.filter(row => row.key === key);
+      if (item) {
+        // 取消下面所有
+        AskTarget.forEach((r) => {
+          if (!item[`${r.value}Content`]) return;
+          const index = contentSelectList.indexOf(`${item.key}|${r.value}`);
+          if (index >= 0) {
+            contentSelectList.splice(index, 1);
+          }
+        });
+      }
+    }
+    this.setState({ selectList, contentSelectList, allCheckbox: false });
   }
 
-  onSelectContent(contentSelectList) {
-    this.setState({ contentSelectList });
+  onSelectContent(contentSelectList, key, checked) {
+    const { selectList, list = [] } = this.state;
+    if (checked) {
+      const [questionNoIdStr] = key.split('|');
+      const questionNoId = Number(questionNoIdStr);
+      const [item] = list.filter(row => row.key === questionNoId);
+      if (item) {
+        // 选中上级
+        if (selectList.indexOf(item.key) < 0) {
+          selectList.push(item.key);
+        }
+      }
+    }
+    this.setState({ selectList, contentSelectList });
   }
 
   onAction(key) {
@@ -216,7 +276,8 @@ export default class extends Page {
           return;
         }
         contentSelectList.forEach(row => {
-          const [questionNoId, target] = row.split('|');
+          const [questionNoIdStr, target] = row.split('|');
+          const questionNoId = Number(questionNoIdStr);
           if (selectList.indexOf(questionNoId) >= 0) return;
           if (!questionNoMap[questionNoId]) {
             questionNoMap[questionNoId] = {};
@@ -371,7 +432,7 @@ export default class extends Page {
                 selectList={selectList}
                 columns={questionColumns}
                 data={[item]}
-                onSelect={l => this.onSelect(l)}
+                onSelect={(l, key, checked) => this.onSelect(l, key, checked)}
                 header={false}
               />
               <UserTable
@@ -382,7 +443,7 @@ export default class extends Page {
                 selectList={contentSelectList}
                 columns={contentColumns}
                 data={item.list}
-                onSelect={l => this.onSelectContent(l)}
+                onSelect={(l, key, checked) => this.onSelectContent(l, key, checked)}
                 header={index === 0}
               />
             </div>

+ 9 - 3
front/project/www/routes/page/cart/page.js

@@ -42,13 +42,19 @@ export default class extends Page {
   }
 
   onAll(checked) {
-    const selectList = [];
+    const { selectList } = this.state;
+    const { list = [] } = this.state;
     if (checked) {
-      const { list = [] } = this.state;
       list.forEach(item => {
         if (selectList.indexOf(item.key) >= 0) return;
         selectList.push(item.key);
       });
+    } else {
+      list.forEach(item => {
+        const index = selectList.indexOf(item.key);
+        if (index < 0) return;
+        selectList.splice(index, 1);
+      });
     }
     this.setState({ selectList, allChecked: checked });
   }
@@ -60,7 +66,7 @@ export default class extends Page {
     } else {
       selectList.splice(selectList.indexOf(key), 1);
     }
-    this.setState({ selectList });
+    this.setState({ selectList, allChecked: false });
   }
 
   onDelete(list) {

+ 11 - 0
front/project/www/routes/page/export/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/export',
+  key: 'export',
+  title: '导出',
+  needLogin: false,
+  repeat: true,
+  hideHeader: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/www/routes/page/export/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#export {}

+ 3 - 1
front/project/www/routes/page/index.js

@@ -4,5 +4,7 @@ import order from './order';
 import cart from './cart';
 import demo from './demo';
 import contract from './contract';
+import ready from './ready';
+import exportDetail from './export';
 
-export default [home, login, order, cart, demo, contract];
+export default [home, login, order, cart, demo, contract, ready, exportDetail];

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

@@ -0,0 +1,11 @@
+export default {
+  path: '/ready',
+  key: 'ready',
+  title: 'GetReady',
+  needLogin: false,
+  repeat: true,
+  tab: 'ready',
+  component() {
+    return import('./page');
+  },
+};

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

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#ready {}

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

@@ -1,3 +1,5 @@
 import detail from './detail';
+import search from './search';
+import searchHistory from './searchHistory';
 
-export default [detail];
+export default [detail, search, searchHistory];

+ 11 - 0
front/project/www/routes/question/search/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/question/search',
+  key: 'question-search',
+  title: '题库搜索',
+  needLogin: false,
+  repeat: true,
+  tab: 'question',
+  component() {
+    return import('./page');
+  },
+};

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

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#question-search {}

+ 11 - 0
front/project/www/routes/question/searchHistory/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/question/search',
+  key: 'question-search',
+  title: '题库搜索历史',
+  needLogin: true,
+  repeat: true,
+  tab: 'question',
+  component() {
+    return import('./page');
+  },
+};

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

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#question-search {}

+ 5 - 1
front/project/www/routes/textbook/index.js

@@ -1,3 +1,7 @@
 import list from './list';
+import main from './main';
+import topic from './topic';
+import topicDetail from './topicDetail';
+import year from './year';
 
-export default [list];
+export default [list, main, topic, topicDetail, year];

+ 11 - 0
front/project/www/routes/textbook/main/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/textbook/list',
+  key: 'textbook-index',
+  title: '机经首页',
+  needLogin: false,
+  repeat: true,
+  tab: 'textbook',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/www/routes/textbook/main/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#textbook-index {}

+ 11 - 0
front/project/www/routes/textbook/topic/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/textbook/topic',
+  key: 'textbook-topic',
+  title: '机经目录',
+  needLogin: false,
+  repeat: true,
+  tab: 'textbook',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/www/routes/textbook/topic/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#textbook-topic {}

+ 11 - 0
front/project/www/routes/textbook/topicDetail/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/textbook/topic/detail',
+  key: 'textbook-topic-detail',
+  title: '机经内容页',
+  needLogin: false,
+  repeat: true,
+  tab: 'textbook',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/www/routes/textbook/topicDetail/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#textbook-topic-detail {}

+ 11 - 0
front/project/www/routes/textbook/year/index.js

@@ -0,0 +1,11 @@
+export default {
+  path: '/textbook/year',
+  key: 'textbook-year',
+  title: '年份换库表',
+  needLogin: false,
+  repeat: true,
+  tab: 'textbook',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/www/routes/textbook/year/index.less

@@ -0,0 +1,3 @@
+@charset "utf-8";
+
+#textbook-year {}

+ 11 - 3
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java

@@ -6,9 +6,9 @@ package com.qxgmat.data.constants.enums;
 public enum MessageCategory {
     REGISTER("register", "注册消息"),
     LOGIN_ABNORMAL("login_abnormal", "登录异常"),
-    TEXTBOOK_UPDATE("textbook_update","机经更新"),
+    TEXTBOOK_LIBRARY("textbook_library","机经换库"),
     PREVIEW_NOTICE("preview_notice", "预习作业提醒"),
-    PAYED("payed", "支付成功提醒"),
+
     DATA_UPDATE("data_update", "资料更新"),
     ASK_QUESTION("ask_question", "题目提问回复"),
     ASK_COURSE("ask_course", "课程提问回复"),
@@ -18,7 +18,15 @@ public enum MessageCategory {
     INVITED("invited", "邀请好友注册"),
     EMAIL_CHANGE("email_change", "邮箱变更"),
     EMAIL_UNNBIND("email_unbind", "邮箱解绑"),
-    EMAIL_BIND("email_bind", "邮箱绑定"),
+
+    COURSE_USE_EXPIRE("course_use_expire", "课程使用到期提醒"),
+    COURSE_EXPIRE("course_expire", "课程开通到期提醒"),
+
+    TEXTBOOK_USE_EXPIRE("textbook_use_expire", "机经使用到期提醒"),
+    TEXTBOOK_EXPIRE("textbook_expire", "机经开通到期提醒"),
+
+    QX_CAT_USE_EXPIRE("qx_cat_use_expire", "模考使用到期提醒"),
+    QX_CAT_EXPIRE("qx_cat_expire", "模考开通到期提醒"),
 
     CUSTOM("custom", "自定义消息")
     ;

+ 12 - 3
server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java

@@ -59,6 +59,9 @@ public class CourseExperience implements Serializable {
     @Column(name = "`experience_percent`")
     private String experiencePercent;
 
+    /**
+     * 考试时间
+     */
     @Column(name = "`experience_time`")
     private Date experienceTime;
 
@@ -253,14 +256,18 @@ public class CourseExperience implements Serializable {
     }
 
     /**
-     * @return experience_time
+     * 获取考试时间
+     *
+     * @return experience_time - 考试时间
      */
     public Date getExperienceTime() {
         return experienceTime;
     }
 
     /**
-     * @param experienceTime
+     * 设置考试时间
+     *
+     * @param experienceTime 考试时间
      */
     public void setExperienceTime(Date experienceTime) {
         this.experienceTime = experienceTime;
@@ -492,7 +499,9 @@ public class CourseExperience implements Serializable {
         }
 
         /**
-         * @param experienceTime
+         * 设置考试时间
+         *
+         * @param experienceTime 考试时间
          */
         public Builder experienceTime(Date experienceTime) {
             obj.setExperienceTime(experienceTime);

+ 105 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibraryHistory.java

@@ -30,6 +30,12 @@ public class TextbookLibraryHistory implements Serializable {
     private Integer quantVersion;
 
     /**
+     * 数学更新简介
+     */
+    @Column(name = "`quant_description`")
+    private String quantDescription;
+
+    /**
      * 阅读
      */
     @Column(name = "`rc`")
@@ -42,6 +48,12 @@ public class TextbookLibraryHistory implements Serializable {
     private Integer rcVersion;
 
     /**
+     * 阅读更新简介
+     */
+    @Column(name = "`rc_description`")
+    private String rcDescription;
+
+    /**
      * 综合推理
      */
     @Column(name = "`ir`")
@@ -53,6 +65,12 @@ public class TextbookLibraryHistory implements Serializable {
     @Column(name = "`ir_version`")
     private Integer irVersion;
 
+    /**
+     * 综合推理更新简介
+     */
+    @Column(name = "`ir_description`")
+    private String irDescription;
+
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -145,6 +163,24 @@ public class TextbookLibraryHistory implements Serializable {
     }
 
     /**
+     * 获取数学更新简介
+     *
+     * @return quant_description - 数学更新简介
+     */
+    public String getQuantDescription() {
+        return quantDescription;
+    }
+
+    /**
+     * 设置数学更新简介
+     *
+     * @param quantDescription 数学更新简介
+     */
+    public void setQuantDescription(String quantDescription) {
+        this.quantDescription = quantDescription;
+    }
+
+    /**
      * 获取阅读
      *
      * @return rc - 阅读
@@ -181,6 +217,24 @@ public class TextbookLibraryHistory implements Serializable {
     }
 
     /**
+     * 获取阅读更新简介
+     *
+     * @return rc_description - 阅读更新简介
+     */
+    public String getRcDescription() {
+        return rcDescription;
+    }
+
+    /**
+     * 设置阅读更新简介
+     *
+     * @param rcDescription 阅读更新简介
+     */
+    public void setRcDescription(String rcDescription) {
+        this.rcDescription = rcDescription;
+    }
+
+    /**
      * 获取综合推理
      *
      * @return ir - 综合推理
@@ -217,6 +271,24 @@ public class TextbookLibraryHistory implements Serializable {
     }
 
     /**
+     * 获取综合推理更新简介
+     *
+     * @return ir_description - 综合推理更新简介
+     */
+    public String getIrDescription() {
+        return irDescription;
+    }
+
+    /**
+     * 设置综合推理更新简介
+     *
+     * @param irDescription 综合推理更新简介
+     */
+    public void setIrDescription(String irDescription) {
+        this.irDescription = irDescription;
+    }
+
+    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -294,10 +366,13 @@ public class TextbookLibraryHistory implements Serializable {
         sb.append(", libraryId=").append(libraryId);
         sb.append(", quant=").append(quant);
         sb.append(", quantVersion=").append(quantVersion);
+        sb.append(", quantDescription=").append(quantDescription);
         sb.append(", rc=").append(rc);
         sb.append(", rcVersion=").append(rcVersion);
+        sb.append(", rcDescription=").append(rcDescription);
         sb.append(", ir=").append(ir);
         sb.append(", irVersion=").append(irVersion);
+        sb.append(", irDescription=").append(irDescription);
         sb.append(", createTime=").append(createTime);
         sb.append(", quantContent=").append(quantContent);
         sb.append(", rcContent=").append(rcContent);
@@ -356,6 +431,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置数学更新简介
+         *
+         * @param quantDescription 数学更新简介
+         */
+        public Builder quantDescription(String quantDescription) {
+            obj.setQuantDescription(quantDescription);
+            return this;
+        }
+
+        /**
          * 设置数学版本
          *
          * @param quantVersion 数学版本
@@ -386,6 +471,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置阅读更新简介
+         *
+         * @param rcDescription 阅读更新简介
+         */
+        public Builder rcDescription(String rcDescription) {
+            obj.setRcDescription(rcDescription);
+            return this;
+        }
+
+        /**
          * 设置阅读版本
          *
          * @param rcVersion 阅读版本
@@ -416,6 +511,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置综合推理更新简介
+         *
+         * @param irDescription 综合推理更新简介
+         */
+        public Builder irDescription(String irDescription) {
+            obj.setIrDescription(irDescription);
+            return this;
+        }
+
+        /**
          * 设置综合推理版本
          *
          * @param irVersion 综合推理版本

+ 140 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserMessage.java

@@ -30,6 +30,12 @@ public class UserMessage implements Serializable {
     private String messageCategory;
 
     /**
+     * 关联id
+     */
+    @Column(name = "`relation_id`")
+    private Integer relationId;
+
+    /**
      * 标题
      */
     @Column(name = "`title`")
@@ -42,6 +48,24 @@ public class UserMessage implements Serializable {
     private String link;
 
     /**
+     * 链接标题
+     */
+    @Column(name = "`link_title`")
+    private String linkTitle;
+
+    /**
+     * 链接2
+     */
+    @Column(name = "`link_second`")
+    private String linkSecond;
+
+    /**
+     * 链接2标题
+     */
+    @Column(name = "`link_second_title`")
+    private String linkSecondTitle;
+
+    /**
      * 已读状态:0未读,1已读
      */
     @Column(name = "`is_read`")
@@ -127,6 +151,24 @@ public class UserMessage implements Serializable {
     }
 
     /**
+     * 获取关联id
+     *
+     * @return relation_id - 关联id
+     */
+    public Integer getRelationId() {
+        return relationId;
+    }
+
+    /**
+     * 设置关联id
+     *
+     * @param relationId 关联id
+     */
+    public void setRelationId(Integer relationId) {
+        this.relationId = relationId;
+    }
+
+    /**
      * 获取标题
      *
      * @return title - 标题
@@ -163,6 +205,60 @@ public class UserMessage implements Serializable {
     }
 
     /**
+     * 获取链接标题
+     *
+     * @return link_title - 链接标题
+     */
+    public String getLinkTitle() {
+        return linkTitle;
+    }
+
+    /**
+     * 设置链接标题
+     *
+     * @param linkTitle 链接标题
+     */
+    public void setLinkTitle(String linkTitle) {
+        this.linkTitle = linkTitle;
+    }
+
+    /**
+     * 获取链接2
+     *
+     * @return link_second - 链接2
+     */
+    public String getLinkSecond() {
+        return linkSecond;
+    }
+
+    /**
+     * 设置链接2
+     *
+     * @param linkSecond 链接2
+     */
+    public void setLinkSecond(String linkSecond) {
+        this.linkSecond = linkSecond;
+    }
+
+    /**
+     * 获取链接2标题
+     *
+     * @return link_second_title - 链接2标题
+     */
+    public String getLinkSecondTitle() {
+        return linkSecondTitle;
+    }
+
+    /**
+     * 设置链接2标题
+     *
+     * @param linkSecondTitle 链接2标题
+     */
+    public void setLinkSecondTitle(String linkSecondTitle) {
+        this.linkSecondTitle = linkSecondTitle;
+    }
+
+    /**
      * 获取已读状态:0未读,1已读
      *
      * @return is_read - 已读状态:0未读,1已读
@@ -222,8 +318,12 @@ public class UserMessage implements Serializable {
         sb.append(", userId=").append(userId);
         sb.append(", type=").append(type);
         sb.append(", messageCategory=").append(messageCategory);
+        sb.append(", relationId=").append(relationId);
         sb.append(", title=").append(title);
         sb.append(", link=").append(link);
+        sb.append(", linkTitle=").append(linkTitle);
+        sb.append(", linkSecond=").append(linkSecond);
+        sb.append(", linkSecondTitle=").append(linkSecondTitle);
         sb.append(", isRead=").append(isRead);
         sb.append(", createTime=").append(createTime);
         sb.append(", content=").append(content);
@@ -281,6 +381,16 @@ public class UserMessage implements Serializable {
         }
 
         /**
+         * 设置关联id
+         *
+         * @param relationId 关联id
+         */
+        public Builder relationId(Integer relationId) {
+            obj.setRelationId(relationId);
+            return this;
+        }
+
+        /**
          * 设置标题
          *
          * @param title 标题
@@ -301,6 +411,36 @@ public class UserMessage implements Serializable {
         }
 
         /**
+         * 设置链接2
+         *
+         * @param linkSecond 链接2
+         */
+        public Builder linkSecond(String linkSecond) {
+            obj.setLinkSecond(linkSecond);
+            return this;
+        }
+
+        /**
+         * 设置链接2标题
+         *
+         * @param linkSecondTitle 链接2标题
+         */
+        public Builder linkSecondTitle(String linkSecondTitle) {
+            obj.setLinkSecondTitle(linkSecondTitle);
+            return this;
+        }
+
+        /**
+         * 设置链接标题
+         *
+         * @param linkTitle 链接标题
+         */
+        public Builder linkTitle(String linkTitle) {
+            obj.setLinkTitle(linkTitle);
+            return this;
+        }
+
+        /**
          * 设置已读状态:0未读,1已读
          *
          * @param isRead 已读状态:0未读,1已读

+ 5 - 2
server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryHistoryMapper.xml

@@ -9,10 +9,13 @@
     <result column="library_id" jdbcType="INTEGER" property="libraryId" />
     <result column="quant" jdbcType="VARCHAR" property="quant" />
     <result column="quant_version" jdbcType="INTEGER" property="quantVersion" />
+    <result column="quant_description" jdbcType="VARCHAR" property="quantDescription" />
     <result column="rc" jdbcType="VARCHAR" property="rc" />
     <result column="rc_version" jdbcType="INTEGER" property="rcVersion" />
+    <result column="rc_description" jdbcType="VARCHAR" property="rcDescription" />
     <result column="ir" jdbcType="VARCHAR" property="ir" />
     <result column="ir_version" jdbcType="INTEGER" property="irVersion" />
+    <result column="ir_description" jdbcType="VARCHAR" property="irDescription" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
   <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.TextbookLibraryHistory">
@@ -27,8 +30,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `library_id`, `quant`, `quant_version`, `rc`, `rc_version`, `ir`, `ir_version`, 
-    `create_time`
+    `id`, `library_id`, `quant`, `quant_version`, `quant_description`, `rc`, `rc_version`, 
+    `rc_description`, `ir`, `ir_version`, `ir_description`, `create_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

+ 6 - 1
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMessageMapper.xml

@@ -9,8 +9,12 @@
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="type" jdbcType="VARCHAR" property="type" />
     <result column="message_category" jdbcType="VARCHAR" property="messageCategory" />
+    <result column="relation_id" jdbcType="INTEGER" property="relationId" />
     <result column="title" jdbcType="VARCHAR" property="title" />
     <result column="link" jdbcType="VARCHAR" property="link" />
+    <result column="link_title" jdbcType="VARCHAR" property="linkTitle" />
+    <result column="link_second" jdbcType="VARCHAR" property="linkSecond" />
+    <result column="link_second_title" jdbcType="VARCHAR" property="linkSecondTitle" />
     <result column="is_read" jdbcType="INTEGER" property="isRead" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
@@ -24,7 +28,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `type`, `message_category`, `title`, `link`, `is_read`, `create_time`
+    `id`, `user_id`, `type`, `message_category`, `relation_id`, `title`, `link`, `link_title`, 
+    `link_second`, `link_second_title`, `is_read`, `create_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

+ 4 - 0
server/data/src/main/resources/db/migration/V1__init_table.sql

@@ -1183,10 +1183,14 @@ CREATE TABLE user_message (
   id int(11) unsigned NOT NULL AUTO_INCREMENT,
   user_id int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
   type varchar(50) NOT NULL DEFAULT '' COMMENT '消息',
+  relation_id int(11) NOT NULL DEFAULT '0' COMMENT '关联id',
   message_category varchar(50) NOT NULL DEFAULT '' COMMENT '消息类型',
   title varchar(50) NOT NULL DEFAULT '' COMMENT '标题',
   content text COMMENT '内容',
   link varchar(255) NOT NULL DEFAULT '' COMMENT '链接',
+  link_title varchar(20) NOT NULL DEFAULT '' COMMENT '链接标题',
+  link_second varchar(255) NOT NULL DEFAULT '' COMMENT '链接2',
+  link_second_title varchar(255) NOT NULL DEFAULT '' COMMENT '链接2标题',
   is_read tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '已读状态:0未读,1已读',
   create_time datetime DEFAULT NULL,
   PRIMARY KEY (id),

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

@@ -22,6 +22,7 @@ import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.help.SmsHelp;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.UserServiceService;
+import com.qxgmat.service.extend.MessageExtendService;
 import com.qxgmat.service.extend.PreviewService;
 import com.qxgmat.service.inline.TextbookLibraryService;
 import com.qxgmat.service.inline.UserAbnormalService;
@@ -85,6 +86,9 @@ public class AuthController {
     @Autowired
     private PreviewService previewService;
 
+    @Autowired
+    private MessageExtendService messageExtendService;
+
 
     @RequestMapping(value = "/token", method = RequestMethod.POST)
     @ApiOperation(value = "验证token", httpMethod = "POST")
@@ -274,6 +278,7 @@ public class AuthController {
         // 未读消息
         Page<UserMessage> messageList = userMessageService.list(1, 4, user.getId(), null, 0);
         dto.setMessageNumber((int)messageList.getTotal());
+        messageExtendService.refreshMessage(messageList);
         dto.setMessages(messageList);
         // 未完成作业
         List<UserOrderRecord> recordList = userOrderRecordService.listWithCourse(1, 1000, null, null, true, false, null, null);

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

@@ -405,7 +405,7 @@ public class MyController {
     )  {
         User user = (User) shiroHelp.getLoginUser();
         Page<UserMessage> p = userMessageService.list(page, size, user.getId(), MessageType.ValueOf(messageType), read);
-
+        messageExtendService.refreshMessage(p);
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
 

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

@@ -11,14 +11,20 @@ public class TextbookLibraryHistoryDto {
 
     private String quantContent = "";
 
+    private String quantDescription = "";
+
     private String ir = "";
 
     private String irContent = "";
 
+    private String irDescription = "";
+
     private String rc = "";
 
     private String rcContent = "";
 
+    private String rcDescription = "";
+
     public Integer getLibraryId() {
         return libraryId;
     }
@@ -74,4 +80,28 @@ public class TextbookLibraryHistoryDto {
     public void setRcContent(String rcContent) {
         this.rcContent = rcContent;
     }
+
+    public String getQuantDescription() {
+        return quantDescription;
+    }
+
+    public void setQuantDescription(String quantDescription) {
+        this.quantDescription = quantDescription;
+    }
+
+    public String getIrDescription() {
+        return irDescription;
+    }
+
+    public void setIrDescription(String irDescription) {
+        this.irDescription = irDescription;
+    }
+
+    public String getRcDescription() {
+        return rcDescription;
+    }
+
+    public void setRcDescription(String rcDescription) {
+        this.rcDescription = rcDescription;
+    }
 }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/help/SmsHelp.java

@@ -29,7 +29,7 @@ public class SmsHelp {
     final public String[] VALID_TEMPLATE = new String[]{"34888",""}; // yzm: 验证码
 
     final public String[] ABNORMAL_TEMPLATE = new String[]{"34889", ""}; //time: yyyy年MM月dd日hh:mm:ss
-    final public String[] COURSE_EXPIRE_TEMPLATE = new String[]{"34891",""}; // yhnc: 用户昵称 %date%: 格式为:yyyy年MM月dd日 wktfw: 未开通的服务名称
+    final public String[] EXPIRE_TEMPLATE = new String[]{"34891",""}; // yhnc: 用户昵称 %date%: 格式为:yyyy年MM月dd日 wktfw: 未开通的服务名称
     final public String[] COURSE_USE_EXPIRE_TEMPLATE = new String[]{"34893", ""}; // yhnc: 用户昵称 %kcmc%: 课程名称 %date%: 格式为:yyyy年MM月dd日
     final public String[] LIBRARY_TEMPLATE = new String[]{}; // 最新换库时间是%zx_date%日,上次换库时间是%sc_date%日,间隔%jgts%天,库长%kcts%天。获取机经: %jjdz% ,也可搜索微信订阅号“%dyh%”查阅机经。
 

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

@@ -7,6 +7,7 @@ import com.qxgmat.data.constants.enums.MessageType;
 import com.qxgmat.data.constants.enums.status.AnswerStatus;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.help.MailHelp;
+import com.qxgmat.help.SmsHelp;
 import com.qxgmat.help.WechatHelp;
 import com.qxgmat.service.inline.MessageTemplateService;
 import com.qxgmat.service.inline.UserMessageService;
@@ -29,6 +30,9 @@ public class MessageExtendService {
     private WechatHelp wechatHelp;
 
     @Resource
+    private SmsHelp smsHelp;
+
+    @Resource
     private UserMessageService userMessageService;
 
     @Resource
@@ -90,6 +94,57 @@ public class MessageExtendService {
     }
 
     private void sendSms(String area, String mobile, MessageCategory category, Map<String, String> params){
+        String[] template;
+        Map<String, String> smsParams = new HashMap<>();
+        SimpleDateFormat sdf;
+        String time;
+        switch(category){
+            case LOGIN_ABNORMAL:
+                template = smsHelp.ABNORMAL_TEMPLATE;
+                // time: yyyy年MM月dd日hh:mm:ss
+                sdf = new SimpleDateFormat("yyyy年MM月dd hh:mm:ss");
+                smsParams.put("time", sdf.format(params.get("time")));
+                break;
+            case TEXTBOOK_LIBRARY:
+                template = smsHelp.LIBRARY_TEMPLATE;
+                // 最新换库时间是%zx_date%日
+                // 上次换库时间是%sc_date%日
+                // 间隔%jgts%天,库长%kcts%天
+                // 获取机经: %jjdz%
+                // 也可搜索微信订阅号“%dyh%”查阅机经。
+                smsParams.put("dyh", "");
+                smsParams.put("jjdz", pcUrl+"/textbook");
+                break;
+            case COURSE_USE_EXPIRE:
+                template = smsHelp.COURSE_USE_EXPIRE_TEMPLATE;
+                // yhnc: 用户昵称
+                // %kcmc%: 课程名称
+                // %date%: 格式为:yyyy年MM月dd日
+                sdf = new SimpleDateFormat("yyyy年MM月dd");
+                smsParams.put("date", sdf.format(params.get("time")));
+                smsParams.put("yhnc", params.get("nickname"));
+                smsParams.put("kcmc", params.get("title"));
+                break;
+            case COURSE_EXPIRE:
+            case TEXTBOOK_EXPIRE:
+            case QX_CAT_EXPIRE:
+                template = smsHelp.EXPIRE_TEMPLATE;
+                // yhnc: 用户昵称
+                // %date%: 格式为:yyyy年MM月dd日
+                // wktfw: 未开通的服务名称
+                sdf = new SimpleDateFormat("yyyy年MM月dd");
+                smsParams.put("date", sdf.format(params.get("time")));
+                smsParams.put("yhnc", params.get("nickname"));
+                smsParams.put("wktfw", params.get("title"));
+                break;
+            default:
+                // 其余没有模版id,不发送
+                return;
+        }
+        smsHelp.send(area, mobile, template, smsParams);
+    }
+
+    public void refreshMessage(List<UserMessage> messageList){
 
     }
 
@@ -128,7 +183,7 @@ public class MessageExtendService {
      */
     public void sendTextbookUpdate(User user, TextbookLibrary textbookLibrary){
         Map<String, String> map = new HashMap<>();
-        send(user, MessageCategory.TEXTBOOK_UPDATE, map);
+        send(user, MessageCategory.TEXTBOOK_LIBRARY, map);
     }
 
     /**