Browse Source

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

KaysonCui 5 years ago
parent
commit
3aae9d70e5
36 changed files with 3603 additions and 2595 deletions
  1. 2396 2374
      front/package-lock.json
  2. 21 10
      front/project/admin/routes/interaction/faq/page.js
  3. 1 1
      front/project/admin/routes/student/askCourseDetail/page.js
  4. 6 1
      front/project/www/routes/course/answer/page.js
  5. 1 1
      front/project/www/routes/paper/question/page.js
  6. 7 0
      front/project/www/routes/question/detail/page.js
  7. 86 15
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java
  8. 16 4
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageType.java
  9. 7 4
      server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java
  10. 1 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/trade/RecordSource.java
  11. 8 6
      server/data/src/main/java/com/qxgmat/data/constants/enums/user/AskTarget.java
  12. 24 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/user/FeedbackTarget.java
  13. 105 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibrary.java
  14. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserTextbookFeedback.java
  15. 7 3
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryMapper.xml
  16. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserTextbookFeedbackMapper.xml
  17. 8 1
      server/data/src/main/resources/db/migration/V1__init_table.sql
  18. 8 5
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/CourseController.java
  19. 8 7
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/QuestionController.java
  20. 14 4
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  21. 2 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/TextbookController.java
  22. 20 5
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  23. 3 3
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  24. 0 10
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/FaqDto.java
  25. 19 2
      server/gateway-api/src/main/java/com/qxgmat/help/MailHelp.java
  26. 2 2
      server/gateway-api/src/main/java/com/qxgmat/help/WechatHelp.java
  27. 16 0
      server/gateway-api/src/main/java/com/qxgmat/service/UserServiceService.java
  28. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java
  29. 541 89
      server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java
  30. 34 10
      server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java
  31. 6 3
      server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookLibraryService.java
  32. 74 2
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderRecordService.java
  33. 31 5
      server/gateway-api/src/main/java/com/qxgmat/task/AsyncTask.java
  34. 78 17
      server/gateway-api/src/main/java/com/qxgmat/task/ScheduledTask.java
  35. 3 2
      server/gateway-api/src/main/resources/application.yml
  36. 11 6
      server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudMail.java

File diff suppressed because it is too large
+ 2396 - 2374
front/package-lock.json


+ 21 - 10
front/project/admin/routes/interaction/faq/page.js

@@ -3,10 +3,10 @@ import './index.less';
 import Page from '@src/containers/Page';
 import Page from '@src/containers/Page';
 import Block from '@src/components/Block';
 import Block from '@src/components/Block';
 import FilterLayout from '@src/layouts/FilterLayout';
 import FilterLayout from '@src/layouts/FilterLayout';
-// import ActionLayout from '@src/layouts/ActionLayout';
+import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
 import TableLayout from '@src/layouts/TableLayout';
 import { getMap, formatDate, formatTreeData, bindSearch, flattenTree } from '@src/services/Tools';
 import { getMap, formatDate, formatTreeData, bindSearch, flattenTree } from '@src/services/Tools';
-import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { asyncSMessage, asyncForm, asyncDelConfirm } from '@src/services/AsyncTools';
 import { FaqChannel, SwitchSelect, AnswerStatus, MoneyRange } from '../../../../Constant';
 import { FaqChannel, SwitchSelect, AnswerStatus, MoneyRange } from '../../../../Constant';
 import { System } from '../../../stores/system';
 import { System } from '../../../stores/system';
 import { User } from '../../../stores/user';
 import { User } from '../../../stores/user';
@@ -25,6 +25,12 @@ const FaqChannelFlatten = flattenTree(FaqChannelTree, (row, item) => {
 const FaqChannelMap = getMap(FaqChannelFlatten, 'value', 'label');
 const FaqChannelMap = getMap(FaqChannelFlatten, 'value', 'label');
 export default class extends Page {
 export default class extends Page {
   init() {
   init() {
+    this.actionList = [{
+      key: 'ignore',
+      type: 'danger',
+      name: '批量忽略',
+      needSelect: 1,
+    }];
     this.formF = null;
     this.formF = null;
     this.itemList = [{
     this.itemList = [{
       key: 'id',
       key: 'id',
@@ -146,11 +152,6 @@ export default class extends Page {
       dataIndex: 'handler',
       dataIndex: 'handler',
       render: (text, record) => {
       render: (text, record) => {
         return <div className="table-button">
         return <div className="table-button">
-          {(
-            <a onClick={() => {
-              this.editAction(record);
-            }}>编辑</a>
-          )}
           {record.answerStatus === 0 && (
           {record.answerStatus === 0 && (
             <a onClick={() => {
             <a onClick={() => {
               this.answerAction(record);
               this.answerAction(record);
@@ -223,7 +224,7 @@ export default class extends Page {
 
 
   answerAction(row) {
   answerAction(row) {
     asyncForm('回复', this.answerList, row, data => {
     asyncForm('回复', this.answerList, row, data => {
-      data.sendUser = true;
+      data.status = 1;
       return System.editFAQ(data).then(() => {
       return System.editFAQ(data).then(() => {
         asyncSMessage('回复成功!');
         asyncSMessage('回复成功!');
         this.refresh();
         this.refresh();
@@ -240,6 +241,16 @@ export default class extends Page {
     });
     });
   }
   }
 
 
+  ignoreAction() {
+    const { selectedKeys } = this.state;
+    asyncDelConfirm('忽略确认', '是否忽略选中记录?', () => {
+      return Promise.all(selectedKeys.map(row => System.editFAQ({ id: row, status: 2 }))).then(() => {
+        asyncSMessage('操作成功!');
+        this.refresh();
+      });
+    });
+  }
+
   renderView() {
   renderView() {
     const { search } = this.state;
     const { search } = this.state;
     return <Block flex>
     return <Block flex>
@@ -252,11 +263,11 @@ export default class extends Page {
           data.channel = data.channel.join('-');
           data.channel = data.channel.join('-');
           this.search(data);
           this.search(data);
         }} />
         }} />
-      {/* <ActionLayout
+      <ActionLayout
         itemList={this.actionList}
         itemList={this.actionList}
         selectedKeys={this.state.selectedKeys}
         selectedKeys={this.state.selectedKeys}
         onAction={key => this.onAction(key)}
         onAction={key => this.onAction(key)}
-      /> */}
+      />
       <TableLayout
       <TableLayout
         columns={this.tableSort(this.columns)}
         columns={this.tableSort(this.columns)}
         list={this.state.list}
         list={this.state.list}

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

@@ -158,7 +158,7 @@ export default class extends Page {
           this.orderQuestion(oldIndex, newIndex);
           this.orderQuestion(oldIndex, newIndex);
         }}
         }}
         renderItem={(item) => (
         renderItem={(item) => (
-          <List.Item actions={[<Icon type='bars' className='icon' />, <Typography.Text copyable={{ text: `${PcUrl}/course/ask?askId=${item.id}` }} />]}>
+          <List.Item actions={[<Icon type='bars' className='icon' />, <Typography.Text copyable={{ text: `${PcUrl}/course/answer/${item.courseId}?courseNoId=${item.courseNoId}&askId=${item.id}` }} />]}>
             <Row style={{ width: '100%' }}>
             <Row style={{ width: '100%' }}>
               <Col span={11}>问题:<span dangerouslySetInnerHTML={{ __html: item.content }} /></Col>
               <Col span={11}>问题:<span dangerouslySetInnerHTML={{ __html: item.content }} /></Col>
               <Col span={11} offset={1}>答复:<span dangerouslySetInnerHTML={{ __html: item.answer }} /></Col>
               <Col span={11} offset={1}>答复:<span dangerouslySetInnerHTML={{ __html: item.answer }} /></Col>

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

@@ -88,6 +88,11 @@ export default class extends Page {
     });
     });
     // paixu
     // paixu
     Course.listAsk(Object.assign({ courseId: id }, this.state.search)).then(result => {
     Course.listAsk(Object.assign({ courseId: id }, this.state.search)).then(result => {
+      // 只显示单个提问
+      if (this.state.search.askId) {
+        const askId = Number(this.state.search.askId);
+        result.list = result.list.filter(row => row.id === askId);
+      }
       this.setState({ list: result.list, total: result.total });
       this.setState({ list: result.list, total: result.total });
     });
     });
   }
   }
@@ -121,7 +126,7 @@ export default class extends Page {
     this.initData();
     this.initData();
   }
   }
 
 
-  onAction() {}
+  onAction() { }
 
 
   delAsk(id) {
   delAsk(id) {
     My.delCourseAsk(id).then(() => {
     My.delCourseAsk(id).then(() => {

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

@@ -80,7 +80,7 @@ export default class extends Page {
       // 只显示单个提问
       // 只显示单个提问
       if (search.askId) {
       if (search.askId) {
         const askId = Number(search.askId);
         const askId = Number(search.askId);
-        userQuestion.asks = (userQuestion.asks || []).filter(row => row.askId === askId);
+        userQuestion.asks = (userQuestion.asks || []).filter(row => row.id === askId);
       }
       }
       this.setState({ userQuestion, question, questionNo, note, paper, report, questionNos, questionStatus });
       this.setState({ userQuestion, question, questionNo, note, paper, report, questionNos, questionStatus });
     });
     });

+ 7 - 0
front/project/www/routes/question/detail/page.js

@@ -7,6 +7,7 @@ import Detail from '../../paper/question/detail';
 export default class extends Page {
 export default class extends Page {
   initData() {
   initData() {
     const { id } = this.params;
     const { id } = this.params;
+    const { search } = this.state;
     Question.getInfoById(id).then(userQuestion => {
     Question.getInfoById(id).then(userQuestion => {
       const { question, questionNos, note, questionStatus } = userQuestion;
       const { question, questionNos, note, questionStatus } = userQuestion;
       let { questionNo, paper } = userQuestion;
       let { questionNo, paper } = userQuestion;
@@ -15,6 +16,12 @@ export default class extends Page {
       if (!question.answerDistributed) question.answerDistributed = { questions: [] };
       if (!question.answerDistributed) question.answerDistributed = { questions: [] };
       if (!userQuestion.userAnswer) userQuestion.userAnswer = { questions: [] };
       if (!userQuestion.userAnswer) userQuestion.userAnswer = { questions: [] };
       if (!paper) paper = {};
       if (!paper) paper = {};
+
+      // 只显示单个提问
+      if (search.askId) {
+        const askId = Number(search.askId);
+        userQuestion.asks = (userQuestion.asks || []).filter(row => row.id === askId);
+      }
       this.setState({ userQuestion, question, questionNo, note, paper, questionNos, questionStatus });
       this.setState({ userQuestion, question, questionNo, note, paper, questionNos, questionStatus });
     });
     });
   }
   }

+ 86 - 15
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java

@@ -5,28 +5,63 @@ package com.qxgmat.data.constants.enums;
  */
  */
 public enum MessageCategory {
 public enum MessageCategory {
     REGISTER("register", "注册消息"),
     REGISTER("register", "注册消息"),
+    REAL("real", "实名认证"),
+    PREPARE("prepare", "完善备考信息"),
+    INVITED("invited", "邀请好友注册","/", "立即注册"),
+    INVITED_SUCCESS("invited_success", "邀请成功"),
+    EMAIL_CHANGE("email_change", "邮箱变更"),
+    EMAIL_UNNBIND("email_unbind", "邮箱解绑"),
     LOGIN_ABNORMAL("login_abnormal", "登录异常"),
     LOGIN_ABNORMAL("login_abnormal", "登录异常"),
-    TEXTBOOK_LIBRARY("textbook_library","机经换库"),
+
+    TEXTBOOK_LIBRARY("textbook_library","机经换库", "/textbook", "获取机经"),
+    TEXTBOOK_UPDATE_SUBSCRIBE("textbook_update_subscribe","机经更新-订阅", "/textbook", "在线查看"),
+    TEXTBOOK_UPDATE("textbook_update","机经更新", "/textbook", "在线查看"),
+
     PREVIEW_NOTICE("preview_notice", "预习作业提醒"),
     PREVIEW_NOTICE("preview_notice", "预习作业提醒"),
 
 
-    DATA_UPDATE("data_update", "资料更新"),
+    DATA_UPDATE_PAPER("data_update_paper", "纸质资料更新", "/course/data/detail/{id}", "在线查看","/course/data?tab=history&dataId={id}", "更新日志"),
-    ASK_QUESTION("ask_question", "题目提问回复"),
+    DATA_UPDATE_BASE("data_update_base", "资料更新", "/course/data/detail/{id}", "在线查看","/course/data?tab=history&dataId={id}", "更新日志"),
-    ASK_COURSE("ask_course", "课程提问回复"),
+    DATA_UPDATE_SUBSCRIBE("data_update_subscribe", "资料订阅","/course/data/detail/{id}", "在线查看","/course/data?tab=history&dataId={id}", "更新日志"),
-    FAQ_CALLBACK("faq_callback", "咨询回复"),
-    FEEDBACK_CALLBACK("feedback_callback", "纠错回复"),
 
 
-    INVITED("invited", "邀请好友注册"),
+    ASK_QUESTION_HANDLE("ask_question_handle", "题目提问回复"),
-    EMAIL_CHANGE("email_change", "邮箱变更"),
+    ASK_QUESTION_SPECIAL("ask_question_special", "题目提问回复", "/question/detail/{questionNoId}?askId={id}", "查看详情"),
-    EMAIL_UNNBIND("email_unbind", "邮箱解绑"),
+    ASK_QUESTION_IGNORE("ask_question_ignore", "题目提问忽略"),
+    ASK_COURSE_HANDLE("ask_course_handle", "课程提问回复"),
+    ASK_COURSE_SPECIAL("ask_course_special", "课程提问回复", "/course/answer/{courseId}?courseNoId={courseNoId}&askId={id}", "查看详情"),
+    ASK_COURSE_IGNORE("ask_course_ignore", "课程提问忽略"),
+
+    FAQ_HANDLE("faq_handle", "咨询回复-回答"),
+    FAQ_IGNORE("faq_ignore", "咨询回复-忽略"),
+
+    FEEDBACK_ERROR_HANDLE("feedback_error_handle", "纠错回复-采纳"),
+    FEEDBACK_ERROR_IGNORE("feedback_error_ignore", "纠错回复-忽略"),
+    FEEDBACK_ERROR_NOHANDLE("feedback_error_nohandle", "纠错回复-不处理"),
 
 
-    COURSE_USE_EXPIRE("course_use_expire", "课程使用到期提醒"),
+    TEXTBOOK_FEEDBACK_HANDLE("textbook_feedback_handle", "机经反馈回复-采纳"),
-    COURSE_EXPIRE("course_expire", "课程开通到期提醒"),
+    TEXTBOOK_FEEDBACK_IGNORE("textbook_feedback_ignore", "机经反馈回复-忽略"),
+    TEXTBOOK_FEEDBACK_NOHANDLE("textbook_feedback_nohandle", "机经反馈回复-不处理"),
 
 
-    TEXTBOOK_USE_EXPIRE("textbook_use_expire", "机经使用到期提醒"),
+    PAY_MULTI("pay_multi", "多个购买"),
-    TEXTBOOK_EXPIRE("textbook_expire", "机经开通到期提醒"),
 
 
-    QX_CAT_USE_EXPIRE("qx_cat_use_expire", "模考使用到期提醒"),
+    DATA_PAY("data_pay", "资料购买"),
-    QX_CAT_EXPIRE("qx_cat_expire", "模考开通到期提醒"),
+    DATA_PAY_MULTI("data_pay_multi", "多份资料购买"),
+
+    COURSE_PAY("course_pay", "课程购买"),
+    COURSE_PAY_MULTI("course_pay_multi", "多个课程购买"),
+    COURSE_USE_EXPIRE("course_use_expire", "课程使用到期提醒", "/my/course", "立刻使用", true),
+    COURSE_OPEN_EXPIRE("course_open_expire", "课程开通到期提醒", "/my/course", "立即开通"),
+    COURSE_GIFT("course_gift", "课程赠品"),
+
+    TEXTBOOK_PAY("textbook_pay", "机经购买", "/my/tools?tab=textbook", "立即开通"),
+    TEXTBOOK_USE_EXPIRE("textbook_use_expire", "机经使用到期提醒","/textbook", "立刻使用", true),
+    TEXTBOOK_OPEN_EXPIRE("textbook_open_expire", "机经开通到期提醒", "/my/tools?tab=textbook", "立即开通"),
+
+    QX_CAT_PAY("qx_cat_pay", "模考购买", "/my/tools?tab=examination", "立即开通"),
+    QX_CAT_USE_EXPIRE("qx_cat_use_expire", "模考使用到期提醒", "/examination", "立刻使用", true),
+    QX_CAT_OPEN_EXPIRE("qx_cat_open_expire", "模考开通到期提醒", "/my/tools?tab=examination", "立即开通"),
+
+    VIP_PAY("vip_pay", "vip购买"),
+    VIP_USE_EXPIRE("vip_use_expire", "vip使用到期提醒", "/my", "立刻使用", true),
 
 
     CUSTOM("custom", "自定义消息")
     CUSTOM("custom", "自定义消息")
     ;
     ;
@@ -35,11 +70,47 @@ public enum MessageCategory {
 
 
     public String key;
     public String key;
     public String title;
     public String title;
+
+    public String link;
+    public String linkTitle;
+    // 只有email使用该link
+    public Boolean emailLink = false;
+
+    public String linkSecond;
+    public String linkSecondTitle;
     private MessageCategory(String key, String title){
     private MessageCategory(String key, String title){
         this.key = key;
         this.key = key;
         this.title = title;
         this.title = title;
     }
     }
 
 
+    private MessageCategory(String key, String title, String link, String linkTitle){
+        this.key = key;
+        this.title = title;
+
+        this.link = link;
+        this.linkTitle = linkTitle;
+    }
+
+    private MessageCategory(String key, String title, String link, String linkTitle, Boolean emailLink){
+        this.key = key;
+        this.title = title;
+
+        this.link = link;
+        this.linkTitle = linkTitle;
+        this.emailLink = true;
+    }
+
+    private MessageCategory(String key, String title, String link, String linkTitle, String linkSecond, String linkSecondTitle){
+        this.key = key;
+        this.title = title;
+
+        this.link = link;
+        this.linkTitle = linkTitle;
+
+        this.linkSecond = linkSecond;
+        this.linkSecondTitle = linkSecondTitle;
+    }
+
     public static MessageCategory ValueOf(String name){
     public static MessageCategory ValueOf(String name){
         if (name == null) return null;
         if (name == null) return null;
         return MessageCategory.valueOf(name.toUpperCase());
         return MessageCategory.valueOf(name.toUpperCase());

+ 16 - 4
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageType.java

@@ -28,10 +28,22 @@ public enum MessageType {
 
 
     public static MessageType FromCategory(MessageCategory messageCategory){
     public static MessageType FromCategory(MessageCategory messageCategory){
         switch (messageCategory){
         switch (messageCategory){
-            case ASK_QUESTION:
+            case ASK_QUESTION_HANDLE:
-            case ASK_COURSE:
+            case ASK_QUESTION_IGNORE:
-            case FAQ_CALLBACK:
+            case ASK_COURSE_HANDLE:
-            case FEEDBACK_CALLBACK:
+            case ASK_COURSE_IGNORE:
+            case FAQ_HANDLE:
+            case FAQ_IGNORE:
+            case FEEDBACK_ERROR_HANDLE:
+            case FEEDBACK_ERROR_IGNORE:
+            case FEEDBACK_ERROR_NOHANDLE:
+            case TEXTBOOK_FEEDBACK_HANDLE:
+            case TEXTBOOK_FEEDBACK_IGNORE:
+            case TEXTBOOK_FEEDBACK_NOHANDLE:
+            case PREVIEW_NOTICE:
+            case DATA_UPDATE_BASE:
+            case DATA_UPDATE_PAPER:
+            case DATA_UPDATE_SUBSCRIBE:
                 return FEED;
                 return FEED;
             default:
             default:
                 return SYSTEM;
                 return SYSTEM;

+ 7 - 4
server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java

@@ -1,9 +1,9 @@
 package com.qxgmat.data.constants.enums;
 package com.qxgmat.data.constants.enums;
 
 
 public enum ServiceKey {
 public enum ServiceKey {
-    VIP("vip", 0, 0), // 收藏和错题处的组卷、导出;笔记导出功能;部分解析只有VIP可以看;下载模考报告; 解锁完整版模考报告;“提问开放”期间有提问权限
+    VIP("vip", "VIP", 0, 0), // 收藏和错题处的组卷、导出;笔记导出功能;部分解析只有VIP可以看;下载模考报告; 解锁完整版模考报告;“提问开放”期间有提问权限
-    TEXTBOOK("textbook", 180, 30),
+    TEXTBOOK("textbook", "千行-CAT模考(6套)", 180, 30),
-    QX_CAT("qx_cat", 180, 180), // 可以考2次
+    QX_CAT("qx_cat", "千行机经", 180, 180), // 可以考2次
 
 
     ;
     ;
     public String key;
     public String key;
@@ -14,10 +14,13 @@ public enum ServiceKey {
     // 天数
     // 天数
     public Integer useExpireDay;
     public Integer useExpireDay;
 
 
-    private ServiceKey(String key, Integer expireDay, Integer useExpireDay){
+    public String title;
+
+    private ServiceKey(String key, String title, Integer expireDay, Integer useExpireDay){
         this.key = key;
         this.key = key;
         this.expireDay = expireDay;
         this.expireDay = expireDay;
         this.useExpireDay = useExpireDay;
         this.useExpireDay = useExpireDay;
+        this.title = title;
     }
     }
 
 
     public static ServiceKey ValueOf(String name){
     public static ServiceKey ValueOf(String name){

+ 1 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/trade/RecordSource.java

@@ -12,6 +12,7 @@ public enum RecordSource {
     // 赠送用gift开头,方便过滤
     // 赠送用gift开头,方便过滤
     GIFT_COURSE("gift_course"),
     GIFT_COURSE("gift_course"),
     GIFT_ACTIVITY("gift_activity"),
     GIFT_ACTIVITY("gift_activity"),
+    GIFT_ACTION("gift_action"),
 
 
     ;
     ;
     public String key;
     public String key;

+ 8 - 6
server/data/src/main/java/com/qxgmat/data/constants/enums/user/AskTarget.java

@@ -2,15 +2,17 @@ package com.qxgmat.data.constants.enums.user;
 
 
 
 
 public enum AskTarget {
 public enum AskTarget {
-    QUESTION("question"),
+    QUESTION("question", "题目"),
-    OFFICIAL("official"),
+    OFFICIAL("official", "官方解析"),
-    QX("qx"),
+    QX("qx", "千行解析"),
-    ASSOCIATION("association"),
+    ASSOCIATION("association", "题源联想"),
-    QA("qa"),
+    QA("qa", "相关问答"),
     ;
     ;
     public String key;
     public String key;
-    private AskTarget(String key){
+    public String title;
+    private AskTarget(String key, String title){
         this.key = key;
         this.key = key;
+        this.title = title;
     }
     }
 
 
     public static AskTarget ValueOf(String name){
     public static AskTarget ValueOf(String name){

+ 24 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/user/FeedbackTarget.java

@@ -0,0 +1,24 @@
+package com.qxgmat.data.constants.enums.user;
+
+
+public enum FeedbackTarget {
+    CORRECT("correct", "纠正解析", "正确的解析和答案是", "需要修改,已更正"),
+    PERFECT("perfect", "完善已有机经", "补充内容是", "已采纳"),
+    NEW("new", "千行解析", "新机经是", "已采纳"),
+    ;
+    public String key;
+    public String title;
+    public String result;
+    public String handle;
+    private FeedbackTarget(String key, String title, String result, String handle){
+        this.key = key;
+        this.title = title;
+        this.result = result;
+        this.handle = handle;
+    }
+
+    public static FeedbackTarget ValueOf(String name){
+        if (name == null || name.isEmpty()) return null;
+        return FeedbackTarget.valueOf(name.toUpperCase());
+    }
+}

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

@@ -42,6 +42,12 @@ public class TextbookLibrary implements Serializable {
     private Date quantTime;
     private Date quantTime;
 
 
     /**
     /**
+     * 数学更新简介
+     */
+    @Column(name = "`quant_description`")
+    private String quantDescription;
+
+    /**
      * 数学题目数
      * 数学题目数
      */
      */
     @Column(name = "`quant_number`")
     @Column(name = "`quant_number`")
@@ -66,6 +72,12 @@ public class TextbookLibrary implements Serializable {
     private Date irTime;
     private Date irTime;
 
 
     /**
     /**
+     * 综合逻辑更新简介
+     */
+    @Column(name = "`ir_description`")
+    private String irDescription;
+
+    /**
      * 综合逻辑题目数
      * 综合逻辑题目数
      */
      */
     @Column(name = "`ir_number`")
     @Column(name = "`ir_number`")
@@ -90,6 +102,12 @@ public class TextbookLibrary implements Serializable {
     private Date rcTime;
     private Date rcTime;
 
 
     /**
     /**
+     * 阅读更新简介
+     */
+    @Column(name = "`rc_description`")
+    private String rcDescription;
+
+    /**
      * 阅读题目数
      * 阅读题目数
      */
      */
     @Column(name = "`rc_number`")
     @Column(name = "`rc_number`")
@@ -220,6 +238,24 @@ public class TextbookLibrary implements Serializable {
     }
     }
 
 
     /**
     /**
+     * 获取数学更新简介
+     *
+     * @return quant_description - 数学更新简介
+     */
+    public String getQuantDescription() {
+        return quantDescription;
+    }
+
+    /**
+     * 设置数学更新简介
+     *
+     * @param quantDescription 数学更新简介
+     */
+    public void setQuantDescription(String quantDescription) {
+        this.quantDescription = quantDescription;
+    }
+
+    /**
      * 获取数学题目数
      * 获取数学题目数
      *
      *
      * @return quant_number - 数学题目数
      * @return quant_number - 数学题目数
@@ -292,6 +328,24 @@ public class TextbookLibrary implements Serializable {
     }
     }
 
 
     /**
     /**
+     * 获取综合逻辑更新简介
+     *
+     * @return ir_description - 综合逻辑更新简介
+     */
+    public String getIrDescription() {
+        return irDescription;
+    }
+
+    /**
+     * 设置综合逻辑更新简介
+     *
+     * @param irDescription 综合逻辑更新简介
+     */
+    public void setIrDescription(String irDescription) {
+        this.irDescription = irDescription;
+    }
+
+    /**
      * 获取综合逻辑题目数
      * 获取综合逻辑题目数
      *
      *
      * @return ir_number - 综合逻辑题目数
      * @return ir_number - 综合逻辑题目数
@@ -364,6 +418,24 @@ public class TextbookLibrary implements Serializable {
     }
     }
 
 
     /**
     /**
+     * 获取阅读更新简介
+     *
+     * @return rc_description - 阅读更新简介
+     */
+    public String getRcDescription() {
+        return rcDescription;
+    }
+
+    /**
+     * 设置阅读更新简介
+     *
+     * @param rcDescription 阅读更新简介
+     */
+    public void setRcDescription(String rcDescription) {
+        this.rcDescription = rcDescription;
+    }
+
+    /**
      * 获取阅读题目数
      * 获取阅读题目数
      *
      *
      * @return rc_number - 阅读题目数
      * @return rc_number - 阅读题目数
@@ -457,14 +529,17 @@ public class TextbookLibrary implements Serializable {
         sb.append(", quant=").append(quant);
         sb.append(", quant=").append(quant);
         sb.append(", quantVersion=").append(quantVersion);
         sb.append(", quantVersion=").append(quantVersion);
         sb.append(", quantTime=").append(quantTime);
         sb.append(", quantTime=").append(quantTime);
+        sb.append(", quantDescription=").append(quantDescription);
         sb.append(", quantNumber=").append(quantNumber);
         sb.append(", quantNumber=").append(quantNumber);
         sb.append(", ir=").append(ir);
         sb.append(", ir=").append(ir);
         sb.append(", irVersion=").append(irVersion);
         sb.append(", irVersion=").append(irVersion);
         sb.append(", irTime=").append(irTime);
         sb.append(", irTime=").append(irTime);
+        sb.append(", irDescription=").append(irDescription);
         sb.append(", irNumber=").append(irNumber);
         sb.append(", irNumber=").append(irNumber);
         sb.append(", rc=").append(rc);
         sb.append(", rc=").append(rc);
         sb.append(", rcVersion=").append(rcVersion);
         sb.append(", rcVersion=").append(rcVersion);
         sb.append(", rcTime=").append(rcTime);
         sb.append(", rcTime=").append(rcTime);
+        sb.append(", rcDescription=").append(rcDescription);
         sb.append(", rcNumber=").append(rcNumber);
         sb.append(", rcNumber=").append(rcNumber);
         sb.append(", historyNumber=").append(historyNumber);
         sb.append(", historyNumber=").append(historyNumber);
         sb.append(", questionStatus=").append(questionStatus);
         sb.append(", questionStatus=").append(questionStatus);
@@ -534,6 +609,16 @@ public class TextbookLibrary implements Serializable {
         }
         }
 
 
         /**
         /**
+         * 设置数学更新简介
+         *
+         * @param quantDescription 数学更新简介
+         */
+        public Builder quantDescription(String quantDescription) {
+            obj.setQuantDescription(quantDescription);
+            return this;
+        }
+
+        /**
          * 设置数学时间
          * 设置数学时间
          *
          *
          * @param quantTime 数学时间
          * @param quantTime 数学时间
@@ -574,6 +659,16 @@ public class TextbookLibrary implements Serializable {
         }
         }
 
 
         /**
         /**
+         * 设置综合逻辑更新简介
+         *
+         * @param irDescription 综合逻辑更新简介
+         */
+        public Builder irDescription(String irDescription) {
+            obj.setIrDescription(irDescription);
+            return this;
+        }
+
+        /**
          * 设置综合逻辑时间
          * 设置综合逻辑时间
          *
          *
          * @param irTime 综合逻辑时间
          * @param irTime 综合逻辑时间
@@ -614,6 +709,16 @@ public class TextbookLibrary implements Serializable {
         }
         }
 
 
         /**
         /**
+         * 设置阅读更新简介
+         *
+         * @param rcDescription 阅读更新简介
+         */
+        public Builder rcDescription(String rcDescription) {
+            obj.setRcDescription(rcDescription);
+            return this;
+        }
+
+        /**
          * 设置阅读时间
          * 设置阅读时间
          *
          *
          * @param rcTime 阅读时间
          * @param rcTime 阅读时间

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

@@ -42,6 +42,12 @@ public class UserTextbookFeedback implements Serializable {
     private Integer libraryId;
     private Integer libraryId;
 
 
     /**
     /**
+     * 处理人id
+     */
+    @Column(name = "`manager_id`")
+    private Integer managerId;
+
+    /**
      * 反馈类型
      * 反馈类型
      */
      */
     @Column(name = "`target`")
     @Column(name = "`target`")
@@ -175,6 +181,24 @@ public class UserTextbookFeedback implements Serializable {
     }
     }
 
 
     /**
     /**
+     * 获取处理人id
+     *
+     * @return manager_id - 处理人id
+     */
+    public Integer getManagerId() {
+        return managerId;
+    }
+
+    /**
+     * 设置处理人id
+     *
+     * @param managerId 处理人id
+     */
+    public void setManagerId(Integer managerId) {
+        this.managerId = managerId;
+    }
+
+    /**
      * 获取反馈类型
      * 获取反馈类型
      *
      *
      * @return target - 反馈类型
      * @return target - 反馈类型
@@ -272,6 +296,7 @@ public class UserTextbookFeedback implements Serializable {
         sb.append(", no=").append(no);
         sb.append(", no=").append(no);
         sb.append(", topicId=").append(topicId);
         sb.append(", topicId=").append(topicId);
         sb.append(", libraryId=").append(libraryId);
         sb.append(", libraryId=").append(libraryId);
+        sb.append(", managerId=").append(managerId);
         sb.append(", target=").append(target);
         sb.append(", target=").append(target);
         sb.append(", createTime=").append(createTime);
         sb.append(", createTime=").append(createTime);
         sb.append(", status=").append(status);
         sb.append(", status=").append(status);
@@ -351,6 +376,16 @@ public class UserTextbookFeedback implements Serializable {
         }
         }
 
 
         /**
         /**
+         * 设置处理人id
+         *
+         * @param managerId 处理人id
+         */
+        public Builder managerId(Integer managerId) {
+            obj.setManagerId(managerId);
+            return this;
+        }
+
+        /**
          * 设置反馈类型
          * 设置反馈类型
          *
          *
          * @param target 反馈类型
          * @param target 反馈类型

+ 7 - 3
server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryMapper.xml

@@ -11,14 +11,17 @@
     <result column="quant" jdbcType="VARCHAR" property="quant" />
     <result column="quant" jdbcType="VARCHAR" property="quant" />
     <result column="quant_version" jdbcType="INTEGER" property="quantVersion" />
     <result column="quant_version" jdbcType="INTEGER" property="quantVersion" />
     <result column="quant_time" jdbcType="TIMESTAMP" property="quantTime" />
     <result column="quant_time" jdbcType="TIMESTAMP" property="quantTime" />
+    <result column="quant_description" jdbcType="VARCHAR" property="quantDescription" />
     <result column="quant_number" jdbcType="INTEGER" property="quantNumber" />
     <result column="quant_number" jdbcType="INTEGER" property="quantNumber" />
     <result column="ir" jdbcType="VARCHAR" property="ir" />
     <result column="ir" jdbcType="VARCHAR" property="ir" />
     <result column="ir_version" jdbcType="INTEGER" property="irVersion" />
     <result column="ir_version" jdbcType="INTEGER" property="irVersion" />
     <result column="ir_time" jdbcType="TIMESTAMP" property="irTime" />
     <result column="ir_time" jdbcType="TIMESTAMP" property="irTime" />
+    <result column="ir_description" jdbcType="VARCHAR" property="irDescription" />
     <result column="ir_number" jdbcType="INTEGER" property="irNumber" />
     <result column="ir_number" jdbcType="INTEGER" property="irNumber" />
     <result column="rc" jdbcType="VARCHAR" property="rc" />
     <result column="rc" jdbcType="VARCHAR" property="rc" />
     <result column="rc_version" jdbcType="INTEGER" property="rcVersion" />
     <result column="rc_version" jdbcType="INTEGER" property="rcVersion" />
     <result column="rc_time" jdbcType="TIMESTAMP" property="rcTime" />
     <result column="rc_time" jdbcType="TIMESTAMP" property="rcTime" />
+    <result column="rc_description" jdbcType="VARCHAR" property="rcDescription" />
     <result column="rc_number" jdbcType="INTEGER" property="rcNumber" />
     <result column="rc_number" jdbcType="INTEGER" property="rcNumber" />
     <result column="history_number" jdbcType="INTEGER" property="historyNumber" />
     <result column="history_number" jdbcType="INTEGER" property="historyNumber" />
     <result column="question_status" jdbcType="INTEGER" property="questionStatus" />
     <result column="question_status" jdbcType="INTEGER" property="questionStatus" />
@@ -29,8 +32,9 @@
     <!--
     <!--
       WARNING - @mbg.generated
       WARNING - @mbg.generated
     -->
     -->
-    `id`, `start_date`, `end_date`, `quant`, `quant_version`, `quant_time`, `quant_number`, 
+    `id`, `start_date`, `end_date`, `quant`, `quant_version`, `quant_time`, `quant_description`, 
-    `ir`, `ir_version`, `ir_time`, `ir_number`, `rc`, `rc_version`, `rc_time`, `rc_number`, 
+    `quant_number`, `ir`, `ir_version`, `ir_time`, `ir_description`, `ir_number`, `rc`, 
-    `history_number`, `question_status`, `create_time`, `update_time`
+    `rc_version`, `rc_time`, `rc_description`, `rc_number`, `history_number`, `question_status`, 
+    `create_time`, `update_time`
   </sql>
   </sql>
 </mapper>
 </mapper>

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

@@ -11,6 +11,7 @@
     <result column="no" jdbcType="INTEGER" property="no" />
     <result column="no" jdbcType="INTEGER" property="no" />
     <result column="topic_id" jdbcType="INTEGER" property="topicId" />
     <result column="topic_id" jdbcType="INTEGER" property="topicId" />
     <result column="library_id" jdbcType="INTEGER" property="libraryId" />
     <result column="library_id" jdbcType="INTEGER" property="libraryId" />
+    <result column="manager_id" jdbcType="INTEGER" property="managerId" />
     <result column="target" jdbcType="VARCHAR" property="target" />
     <result column="target" jdbcType="VARCHAR" property="target" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="status" jdbcType="INTEGER" property="status" />
     <result column="status" jdbcType="INTEGER" property="status" />
@@ -26,8 +27,8 @@
     <!--
     <!--
       WARNING - @mbg.generated
       WARNING - @mbg.generated
     -->
     -->
-    `id`, `user_id`, `question_subject`, `no`, `topic_id`, `library_id`, `target`, `create_time`, 
+    `id`, `user_id`, `question_subject`, `no`, `topic_id`, `library_id`, `manager_id`, 
-    `status`, `handle_time`
+    `target`, `create_time`, `status`, `handle_time`
   </sql>
   </sql>
   <sql id="Blob_Column_List">
   <sql id="Blob_Column_List">
     <!--
     <!--

+ 8 - 1
server/data/src/main/resources/db/migration/V1__init_table.sql

@@ -373,7 +373,7 @@ CREATE TABLE message_template (
   id int(11) unsigned NOT NULL AUTO_INCREMENT,
   id int(11) unsigned NOT NULL AUTO_INCREMENT,
   title varchar(255) NOT NULL DEFAULT '' COMMENT '消息标题',
   title varchar(255) NOT NULL DEFAULT '' COMMENT '消息标题',
   message_method varchar(20) NOT NULL DEFAULT '' COMMENT '消息形式',
   message_method varchar(20) NOT NULL DEFAULT '' COMMENT '消息形式',
-  message_category varchar(20) NOT NULL DEFAULT '' COMMENT '消息类型',
+  message_category varchar(50) NOT NULL DEFAULT '' COMMENT '消息类型',
   content text COMMENT '消息内容',
   content text COMMENT '消息内容',
   link varchar(255) NOT NULL DEFAULT '' COMMENT '消息链接',
   link varchar(255) NOT NULL DEFAULT '' COMMENT '消息链接',
   send_time datetime DEFAULT NULL COMMENT '发送时间',
   send_time datetime DEFAULT NULL COMMENT '发送时间',
@@ -743,14 +743,17 @@ CREATE TABLE textbook_library (
   quant varchar(255) NOT NULL DEFAULT '' COMMENT '数学',
   quant varchar(255) NOT NULL DEFAULT '' COMMENT '数学',
   quant_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学版本',
   quant_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学版本',
   quant_time datetime DEFAULT NULL COMMENT '数学时间',
   quant_time datetime DEFAULT NULL COMMENT '数学时间',
+  quant_description varchar(255) DEFAULT NULL COMMENT '数学更新简介',
   quant_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学题目数',
   quant_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学题目数',
   ir varchar(255) NOT NULL DEFAULT '' COMMENT '综合逻辑',
   ir varchar(255) NOT NULL DEFAULT '' COMMENT '综合逻辑',
   ir_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合逻辑版本',
   ir_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合逻辑版本',
   ir_time datetime DEFAULT NULL COMMENT '综合逻辑时间',
   ir_time datetime DEFAULT NULL COMMENT '综合逻辑时间',
+  ir_description varchar(255) DEFAULT NULL COMMENT '综合逻辑更新简介',
   ir_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合逻辑题目数',
   ir_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合逻辑题目数',
   rc varchar(255) NOT NULL DEFAULT '' COMMENT '阅读',
   rc varchar(255) NOT NULL DEFAULT '' COMMENT '阅读',
   rc_version int(10) unsigned NOT NULL DEFAULT '0' COMMENT '阅读版本',
   rc_version int(10) unsigned NOT NULL DEFAULT '0' COMMENT '阅读版本',
   rc_time datetime DEFAULT NULL COMMENT '阅读时间',
   rc_time datetime DEFAULT NULL COMMENT '阅读时间',
+  rc_description varchar(255) DEFAULT NULL COMMENT '阅读更新简介',
   rc_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读题目数',
   rc_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读题目数',
   history_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新次数',
   history_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新次数',
   question_status tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '提问状态',
   question_status tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '提问状态',
@@ -842,12 +845,15 @@ CREATE TABLE textbook_library_history (
   quant varchar(255) DEFAULT NULL COMMENT '数学',
   quant varchar(255) DEFAULT NULL COMMENT '数学',
   quant_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学版本',
   quant_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '数学版本',
   quant_content text COMMENT '数学更新日志',
   quant_content text COMMENT '数学更新日志',
+  quant_description varchar(255) DEFAULT NULL COMMENT '数学更新简介',
   rc varchar(255) DEFAULT NULL COMMENT '阅读',
   rc varchar(255) DEFAULT NULL COMMENT '阅读',
   rc_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读版本',
   rc_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读版本',
   rc_content text COMMENT '阅读更新日志',
   rc_content text COMMENT '阅读更新日志',
+  rc_description varchar(255) DEFAULT NULL COMMENT '阅读更新简介',
   ir varchar(255) DEFAULT NULL COMMENT '综合推理',
   ir varchar(255) DEFAULT NULL COMMENT '综合推理',
   ir_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合推理版本',
   ir_version int(11) unsigned NOT NULL DEFAULT '0' COMMENT '综合推理版本',
   ir_content text COMMENT '综合推理更新日志',
   ir_content text COMMENT '综合推理更新日志',
+  ir_description varchar(255) DEFAULT NULL COMMENT '综合逻辑更新简介',
   create_time datetime DEFAULT NULL,
   create_time datetime DEFAULT NULL,
   PRIMARY KEY (id),
   PRIMARY KEY (id),
   KEY library_id (library_id)
   KEY library_id (library_id)
@@ -1461,6 +1467,7 @@ CREATE TABLE user_textbook_feedback (
   no int(11) unsigned NOT NULL DEFAULT 0 COMMENT '序号',
   no int(11) unsigned NOT NULL DEFAULT 0 COMMENT '序号',
   topic_id int(11) unsigned NOT NULL DEFAULT 0 COMMENT '机经问题',
   topic_id int(11) unsigned NOT NULL DEFAULT 0 COMMENT '机经问题',
   library_id int(11) unsigned NOT NULL COMMENT '换库表',
   library_id int(11) unsigned NOT NULL COMMENT '换库表',
+  manager_id int(11) unsigned NOT NULL DEFAULT 0 COMMENT '处理人id',
   target varchar(20) NOT NULL DEFAULT '' COMMENT '反馈类型',
   target varchar(20) NOT NULL DEFAULT '' COMMENT '反馈类型',
   content text COMMENT '正确内容',
   content text COMMENT '正确内容',
   create_time datetime DEFAULT NULL,
   create_time datetime DEFAULT NULL,

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

@@ -759,17 +759,20 @@ public class CourseController {
         UserAskCourse entity = Transform.dtoToEntity(dto);
         UserAskCourse entity = Transform.dtoToEntity(dto);
         UserAskCourse in = userAskCourseService.get(entity.getId());
         UserAskCourse in = userAskCourseService.get(entity.getId());
         // 调整回答
         // 调整回答
-        if(entity.getAnswer() != null && (!entity.getAnswer().isEmpty() || !in.getAnswer().equals(entity.getAnswer()))){
+        if(in.getAnswerStatus() == 0 && dto.getAnswer() != null || dto.getIgnoreStatus() > 0){
             entity.setAnswerTime(new Date());
             entity.setAnswerTime(new Date());
-            entity.setAnswerStatus(AnswerStatus.ANSWER.index);
+
+            if (dto.getIgnoreStatus() != null && dto.getIgnoreStatus() > 0){
+                entity.setAnswerStatus(AnswerStatus.IGNORE.index);
+            }else{
+                entity.setAnswerStatus(AnswerStatus.ANSWER.index);
+            }
             Manager manager = shiroHelp.getLoginManager();
             Manager manager = shiroHelp.getLoginManager();
             entity.setManagerId(manager.getId());
             entity.setManagerId(manager.getId());
+
             User user = usersService.get(in.getUserId());
             User user = usersService.get(in.getUserId());
             messageExtendService.sendAskCourse(user, entity);
             messageExtendService.sendAskCourse(user, entity);
         }
         }
-        if (dto.getIgnoreStatus() != null && dto.getIgnoreStatus() > 0){
-            entity.setAnswerStatus(AnswerStatus.IGNORE.index);
-        }
 
 
         entity = userAskCourseService.edit(entity);
         entity = userAskCourseService.edit(entity);
 
 

+ 8 - 7
server/gateway-api/src/main/java/com/qxgmat/controller/admin/QuestionController.java

@@ -181,18 +181,19 @@ public class QuestionController {
     public Response<Boolean> editAsk(@RequestBody @Validated UserAskQuestionDto dto, HttpServletRequest request) {
     public Response<Boolean> editAsk(@RequestBody @Validated UserAskQuestionDto dto, HttpServletRequest request) {
         UserAskQuestion entity = Transform.dtoToEntity(dto);
         UserAskQuestion entity = Transform.dtoToEntity(dto);
         UserAskQuestion in = userAskQuestionService.get(entity.getId());
         UserAskQuestion in = userAskQuestionService.get(entity.getId());
+
         // 调整回答
         // 调整回答
-        if(entity.getAnswer() != null && (!entity.getAnswer().isEmpty() || !in.getAnswer().equals(entity.getAnswer()))){
+        if(in.getAnswerStatus() == 0 && dto.getAnswer() != null || dto.getIgnoreStatus() > 0){
             entity.setAnswerTime(new Date());
             entity.setAnswerTime(new Date());
-            entity.setAnswerStatus(AnswerStatus.ANSWER.index);
+
+            if (dto.getIgnoreStatus() != null && dto.getIgnoreStatus() > 0){
+                entity.setAnswerStatus(AnswerStatus.IGNORE.index);
+            }else{
+                entity.setAnswerStatus(AnswerStatus.ANSWER.index);
+            }
             Manager manager = shiroHelp.getLoginManager();
             Manager manager = shiroHelp.getLoginManager();
             entity.setManagerId(manager.getId());
             entity.setManagerId(manager.getId());
-            User user = usersService.get(in.getUserId());
-            messageExtendService.sendAskQuestion(user, entity);
-        }
 
 
-        if (dto.getIgnoreStatus() != null && dto.getIgnoreStatus() > 0){
-            entity.setAnswerStatus(AnswerStatus.IGNORE.index);
             User user = usersService.get(in.getUserId());
             User user = usersService.get(in.getUserId());
             messageExtendService.sendAskQuestion(user, entity);
             messageExtendService.sendAskQuestion(user, entity);
         }
         }

+ 14 - 4
server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java

@@ -19,6 +19,7 @@ import com.qxgmat.dto.admin.response.FaqInfoDto;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.MessageExtendService;
 import com.qxgmat.service.extend.MessageExtendService;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.inline.*;
 import com.qxgmat.service.inline.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
@@ -90,6 +91,9 @@ public class SettingController {
     @Autowired
     @Autowired
     private CourseDataService courseDataService;
     private CourseDataService courseDataService;
 
 
+    @Autowired
+    private OrderFlowService orderFlowService;
+
     @RequestMapping(value = "/index", method = RequestMethod.PUT)
     @RequestMapping(value = "/index", method = RequestMethod.PUT)
     @ApiOperation(value = "修改首页配置", httpMethod = "PUT")
     @ApiOperation(value = "修改首页配置", httpMethod = "PUT")
     private Response<Boolean> editIndex(@RequestBody @Validated JSONObject dto){
     private Response<Boolean> editIndex(@RequestBody @Validated JSONObject dto){
@@ -553,14 +557,20 @@ public class SettingController {
     private Response<Boolean> editFaq(@RequestBody @Validated FaqDto dto){
     private Response<Boolean> editFaq(@RequestBody @Validated FaqDto dto){
         Faq entity = Transform.dtoToEntity(dto);
         Faq entity = Transform.dtoToEntity(dto);
         Faq in = faqService.get(entity.getId());
         Faq in = faqService.get(entity.getId());
-        // 调整回答
+
-        if(entity.getAnswer() != null && (!entity.getAnswer().isEmpty() || !in.getAnswer().equals(entity.getAnswer()))){
+        // 回复
+        if(in.getAnswerStatus() == 0){
             entity.setAnswerTime(new Date());
             entity.setAnswerTime(new Date());
-            entity.setAnswerStatus(AnswerStatus.ANSWER.index);
             Manager manager = shiroHelp.getLoginManager();
             Manager manager = shiroHelp.getLoginManager();
             entity.setManagerId(manager.getId());
             entity.setManagerId(manager.getId());
-            User user = usersService.get(in.getUserId());
+            entity.setAnswerStatus(dto.getStatus());
+
+            in.setAnswerStatus(entity.getAnswerStatus());
+            in.setAnswer(entity.getAnswer());
             if(in.getIsSystem() == 0){
             if(in.getIsSystem() == 0){
+                in.setAnswerStatus(dto.getStatus());
+                User user = usersService.get(in.getUserId());
+                UserOrderRecord record = null;
                 messageExtendService.sendFaqCallback(user, in);
                 messageExtendService.sendFaqCallback(user, in);
             }
             }
         }
         }

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

@@ -159,6 +159,8 @@ public class TextbookController {
     public Response<Boolean> addLibrary(@RequestBody @Validated TextbookLibraryStartDto dto, HttpServletRequest request) {
     public Response<Boolean> addLibrary(@RequestBody @Validated TextbookLibraryStartDto dto, HttpServletRequest request) {
         TextbookLibrary entity = Transform.dtoToEntity(dto);
         TextbookLibrary entity = Transform.dtoToEntity(dto);
         textbookService.replace(entity);
         textbookService.replace(entity);
+        // 发送机经到用户邮箱
+        asyncTask.postTextbookLibrary();
         managerLogService.log(request);
         managerLogService.log(request);
         return ResponseHelp.success(true);
         return ResponseHelp.success(true);
     }
     }

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

@@ -6,6 +6,7 @@ import com.nuliji.tools.exception.ParameterException;
 import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.constants.enums.module.FeedbackModule;
 import com.qxgmat.data.constants.enums.module.FeedbackModule;
 import com.qxgmat.data.constants.enums.module.ProductType;
 import com.qxgmat.data.constants.enums.module.ProductType;
+import com.qxgmat.data.constants.enums.status.AnswerStatus;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.constants.enums.status.FeedbackStatus;
 import com.qxgmat.data.constants.enums.status.FeedbackStatus;
 import com.qxgmat.data.constants.enums.trade.PayMethod;
 import com.qxgmat.data.constants.enums.trade.PayMethod;
@@ -210,7 +211,7 @@ public class UserController {
         entity.setRealTime(new Date());
         entity.setRealTime(new Date());
         usersService.edit(entity);
         usersService.edit(entity);
 
 
-        orderFlowService.giveReal(in.getId());
+        orderFlowService.giveReal(in);
         return ResponseHelp.success(true);
         return ResponseHelp.success(true);
     }
     }
 
 
@@ -495,8 +496,14 @@ public class UserController {
             entity.setHandleTime(new Date());
             entity.setHandleTime(new Date());
             Manager manager = shiroHelp.getLoginManager();
             Manager manager = shiroHelp.getLoginManager();
             entity.setManagerId(manager.getId());
             entity.setManagerId(manager.getId());
+
+            in.setStatus(dto.getStatus());
             User user = usersService.get(in.getUserId());
             User user = usersService.get(in.getUserId());
-            messageExtendService.sendFeedbackAnswer(user, in);
+            UserOrderRecord record = null;
+            if (dto.getStatus() == AnswerStatus.ANSWER.index){
+                record = orderFlowService.giveAction(user.getId());
+            }
+            messageExtendService.sendFeedbackError(user, in, record);
         }
         }
 
 
         entity = userFeedbackErrorService.edit(entity);
         entity = userFeedbackErrorService.edit(entity);
@@ -547,17 +554,25 @@ public class UserController {
     @RequestMapping(value = "/textbook_feedback/edit", method = RequestMethod.PUT)
     @RequestMapping(value = "/textbook_feedback/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "修改机经勘误信息", httpMethod = "PUT")
     @ApiOperation(value = "修改机经勘误信息", httpMethod = "PUT")
     public Response<Boolean> editTextbookFeedback(@RequestBody @Validated UserFeedbackErrorDto dto, HttpServletRequest request) {
     public Response<Boolean> editTextbookFeedback(@RequestBody @Validated UserFeedbackErrorDto dto, HttpServletRequest request) {
-        UserFeedbackError entity = Transform.dtoToEntity(dto);
+        UserTextbookFeedback entity = Transform.dtoToEntity(dto);
-        UserFeedbackError in = userFeedbackErrorService.get(entity.getId());
+        UserTextbookFeedback in = userTextbookFeedbackService.get(entity.getId());
 
 
         // 处理设定
         // 处理设定
         if(in.getHandleTime() == null){
         if(in.getHandleTime() == null){
             entity.setHandleTime(new Date());
             entity.setHandleTime(new Date());
             Manager manager = shiroHelp.getLoginManager();
             Manager manager = shiroHelp.getLoginManager();
             entity.setManagerId(manager.getId());
             entity.setManagerId(manager.getId());
+
+            in.setStatus(dto.getStatus());
+            User user = usersService.get(in.getUserId());
+            UserOrderRecord record = null;
+            if (dto.getStatus() == AnswerStatus.ANSWER.index){
+                record = orderFlowService.giveAction(user.getId());
+            }
+            messageExtendService.sendTextbookFeedback(user, in, record);
         }
         }
 
 
-        entity = userFeedbackErrorService.edit(entity);
+        entity = userTextbookFeedbackService.edit(entity);
 
 
         managerLogService.log(request);
         managerLogService.log(request);
         return ResponseHelp.success(true);
         return ResponseHelp.success(true);

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

@@ -236,7 +236,7 @@ public class MyController {
                 .id(user.getId())
                 .id(user.getId())
                 .email(dto.getEmail())
                 .email(dto.getEmail())
                 .build());
                 .build());
-        messageExtendService.sendEmailChange(user, in.getEmail());
+        messageExtendService.sendEmailChange(in, dto.getEmail());
         return ResponseHelp.success(true);
         return ResponseHelp.success(true);
     }
     }
 
 
@@ -364,7 +364,7 @@ public class MyController {
                 .realStatus(1)
                 .realStatus(1)
                 .realTime(new Date())
                 .realTime(new Date())
                 .build());
                 .build());
-        orderFlowService.giveReal(user.getId());
+        orderFlowService.giveReal(in);
         return ResponseHelp.success(dto);
         return ResponseHelp.success(dto);
     }
     }
 
 
@@ -458,7 +458,7 @@ public class MyController {
         entity.setId(user.getId());
         entity.setId(user.getId());
         if (user.getPrepareTime() == null){
         if (user.getPrepareTime() == null){
             // 邀请奖励
             // 邀请奖励
-            orderFlowService.givePrepare(user.getId());
+            orderFlowService.givePrepare(user);
         }
         }
         entity.setPrepareTime(new Date());
         entity.setPrepareTime(new Date());
         usersService.edit(entity);
         usersService.edit(entity);

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

@@ -25,8 +25,6 @@ public class FaqDto {
 
 
     private Integer isSpecial;
     private Integer isSpecial;
 
 
-    private Boolean sendUser;
-
     private Integer status;
     private Integer status;
 
 
     private Date createTime;
     private Date createTime;
@@ -103,14 +101,6 @@ public class FaqDto {
         this.answer = answer;
         this.answer = answer;
     }
     }
 
 
-    public Boolean getSendUser() {
-        return sendUser;
-    }
-
-    public void setSendUser(Boolean sendUser) {
-        this.sendUser = sendUser;
-    }
-
     public Integer getIsShow() {
     public Integer getIsShow() {
         return isShow;
         return isShow;
     }
     }

+ 19 - 2
server/gateway-api/src/main/java/com/qxgmat/help/MailHelp.java

@@ -14,7 +14,9 @@ import org.springframework.core.io.FileSystemResource;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSession;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
@@ -39,12 +41,27 @@ public class MailHelp {
 
 
     public boolean sendBaseMail(String email, String subject, String body){
     public boolean sendBaseMail(String email, String subject, String body){
         if (body==null || body.isEmpty()) return false;
         if (body==null || body.isEmpty()) return false;
-        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, null);
+        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName);
         return response.getResult();
         return response.getResult();
     }
     }
 
 
     public boolean sendAttachMail(String email, String subject, String body, String filePath){
     public boolean sendAttachMail(String email, String subject, String body, String filePath){
-        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, new FileSystemResource(filePath));
+        SendCloudMail.Response response;
+        if (filePath ==null || filePath.isEmpty()){
+            response = mail.sendMail(email, subject, body, from, fromName);
+        }else {
+            response = mail.sendMail(email, subject, body, from, fromName, new FileSystemResource(filePath));
+        }
+        return response.getResult();
+    }
+
+    public boolean sendAttachMail(String email, String subject, String body, List<String> filePaths){
+        List<FileSystemResource> atts = new ArrayList<>();
+        for(String file : filePaths){
+            if (file==null || file.isEmpty()) continue;
+            atts.add(new FileSystemResource(file));
+        }
+        SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, atts);
         return response.getResult();
         return response.getResult();
     }
     }
 
 

+ 2 - 2
server/gateway-api/src/main/java/com/qxgmat/help/WechatHelp.java

@@ -79,10 +79,10 @@ public class WechatHelp {
     public boolean sendMessage(String openid, MessageCategory messageCategory, Map<String, String> dataMap){
     public boolean sendMessage(String openid, MessageCategory messageCategory, Map<String, String> dataMap){
         String templateId = "";
         String templateId = "";
         switch (messageCategory){
         switch (messageCategory){
-            case ASK_QUESTION:
+            case ASK_QUESTION_HANDLE:
                 templateId = this.questionTemplate;
                 templateId = this.questionTemplate;
                 break;
                 break;
-            case ASK_COURSE:
+            case ASK_COURSE_HANDLE:
                 templateId = this.courseTemplate;
                 templateId = this.courseTemplate;
                 break;
                 break;
             default:
             default:

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

@@ -27,6 +27,22 @@ public class UserServiceService extends AbstractService {
     private UserOrderRecordService userOrderRecordService;
     private UserOrderRecordService userOrderRecordService;
 
 
     /**
     /**
+     * 获取所有到期服务
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public List<UserService> allExpire(ServiceKey serviceKey, String startTime, String endTime){
+        Example example = new Example(UserService.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("service", serviceKey.key)
+                        .andGreaterThanOrEqualTo("expireTime", startTime)
+                        .andLessThanOrEqualTo("expireTime", endTime)
+        );
+        return select(userServiceMapper, example);
+    }
+    /**
      * 判断是否有权限
      * 判断是否有权限
      * @param userId
      * @param userId
      * @param key
      * @param key

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

@@ -207,7 +207,7 @@ public class UsersService extends AbstractService {
                 user.setOriginId(origin.getId());
                 user.setOriginId(origin.getId());
                 edit(User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).build());
                 edit(User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).build());
                 // 邀请奖励
                 // 邀请奖励
-                orderFlowService.giveInvite(origin.getId());
+                orderFlowService.giveInvite(origin);
             }
             }
             // 生成邀请码: 10位字符串
             // 生成邀请码: 10位字符串
             user.setInviteCode(Tools.getRandomString(10));
             user.setInviteCode(Tools.getRandomString(10));

+ 541 - 89
server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java

@@ -1,19 +1,25 @@
 package com.qxgmat.service.extend;
 package com.qxgmat.service.extend;
 
 
+import com.alipay.api.domain.TransOrderDetail;
+import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.ParameterException;
-import com.qxgmat.data.constants.enums.MessageCategory;
+import com.qxgmat.data.constants.enums.*;
-import com.qxgmat.data.constants.enums.MessageMethod;
+import com.qxgmat.data.constants.enums.module.FeedbackModule;
-import com.qxgmat.data.constants.enums.MessageType;
+import com.qxgmat.data.constants.enums.module.ProductType;
 import com.qxgmat.data.constants.enums.status.AnswerStatus;
 import com.qxgmat.data.constants.enums.status.AnswerStatus;
+import com.qxgmat.data.constants.enums.trade.RecordSource;
+import com.qxgmat.data.constants.enums.user.AskTarget;
+import com.qxgmat.data.constants.enums.user.DataType;
+import com.qxgmat.data.constants.enums.user.FeedbackTarget;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.help.MailHelp;
 import com.qxgmat.help.MailHelp;
 import com.qxgmat.help.SmsHelp;
 import com.qxgmat.help.SmsHelp;
 import com.qxgmat.help.WechatHelp;
 import com.qxgmat.help.WechatHelp;
-import com.qxgmat.service.inline.MessageTemplateService;
+import com.qxgmat.service.inline.*;
-import com.qxgmat.service.inline.UserMessageService;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import sun.plugin2.message.Message;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
@@ -39,6 +45,21 @@ public class MessageExtendService {
     private MessageTemplateService messageTemplateService;
     private MessageTemplateService messageTemplateService;
 
 
     @Resource
     @Resource
+    private CourseService courseService;
+
+    @Resource
+    private CourseNoService courseNoService;
+
+    @Resource
+    private CoursePackageService coursePackageService;
+
+    @Resource
+    private QuestionNoService questionNoService;
+
+    @Resource
+    private CourseDataService courseDataService;
+
+    @Resource
     private ToolsService toolsService;
     private ToolsService toolsService;
 
 
     @Value("${url.pc}")
     @Value("${url.pc}")
@@ -47,7 +68,10 @@ public class MessageExtendService {
     @Value("${url.h5}")
     @Value("${url.h5}")
     private String h5Url;
     private String h5Url;
 
 
-    private void send(User user, MessageCategory category, Map<String, String>params){
+    @Value("${info.wechat}")
+    private String wechat;
+
+    private void send(User user, MessageCategory category, Map<String, String>params, Integer relationId){
         List<MessageTemplate> templateList = messageTemplateService.listByCategory(category);
         List<MessageTemplate> templateList = messageTemplateService.listByCategory(category);
         for(MessageTemplate template : templateList){
         for(MessageTemplate template : templateList){
             MessageMethod messageMethod = MessageMethod.ValueOf(template.getMessageMethod());
             MessageMethod messageMethod = MessageMethod.ValueOf(template.getMessageMethod());
@@ -55,10 +79,21 @@ public class MessageExtendService {
             String content = replaceBody(template.getContent(), params);
             String content = replaceBody(template.getContent(), params);
             switch(messageMethod){
             switch(messageMethod){
                 case EMAIL:
                 case EMAIL:
-                    sendEmail(params.getOrDefault("emails", user.getEmail()), title, content);
+                    if(params.containsKey("link")){
+                        params.put("linkHtml", makeLink(params.get("link"), params.getOrDefault("linkTitle", category.linkTitle), relationId));
+                    }else if(category.link != null ){
+                        params.put("linkHtml", makeLink(category.link, category.linkTitle, relationId));
+                    }
+
+                    if (params.containsKey("linkSecond")){
+                        params.put("linkSecondHtml", makeLink(params.get("linkSecond"), params.getOrDefault("linkTitle", category.linkSecondTitle), relationId));
+                    }else if(category.linkSecond != null){
+                        params.put("linkSecondHtml", makeLink(category.linkSecond, category.linkSecondTitle, relationId));
+                    }
+                    sendEmail(params.getOrDefault("emails", user.getEmail()), title, content, params.get("attachment"), params.get("attachments"));
                     break;
                     break;
                 case INSIDE:
                 case INSIDE:
-                    sendInside(user.getId(), title, content, template.getLink(), category);
+                    sendInside(user.getId(), title, content, relationId, params.getOrDefault("link", template.getLink()), params.getOrDefault("linkTitle", null), category);
                     break;
                     break;
                 case WECHAT:
                 case WECHAT:
                     sendWechat(user.getWechatOpenidWechat(), category, params);
                     sendWechat(user.getWechatOpenidWechat(), category, params);
@@ -73,19 +108,26 @@ public class MessageExtendService {
         }
         }
     }
     }
 
 
-    private UserMessage sendInside(Integer userId, String title, String content, String link, MessageCategory category){
+    private UserMessage sendInside(Integer userId, String title, String content, Integer relationId, String link, String linkTitle, MessageCategory category){
         // 根据模版创建
         // 根据模版创建
         return userMessageService.add(UserMessage.builder()
         return userMessageService.add(UserMessage.builder()
                 .userId(userId)
                 .userId(userId)
                 .title(title)
                 .title(title)
                 .content(content)
                 .content(content)
+                .relationId(relationId)
                 .link(link)
                 .link(link)
+                .linkTitle(linkTitle)
                 .type(MessageType.FromCategory(category).key)
                 .type(MessageType.FromCategory(category).key)
                 .isRead(0)
                 .isRead(0)
                 .build());
                 .build());
     }
     }
 
 
-    private void sendEmail(String emails, String title, String body){
+    private void sendEmail(String emails, String title, String body, String attachment, String attachments){
+        if (attachment != null){
+            mailHelp.sendAttachMail(emails, title, body, attachment);
+        }else if(attachments != null){
+            mailHelp.sendAttachMail(emails, title, body, Arrays.stream(attachments.split(";")).collect(Collectors.toList()));
+        }
         mailHelp.sendBaseMail(emails, title, body);
         mailHelp.sendBaseMail(emails, title, body);
     }
     }
 
 
@@ -96,14 +138,11 @@ public class MessageExtendService {
     private void sendSms(String area, String mobile, MessageCategory category, Map<String, String> params){
     private void sendSms(String area, String mobile, MessageCategory category, Map<String, String> params){
         String[] template;
         String[] template;
         Map<String, String> smsParams = new HashMap<>();
         Map<String, String> smsParams = new HashMap<>();
-        SimpleDateFormat sdf;
-        String time;
         switch(category){
         switch(category){
             case LOGIN_ABNORMAL:
             case LOGIN_ABNORMAL:
                 template = smsHelp.ABNORMAL_TEMPLATE;
                 template = smsHelp.ABNORMAL_TEMPLATE;
                 // time: yyyy年MM月dd日hh:mm:ss
                 // time: yyyy年MM月dd日hh:mm:ss
-                sdf = new SimpleDateFormat("yyyy年MM月dd hh:mm:ss");
+                smsParams.put("time", params.get("time"));
-                smsParams.put("time", sdf.format(params.get("time")));
                 break;
                 break;
             case TEXTBOOK_LIBRARY:
             case TEXTBOOK_LIBRARY:
                 template = smsHelp.LIBRARY_TEMPLATE;
                 template = smsHelp.LIBRARY_TEMPLATE;
@@ -112,7 +151,11 @@ public class MessageExtendService {
                 // 间隔%jgts%天,库长%kcts%天
                 // 间隔%jgts%天,库长%kcts%天
                 // 获取机经: %jjdz%
                 // 获取机经: %jjdz%
                 // 也可搜索微信订阅号“%dyh%”查阅机经。
                 // 也可搜索微信订阅号“%dyh%”查阅机经。
-                smsParams.put("dyh", "");
+                smsParams.put("zx_date", params.get("date"));
+                smsParams.put("sc_date", params.get("prevDate"));
+                smsParams.put("jgts", params.get("period"));
+                smsParams.put("kcts", params.get("days"));
+                smsParams.put("dyh", wechat);
                 smsParams.put("jjdz", pcUrl+"/textbook");
                 smsParams.put("jjdz", pcUrl+"/textbook");
                 break;
                 break;
             case COURSE_USE_EXPIRE:
             case COURSE_USE_EXPIRE:
@@ -120,20 +163,18 @@ public class MessageExtendService {
                 // yhnc: 用户昵称
                 // yhnc: 用户昵称
                 // %kcmc%: 课程名称
                 // %kcmc%: 课程名称
                 // %date%: 格式为:yyyy年MM月dd日
                 // %date%: 格式为:yyyy年MM月dd日
-                sdf = new SimpleDateFormat("yyyy年MM月dd");
+                smsParams.put("date", params.get("expireDate"));
-                smsParams.put("date", sdf.format(params.get("time")));
                 smsParams.put("yhnc", params.get("nickname"));
                 smsParams.put("yhnc", params.get("nickname"));
                 smsParams.put("kcmc", params.get("title"));
                 smsParams.put("kcmc", params.get("title"));
                 break;
                 break;
-            case COURSE_EXPIRE:
+            case COURSE_OPEN_EXPIRE:
-            case TEXTBOOK_EXPIRE:
+            case TEXTBOOK_OPEN_EXPIRE:
-            case QX_CAT_EXPIRE:
+            case QX_CAT_OPEN_EXPIRE:
                 template = smsHelp.EXPIRE_TEMPLATE;
                 template = smsHelp.EXPIRE_TEMPLATE;
                 // yhnc: 用户昵称
                 // yhnc: 用户昵称
                 // %date%: 格式为:yyyy年MM月dd日
                 // %date%: 格式为:yyyy年MM月dd日
                 // wktfw: 未开通的服务名称
                 // wktfw: 未开通的服务名称
-                sdf = new SimpleDateFormat("yyyy年MM月dd");
+                smsParams.put("date", params.get("expireDate"));
-                smsParams.put("date", sdf.format(params.get("time")));
                 smsParams.put("yhnc", params.get("nickname"));
                 smsParams.put("yhnc", params.get("nickname"));
                 smsParams.put("wktfw", params.get("title"));
                 smsParams.put("wktfw", params.get("title"));
                 break;
                 break;
@@ -145,7 +186,19 @@ public class MessageExtendService {
     }
     }
 
 
     public void refreshMessage(List<UserMessage> messageList){
     public void refreshMessage(List<UserMessage> messageList){
-
+        for(UserMessage message : messageList){
+            if (message.getLink() != null && message.getLink() != null) continue;
+            MessageCategory category = MessageCategory.ValueOf(message.getMessageCategory());
+            if(category.emailLink) continue;
+            if (category.link != null){
+                message.setLink(replaceLink(category.link, "id", message.getRelationId()));
+                message.setLinkTitle(category.linkTitle);
+            }
+            if (category.linkSecond != null){
+                message.setLinkSecond(replaceLink(category.linkSecond, "id", message.getRelationId()));
+                message.setLinkSecondTitle(category.linkSecondTitle);
+            }
+        }
     }
     }
 
 
     public void sendCustom(User user, MessageTemplate template, Map<String, String>params){
     public void sendCustom(User user, MessageTemplate template, Map<String, String>params){
@@ -155,10 +208,10 @@ public class MessageExtendService {
         String content = replaceBody(template.getContent(), params);
         String content = replaceBody(template.getContent(), params);
         switch(messageMethod){
         switch(messageMethod){
             case EMAIL:
             case EMAIL:
-                sendEmail(user.getEmail(), title, content);
+                sendEmail(user.getEmail(), title, content, null, null);
                 break;
                 break;
             case INSIDE:
             case INSIDE:
-                sendInside(user.getId(), title, content, template.getLink(), MessageCategory.CUSTOM);
+                sendInside(user.getId(), title, content, 0, template.getLink(), null, MessageCategory.CUSTOM);
                 break;
                 break;
             default:
             default:
                 throw new ParameterException("消息发送方式错误");
                 throw new ParameterException("消息发送方式错误");
@@ -175,15 +228,85 @@ public class MessageExtendService {
         map.put("code", code);
         map.put("code", code);
         map.put("nickname", user.getNickname());
         map.put("nickname", user.getNickname());
         map.put("url", pcUrl+"/id/"+code);
         map.put("url", pcUrl+"/id/"+code);
-        send(user, MessageCategory.INVITED, map);
+        send(user, MessageCategory.INVITED, map, user.getId());
+    }
+
+    /**
+     * 邀请成功通知
+     * @param user
+     */
+    public void sendInviteSuccess(User user, UserOrderRecord record){
+        Map<String, String> map = new HashMap<>();
+        map.put("nickname", user.getNickname());
+        map.put("number", String.valueOf(user.getInviteNumber()));
+        map.put("useExpireDays", String.valueOf(record.getUseExpireDays()));
+        map.put("expireTime", getTime(record.getUseEndTime()));
+        send(user, MessageCategory.INVITED_SUCCESS, map, record.getId());
+    }
+
+    /**
+     * 实名认证
+     * @param user
+     */
+    public void sendReal(User user, UserOrderRecord record){
+        Map<String, String> map = new HashMap<>();
+        map.put("nickname", user.getNickname());
+        map.put("useExpireDays", String.valueOf(record.getUseExpireDays()));
+        map.put("expireTime", getTime(record.getUseEndTime()));
+        send(user, MessageCategory.REAL, map, record.getId());
+    }
+
+    /**
+     * 完善备考
+     * @param user
+     */
+    public void sendPrepare(User user, UserOrderRecord record){
+        Map<String, String> map = new HashMap<>();
+        map.put("nickname", user.getNickname());
+        map.put("useExpireDays", String.valueOf(record.getUseExpireDays()));
+        map.put("expireTime", getTime(record.getUseEndTime()));
+        send(user, MessageCategory.PREPARE, map, record.getId());
+    }
+
+    /**
+     * 发送机经换库
+     */
+    public void sendTextbookLibrary(User user, TextbookLibrary latest, TextbookLibrary second){
+        Map<String, String> map = new HashMap<>();
+        int days =(int)(latest.getStartDate().getTime() - second.getStartDate().getTime()) / 86400000;
+        map.put("date", getDate(latest.getStartDate()));
+        map.put("prevDate", getDate(second.getStartDate()));
+        map.put("period", String.valueOf(days-1));
+        map.put("days", String.valueOf(days));
+        send(user, MessageCategory.TEXTBOOK_LIBRARY, map, 0);
     }
     }
 
 
     /**
     /**
      * 发送机经更新
      * 发送机经更新
      */
      */
-    public void sendTextbookUpdate(User user, TextbookLibrary textbookLibrary){
+    public void sendTextbookUpdate(User user, TextbookLibrary textbookLibrary, Boolean subscribe){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
-        send(user, MessageCategory.TEXTBOOK_LIBRARY, map);
+        Integer version = textbookLibrary.getQuantVersion();
+        if (textbookLibrary.getIrVersion() > version){
+            version = textbookLibrary.getIrVersion();
+        }
+        if (textbookLibrary.getRcVersion() >version){
+            version = textbookLibrary.getRcVersion();
+        }
+        map.put("email", user.getEmail());
+        map.put("version", String.valueOf(version));
+        map.put("quantVersion", String.valueOf(textbookLibrary.getQuantVersion()));
+        map.put("quantDescription", String.valueOf(textbookLibrary.getQuantDescription()));
+        map.put("irVersion", String.valueOf(textbookLibrary.getIrVersion()));
+        map.put("irDescription", String.valueOf(textbookLibrary.getIrDescription()));
+        map.put("rcVersion", String.valueOf(textbookLibrary.getRcVersion()));
+        map.put("rcDescription", String.valueOf(textbookLibrary.getRcDescription()));
+        if (subscribe){
+            map.put("attachments", String.format("%s;%s;%s", textbookLibrary.getQuant(), textbookLibrary.getIr(), textbookLibrary.getRc()));
+            send(user, MessageCategory.TEXTBOOK_UPDATE_SUBSCRIBE, map, 0);
+        }else{
+            send(user, MessageCategory.TEXTBOOK_UPDATE, map, 0);
+        }
     }
     }
 
 
     /**
     /**
@@ -194,7 +317,8 @@ public class MessageExtendService {
         map.put("nickname", user.getNickname());
         map.put("nickname", user.getNickname());
         map.put("ip", userAbnormal.getLoginIp());
         map.put("ip", userAbnormal.getLoginIp());
         map.put("city", userAbnormal.getLoginCity());
         map.put("city", userAbnormal.getLoginCity());
-        send(user, MessageCategory.LOGIN_ABNORMAL, map);
+        map.put("time", getTime(userAbnormal.getCreateTime()));
+        send(user, MessageCategory.LOGIN_ABNORMAL, map, userAbnormal.getId());
     }
     }
 
 
     /**
     /**
@@ -206,7 +330,7 @@ public class MessageExtendService {
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
         map.put("courseTitle", course.getTitle());
         map.put("courseTitle", course.getTitle());
         map.put("title", previewAssign.getTitle());
         map.put("title", previewAssign.getTitle());
-        send(user, MessageCategory.PREVIEW_NOTICE, map);
+        send(user, MessageCategory.PREVIEW_NOTICE, map, previewAssign.getId());
     }
     }
 
 
     /**
     /**
@@ -215,10 +339,235 @@ public class MessageExtendService {
      * 支付金额:{money}
      * 支付金额:{money}
      * 开通有效期:{expireTime}
      * 开通有效期:{expireTime}
      */
      */
-    public void sendPayed(User user, UserOrder userOrder){
+    public void sendPayed(User user, UserOrder userOrder, List<UserOrderRecord> recordList){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
-        map.put("money", userOrder.getMoney().toString());
+        List<UserOrderRecord> pList = recordList.stream().filter((checkout)-> checkout.getParentId() == 0).collect(Collectors.toList());
-        send(user, MessageCategory.PAYED, map);
+
+        map.put("number", String.valueOf(pList.size()));
+        if(userOrder.getProductTypes().size() == 1){
+            String productType = userOrder.getProductTypes().getString(1);
+            UserOrderRecord one = pList.get(0);
+            switch(ProductType.ValueOf(productType)){
+                case SERVICE:
+                    ServiceKey key = ServiceKey.ValueOf(one.getService());
+                    map.put("title", key.title);
+                    map.put("expireDays", String.valueOf(one.getExpireDays()));
+                    map.put("useExpireDays", String.valueOf(one.getUseExpireDays()));
+                    switch(key){
+                        case VIP:
+                            send(user, MessageCategory.VIP_PAY, map, one.getId());
+                            break;
+                        case TEXTBOOK:
+                            send(user, MessageCategory.TEXTBOOK_PAY, map, one.getId());
+                            break;
+                        case QX_CAT:
+                            send(user, MessageCategory.QX_CAT_PAY, map, one.getId());
+                            break;
+                        default:
+                            return;
+                    }
+                    break;
+                case DATA:
+                    CourseData data = courseDataService.get(one.getProductId());
+                    map.put("title", data.getTitle());
+                    if (pList.size() > 1){
+                        Collection dataIds = Transform.getIds(pList, UserOrderRecord.class, "productId");
+                        List<CourseData> courseDataList = courseDataService.select(dataIds);
+                        List<String> dataTitle = new ArrayList<>();
+                        for(CourseData c: courseDataList){
+                            dataTitle.add(c.getTitle());
+                        }
+                        map.put("titleJoin", String.join("”,“", dataTitle));
+                        send(user, MessageCategory.DATA_PAY_MULTI, map, userOrder.getId());
+                    }else{
+                        send(user, MessageCategory.DATA_PAY, map, userOrder.getId());
+                    }
+                    break;
+                case COURSE:
+                    Course course = courseService.get(one.getProductId());
+                    map.put("title", course.getTitle());
+                    if (pList.size() > 1){
+                        Collection courseIds = Transform.getIds(pList, UserOrderRecord.class, "productId");
+                        List<Course> courseList = courseService.select(courseIds);
+                        List<String> courseTitle = new ArrayList<>();
+                        for(Course c: courseList){
+                            courseTitle.add(c.getTitle());
+                        }
+                        map.put("titleJoin", String.join("”,“", courseTitle));
+                        send(user, MessageCategory.COURSE_PAY_MULTI, map, userOrder.getId());
+                    }else{
+                        map.put("expireDays", String.valueOf(one.getExpireDays()));
+                        map.put("useExpireDays", String.valueOf(one.getUseExpireDays()));
+                        send(user, MessageCategory.COURSE_PAY, map, userOrder.getId());
+                    }
+                    break;
+                case COURSE_PACKAGE:
+                    CoursePackage coursePackage = coursePackageService.get(one.getProductId());
+                    List<Course> courseList = courseService.select(coursePackage.getCourseIds());
+                    List<String> courseTitle = new ArrayList<>();
+                    for(Course c: courseList){
+                        courseTitle.add(c.getTitle());
+                    }
+                    map.put("titleJoin", String.join("”,“", courseTitle));
+                    map.put("number", String.valueOf(courseList.size()));
+                    map.put("title", courseList.get(0).getTitle());
+                    send(user, MessageCategory.COURSE_PAY_MULTI, map, userOrder.getId());
+                    break;
+                default:
+                    return;
+            }
+        }else{
+            List<String> titles = new ArrayList<>();
+
+            List<UserOrderRecord> prService = recordList.stream().filter((row)-> row.getProductType().equals(ProductType.SERVICE.key)).collect(Collectors.toList());
+            for(UserOrderRecord r : prService){
+                ServiceKey key = ServiceKey.ValueOf(r.getService());
+                titles.add(key.title);
+            }
+            // 绑定套餐
+            List<UserOrderRecord> prPackage = recordList.stream().filter((row)-> row.getProductType().equals(ProductType.COURSE_PACKAGE.key)).collect(Collectors.toList());
+            Collection packageIds = Transform.getIds(prPackage, UserOrderRecord.class, "productId");
+            List<CoursePackage> coursePackageList = coursePackageService.select(packageIds);
+            List<Integer> courseExtendIds = new ArrayList<>();
+            Map coursePackageMap = Transform.getMap(coursePackageList, CoursePackage.class, "id");
+            for(UserOrderRecord r : prPackage){
+                if (!coursePackageMap.containsKey(r.getProductId())) continue;
+                CoursePackage coursePackage = (CoursePackage) coursePackageMap.get(r.getProductId());
+                courseExtendIds.addAll(Arrays.stream(coursePackage.getCourseIds()).collect(Collectors.toList()));
+            }
+            List<Course> courseExtendList =  courseService.select(courseExtendIds);
+            Map courseExtendMap = Transform.getMap(courseExtendList, Course.class, "id");
+            for(UserOrderRecord r: prPackage){
+                if (!coursePackageMap.containsKey(r.getProductId())) continue;
+                CoursePackage coursePackage = (CoursePackage) coursePackageMap.get(r.getProductId());
+                for(Integer courseId : coursePackage.getCourseIds()){
+                    if (!courseExtendMap.containsKey(courseId)) continue;
+                    Course course = (Course) courseExtendMap.get(courseId);
+                    titles.add(course.getTitle());
+                }
+            }
+
+            // 绑定资料
+            List<UserOrderRecord> prData = recordList.stream().filter((row)-> row.getProductType().equals(ProductType.DATA.key)).collect(Collectors.toList());
+            Collection dataIds = Transform.getIds(prData, UserOrderRecord.class, "productId");
+            List<CourseData> courseDataList =  courseDataService.select(dataIds);
+            Map courseDataMap = Transform.getMap(courseDataList, CourseData.class, "id");
+            for(UserOrderRecord r: prData){
+                if (courseDataMap.containsKey(r.getProductId())) continue;
+                CourseData courseData = (CourseData) courseDataMap.get(r.getProductId());
+                titles.add(courseData.getTitle());
+            }
+
+            // 绑定课程
+            List<UserOrderRecord> prCourse = recordList.stream().filter((row)-> row.getProductType().equals(ProductType.COURSE.key)).collect(Collectors.toList());
+            Collection courseIds = Transform.getIds(prCourse, UserOrderRecord.class, "productId");
+            List<Course> courseList =  courseService.select(courseIds);
+            Map courseMap = Transform.getMap(courseList, Course.class, "id");
+            for(UserOrderRecord r: prCourse){
+                if (courseMap.containsKey(r.getProductId())) continue;
+                Course course = (Course) courseMap.get(r.getProductId());
+                titles.add(course.getTitle());
+            }
+
+            map.put("number", String.valueOf(titles.size()));
+            map.put("title", titles.get(0));
+            map.put("titleJoin", String.join("”,“", titles));
+            send(user, MessageCategory.PAY_MULTI, map, userOrder.getId());
+        }
+        // 赠品
+        List<UserOrderRecord> gifts = recordList.stream().filter((checkout)-> checkout.getSource().equals(RecordSource.GIFT_COURSE.key)).collect(Collectors.toList());
+        for(UserOrderRecord gift: gifts){
+            Map<String, String> giftMap = new HashMap<>();
+            ServiceKey key = ServiceKey.ValueOf(gift.getService());
+            map.put("title", key.title);
+            // 获取赠品链接
+            switch(key){
+                case QX_CAT:
+                    map.put("link", MessageCategory.QX_CAT_PAY.link);
+                    map.put("linkTitle", MessageCategory.QX_CAT_PAY.linkTitle);
+                    break;
+                case TEXTBOOK:
+                    map.put("link", MessageCategory.TEXTBOOK_PAY.link);
+                    map.put("linkTitle", MessageCategory.TEXTBOOK_PAY.linkTitle);
+                    break;
+            }
+            send(user, MessageCategory.COURSE_GIFT, giftMap, gift.getId());
+        }
+    }
+
+    /**
+     * 开通到期提醒
+     * @param user
+     * @param record
+     * @param days
+     */
+    public void sendOpenExpire(User user, UserOrderRecord record, Integer days){
+        Map<String, String> map = new HashMap<>();
+        map.put("days", String.valueOf(days));
+        map.put("expireDate", getDate(record.getEndTime()));
+        map.put("nickname", user.getNickname());
+        switch(ProductType.ValueOf(record.getProductType())){
+            case SERVICE:
+                ServiceKey key = ServiceKey.ValueOf(record.getService());
+                map.put("title", key.title);
+                switch(key){
+                    case TEXTBOOK:
+                        send(user, MessageCategory.TEXTBOOK_OPEN_EXPIRE, map, record.getId());
+                        break;
+                    case QX_CAT:
+                        send(user, MessageCategory.QX_CAT_OPEN_EXPIRE, map, record.getId());
+                        break;
+                    default:
+                        return;
+                }
+                break;
+            case COURSE:
+                Course course = courseService.get(record.getProductId());
+                map.put("title", course.getTitle());
+                send(user, MessageCategory.COURSE_OPEN_EXPIRE, map, record.getId());
+                break;
+            default:
+                return;
+        }
+    }
+
+    /**
+     * 使用到期提醒
+     * @param user
+     * @param record
+     * @param days
+     */
+    public void sendUseExpire(User user, UserOrderRecord record, Integer days){
+        Map<String, String> map = new HashMap<>();
+        map.put("days", String.valueOf(days));
+        map.put("expireDate", getDate(record.getUseEndTime()));
+        map.put("nickname", user.getNickname());
+        switch(ProductType.ValueOf(record.getProductType())){
+            case SERVICE:
+                ServiceKey key = ServiceKey.ValueOf(record.getService());
+                map.put("title", key.title);
+                switch(key){
+                    case VIP:
+                        send(user, MessageCategory.VIP_USE_EXPIRE, map, record.getId());
+                        break;
+                    case TEXTBOOK:
+                        send(user, MessageCategory.TEXTBOOK_USE_EXPIRE, map, record.getId());
+                        break;
+                    case QX_CAT:
+                        send(user, MessageCategory.QX_CAT_USE_EXPIRE, map, record.getId());
+                        break;
+                    default:
+                        return;
+                }
+                break;
+            case COURSE:
+                Course course = courseService.get(record.getProductId());
+                map.put("title", course.getTitle());
+                send(user, MessageCategory.COURSE_USE_EXPIRE, map, record.getId());
+                break;
+            default:
+                return;
+        }
     }
     }
 
 
     /**
     /**
@@ -226,13 +575,26 @@ public class MessageExtendService {
      * 资料名称:{title}
      * 资料名称:{title}
      * 更新时间:{updateTime}
      * 更新时间:{updateTime}
      */
      */
-    public void sendDataUpdate(User user, CourseData data, CourseDataHistory history){
+    public void sendDataUpdate(User user, CourseData data, CourseDataHistory history, Boolean subscribe){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
         map.put("title", data.getTitle());
         map.put("title", data.getTitle());
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        map.put("nickname", user.getNickname());
-        String time = sdf.format(history.getTime());
+        map.put("email", user.getEmail());
-        map.put("time", time);
+        map.put("version", history.getVersion());
-        send(user, MessageCategory.DATA_UPDATE, map);
+        map.put("description", String.format("位置:%s, 原内容:%s, 更改为:%s", history.getPosition(), history.getOriginContent(), history.getContent()));
+        switch(DataType.ValueOf(data.getDataType())){
+            case PAPER:
+                send(user, MessageCategory.DATA_UPDATE_PAPER, map, data.getId());
+                break;
+            case ELECTRON:
+                if (subscribe){
+                    map.put("attachment", data.getResource());
+                    send(user, MessageCategory.DATA_UPDATE_SUBSCRIBE, map, data.getId());
+                }else{
+                    send(user, MessageCategory.DATA_UPDATE_BASE, map, data.getId());
+                }
+                break;
+        }
     }
     }
 
 
     /**
     /**
@@ -242,21 +604,32 @@ public class MessageExtendService {
      * 提问状态:{status}, 精选、隐藏
      * 提问状态:{status}, 精选、隐藏
      */
      */
     public void sendAskQuestion(User user,UserAskQuestion userAskQuestion){
     public void sendAskQuestion(User user,UserAskQuestion userAskQuestion){
+        // 非学员
+        if (user.getIsCourse() == 0) return;
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
         map.put("content", userAskQuestion.getContent());
         map.put("content", userAskQuestion.getContent());
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        map.put("date", getDate(userAskQuestion.getCreateTime()));
-        String time = sdf.format(new Date());
+        QuestionNo questionNo = questionNoService.get(userAskQuestion.getQuestionNoId());
-        map.put("time", time);
+        map.put("title", questionNo.getTitle());
-        AnswerStatus status = AnswerStatus.ValueOf(userAskQuestion.getAnswerStatus());
+
-        if (status == AnswerStatus.ANSWER){
+        switch(AnswerStatus.ValueOf(userAskQuestion.getAnswerStatus())){
-            map.put("status", "已回答");
+            case ANSWER:
-        }else if (status == AnswerStatus.IGNORE){
+                if(userAskQuestion.getShowStatus() > 0){
-            map.put("status", "已隐藏");
+                    // 替换内容较多
-        }
+                    String link = replaceLink(MessageCategory.ASK_QUESTION_SPECIAL.link, "id", userAskQuestion.getId());
-        if(userAskQuestion.getShowStatus() > 0){
+                    link = replaceLink(link, "questionNoId", userAskQuestion.getQuestionNoId());
-            map.put("status", "精选");
+                    map.put("link", link);
+                    map.put("linkTitle", MessageCategory.ASK_QUESTION_SPECIAL.linkTitle);
+                    send(user, MessageCategory.ASK_QUESTION_SPECIAL, map, userAskQuestion.getId());
+                }else{
+                    send(user, MessageCategory.ASK_QUESTION_HANDLE, map, userAskQuestion.getId());
+                }
+                break;
+            case IGNORE:
+                send(user, MessageCategory.ASK_QUESTION_IGNORE, map, userAskQuestion.getId());
+                break;
+            default:
         }
         }
-        send(user, MessageCategory.ASK_QUESTION, map);
     }
     }
 
 
     /**
     /**
@@ -266,21 +639,34 @@ public class MessageExtendService {
      * 提问状态:{status}, 精选、隐藏
      * 提问状态:{status}, 精选、隐藏
      */
      */
     public void sendAskCourse(User user, UserAskCourse userAskCourse){
     public void sendAskCourse(User user, UserAskCourse userAskCourse){
+        // 非学员
+        if (user.getIsCourse() == 0) return;
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
         map.put("content", userAskCourse.getContent());
         map.put("content", userAskCourse.getContent());
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        map.put("date", getDate(userAskCourse.getCreateTime()));
-        String time = sdf.format(new Date());
+        Course course = courseService.get(userAskCourse.getCourseId());
-        map.put("time", time);
+        map.put("title", course.getTitle());
-        AnswerStatus status = AnswerStatus.ValueOf(userAskCourse.getAnswerStatus());
+        CourseNo courseNo = courseNoService.get(userAskCourse.getCourseNoId());
-        if (status == AnswerStatus.ANSWER){
+        map.put("no", String.valueOf(courseNo.getNo()));
-            map.put("status", "已回答");
+        switch(AnswerStatus.ValueOf(userAskCourse.getAnswerStatus())){
-        }else if (status == AnswerStatus.IGNORE){
+            case ANSWER:
-            map.put("status", "已隐藏");
+                if(userAskCourse.getShowStatus() > 0){
-        }
+                    // 替换内容较多
-        if(userAskCourse.getShowStatus() > 0){
+                    String link = replaceLink(MessageCategory.ASK_COURSE_SPECIAL.link, "id", userAskCourse.getId());
-            map.put("status", "精选");
+                    link = replaceLink(link, "courseId", userAskCourse.getCourseId());
+                    link = replaceLink(link, "courseNoId", userAskCourse.getCourseNoId());
+                    map.put("link", link);
+                    map.put("linkTitle", MessageCategory.ASK_COURSE_SPECIAL.linkTitle);
+                    send(user, MessageCategory.ASK_COURSE_SPECIAL, map, userAskCourse.getId());
+                }else{
+                    send(user, MessageCategory.ASK_COURSE_HANDLE, map, userAskCourse.getId());
+                }
+                break;
+            case IGNORE:
+                send(user, MessageCategory.ASK_COURSE_IGNORE, map, userAskCourse.getId());
+                break;
+            default:
         }
         }
-        send(user, MessageCategory.ASK_COURSE, map);
     }
     }
 
 
     /**
     /**
@@ -335,7 +721,15 @@ public class MessageExtendService {
                 break;
                 break;
         }
         }
         map.put("channel", channel);
         map.put("channel", channel);
-        send(user, MessageCategory.FAQ_CALLBACK, map);
+        switch(AnswerStatus.ValueOf(faq.getAnswerStatus())){
+            case ANSWER:
+                send(user, MessageCategory.FAQ_HANDLE, map, faq.getId());
+                break;
+            case IGNORE:
+                send(user, MessageCategory.FAQ_IGNORE, map, faq.getId());
+                break;
+            default:
+        }
     }
     }
 
 
     /**
     /**
@@ -344,57 +738,97 @@ public class MessageExtendService {
      * 应更正:{correct}
      * 应更正:{correct}
      * 处理结果:{status}
      * 处理结果:{status}
      */
      */
-    public void sendFeedbackAnswer(User user, UserFeedbackError feedbackError){
+    public void sendFeedbackError(User user, UserFeedbackError feedbackError, UserOrderRecord record){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
-        map.put("content", feedbackError.getOriginContent());
+        map.put("time", getTime(feedbackError.getCreateTime()));
-        AnswerStatus status = AnswerStatus.ValueOf(feedbackError.getStatus());
+        map.put("content", feedbackError.getContent());
-        if (status == AnswerStatus.ANSWER){
+        map.put("title", feedbackError.getTitle());
-            map.put("status", "已采纳");
+        if (feedbackError.getModule().equals(FeedbackModule.DATA.key)){
-        }else if (status == AnswerStatus.IGNORE){
+            String[] p = feedbackError.getPosition().split(",");
-            map.put("status", "已忽略");
+            map.put("position", String.format("第%s页第%s行", p[0], p[1]));
-        }else if (status == AnswerStatus.NOHANDLE){
+        }else{
-            map.put("status", "无需处理");
+            map.put("position", AskTarget.ValueOf(feedbackError.getPosition()).title);
+        }
+        map.put("originContent", feedbackError.getOriginContent());
+        if (record != null){
+            map.put("useExpireDays", String.valueOf(record.getUseExpireDays()));
+        }
+        switch(AnswerStatus.ValueOf(feedbackError.getStatus())){
+            case ANSWER:
+                send(user, MessageCategory.FEEDBACK_ERROR_HANDLE, map, feedbackError.getId());
+                break;
+            case IGNORE:
+                send(user, MessageCategory.FEEDBACK_ERROR_IGNORE, map, feedbackError.getId());
+                break;
+            case NOHANDLE:
+                send(user, MessageCategory.FEEDBACK_ERROR_NOHANDLE, map, feedbackError.getId());
+                break;
+            default:
         }
         }
-        map.put("correct", feedbackError.getContent());
-        map.put("object", feedbackError.getTitle());
-        send(user, MessageCategory.FEEDBACK_CALLBACK, map);
     }
     }
 
 
     /**
     /**
-     * 注册成功通知
+     * 纠错对象:{object}
-     * @param user
+     * 错误内容:{content}
+     * 应更正:{correct}
+     * 处理结果:{status}
      */
      */
-    public void sendRegister(User user){
+    public void sendTextbookFeedback(User user, UserTextbookFeedback textbookFeedback, UserOrderRecord record){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
-        map.put("mobile", user.getMobile());
+        map.put("time", getTime(textbookFeedback.getCreateTime()));
-        send(user, MessageCategory.REGISTER, map);
+        map.put("content", textbookFeedback.getContent());
+        map.put("subject", QuestionSubject.ValueOf(textbookFeedback.getQuestionSubject()).title);
+        map.put("no", String.valueOf(textbookFeedback.getNo()));
+        FeedbackTarget target = FeedbackTarget.ValueOf(textbookFeedback.getTarget());
+        map.put("target", target.title);
+        map.put("result", target.result);
+        map.put("handle", target.handle);
+        if (record != null){
+            map.put("useExpireDays", String.valueOf(record.getUseExpireDays()));
+        }
+        switch(AnswerStatus.ValueOf(textbookFeedback.getStatus())){
+            case ANSWER:
+                send(user, MessageCategory.TEXTBOOK_FEEDBACK_HANDLE, map, textbookFeedback.getId());
+                break;
+            case IGNORE:
+                send(user, MessageCategory.TEXTBOOK_FEEDBACK_IGNORE, map, textbookFeedback.getId());
+                break;
+            case NOHANDLE:
+                send(user, MessageCategory.TEXTBOOK_FEEDBACK_NOHANDLE, map, textbookFeedback.getId());
+                break;
+            default:
+        }
     }
     }
 
 
     /**
     /**
-     * 邮箱绑定
+     * 注册成功通知
      * @param user
      * @param user
      */
      */
-    public void sendEmailBind(User user){
+    public void sendRegister(User user){
         Map<String, String> map = new HashMap<>();
         Map<String, String> map = new HashMap<>();
-        map.put("nickname", user.getNickname());
+        map.put("mobile", user.getMobile());
         map.put("email", user.getEmail());
         map.put("email", user.getEmail());
-        send(user, MessageCategory.EMAIL_CHANGE, map);
+        send(user, MessageCategory.REGISTER, map, 0);
     }
     }
 
 
     /**
     /**
      * 邮箱变更
      * 邮箱变更
      * @param user
      * @param user
      */
      */
-    public void sendEmailChange(User user, String email){
+    public void sendEmailChange(User user, String newEmail){
-        if(email != null && !email.isEmpty()){
+        if(user.getEmail() != null && !user.getEmail().isEmpty()){
             Map<String, String> unbindMap = new HashMap<>();
             Map<String, String> unbindMap = new HashMap<>();
             unbindMap.put("nickname", user.getNickname());
             unbindMap.put("nickname", user.getNickname());
-            unbindMap.put("emails", email);
+            unbindMap.put("emails", user.getEmail());
-            send(user, MessageCategory.EMAIL_UNNBIND, unbindMap);
+            String[] emails = newEmail.split("@");
+            emails[0] = "****"+emails[0].substring(emails[0].length() - 3);
+            unbindMap.put("email", String.join("@", emails));
+            send(user, MessageCategory.EMAIL_UNNBIND, unbindMap, 0);
         }
         }
         Map<String, String> changeMap = new HashMap<>();
         Map<String, String> changeMap = new HashMap<>();
         changeMap.put("nickname", user.getNickname());
         changeMap.put("nickname", user.getNickname());
-        send(user, MessageCategory.EMAIL_CHANGE, changeMap);
+        changeMap.put("emails", newEmail);
+        send(user, MessageCategory.EMAIL_CHANGE, changeMap, 0);
     }
     }
 
 
     private String replaceBody(String body, Map<String, String> params){
     private String replaceBody(String body, Map<String, String> params){
@@ -404,4 +838,22 @@ public class MessageExtendService {
         }
         }
         return body;
         return body;
     }
     }
+
+    private String replaceLink(String link, String p, Integer relationId){
+        return link.replace("\\{"+p+"}", String.valueOf(relationId));
+    }
+
+    private String makeLink(String link, String linkTitle, Integer relationId){
+        return String.format("<a href=\"%s\" target=\"_blank\">%s</a>", pcUrl + replaceLink(link, "id",relationId), linkTitle);
+    }
+
+    private String getDate(Date date){
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
+        return sdf.format(date);
+    }
+
+    private String getTime(Date time){
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
+        return sdf.format(time);
+    }
 }
 }

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

@@ -774,6 +774,7 @@ public class OrderFlowService {
         List<UserOrderCheckout> checkoutList = userOrderCheckoutService.allByUser(userId, orderId);
         List<UserOrderCheckout> checkoutList = userOrderCheckoutService.allByUser(userId, orderId);
         List<UserOrderCheckout> pList = checkoutList.stream().filter((checkout)-> checkout.getParentId() == 0).collect(Collectors.toList());
         List<UserOrderCheckout> pList = checkoutList.stream().filter((checkout)-> checkout.getParentId() == 0).collect(Collectors.toList());
         List<UserOrderCheckout> cList = checkoutList.stream().filter((checkout)-> checkout.getParentId() > 0).collect(Collectors.toList());
         List<UserOrderCheckout> cList = checkoutList.stream().filter((checkout)-> checkout.getParentId() > 0).collect(Collectors.toList());
+
         Map<Integer, Integer> pMap = new HashMap<>();
         Map<Integer, Integer> pMap = new HashMap<>();
         for(UserOrderCheckout checkout : pList){
         for(UserOrderCheckout checkout : pList){
             UserOrderRecord record = Transform.convert(checkout, UserOrderRecord.class);
             UserOrderRecord record = Transform.convert(checkout, UserOrderRecord.class);
@@ -833,7 +834,8 @@ public class OrderFlowService {
             // 设定标志
             // 设定标志
             usersService.edit(User.builder().id(userOrder.getUserId()).isCourse(1).build());
             usersService.edit(User.builder().id(userOrder.getUserId()).isCourse(1).build());
         }
         }
-        messageExtendService.sendPayed(user, userOrder);
+        List<UserOrderRecord> recordList = userOrderRecordService.allByUser(user.getId(), userOrder.getId());
+        messageExtendService.sendPayed(user, userOrder, recordList);
         return true;
         return true;
     }
     }
 
 
@@ -1077,11 +1079,11 @@ public class OrderFlowService {
 
 
     /**
     /**
      * 给用户邀请奖励: 7天vip
      * 给用户邀请奖励: 7天vip
-     * @param userId
+     * @param user
      */
      */
-    public void giveInvite(Integer userId){
+    public void giveInvite(User user){
         UserOrderRecord record = UserOrderRecord.builder()
         UserOrderRecord record = UserOrderRecord.builder()
-                .userId(userId)
+                .userId(user.getId())
                 .productType(ProductType.SERVICE.key)
                 .productType(ProductType.SERVICE.key)
                 .service(ServiceKey.VIP.key)
                 .service(ServiceKey.VIP.key)
                 .param(ServiceVipKey.DAY7.key)
                 .param(ServiceVipKey.DAY7.key)
@@ -1092,15 +1094,16 @@ public class OrderFlowService {
         // 虚拟order
         // 虚拟order
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         userOrderRecordService.add(record);
         userOrderRecordService.add(record);
+        messageExtendService.sendInviteSuccess(user, record);
     }
     }
 
 
     /**
     /**
      * 给用户备考奖励: 7天vip
      * 给用户备考奖励: 7天vip
-     * @param userId
+     * @param user
      */
      */
-    public void givePrepare(Integer userId){
+    public void givePrepare(User user){
         UserOrderRecord record = UserOrderRecord.builder()
         UserOrderRecord record = UserOrderRecord.builder()
-                .userId(userId)
+                .userId(user.getId())
                 .productType(ProductType.SERVICE.key)
                 .productType(ProductType.SERVICE.key)
                 .service(ServiceKey.VIP.key)
                 .service(ServiceKey.VIP.key)
                 .param(ServiceVipKey.DAY7.key)
                 .param(ServiceVipKey.DAY7.key)
@@ -1111,15 +1114,16 @@ public class OrderFlowService {
         // 虚拟order
         // 虚拟order
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         userOrderRecordService.add(record);
         userOrderRecordService.add(record);
+        messageExtendService.sendPrepare(user, record);
     }
     }
 
 
     /**
     /**
      * 给用户实名认证奖励: 180天vip
      * 给用户实名认证奖励: 180天vip
-     * @param userId
+     * @param user
      */
      */
-    public void giveReal(Integer userId){
+    public void giveReal(User user){
         UserOrderRecord record = UserOrderRecord.builder()
         UserOrderRecord record = UserOrderRecord.builder()
-                .userId(userId)
+                .userId(user.getId())
                 .productType(ProductType.SERVICE.key)
                 .productType(ProductType.SERVICE.key)
                 .service(ServiceKey.VIP.key)
                 .service(ServiceKey.VIP.key)
                 .param(ServiceVipKey.MONTH3.key)
                 .param(ServiceVipKey.MONTH3.key)
@@ -1130,6 +1134,26 @@ public class OrderFlowService {
         // 虚拟order
         // 虚拟order
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
         userOrderRecordService.add(record);
         userOrderRecordService.add(record);
+        messageExtendService.sendReal(user, record);
     }
     }
 
 
+    /**
+     * 反馈奖励:7天vip
+     * @param userId
+     */
+    public UserOrderRecord giveAction(Integer userId){
+        UserOrderRecord record = UserOrderRecord.builder()
+                .userId(userId)
+                .productType(ProductType.SERVICE.key)
+                .service(ServiceKey.VIP.key)
+                .param(ServiceVipKey.DAY7.key)
+                .source(RecordSource.GIFT_ACTION.key)
+                .expireDays(0)
+                .useExpireDays(ServiceVipKey.DAY7.useExpireDay)
+                .build();
+        // 虚拟order
+        record = initRecordCallback.get(ProductType.SERVICE).callback(UserOrder.builder().build(), record);
+        userOrderRecordService.add(record);
+        return record;
+    }
 }
 }

+ 6 - 3
server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookLibraryService.java

@@ -110,19 +110,22 @@ public class TextbookLibraryService extends AbstractService {
             library.setQuant(history.getQuant());
             library.setQuant(history.getQuant());
             library.setQuantVersion(library.getQuantVersion() + 1);
             library.setQuantVersion(library.getQuantVersion() + 1);
             library.setQuantTime(now);
             library.setQuantTime(now);
-            history.setQuantVersion(library.getQuantVersion());
+            library.setQuantDescription(history.getQuantDescription());
+            history.setQuantVersion(library.getQuantVersion() + 1);
         }
         }
         if (!history.getIr().isEmpty()){
         if (!history.getIr().isEmpty()){
             library.setIr(history.getIr());
             library.setIr(history.getIr());
             library.setIrVersion(library.getIrVersion() + 1);
             library.setIrVersion(library.getIrVersion() + 1);
             library.setIrTime(now);
             library.setIrTime(now);
-            history.setIrVersion(library.getIrVersion());
+            library.setIrDescription(history.getIrDescription());
+            history.setIrVersion(library.getIrVersion() + 1);
         }
         }
         if (!history.getRc().isEmpty()){
         if (!history.getRc().isEmpty()){
             library.setRc(history.getRc());
             library.setRc(history.getRc());
             library.setRcVersion(library.getRcVersion() + 1);
             library.setRcVersion(library.getRcVersion() + 1);
             library.setRcTime(now);
             library.setRcTime(now);
-            history.setRcVersion(library.getRcVersion());
+            library.setRcDescription(history.getRcDescription());
+            history.setRcVersion(library.getRcVersion() + 1);
         }
         }
         library.setHistoryNumber(library.getHistoryNumber() + 1);
         library.setHistoryNumber(library.getHistoryNumber() + 1);
 
 

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

@@ -85,12 +85,12 @@ public class UserOrderRecordService extends AbstractService {
     }
     }
 
 
     /**
     /**
-     * 获取所有到期的课程记录
+     * 获取所有使用到期的课程记录
      * @param startTime
      * @param startTime
      * @param endTime
      * @param endTime
      * @return
      * @return
      */
      */
-    public List<UserOrderRecord> allCourseExpire(String startTime, String endTime){
+    public List<UserOrderRecord> allCourseUseExpire(String startTime, String endTime){
         Example example = new Example(UserOrderRecord.class);
         Example example = new Example(UserOrderRecord.class);
         example.and(
         example.and(
                 example.createCriteria()
                 example.createCriteria()
@@ -103,6 +103,61 @@ public class UserOrderRecordService extends AbstractService {
     }
     }
 
 
     /**
     /**
+     * 获取所有开通到期的课程记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public List<UserOrderRecord> allCourseOpenExpire(String startTime, String endTime){
+        Example example = new Example(UserOrderRecord.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("productType", ProductType.COURSE.key)
+                        .andEqualTo("isUse", 0)
+                        .andGreaterThanOrEqualTo("endTime", startTime)
+                        .andLessThanOrEqualTo("endTime", endTime)
+        );
+        return select(userOrderRecordMapper, example);
+    }
+
+    /**
+     * 获取所有到期的课程记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public List<UserOrderRecord> allServiceUseExpire(ServiceKey serviceKey, String startTime, String endTime){
+        Example example = new Example(UserOrderRecord.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("productType", ProductType.SERVICE.key)
+                        .andEqualTo("service", serviceKey.key)
+                        .andGreaterThanOrEqualTo("useEndTime", startTime)
+                        .andLessThanOrEqualTo("useEndTime", endTime)
+        );
+        return select(userOrderRecordMapper, example);
+    }
+
+    /**
+     * 获取所有到期的课程记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public List<UserOrderRecord> allServiceOpenExpire(ServiceKey serviceKey, String startTime, String endTime){
+        Example example = new Example(UserOrderRecord.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("productType", ProductType.SERVICE.key)
+                        .andEqualTo("service", serviceKey.key)
+                        .andEqualTo("isUse", 0)
+                        .andGreaterThanOrEqualTo("endTime", startTime)
+                        .andLessThanOrEqualTo("endTime", endTime)
+        );
+        return select(userOrderRecordMapper, example);
+    }
+
+    /**
      * 列出购买资料的记录
      * 列出购买资料的记录
      * @param page
      * @param page
      * @param size
      * @param size
@@ -145,6 +200,23 @@ public class UserOrderRecordService extends AbstractService {
 
 
     /**
     /**
      * 列出购买资料的记录
      * 列出购买资料的记录
+     * @param dataId
+     * @param userIds
+     * @return
+     */
+    public List<UserOrderRecord> listWithDataUser(Integer dataId, Collection userIds){
+        Example example = new Example(UserOrderRecord.class);
+        example.and(
+                example.createCriteria()
+                        .andIn("userId", userIds)
+                        .andEqualTo("productType", ProductType.DATA.key)
+                        .andEqualTo("productId", dataId)
+        );
+        return select(userOrderRecordMapper, example);
+    }
+
+    /**
+     * 列出购买资料的记录
      * @param userId
      * @param userId
      * @param dataId
      * @param dataId
      * @return
      * @return

+ 31 - 5
server/gateway-api/src/main/java/com/qxgmat/task/AsyncTask.java

@@ -253,19 +253,42 @@ public class AsyncTask {
     }
     }
 
 
     @Async
     @Async
+    public void postTextbookLibrary(){
+        logger.info("换库");
+        long start = System.currentTimeMillis();
+        TextbookLibrary latest = textbookLibraryService.getLatest();
+        TextbookLibrary second = textbookLibraryService.getSecond();
+        List<User> userList;
+        // 所有用户
+        int page = 1;
+        int size = 20;
+        do {
+            userList = usersService.select(page, size);
+            for(User user : userList){
+                messageExtendService.sendTextbookLibrary(user, latest, second);
+            }
+        }while(userList.size() >= size);
+        long end = System.currentTimeMillis();
+        logger.info("发布机经,耗时:" + (end - start) + "毫秒");
+    }
+
+    @Async
     public void postTextbookUpdate(){
     public void postTextbookUpdate(){
         logger.info("发布机经:发送到订阅用户邮箱");
         logger.info("发布机经:发送到订阅用户邮箱");
         long start = System.currentTimeMillis();
         long start = System.currentTimeMillis();
         TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
         TextbookLibrary textbookLibrary = textbookLibraryService.getLatest();
         List<UserService> userServiceList;
         List<UserService> userServiceList;
+        // 所有开通机经的用户
         int page = 1;
         int page = 1;
         int size = 20;
         int size = 20;
         do {
         do {
-            userServiceList = userServiceService.listByService(page, size, ServiceKey.TEXTBOOK, true);
+            userServiceList = userServiceService.listByService(page, size, ServiceKey.TEXTBOOK, null);
+            Map userServiceMap = Transform.getMap(userServiceList, UserService.class, "userId");
             Collection userIds = Transform.getIds(userServiceList, UserService.class, "userId");
             Collection userIds = Transform.getIds(userServiceList, UserService.class, "userId");
             List<User> userList = usersService.select(userIds);
             List<User> userList = usersService.select(userIds);
             for(User user : userList){
             for(User user : userList){
-                 messageExtendService.sendTextbookUpdate(user, textbookLibrary);
+                UserService userService = (UserService) userServiceMap.get(user.getId());
+                messageExtendService.sendTextbookUpdate(user, textbookLibrary, userService.getIsSubscribe() > 0);
             }
             }
         }while(userServiceList.size() >= size);
         }while(userServiceList.size() >= size);
         long end = System.currentTimeMillis();
         long end = System.currentTimeMillis();
@@ -279,18 +302,21 @@ public class AsyncTask {
         CourseDataHistory history = courseDataHistoryService.get(dataHistoryId);
         CourseDataHistory history = courseDataHistoryService.get(dataHistoryId);
         CourseData courseData = courseDataService.get(history.getDataId());
         CourseData courseData = courseDataService.get(history.getDataId());
         List<UserCourseDataSubscribe> subscribeList;
         List<UserCourseDataSubscribe> subscribeList;
+        // 所有订阅资料的用户
         int page = 1;
         int page = 1;
         int size = 20;
         int size = 20;
         do {
         do {
             subscribeList = userCourseDataSubscribeService.listByData(page, size, courseData.getId());
             subscribeList = userCourseDataSubscribeService.listByData(page, size, courseData.getId());
             Collection userIds = Transform.getIds(subscribeList, UserCourseDataSubscribe.class, "userId");
             Collection userIds = Transform.getIds(subscribeList, UserCourseDataSubscribe.class, "userId");
             List<User> userList = usersService.select(userIds);
             List<User> userList = usersService.select(userIds);
+            List<UserOrderRecord> records = userOrderRecordService.listWithDataUser(courseData.getId(), userIds);
+            Map recordMap = Transform.getMap(records, UserOrderRecord.class, "userId");
             for(User user : userList){
             for(User user : userList){
-                if (user.getDataEmailSubscribe() == 0) continue;
+                // 购买并且开通邮箱订阅:才算邮箱订阅
-                messageExtendService.sendDataUpdate(user, courseData, history);
+                // 纸质没有购买记录,发送也不区分是否订阅状态
+                messageExtendService.sendDataUpdate(user, courseData, history, user.getDataEmailSubscribe() > 0 && recordMap.get(user.getId())!= null);
             }
             }
         }while(subscribeList.size() >= size);
         }while(subscribeList.size() >= size);
-        // messageExtendService.sendDataUpdate(user, courseData, history);
         long end = System.currentTimeMillis();
         long end = System.currentTimeMillis();
         logger.info("资料更新,耗时:" + (end - start) + "毫秒");
         logger.info("资料更新,耗时:" + (end - start) + "毫秒");
     }
     }

+ 78 - 17
server/gateway-api/src/main/java/com/qxgmat/task/ScheduledTask.java

@@ -6,13 +6,16 @@ import com.github.pagehelper.Page;
 import com.nuliji.tools.Tools;
 import com.nuliji.tools.Tools;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.third.OauthData;
 import com.nuliji.tools.third.OauthData;
+import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.constants.enums.SettingKey;
 import com.qxgmat.data.constants.enums.SettingKey;
 import com.qxgmat.data.constants.enums.module.CourseModule;
 import com.qxgmat.data.constants.enums.module.CourseModule;
+import com.qxgmat.data.constants.enums.module.ProductType;
 import com.qxgmat.data.constants.enums.status.MessageStatus;
 import com.qxgmat.data.constants.enums.status.MessageStatus;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.relation.entity.UserPrepareRelation;
 import com.qxgmat.data.relation.entity.UserPrepareRelation;
 import com.qxgmat.help.WechatHelp;
 import com.qxgmat.help.WechatHelp;
 import com.qxgmat.service.UserPaperService;
 import com.qxgmat.service.UserPaperService;
+import com.qxgmat.service.UserServiceService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.CourseExtendService;
 import com.qxgmat.service.extend.CourseExtendService;
 import com.qxgmat.service.extend.MessageExtendService;
 import com.qxgmat.service.extend.MessageExtendService;
@@ -85,6 +88,9 @@ public class ScheduledTask {
     @Autowired
     @Autowired
     private UserCourseRecordService userCourseRecordService;
     private UserCourseRecordService userCourseRecordService;
 
 
+    @Autowired
+    private UserServiceService userServiceService;
+
     /**
     /**
      * cron表达式:* * * * * *(共6位,使用空格隔开,具体如下)
      * cron表达式:* * * * * *(共6位,使用空格隔开,具体如下)
      * cron表达式:*(秒0-59) *(分钟0-59) *(小时0-23) *(日期1-31) *(月份1-12或是JAN-DEC) *(星期1-7或是SUN-SAT)
      * cron表达式:*(秒0-59) *(分钟0-59) *(小时0-23) *(日期1-31) *(月份1-12或是JAN-DEC) *(星期1-7或是SUN-SAT)
@@ -265,12 +271,12 @@ public class ScheduledTask {
     }
     }
 
 
     // 课程到期:判断延期奖励,听课频率<=2天/作业100%->10天,90%以上7天
     // 课程到期:判断延期奖励,听课频率<=2天/作业100%->10天,90%以上7天
-    @Scheduled(cron="0 0 * * * *")
+//    @Scheduled(cron="0 0 * * * *")
     public void awardCourseExpire(){
     public void awardCourseExpire(){
         // 下一小时内到期的课程
         // 下一小时内到期的课程
         Date startTime = new Date();
         Date startTime = new Date();
         Date endTime = Tools.addHour(startTime, 1);
         Date endTime = Tools.addHour(startTime, 1);
-        List<UserOrderRecord> recordList = userOrderRecordService.allCourseExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allCourseUseExpire(startTime.toString(), endTime.toString());
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
             Course course = courseService.get(record.getId());
             Course course = courseService.get(record.getId());
             if (CourseModule.ValueOf(course.getCourseModule()) != CourseModule.VIDEO){
             if (CourseModule.ValueOf(course.getCourseModule()) != CourseModule.VIDEO){
@@ -323,14 +329,38 @@ public class ScheduledTask {
         }
         }
     }
     }
 
 
+    // 每天判断VIP,使用有效期还剩10天
+    @Scheduled(cron="0 1 0 * * *")
+    public void vipUseExpire() {
+        Date endTime = Tools.today();
+        Date startTime = Tools.addDate(endTime, 10);
+        List<UserService> serviceList = userServiceService.allExpire(ServiceKey.VIP, startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(serviceList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
+        for(UserService service : serviceList){
+            User user = (User) userMap.get(service.getUserId());
+            messageExtendService.sendUseExpire(user, UserOrderRecord.builder()
+                    .userId(user.getId())
+                    .productType(ProductType.SERVICE.key)
+                    .service(ServiceKey.VIP.key)
+                    .useEndTime(service.getExpireTime())
+                    .build(), 10);
+        }
+    }
+
     // 每天判断模考一直没开通、距离开通有效期还剩30天
     // 每天判断模考一直没开通、距离开通有效期还剩30天
     @Scheduled(cron="0 1 0 * * *")
     @Scheduled(cron="0 1 0 * * *")
-    public void catExpire() {
+    public void catOpenExpire() {
         Date endTime = Tools.today();
         Date endTime = Tools.today();
         Date startTime = Tools.addDate(endTime, 30);
         Date startTime = Tools.addDate(endTime, 30);
-        List<UserOrderRecord> recordList = userOrderRecordService.allSuspendExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allServiceOpenExpire(ServiceKey.QX_CAT, startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
-            courseExtendService.restoreCourse(record.getUserId(), record.getId());
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendOpenExpire(user, record, 30);
         }
         }
     }
     }
 
 
@@ -338,32 +368,59 @@ public class ScheduledTask {
     @Scheduled(cron="0 1 0 * * *")
     @Scheduled(cron="0 1 0 * * *")
     public void catUseExpire() {
     public void catUseExpire() {
         Date endTime = Tools.today();
         Date endTime = Tools.today();
-        Date startTime = Tools.addDate(endTime, 30);
+        Date startTime = Tools.addDate(endTime, 10);
-        List<UserOrderRecord> recordList = userOrderRecordService.allSuspendExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allServiceUseExpire(ServiceKey.QX_CAT, startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
-            courseExtendService.restoreCourse(record.getUserId(), record.getId());
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendUseExpire(user, record, 10);
         }
         }
     }
     }
 
 
     // 每天判断机经一直没开通、距离开通有效期还剩30天。
     // 每天判断机经一直没开通、距离开通有效期还剩30天。
     @Scheduled(cron="0 1 0 * * *")
     @Scheduled(cron="0 1 0 * * *")
-    public void textbookExpire() {
+    public void textbookOpenExpire() {
         Date endTime = Tools.today();
         Date endTime = Tools.today();
         Date startTime = Tools.addDate(endTime, 30);
         Date startTime = Tools.addDate(endTime, 30);
-        List<UserOrderRecord> recordList = userOrderRecordService.allSuspendExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allServiceOpenExpire(ServiceKey.TEXTBOOK, startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
-            courseExtendService.restoreCourse(record.getUserId(), record.getId());
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendOpenExpire(user, record, 30);
+        }
+    }
+
+    // 每天判断机经已开通,使用有效期还剩10天
+    @Scheduled(cron="0 1 0 * * *")
+    public void textbookUseExpire() {
+        Date endTime = Tools.today();
+        Date startTime = Tools.addDate(endTime, 30);
+        List<UserOrderRecord> recordList = userOrderRecordService.allServiceUseExpire(ServiceKey.TEXTBOOK, startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
+        for(UserOrderRecord record : recordList){
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendUseExpire(user, record, 10);
         }
         }
     }
     }
 
 
     // 每天判断课程一直没开通、距离开通有效期还剩30天。
     // 每天判断课程一直没开通、距离开通有效期还剩30天。
     @Scheduled(cron="0 1 0 * * *")
     @Scheduled(cron="0 1 0 * * *")
-    public void courseExpire() {
+    public void courseOpenExpire() {
         Date endTime = Tools.today();
         Date endTime = Tools.today();
         Date startTime = Tools.addDate(endTime, 30);
         Date startTime = Tools.addDate(endTime, 30);
-        List<UserOrderRecord> recordList = userOrderRecordService.allSuspendExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allCourseOpenExpire(startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
-            courseExtendService.restoreCourse(record.getUserId(), record.getId());
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendOpenExpire(user, record, 30);
         }
         }
     }
     }
 
 
@@ -371,10 +428,14 @@ public class ScheduledTask {
     @Scheduled(cron="0 1 0 * * *")
     @Scheduled(cron="0 1 0 * * *")
     public void courseUseExpire() {
     public void courseUseExpire() {
         Date endTime = Tools.today();
         Date endTime = Tools.today();
-        Date startTime = Tools.addDate(endTime, 30);
+        Date startTime = Tools.addDate(endTime, 10);
-        List<UserOrderRecord> recordList = userOrderRecordService.allSuspendExpire(startTime.toString(), endTime.toString());
+        List<UserOrderRecord> recordList = userOrderRecordService.allCourseUseExpire(startTime.toString(), endTime.toString());
+        Collection userIds = Transform.getIds(recordList, UserOrderRecord.class, "userId");
+        List<User> userList = usersService.select(userIds);
+        Map userMap = Transform.getMap(userList, User.class, "id");
         for(UserOrderRecord record : recordList){
         for(UserOrderRecord record : recordList){
-            courseExtendService.restoreCourse(record.getUserId(), record.getId());
+            User user = (User) userMap.get(record.getUserId());
+            messageExtendService.sendUseExpire(user, record, 10);
         }
         }
     }
     }
 }
 }

+ 3 - 2
server/gateway-api/src/main/resources/application.yml

@@ -68,8 +68,6 @@ ip:
 self:
 self:
   secret: qianxing-duoshaojiaoyu
   secret: qianxing-duoshaojiaoyu
 
 
-
-
 paper:
 paper:
   sentenceLength: 20
   sentenceLength: 20
   textbookLength: 31
   textbookLength: 31
@@ -82,3 +80,6 @@ examination:
   verbalB: 2
   verbalB: 2
   quantC: 1
   quantC: 1
   quantD: 2
   quantD: 2
+
+info:
+  wechat: 千行GMAT

+ 11 - 6
server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudMail.java

@@ -13,10 +13,7 @@ import org.springframework.util.MultiValueMap;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.RestTemplate;
 
 
 import java.io.File;
 import java.io.File;
-import java.util.Comparator;
+import java.util.*;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
 
 
 public class SendCloudMail {
 public class SendCloudMail {
     private static final Logger logger = LoggerFactory.getLogger(SendCloudMail.class);
     private static final Logger logger = LoggerFactory.getLogger(SendCloudMail.class);
@@ -54,7 +51,15 @@ public class SendCloudMail {
         }
         }
     }
     }
 
 
-    public Response sendMail(String to, String subject, String body, String from, String fromName, FileSystemResource attachments) {
+    public Response sendMail(String to, String subject, String body, String from, String fromName) {
+        return sendMail(to, subject, body, from, fromName, new ArrayList<>());
+    }
+
+    public Response sendMail(String to, String subject, String body, String from, String fromName, FileSystemResource attachment) {
+        return sendMail(to, subject, body, from, fromName, new ArrayList<FileSystemResource>(){{add(attachment);}});
+    }
+
+    public Response sendMail(String to, String subject, String body, String from, String fromName, List<FileSystemResource> attachments) {
         MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
         MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
         params.add("apiUser", apiUser);
         params.add("apiUser", apiUser);
         params.add("apiKey", apiKey);
         params.add("apiKey", apiKey);
@@ -66,7 +71,7 @@ public class SendCloudMail {
         if (fromName != null) {
         if (fromName != null) {
             params.add("fromName", fromName);
             params.add("fromName", fromName);
         }
         }
-        if (attachments != null){
+        if (attachments != null && attachments.size() > 0){
             params.add("attachments", attachments);
             params.add("attachments", attachments);
         }
         }