소스 검색

feat(server): 备考资料

Go 4 년 전
부모
커밋
2d7a9b36e6
56개의 변경된 파일1765개의 추가작업 그리고 468개의 파일을 삭제
  1. 27 0
      front/project/Constant.js
  2. 15 0
      front/project/admin/routes/ready/data/index.js
  3. 3 0
      front/project/admin/routes/ready/data/index.less
  4. 152 0
      front/project/admin/routes/ready/data/page.js
  5. 2 1
      front/project/admin/routes/ready/index.js
  6. 2 1
      front/project/admin/routes/show/index.js
  7. 157 119
      front/project/admin/routes/show/message/page.js
  8. 16 0
      front/project/admin/routes/show/messageDetail/index.js
  9. 3 0
      front/project/admin/routes/show/messageDetail/index.less
  10. 112 0
      front/project/admin/routes/show/messageDetail/page.js
  11. 20 0
      front/project/admin/stores/ready.js
  12. 4 8
      front/project/admin/stores/system.js
  13. 2 11
      front/project/h5/routes/page/bind/page.js
  14. 6 4
      front/project/h5/stores/user.js
  15. 6 23
      front/project/www/components/Login/index.js
  16. 6 4
      front/project/www/stores/user.js
  17. 37 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java
  18. 25 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageMethod.java
  19. 40 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/MessageType.java
  20. 0 1
      server/data/src/main/java/com/qxgmat/data/constants/enums/SettingKey.java
  21. 0 21
      server/data/src/main/java/com/qxgmat/data/constants/enums/user/MessageType.java
  22. 0 7
      server/data/src/main/java/com/qxgmat/data/dao/MessageMapper.java
  23. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/MessageTemplateMapper.java
  24. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/ReadyDataMapper.java
  25. 77 7
      server/data/src/main/java/com/qxgmat/data/dao/entity/Message.java
  26. 247 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/ReadyData.java
  27. 7 4
      server/data/src/main/java/com/qxgmat/data/dao/mapping/MessageMapper.xml
  28. 33 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/ReadyDataMapper.xml
  29. 47 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/ReadyController.java
  30. 34 33
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  31. 2 2
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  32. 2 2
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  33. 15 2
      server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java
  34. 4 3
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  35. 2 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  36. 5 3
      server/gateway-api/src/main/java/com/qxgmat/controller/api/SentenceController.java
  37. 13 3
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/MessageDto.java
  38. 58 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/ReadyDataDto.java
  39. 14 0
      server/gateway-api/src/main/java/com/qxgmat/dto/request/UserLoginDto.java
  40. 14 0
      server/gateway-api/src/main/java/com/qxgmat/dto/request/UserValidMobileDto.java
  41. 2 12
      server/gateway-api/src/main/java/com/qxgmat/help/MailHelp.java
  42. 28 0
      server/gateway-api/src/main/java/com/qxgmat/help/WechatHelp.java
  43. 2 2
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java
  44. 162 34
      server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java
  45. 0 12
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ToolsService.java
  46. 0 104
      server/gateway-api/src/main/java/com/qxgmat/service/inline/MessageService.java
  47. 140 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/MessageTemplateService.java
  48. 105 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/ReadyDataService.java
  49. 19 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/ReadyReadService.java
  50. 18 18
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserMessageService.java
  51. 13 9
      server/gateway-api/src/main/java/com/qxgmat/task/ScheduledTask.java
  52. 16 1
      server/gateway-api/src/main/profile/dev/application-runtime.yml
  53. 17 1
      server/gateway-api/src/main/profile/prod/application-runtime.yml
  54. 17 1
      server/gateway-api/src/main/profile/test/application-runtime.yml
  55. 0 11
      server/gateway-api/src/main/resources/application.yml
  56. 3 3
      server/tools/src/main/java/com/nuliji/tools/third/wechat/WechatClient.java

+ 27 - 0
front/project/Constant.js

@@ -108,6 +108,33 @@ export const ContractKey = [{ label: '注册', value: 'register' }, { label: '
 
 export const RoomPosition = [{ label: '北部', value: 'north' }, { label: '东部', value: 'east' }, { label: '中南', value: 'south_central' }, { label: '西部', value: 'west' }, { label: '海外', value: 'overseas' }];
 
+export const MessageCategory = [
+  { label: '注册消息', value: 'register', params: [''] },
+  { label: '登录异常', value: 'login_abnormal', params: [''] },
+  { label: '机经更新', value: 'textbook_update', params: [''] },
+  { label: '预习作业提醒', value: 'preview_notice', params: [''] },
+  { label: '支付成功提醒', value: 'payed', params: [''] },
+  { label: '资料更新', value: 'data_update', params: [''] },
+  { label: '题目提问回复', value: 'ask_auestion', params: [''] },
+  { label: '课程提问回复', value: 'ask_course', params: [''] },
+  { label: '咨询回复', value: 'faq_callback', params: [''] },
+  { label: '纠错回复', value: 'feedback_callback', params: [''] },
+  { label: '邀请好友注册', value: 'invited', params: [''] },
+  { label: '邮箱变更', value: 'email_change', params: [''] },
+  { label: '自定义消息', value: 'custom', params: [''] },
+];
+
+export const MessageEmailStatus = [
+  { label: '已停止', value: 0 },
+  { label: '运行中', value: 1 },
+];
+
+export const MessageCustomStatus = [
+  { label: '未发送', value: 0 },
+  { label: '发送中', value: 1 },
+  { label: '发送完', value: 2 },
+];
+
 export const FaqChannel = [
   { label: 'GetReady', value: 'getready' },
   { label: '模考', value: 'examination' },

+ 15 - 0
front/project/admin/routes/ready/data/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/ready/data',
+  key: 'ready-data',
+  title: '备考资料',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/ready/data/index.less

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

+ 152 - 0
front/project/admin/routes/ready/data/page.js

@@ -0,0 +1,152 @@
+import React from 'react';
+import { Tabs } from 'antd';
+import './index.less';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import ShowImage from '@src/components/ShowImage';
+import ActionLayout from '@src/layouts/ActionLayout';
+import TableLayout from '@src/layouts/TableLayout';
+// import { formatDate } from '@src/services/Tools';
+import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
+import { Ready } from '../../../stores/ready';
+import { System } from '../../../stores/system';
+
+export default class extends Page {
+  init() {
+    this.itemList = [{
+      key: 'id',
+      type: 'hidden',
+    }, {
+      key: 'title',
+      type: 'input',
+      name: '标题',
+    }, {
+      key: 'content',
+      type: 'textarea',
+      name: '内容',
+    }, {
+      key: 'cover',
+      type: 'image',
+      name: '封面图片',
+      onUpload: ({ file }) => {
+        return System.uploadImage(file).then(result => { return result; });
+      },
+    }];
+
+    this.columns = [{
+      title: '资料封面',
+      dataIndex: 'cover',
+      render: (text) => {
+        return <ShowImage src={text} />;
+      },
+    }, {
+      title: '资料标题',
+      dataIndex: 'title',
+    }, {
+      title: '内容',
+      dataIndex: 'content',
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <a onClick={() => {
+              this.editAction(record);
+            }}>编辑</a>
+          )}
+        </div>;
+      },
+    }];
+
+    this.actionList = [{
+      key: 'add',
+      name: '增加',
+    }];
+  }
+
+  initData() {
+    this.refreshTab(this.state.search.tab || 'official');
+  }
+
+  refreshTab(tab) {
+    const { search } = this.state;
+    search.tab = tab;
+    this.setState({ search });
+    if (tab === 'official') {
+      return this.refreshOfficial();
+    }
+    if (tab === 'unofficial') {
+      return this.refreshUnofficial();
+    }
+    return Promise.reject();
+  }
+
+  refreshOfficial() {
+    return Ready.listData({ isOfficial: true }).then((result) => {
+      this.setState({ list: result.list, total: result.total });
+    });
+  }
+
+  refreshUnofficial() {
+    return Ready.listData({ isOfficial: false }).then((result) => {
+      this.setState({ list: result.list, total: result.total });
+    });
+  }
+
+  addAction() {
+    asyncForm('创建资料', this.itemList, {}, data => {
+      data.isOfficial = this.state.search.tab === 'official' ? 1 : 0;
+      return Ready.addData(data).then(() => {
+        asyncSMessage('添加成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  editAction(row) {
+    asyncForm('编辑资料', this.itemList, row, data => {
+      return Ready.editData(data).then(() => {
+        asyncSMessage('编辑成功!');
+        this.refresh();
+      });
+    });
+  }
+
+  renderOfficial() {
+    return <Block flex>
+      <ActionLayout
+        itemList={this.actionList}
+        selectedKeys={this.state.selectedKeys}
+        onAction={key => this.onAction(key)}
+      />
+      <TableLayout
+        columns={this.tableSort(this.columns)}
+        list={this.state.list}
+        pagination={false}
+        loading={this.props.core.loading}
+      />
+    </Block>;
+  }
+
+  renderUnofficial() {
+    return this.renderOfficial();
+  }
+
+  renderView() {
+    const { search } = this.state;
+    const { tab } = search;
+    return <div>
+      <Tabs activeKey={tab || 'official'} onChange={(value) => {
+        this.search({ tab: value });
+      }}>
+        <Tabs.TabPane tab="官方资料" key="official">
+          {this.renderOfficial()}
+        </Tabs.TabPane>
+        <Tabs.TabPane tab="非官方资料" key="unofficial">
+          {this.renderUnofficial()}
+        </Tabs.TabPane>
+      </Tabs>
+    </div>;
+  }
+}

+ 2 - 1
front/project/admin/routes/ready/index.js

@@ -1,7 +1,8 @@
 import article from './article';
 import articleDetail from './articleDetail';
 import room from './room';
+import data from './data';
 import read from './read';
 import readDetail from './readDetail';
 
-export default [article, articleDetail, room, read, readDetail];
+export default [article, articleDetail, room, data, read, readDetail];

+ 2 - 1
front/project/admin/routes/show/index.js

@@ -4,6 +4,7 @@ import faq from './faq';
 import comment from './comment';
 import ad from './ad';
 import message from './message';
+import messageDetail from './messageDetail';
 import deploy from './deploy';
 
-export default [tips, faq, comment, ad, message, deploy];
+export default [tips, faq, comment, ad, message, messageDetail, deploy];

+ 157 - 119
front/project/admin/routes/show/message/page.js

@@ -1,13 +1,19 @@
 import React from 'react';
-import { Button, Tabs, Row, Col, Form, Input } from 'antd';
+import { Tabs } from 'antd';
+import { Link } from 'react-router-dom';
 import './index.less';
 import Page from '@src/containers/Page';
 import Block from '@src/components/Block';
 import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
-import { formatFormError, formatDate } from '@src/services/Tools';
+import { formatDate, getMap } from '@src/services/Tools';
 import { asyncSMessage, asyncForm } from '@src/services/AsyncTools';
 import { System } from '../../../stores/system';
+import { MessageCategory, MessageEmailStatus, MessageCustomStatus } from '../../../../Constant';
+
+const MessageCategoryMap = getMap(MessageCategory, 'value', 'label');
+const MessageEmailStatusMap = getMap(MessageEmailStatus, 'value', 'label');
+const MessageCustomStatusMap = getMap(MessageCustomStatus, 'value', 'label');
 
 export default class extends Page {
   init() {
@@ -45,11 +51,17 @@ export default class extends Page {
         return formatDate(text);
       },
     }, {
+      title: '状态',
+      dataIndex: 'sendStatus',
+      render: (text) => {
+        return MessageCustomStatusMap[text] || '';
+      },
+    }, {
       title: '操作',
       dataIndex: 'handler',
       render: (text, record) => {
         return <div className="table-button">
-          {(
+          {record.sendStatus === 0 && (
             <a onClick={() => {
               this.editAction(record);
             }}>编辑</a>
@@ -58,74 +70,135 @@ export default class extends Page {
       },
     }];
 
+    this.insideColumns = [{
+      title: '站内信标题',
+      dataIndex: 'title',
+    }, {
+      title: '触发场景',
+      dataIndex: 'messageCategory',
+      render: (text) => {
+        return MessageCategoryMap[text] || '';
+      },
+    }, {
+      title: '发送数量',
+      dataIndex: 'sendNumber',
+    }, {
+      title: '状态',
+      dataIndex: 'sendStatus',
+      render: (text) => {
+        return MessageEmailStatusMap[text] || '';
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <Link to={`/show/message/detail/${record.id}`}>编辑</Link>
+          )}
+          {record.sendStatus === 0 && (
+            <a onClick={() => {
+              this.openAction(record);
+            }}>开启</a>
+          )}
+          {record.sendStatus === 1 && (
+            <a onClick={() => {
+              this.closeAction(record);
+            }}>关闭</a>
+          )}
+        </div>;
+      },
+    }];
+
+    this.emailColumns = [{
+      title: '邮件标题',
+      dataIndex: 'title',
+    }, {
+      title: '触发场景',
+      dataIndex: 'messageCategory',
+      render: (text) => {
+        return MessageCategoryMap[text] || '';
+      },
+    }, {
+      title: '发送数量',
+      dataIndex: 'sendNumber',
+    }, {
+      title: '状态',
+      dataIndex: 'sendStatus',
+      render: (text) => {
+        return MessageEmailStatusMap[text] || '';
+      },
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {(
+            <Link to={`/show/message/detail/${record.id}`}>编辑</Link>
+          )}
+          {record.sendStatus === 0 && (
+            <a onClick={() => {
+              this.openAction(record);
+            }}>开启</a>
+          )}
+          {record.sendStatus === 1 && (
+            <a onClick={() => {
+              this.closeAction(record);
+            }}>关闭</a>
+          )}
+        </div>;
+      },
+    }];
+
     this.actionList = [{
       key: 'add',
       name: '增加',
     }];
-
-    this.state.tab = 'template';
   }
 
   initData() {
-    this.refresh(this.state.tab);
+    this.refreshTab(this.state.search.tab || 'inside');
   }
 
-  refresh(tab) {
-    if (tab === 'template') {
-      return this.refreshTemplate();
+  refreshTab(tab) {
+    const { search } = this.state;
+    search.tab = tab;
+    this.setState({ search });
+    if (tab === 'inside') {
+      return this.refreshInside();
     }
     if (tab === 'custom') {
       return this.refreshCustom();
     }
+    if (tab === 'email') {
+      return this.refreshEmail();
+    }
     return Promise.reject();
   }
 
-  refreshTemplate() {
-    const { form } = this.props;
-    return System.getMessageTemplate().then(result => {
-      form.setFieldsValue(result);
-      this.setState({ template: result || {} });
+  refreshInside() {
+    return System.listMessage({ messageMethod: 'inside', needCustom: false }).then((result) => {
+      this.setState({ list: result.list, total: result.total });
     });
   }
 
   refreshCustom() {
-    return System.listMessage().then((result) => {
+    return System.listMessage({ messageMethod: 'inside', messageCategory: 'custom' }).then((result) => {
       this.setState({ list: result.list, total: result.total });
     });
   }
 
-  submit(tab) {
-    if (tab === 'template') {
-      this.submitTemplate();
-    }
-  }
-
-  submitTemplate() {
-    const { form } = this.props;
-    form.validateFields((err) => {
-      if (!err) {
-        const data = form.getFieldsValue();
-        System.setMessageTemplate(data)
-          .then(() => {
-            this.setState(data);
-            asyncSMessage('保存成功');
-          }).catch((e) => {
-            form.setFields(formatFormError(data, e.result));
-          });
-      }
+  refreshEmail() {
+    return System.listMessage({ messageMethod: 'email', needCustom: false }).then((result) => {
+      this.setState({ list: result.list, total: result.total });
     });
   }
 
-  submitCustom() {
-    const { exercise } = this.state;
-    return System.setExerciseTime(exercise);
-  }
-
   addAction() {
     asyncForm('创建消息', this.itemList, {}, data => {
       return System.addMessage(data).then(() => {
         asyncSMessage('添加成功!');
-        this.refresh('custom');
+        this.refreshTab('custom');
       });
     });
   }
@@ -134,75 +207,34 @@ export default class extends Page {
     asyncForm('编辑消息', this.itemList, row, data => {
       return System.editMessage(data).then(() => {
         asyncSMessage('编辑成功!');
-        this.refresh('custom');
+        this.refreshTab('custom');
       });
     });
   }
 
-  renderTemplate() {
-    const { getFieldDecorator } = this.props.form;
-    return <div>
-      <Block>
-        <h1>购买消息</h1>
-        <Form>
-          <Form.Item help='可插入自定义字段' />
-          <Form.Item>
-            {getFieldDecorator('pay.content', {
-              rules: [
-                { required: false, message: '请输入购买消息内容' },
-              ],
-            })(
-              <Input.TextArea />,
-            )}
-          </Form.Item>
-          <Form.Item label='链接地址'>
-            {getFieldDecorator('pay.link')(
-              <Input placeholder='请输入跳转地址' />,
-            )}
-          </Form.Item>
-        </Form>
-      </Block>
-      <Block>
-        <h1>换库消息</h1>
-        <Form>
-          <Form.Item help='可插入自定义字段' />
-          <Form.Item>
-            {getFieldDecorator('library.content', {
-              rules: [
-                { required: false, message: '请输入换库消息内容' },
-              ],
-            })(
-              <Input.TextArea />,
-            )}
-          </Form.Item>
-          <Form.Item label='链接地址'>
-            {getFieldDecorator('library.link')(
-              <Input placeholder='请输入跳转地址' />,
-            )}
-          </Form.Item>
-        </Form>
-      </Block>
-      <Block>
-        <h1>课程消息</h1>
-        <Form>
-          <Form.Item help='可插入自定义字段' />
-          <Form.Item>
-            {getFieldDecorator('course.content', {
-              rules: [
-                { required: false, message: '请输入课程消息内容' },
-              ],
-            })(
-              <Input.TextArea />,
-            )}
-          </Form.Item>
-          <Form.Item label='链接地址'>
-            {getFieldDecorator('course.link')(
-              <Input placeholder='请输入跳转地址' />,
-            )}
-          </Form.Item>
-        </Form>
-      </Block>
-    </div>;
+  openAction(row) {
+    System.editMessage({ id: row.id, sendStatus: 1 }).then(() => {
+      asyncSMessage('操作成功!');
+      this.refresh();
+    });
+  }
+
+  closeAction(row) {
+    System.editMessage({ id: row.id, sendStatus: 0 }).then(() => {
+      asyncSMessage('操作成功!');
+      this.refresh();
+    });
+  }
+
+  renderInside() {
+    return <Block flex>
+      <TableLayout
+        columns={this.tableSort(this.insideColumns)}
+        list={this.state.list}
+        pagination={false}
+        loading={this.props.core.loading}
+      />
+    </Block>;
   }
 
   renderCustom() {
@@ -221,28 +253,34 @@ export default class extends Page {
     </Block>;
   }
 
+  renderEmail() {
+    return <Block flex>
+      <TableLayout
+        columns={this.tableSort(this.emailColumns)}
+        list={this.state.list}
+        pagination={false}
+        loading={this.props.core.loading}
+      />
+    </Block>;
+  }
+
   renderView() {
-    const { tab } = this.state;
+    const { search } = this.state;
+    const { tab } = search;
     return <div>
-      <Tabs activeKey={tab} onChange={(value) => {
-        this.setState({ tab: value, selectedKeys: [], checkedKeys: [] });
-        this.refresh(value);
+      <Tabs activeKey={tab || 'inside'} onChange={(value) => {
+        this.search({ tab: value });
       }}>
-        <Tabs.TabPane tab="模版消息" key="template">
-          {this.renderTemplate()}
+        <Tabs.TabPane tab="模版消息" key="inside">
+          {this.renderInside()}
         </Tabs.TabPane>
         <Tabs.TabPane tab="自定义消息" key="custom">
           {this.renderCustom()}
         </Tabs.TabPane>
+        <Tabs.TabPane tab="邮件模版" key="email">
+          {this.renderEmail()}
+        </Tabs.TabPane>
       </Tabs>
-      {tab !== 'custom' && <Row type="flex" justify="center">
-        <Col>
-          <Button type="primary" onClick={() => {
-            this.submit(tab);
-          }}>保存</Button>
-        </Col>
-      </Row>}
-
     </div>;
   }
 }

+ 16 - 0
front/project/admin/routes/show/messageDetail/index.js

@@ -0,0 +1,16 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/show/message/detail',
+  matchPath: '/show/message/detail/:id?',
+  key: 'show-message-detail',
+  title: '消息详情',
+  needLogin: true,
+  module,
+  group,
+  showKey: 'show-message',
+  component() {
+    return import('./page');
+  },
+};

+ 3 - 0
front/project/admin/routes/show/messageDetail/index.less

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

+ 112 - 0
front/project/admin/routes/show/messageDetail/page.js

@@ -0,0 +1,112 @@
+import React from 'react';
+import { Form, Input, Button, Row, Col } from 'antd';
+import './index.less';
+import Editor from '@src/components/Editor';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+// import FileUpload from '@src/components/FileUpload';
+import { formatFormError, getMap } from '@src/services/Tools';
+import { asyncSMessage } from '@src/services/AsyncTools';
+// import {  } from '../../../../Constant';
+// import { User } from '../../../stores/user';
+import { System } from '../../../stores/system';
+import { MessageCategory } from '../../../../Constant';
+
+const MessageCategoryMap = getMap(MessageCategory, 'value', 'label');
+const MessageCategoryParamsMap = getMap(MessageCategory, 'value', 'params');
+
+export default class extends Page {
+  initData() {
+    const { id } = this.params;
+    const { form } = this.props;
+    let handler;
+    if (id) {
+      handler = System.getMessage({ id });
+    } else {
+      handler = Promise.resolve({});
+    }
+
+    handler
+      .then(result => {
+        form.setFieldsValue(result);
+        this.setState({ data: result });
+      });
+  }
+
+  submit() {
+    const { form } = this.props;
+    form.validateFields((err) => {
+      if (!err) {
+        const data = form.getFieldsValue();
+        System.setContract(data).then(() => {
+          asyncSMessage('保存成功');
+          goBack();
+        }).catch((e) => {
+          if (e.result) form.setFields(formatFormError(data, e.result));
+        });
+      }
+    });
+  }
+
+  renderBase() {
+    const { getFieldDecorator } = this.props.form;
+    const { data } = this.state;
+    return <Block>
+      <h1>{data.messageMethod === 'email' && '邮件模版'}{data.messageMethod === 'inside' && '站内信模版'}</h1>
+      <Form>
+        {getFieldDecorator('id')(<input hidden />)}
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='触发场景'>
+          {MessageCategoryMap[data.messageCategory] || ''}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='消息标题'>
+          {getFieldDecorator('title', {
+            rules: [
+              { required: true, message: '请输入名称' },
+            ],
+          })(
+            <Input placeholder='请输入名称' />,
+          )}
+        </Form.Item>
+        {data.messageMethod === 'inside' && <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='跳转链接'>
+          {getFieldDecorator('link', {
+            rules: [
+              { required: true, message: '请输入链接' },
+            ],
+          })(
+            <Input placeholder='请输入链接' />,
+          )}
+        </Form.Item>}
+      </Form>
+    </Block>;
+  }
+
+  renderContent() {
+    const { getFieldDecorator } = this.props.form;
+    const { data } = this.state;
+    return <Block>
+      <Form>
+        <Form.Item label='正文'>
+          可插入自定义字段:{(MessageCategoryParamsMap[data.messageCategory] || []).map(row => `{${row}}`).join(', ')}
+          {getFieldDecorator('content', {
+          })(
+            <Editor placeholder='输入内容' />,
+          )}
+        </Form.Item>
+      </Form>
+    </Block>;
+  }
+
+  renderView() {
+    return <div flex>
+      {this.renderBase()}
+      {this.renderContent()}
+      <Row type="flex" justify="center">
+        <Col>
+          <Button type="primary" onClick={() => {
+            this.submit();
+          }}>保存</Button>
+        </Col>
+      </Row>
+    </div>;
+  }
+}

+ 20 - 0
front/project/admin/stores/ready.js

@@ -78,6 +78,26 @@ export default class ReadyStore extends BaseStore {
   getRead(params) {
     return this.apiGet('/ready/read/detail', params);
   }
+
+  listData(params) {
+    return this.apiGet('/ready/data/list', params);
+  }
+
+  addData(params) {
+    return this.apiPost('/ready/data/add', params);
+  }
+
+  editData(params) {
+    return this.apiPut('/ready/data/edit', params);
+  }
+
+  delData(params) {
+    return this.apiDel('/ready/data/delete', params);
+  }
+
+  getData(params) {
+    return this.apiGet('/ready/data/detail', params);
+  }
 }
 
 export const Ready = new ReadyStore({ key: 'ready' });

+ 4 - 8
front/project/admin/stores/system.js

@@ -29,14 +29,6 @@ export default class SystemStore extends BaseStore {
     return this.apiPut('/setting/index', params);
   }
 
-  getMessageTemplate() {
-    return this.apiGet('/setting/message_template');
-  }
-
-  setMessageTemplate(params) {
-    return this.apiPut('/setting/message_template', params);
-  }
-
   getPlace() {
     return this.apiGet('/setting/place');
   }
@@ -249,6 +241,10 @@ export default class SystemStore extends BaseStore {
     return this.apiGet('/setting/message/list', params);
   }
 
+  getMessage(params) {
+    return this.apiGet('/setting/message/detail', params);
+  }
+
   addMessage(params) {
     return this.apiPost('/setting/message/add', params);
   }

+ 2 - 11
front/project/h5/routes/page/bind/page.js

@@ -6,7 +6,6 @@ import { MobileArea } from '../../../../Constant';
 import Input, { SelectInput, VerificationInput } from '../../../components/Input';
 import Button from '../../../components/Button';
 import { User } from '../../../stores/user';
-import { My } from '../../../stores/my';
 import { Common } from '../../../stores/common';
 
 export default class extends Page {
@@ -76,16 +75,8 @@ export default class extends Page {
     if (mobileError || validError) return;
     if (!area || !mobile || !mobileVerifyCode) return;
     if (needEmail && !email) return;
-    User.bind(area, mobile, mobileVerifyCode).then(() => {
-      let handler = null;
-      if (needEmail) {
-        handler = My.bindEmail(email);
-      } else {
-        handler = Promise.resolve();
-      }
-      handler.then(() => {
-        linkTo('/');
-      });
+    User.bind(area, mobile, mobileVerifyCode, email).then(() => {
+      linkTo('/');
     })
       .catch(err => {
         if (err.message.indexOf('验证码') >= 0) {

+ 6 - 4
front/project/h5/stores/user.js

@@ -40,12 +40,13 @@ export default class UserStore extends BaseStore {
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
+   * @param {*} email 绑定邮箱
    */
-  login(area, mobile, mobileVerifyCode, inviteCode) {
+  login(area, mobile, mobileVerifyCode, inviteCode, email) {
     if (!inviteCode) {
       ({ inviteCode } = this.state);
     }
-    return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode }).then(result => {
+    return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode, email }).then(result => {
       this.infoHandle(result);
       return result;
     });
@@ -71,12 +72,13 @@ export default class UserStore extends BaseStore {
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
+   * @param {*} email 绑定邮箱
    */
-  bind(area, mobile, mobileVerifyCode, inviteCode) {
+  bind(area, mobile, mobileVerifyCode, inviteCode, email) {
     if (!inviteCode) {
       ({ inviteCode } = this.state);
     }
-    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode });
+    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email });
   }
 
   /**

+ 6 - 23
front/project/www/components/Login/index.js

@@ -6,7 +6,6 @@ import { asyncSMessage } from '@src/services/AsyncTools';
 import { Icon as GIcon } from '../Icon';
 import { Button as GButton } from '../Button';
 import { User } from '../../stores/user';
-import { My } from '../../stores/my';
 import { Common } from '../../stores/common';
 import { MobileArea, WechatUserAppId } from '../../../Constant';
 
@@ -47,21 +46,13 @@ export default class Login extends Component {
     if (mobileError || validError) return;
     if (!area || !mobile || !mobileVerifyCode) return;
     if (needEmail && !email) return;
-    User.login(area, mobile, mobileVerifyCode)
+    User.login(area, mobile, mobileVerifyCode, email)
       .then((result) => {
-        let handler = null;
-        if (needEmail) {
-          handler = My.bindEmail(email);
+        if (result.bindWechat) {
+          this.close();
         } else {
-          handler = Promise.resolve();
+          this.setState({ type: BIND_WX });
         }
-        handler.then(() => {
-          if (result.bindWechat) {
-            this.close();
-          } else {
-            this.setState({ type: BIND_WX });
-          }
-        });
       })
       .catch(err => {
         if (err.message.indexOf('验证码') >= 0) {
@@ -78,17 +69,9 @@ export default class Login extends Component {
     if (mobileError || validError) return;
     if (!area || !mobile || !mobileVerifyCode) return;
     if (needEmail && !email) return;
-    User.bind(area, mobile, mobileVerifyCode)
+    User.bind(area, mobile, mobileVerifyCode, email)
       .then(() => {
-        let handler = null;
-        if (needEmail) {
-          handler = My.bindEmail(email);
-        } else {
-          handler = Promise.resolve();
-        }
-        handler.then(() => {
-          this.close();
-        });
+        this.close();
       })
       .catch(err => {
         if (err.message.indexOf('验证码') >= 0) {

+ 6 - 4
front/project/www/stores/user.js

@@ -89,12 +89,13 @@ export default class UserStore extends BaseStore {
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
+   * @param {*} email 绑定邮箱
    */
-  login(area, mobile, mobileVerifyCode, inviteCode) {
+  login(area, mobile, mobileVerifyCode, inviteCode, email) {
     if (!inviteCode) {
       ({ inviteCode } = this.state);
     }
-    return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode }).then(result => {
+    return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode, email }).then(result => {
       this.infoHandle(result);
       return result;
     });
@@ -131,9 +132,10 @@ export default class UserStore extends BaseStore {
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
    * @param {*} inviteCode 邀请人手机/邀请码
+   * @param {*} email 绑定邮箱
    */
-  bind(area, mobile, mobileVerifyCode, inviteCode) {
-    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode });
+  bind(area, mobile, mobileVerifyCode, inviteCode, email) {
+    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email });
   }
 
   /**

+ 37 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageCategory.java

@@ -0,0 +1,37 @@
+package com.qxgmat.data.constants.enums;
+
+/**
+ * Created by gaojie on 2017/11/19.
+ */
+public enum MessageCategory {
+    REGISTER("register", "注册消息"),
+    LOGIN_ABNORMAL("login_abnormal", "登录异常"),
+    TEXTBOOK_UPDATE("textbook_update","机经更新"),
+    PREVIEW_NOTICE("preview_notice", "预习作业提醒"),
+    PAYED("payed", "支付成功提醒"),
+    DATA_UPDATE("data_update", "资料更新"),
+    ASK_QUESTION("ask_question", "题目提问回复"),
+    ASK_COURSE("ask_course", "课程提问回复"),
+    FAQ_CALLBACK("faq_callback", "咨询回复"),
+    FEEDBACK_CALLBACK("feedback_callback", "纠错回复"),
+
+    INVITED("invited", "邀请好友注册"),
+    EMAIL_CHANGE("email_change", "邮箱变更"),
+
+    CUSTOM("custom", "自定义消息")
+    ;
+
+    final static public String message = "消息种类";
+
+    public String key;
+    public String title;
+    private MessageCategory(String key, String title){
+        this.key = key;
+        this.title = title;
+    }
+
+    public static MessageCategory ValueOf(String name){
+        if (name == null) return null;
+        return MessageCategory.valueOf(name.toUpperCase());
+    }
+}

+ 25 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageMethod.java

@@ -0,0 +1,25 @@
+package com.qxgmat.data.constants.enums;
+
+/**
+ * Created by gaojie on 2017/11/19.
+ */
+public enum MessageMethod {
+    INSIDE("inside", "站内信"),
+    EMAIL("email", "邮件"),
+    WECHAT("wechat","微信通知"),
+    ;
+
+    final static public String message = "消息方式";
+
+    public String key;
+    public String title;
+    private MessageMethod(String key, String title){
+        this.key = key;
+        this.title = title;
+    }
+
+    public static MessageMethod ValueOf(String name){
+        if (name == null) return null;
+        return MessageMethod.valueOf(name.toUpperCase());
+    }
+}

+ 40 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/MessageType.java

@@ -0,0 +1,40 @@
+package com.qxgmat.data.constants.enums;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/19.
+ */
+public enum MessageType {
+    SYSTEM("system", "系统消息"),
+    FEED("feed", "动态消息"),
+    ;
+
+    final static public String message = "消息类型";
+
+    public String key;
+    public String title;
+    private MessageType(String key, String title){
+        this.key = key;
+        this.title = title;
+    }
+
+    public static MessageType ValueOf(String name){
+        if (name == null) return null;
+        return MessageType.valueOf(name.toUpperCase());
+    }
+
+    public static MessageType FromCategory(MessageCategory messageCategory){
+        switch (messageCategory){
+            case ASK_QUESTION:
+            case ASK_COURSE:
+            case FAQ_CALLBACK:
+            case FEEDBACK_CALLBACK:
+                return FEED;
+            default:
+                return SYSTEM;
+        }
+    }
+}

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

@@ -9,7 +9,6 @@ import com.qxgmat.data.constants.enums.module.QuestionModule;
 public enum SettingKey {
     INDEX("index"), // 首页设置
     SENTENCE("sentence"), // 长难句目录
-    MESSAGE_TEMPLATE("message_template"), // 消息模版
     PLACE("place"), // 难点设置
     EXERCISE_TIME("exercise_time"), // 练习题目时间
     EXERCISE_PAPER_AUTO("exercise_paper_auto"), // 自动组卷设置

+ 0 - 21
server/data/src/main/java/com/qxgmat/data/constants/enums/user/MessageType.java

@@ -1,21 +0,0 @@
-package com.qxgmat.data.constants.enums.user;
-
-/**
- * Created by gaojie on 2017/11/20.
- */
-public enum MessageType {
-    PAY("pay", "购买"),
-    LIBRARY("library", "换库"),
-    COURSE("course", "课程"),
-
-    ;
-    final static public String message = "消息类型";
-
-
-    public String type;
-    public String title;
-    private MessageType(String type, String title){
-        this.type = type;
-        this.title = title;
-    }
-}

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

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

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

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

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

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

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

@@ -4,8 +4,8 @@ import java.io.Serializable;
 import java.util.Date;
 import javax.persistence.*;
 
-@Table(name = "message")
-public class Message implements Serializable {
+@Table(name = "message_template")
+public class MessageTemplate implements Serializable {
     @Id
     @Column(name = "`id`")
     @GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -18,6 +18,18 @@ public class Message implements Serializable {
     private String title;
 
     /**
+     * 消息形式
+     */
+    @Column(name = "`message_method`")
+    private String messageMethod;
+
+    /**
+     * 消息类型
+     */
+    @Column(name = "`message_category`")
+    private String messageCategory;
+
+    /**
      * 消息链接
      */
     @Column(name = "`link`")
@@ -85,6 +97,42 @@ public class Message implements Serializable {
     }
 
     /**
+     * 获取消息形式
+     *
+     * @return message_method - 消息形式
+     */
+    public String getMessageMethod() {
+        return messageMethod;
+    }
+
+    /**
+     * 设置消息形式
+     *
+     * @param messageMethod 消息形式
+     */
+    public void setMessageMethod(String messageMethod) {
+        this.messageMethod = messageMethod;
+    }
+
+    /**
+     * 获取消息类型
+     *
+     * @return message_category - 消息类型
+     */
+    public String getMessageCategory() {
+        return messageCategory;
+    }
+
+    /**
+     * 设置消息类型
+     *
+     * @param messageCategory 消息类型
+     */
+    public void setMessageCategory(String messageCategory) {
+        this.messageCategory = messageCategory;
+    }
+
+    /**
      * 获取消息链接
      *
      * @return link - 消息链接
@@ -196,6 +244,8 @@ public class Message implements Serializable {
         sb.append("Hash = ").append(hashCode());
         sb.append(", id=").append(id);
         sb.append(", title=").append(title);
+        sb.append(", messageMethod=").append(messageMethod);
+        sb.append(", messageCategory=").append(messageCategory);
         sb.append(", link=").append(link);
         sb.append(", sendTime=").append(sendTime);
         sb.append(", sendNumber=").append(sendNumber);
@@ -206,15 +256,15 @@ public class Message implements Serializable {
         return sb.toString();
     }
 
-    public static Message.Builder builder() {
-        return new Message.Builder();
+    public static MessageTemplate.Builder builder() {
+        return new MessageTemplate.Builder();
     }
 
     public static class Builder {
-        private Message obj;
+        private MessageTemplate obj;
 
         public Builder() {
-            this.obj = new Message();
+            this.obj = new MessageTemplate();
         }
 
         /**
@@ -236,6 +286,26 @@ public class Message implements Serializable {
         }
 
         /**
+         * 设置消息形式
+         *
+         * @param messageMethod 消息形式
+         */
+        public Builder messageMethod(String messageMethod) {
+            obj.setMessageMethod(messageMethod);
+            return this;
+        }
+
+        /**
+         * 设置消息类型
+         *
+         * @param messageCategory 消息类型
+         */
+        public Builder messageCategory(String messageCategory) {
+            obj.setMessageCategory(messageCategory);
+            return this;
+        }
+
+        /**
          * 设置消息链接
          *
          * @param link 消息链接
@@ -293,7 +363,7 @@ public class Message implements Serializable {
             return this;
         }
 
-        public Message build() {
+        public MessageTemplate build() {
             return this.obj;
         }
     }

+ 247 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/ReadyData.java

@@ -0,0 +1,247 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "ready_data")
+public class ReadyData implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 资料名称
+     */
+    @Column(name = "`title`")
+    private String title;
+
+    /**
+     * 资料封面
+     */
+    @Column(name = "`cover`")
+    private String cover;
+
+    @Column(name = "`is_official`")
+    private Integer isOfficial;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    @Column(name = "`update_time`")
+    private Date updateTime;
+
+    /**
+     * 资料内容
+     */
+    @Column(name = "`content`")
+    private String content;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取资料名称
+     *
+     * @return title - 资料名称
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * 设置资料名称
+     *
+     * @param title 资料名称
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * 获取资料封面
+     *
+     * @return cover - 资料封面
+     */
+    public String getCover() {
+        return cover;
+    }
+
+    /**
+     * 设置资料封面
+     *
+     * @param cover 资料封面
+     */
+    public void setCover(String cover) {
+        this.cover = cover;
+    }
+
+    /**
+     * @return is_official
+     */
+    public Integer getIsOfficial() {
+        return isOfficial;
+    }
+
+    /**
+     * @param isOfficial
+     */
+    public void setIsOfficial(Integer isOfficial) {
+        this.isOfficial = isOfficial;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * @return update_time
+     */
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    /**
+     * @param updateTime
+     */
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    /**
+     * 获取资料内容
+     *
+     * @return content - 资料内容
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * 设置资料内容
+     *
+     * @param content 资料内容
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", title=").append(title);
+        sb.append(", cover=").append(cover);
+        sb.append(", isOfficial=").append(isOfficial);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
+        sb.append(", content=").append(content);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static ReadyData.Builder builder() {
+        return new ReadyData.Builder();
+    }
+
+    public static class Builder {
+        private ReadyData obj;
+
+        public Builder() {
+            this.obj = new ReadyData();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置资料名称
+         *
+         * @param title 资料名称
+         */
+        public Builder title(String title) {
+            obj.setTitle(title);
+            return this;
+        }
+
+        /**
+         * 设置资料封面
+         *
+         * @param cover 资料封面
+         */
+        public Builder cover(String cover) {
+            obj.setCover(cover);
+            return this;
+        }
+
+        /**
+         * @param isOfficial
+         */
+        public Builder isOfficial(Integer isOfficial) {
+            obj.setIsOfficial(isOfficial);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        /**
+         * @param updateTime
+         */
+        public Builder updateTime(Date updateTime) {
+            obj.setUpdateTime(updateTime);
+            return this;
+        }
+
+        /**
+         * 设置资料内容
+         *
+         * @param content 资料内容
+         */
+        public Builder content(String content) {
+            obj.setContent(content);
+            return this;
+        }
+
+        public ReadyData build() {
+            return this.obj;
+        }
+    }
+}

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

@@ -1,19 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.qxgmat.data.dao.MessageMapper">
-  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.Message">
+<mapper namespace="com.qxgmat.data.dao.MessageTemplateMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.MessageTemplate">
     <!--
       WARNING - @mbg.generated
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="title" jdbcType="VARCHAR" property="title" />
+    <result column="message_method" jdbcType="VARCHAR" property="messageMethod" />
+    <result column="message_category" jdbcType="VARCHAR" property="messageCategory" />
     <result column="link" jdbcType="VARCHAR" property="link" />
     <result column="send_time" jdbcType="TIMESTAMP" property="sendTime" />
     <result column="send_number" jdbcType="INTEGER" property="sendNumber" />
     <result column="send_status" jdbcType="INTEGER" property="sendStatus" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
-  <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.Message">
+  <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.MessageTemplate">
     <!--
       WARNING - @mbg.generated
     -->
@@ -23,7 +25,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `title`, `link`, `send_time`, `send_number`, `send_status`, `create_time`
+    `id`, `title`, `message_method`, `message_category`, `link`, `send_time`, `send_number`, 
+    `send_status`, `create_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

+ 33 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/ReadyDataMapper.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.qxgmat.data.dao.ReadyDataMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.ReadyData">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="title" jdbcType="VARCHAR" property="title" />
+    <result column="cover" jdbcType="VARCHAR" property="cover" />
+    <result column="is_official" jdbcType="INTEGER" property="isOfficial" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+  </resultMap>
+  <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.ReadyData">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <result column="content" jdbcType="LONGVARCHAR" property="content" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `id`, `title`, `cover`, `is_official`, `create_time`, `update_time`
+  </sql>
+  <sql id="Blob_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `content`
+  </sql>
+</mapper>

+ 47 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/admin/ReadyController.java

@@ -8,6 +8,7 @@ import com.nuliji.tools.Transform;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.dto.admin.request.ReadyArticleDto;
+import com.qxgmat.dto.admin.request.ReadyDataDto;
 import com.qxgmat.dto.admin.request.ReadyReadDto;
 import com.qxgmat.dto.admin.request.ReadyRoomDto;
 import com.qxgmat.help.ShiroHelp;
@@ -47,6 +48,9 @@ public class ReadyController {
     @Autowired
     private ReadyReadService readyReadService;
 
+    @Autowired
+    private ReadyDataService readyDataService;
+
     @RequestMapping(value = "/category/all", method = RequestMethod.GET)
     @ApiOperation(value = "所有分类层级", httpMethod = "GET")
     public Response<List<ReadyArticleCategory>> allCategory(HttpSession session) {
@@ -201,4 +205,47 @@ public class ReadyController {
         Page<ReadyRead> p = readyReadService.listAdmin(page, size, plate, order, DirectionStatus.ValueOf(direction));
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
+
+    @RequestMapping(value = "/data/add", method = RequestMethod.POST)
+    @ApiOperation(value = "添加资料", httpMethod = "POST")
+    private Response<Boolean> addData(@RequestBody @Validated ReadyDataDto dto){
+        ReadyData entity = Transform.dtoToEntity(dto);
+        readyDataService.add(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/data/edit", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改文章", httpMethod = "PUT")
+    private Response<Boolean> editData(@RequestBody @Validated ReadyDataDto dto){
+        ReadyData entity = Transform.dtoToEntity(dto);
+        readyDataService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/data/delete", method = RequestMethod.DELETE)
+    @ApiOperation(value = "删除文章", httpMethod = "DELETE")
+    private Response<Boolean> deleteData(@RequestParam int id){
+        readyDataService.delete(id);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/data/detail", method = RequestMethod.GET)
+    @ApiOperation(value = "文章详情", httpMethod = "GET")
+    private Response<ReadyData> detailData(@RequestParam int id){
+        ReadyData entity = readyDataService.get(id);
+        return ResponseHelp.success(entity);
+    }
+
+    @RequestMapping(value = "/data/list", method = RequestMethod.GET)
+    @ApiOperation(value = "获取文章列表", httpMethod = "GET")
+    private Response<PageMessage<ReadyData>> listData(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = false) Boolean isOfficial,
+            @RequestParam(required = false, defaultValue = "id") String order,
+            @RequestParam(required = false, defaultValue = "desc") String direction
+    ){
+        Page<ReadyData> p = readyDataService.listAdmin(page, size, isOfficial, order, DirectionStatus.ValueOf(direction));
+        return ResponseHelp.success(p, page, size, p.getTotal());
+    }
 }

+ 34 - 33
server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java

@@ -3,6 +3,10 @@ package com.qxgmat.controller.admin;
 import com.alibaba.fastjson.JSONObject;
 import com.github.pagehelper.Page;
 import com.nuliji.tools.*;
+import com.nuliji.tools.exception.ParameterException;
+import com.qxgmat.data.constants.enums.MessageCategory;
+import com.qxgmat.data.constants.enums.MessageMethod;
+import com.qxgmat.data.constants.enums.MessageType;
 import com.qxgmat.data.constants.enums.SettingKey;
 import com.qxgmat.data.constants.enums.status.AskStatus;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
@@ -11,13 +15,13 @@ import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.dto.admin.extend.InfoExtendDto;
 import com.qxgmat.dto.admin.extend.UserExtendDto;
 import com.qxgmat.dto.admin.request.*;
-import com.qxgmat.dto.admin.response.AdInfoDto;
 import com.qxgmat.dto.admin.response.CommentInfoDto;
 import com.qxgmat.dto.admin.response.FaqInfoDto;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.MessageExtendService;
 import com.qxgmat.service.inline.*;
+import com.sun.org.apache.xpath.internal.operations.Bool;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.apache.poi.hssf.usermodel.HSSFCell;
@@ -75,7 +79,7 @@ public class SettingController {
     private FaqService faqService;
 
     @Autowired
-    private MessageService messageService;
+    private MessageTemplateService messageTemplateService;
 
     @Autowired
     private MessageExtendService messageExtendService;
@@ -123,23 +127,6 @@ public class SettingController {
         return ResponseHelp.success(entity.getValue());
     }
 
-    @RequestMapping(value = "/message_template", method = RequestMethod.PUT)
-    @ApiOperation(value = "修改消息模版", httpMethod = "PUT")
-    private Response<Boolean> editMessageTemplate(@RequestBody @Validated JSONObject dto){
-        Setting entity = settingService.getByKey(SettingKey.MESSAGE_TEMPLATE);
-        entity.setValue(dto);
-        settingService.edit(entity);
-        return ResponseHelp.success(true);
-    }
-
-    @RequestMapping(value = "/message_template", method = RequestMethod.GET)
-    @ApiOperation(value = "获取消息模版", httpMethod = "GET")
-    private Response<JSONObject> getMessageTemplate(){
-        Setting entity = settingService.getByKey(SettingKey.MESSAGE_TEMPLATE);
-
-        return ResponseHelp.success(entity.getValue());
-    }
-
     @RequestMapping(value = "/sentence", method = RequestMethod.PUT)
     @ApiOperation(value = "修改长难句设置", httpMethod = "PUT")
     private Response<Boolean> editSentence(@RequestBody @Validated JSONObject dto){
@@ -595,42 +582,56 @@ public class SettingController {
 
     @RequestMapping(value = "/message/add", method = RequestMethod.POST)
     @ApiOperation(value = "添加消息", httpMethod = "POST")
-    private Response<Boolean> addMessage(@RequestBody @Validated MessageDto dto){
-        Message entity = Transform.dtoToEntity(dto);
-        messageService.add(entity);
+    private Response<Boolean> addMessage(@RequestBody @Validated MessageTemplateDto dto){
+        MessageTemplate entity = Transform.dtoToEntity(dto);
+        messageTemplateService.add(entity);
         return ResponseHelp.success(true);
     }
 
     @RequestMapping(value = "/message/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "修改消息", httpMethod = "PUT")
-    private Response<Boolean> editMessage(@RequestBody @Validated MessageDto dto){
-        Message entity = Transform.dtoToEntity(dto);
-        messageService.edit(entity);
+    private Response<Boolean> editMessage(@RequestBody @Validated MessageTemplateDto dto){
+        MessageTemplate entity = Transform.dtoToEntity(dto);
+        messageTemplateService.edit(entity);
         return ResponseHelp.success(true);
     }
 
     @RequestMapping(value = "/message/delete", method = RequestMethod.DELETE)
     @ApiOperation(value = "删除消息", httpMethod = "DELETE")
     private Response<Boolean> deleteMessage(@RequestParam int id){
-        messageService.delete(id);
+        MessageTemplate in = messageTemplateService.get(id);
+        if(!in.getMessageCategory().equals(MessageCategory.CUSTOM.key)){
+            throw new ParameterException("不允许删除非自定义消息模版");
+        }
+        messageTemplateService.delete(id);
         return ResponseHelp.success(true);
     }
 
+    @RequestMapping(value = "/message/detail", method = RequestMethod.GET)
+    @ApiOperation(value = "获取消息", httpMethod = "GET")
+    public Response<MessageTemplate> detailMessage(@RequestParam int id, HttpSession session) {
+        MessageTemplate entity = messageTemplateService.get(id);
+        return ResponseHelp.success(entity);
+    }
+
     @RequestMapping(value = "/message/list", method = RequestMethod.GET)
     @ApiOperation(value = "获取消息列表", httpMethod = "GET")
-    private Response<PageMessage<Message>> listMessage(
+    private Response<PageMessage<MessageTemplate>> listMessage(
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = false) String messageCategory,
+            @RequestParam(required = false) String messageMethod,
+            @RequestParam(required = false) Boolean needCustom,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction
     ){
-        Page<Message> p = messageService.listAdmin(page, size, order, DirectionStatus.ValueOf(direction));
+        Page<MessageTemplate> p = messageTemplateService.listAdmin(page, size, MessageCategory.ValueOf(messageCategory), MessageMethod.ValueOf(messageMethod), needCustom,order, DirectionStatus.ValueOf(direction));
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
 
     @RequestMapping(value = "/ad/add", method = RequestMethod.POST)
     @ApiOperation(value = "添加广告", httpMethod = "POST")
-    public Response<Ad> add(@RequestBody @Validated AdDto dto, HttpServletRequest request) {
+    public Response<Ad> addAd(@RequestBody @Validated AdDto dto, HttpServletRequest request) {
         Ad entity = Transform.dtoToEntity(dto);
         entity = adService.addAd(entity);
         managerLogService.log(request);
@@ -639,7 +640,7 @@ public class SettingController {
 
     @RequestMapping(value = "/ad/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "修改广告", httpMethod = "PUT")
-    public Response<Boolean> edit(@RequestBody @Validated AdDto dto, HttpServletRequest request) {
+    public Response<Boolean> editAd(@RequestBody @Validated AdDto dto, HttpServletRequest request) {
         Ad entity = Transform.dtoToEntity(dto);
         entity = adService.editAd(entity);
         managerLogService.log(request);
@@ -648,7 +649,7 @@ public class SettingController {
 
     @RequestMapping(value = "/ad/delete", method = RequestMethod.DELETE)
     @ApiOperation(value = "删除广告", httpMethod = "DELETE")
-    public Response<Boolean> delete(@RequestParam int id, HttpServletRequest request) {
+    public Response<Boolean> deleteAd(@RequestParam int id, HttpServletRequest request) {
         adService.delete(id);
         managerLogService.log(request);
         return ResponseHelp.success(true);
@@ -656,14 +657,14 @@ public class SettingController {
 
     @RequestMapping(value = "/ad/detail", method = RequestMethod.GET)
     @ApiOperation(value = "获取广告", httpMethod = "GET")
-    public Response<Ad> detail(@RequestParam int id, HttpSession session) {
+    public Response<Ad> detailAd(@RequestParam int id, HttpSession session) {
         Ad entity = adService.get(id);
         return ResponseHelp.success(entity);
     }
 
     @RequestMapping(value = "/ad/list", method = RequestMethod.GET)
     @ApiOperation(value = "广告列表", httpMethod = "GET")
-    public Response<PageMessage<Ad>> list(
+    public Response<PageMessage<Ad>> listAd(
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) String channel,

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

@@ -423,7 +423,7 @@ public class UserController {
             User user = usersService.getByMobile(dto.getArea(), dto.getMobile());
             if (user == null){
                 // 创建user
-                user = usersService.register(dto.getArea(), dto.getMobile(), null, null,"", null);
+                user = usersService.register(dto.getArea(), dto.getMobile(), null,null, null,"", null);
             }
             entity.setUserId(user.getId());
         }
@@ -445,7 +445,7 @@ public class UserController {
             User user = usersService.getByMobile(dto.getArea(), dto.getMobile());
             if (user == null){
                 // 创建user
-                user = usersService.register(dto.getArea(), dto.getMobile(), null, null,"", null);
+                user = usersService.register(dto.getArea(), dto.getMobile(), null,null, null,"", null);
             }
             entity.setUserId(user.getId());
         }

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

@@ -102,7 +102,7 @@ public class AuthController {
 //        }
         try {
             String ip = Tools.getClientIp(request);
-            User user = usersService.register(userLoginDto.getArea(), userLoginDto.getMobile(), userLoginDto.getInviteCode(), null, ip, aiHelp.parseIp(ip));
+            User user = usersService.register(userLoginDto.getArea(), userLoginDto.getMobile(), userLoginDto.getInviteCode(), userLoginDto.getEmail(), null, ip, aiHelp.parseIp(ip));
         }catch (ParameterException e){
             // 忽略已注册信息
         }
@@ -170,7 +170,7 @@ public class AuthController {
         try{
             // 创建新的账号,设定手机号,绑定第三方登录
             String ip = Tools.getClientIp(request);
-            User user = usersService.register(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getInviteCode(), openUser, ip, aiHelp.parseIp(ip));
+            User user = usersService.register(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getInviteCode(), userValidMobileDto.getEmail(), openUser, ip, aiHelp.parseIp(ip));
         }catch (ParameterException e){
             throw new ParameterException("该手机号绑定其他账号,请更换手机号码!");
         }

+ 15 - 2
server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java

@@ -17,6 +17,7 @@ import com.qxgmat.dto.response.CommentDto;
 import com.qxgmat.dto.response.FaqDto;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.inline.*;
+import com.sun.org.apache.xpath.internal.operations.Bool;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -65,6 +66,9 @@ public class BaseController {
     private ReadyReadService readyReadService;
 
     @Autowired
+    private ReadyDataService readyDataService;
+
+    @Autowired
     private UsersService usersService;
 
     @RequestMapping(value = "/index", method = RequestMethod.GET)
@@ -231,7 +235,7 @@ public class BaseController {
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = true) String channel,
-            @RequestParam(required = true) String position,
+            @RequestParam(required = false) String position,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
@@ -251,7 +255,7 @@ public class BaseController {
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = true) String channel,
-            @RequestParam(required = true) String position,
+            @RequestParam(required = false) String position,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
@@ -291,4 +295,13 @@ public class BaseController {
         Page<ReadyRead> p = readyReadService.list(page, size, plate, order, DirectionStatus.ValueOf(direction));
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
+
+    @RequestMapping(value = "/data/all", method = RequestMethod.GET)
+    @ApiOperation(value = "资料", httpMethod = "GET")
+    public Response<List<ReadyData>> allData(
+            @RequestParam(required = true) Boolean isOfficial,
+            HttpSession session) {
+        List<ReadyData> p = readyDataService.all(isOfficial);
+        return ResponseHelp.success(p);
+    }
 }

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

@@ -6,6 +6,7 @@ import com.github.pagehelper.Page;
 import com.nuliji.tools.*;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
+import com.qxgmat.data.constants.enums.MessageType;
 import com.qxgmat.data.constants.enums.QuestionSubject;
 import com.qxgmat.data.constants.enums.QuestionType;
 import com.qxgmat.data.constants.enums.SettingKey;
@@ -328,7 +329,7 @@ public class MyController {
     @ApiOperation(value = "发送邮件邀请", httpMethod = "POST")
     public Response<Boolean> inviteEmail(@RequestBody @Validated InviteEmailDto dto)  {
         User user = (User) shiroHelp.getLoginUser();
-        messageExtendService.sendInviteEmail(dto.getEmails(), user.getInviteCode());
+        messageExtendService.sendInviteEmail(user, dto.getEmails(), user.getInviteCode());
         return ResponseHelp.success(true);
     }
 
@@ -337,11 +338,11 @@ public class MyController {
     public Response<PageMessage<UserMessage>> message(
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
-            @RequestParam(required = false) String type,
+            @RequestParam(required = false) String messageType,
             @RequestParam(required = false) Integer read
     )  {
         User user = (User) shiroHelp.getLoginUser();
-        Page<UserMessage> p = userMessageService.select(page, size, user.getId(), type, read);
+        Page<UserMessage> p = userMessageService.list(page, size, user.getId(), MessageType.ValueOf(messageType), read);
 
         return ResponseHelp.success(p, page, size, p.getTotal());
     }

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

@@ -2,6 +2,7 @@ package com.qxgmat.controller.api;
 
 
 import com.alibaba.fastjson.JSONObject;
+import com.github.pagehelper.Page;
 import com.nuliji.tools.*;
 import com.nuliji.tools.exception.AuthException;
 import com.nuliji.tools.exception.ParameterException;
@@ -360,7 +361,7 @@ public class QuestionController {
             @RequestParam(required = false)  Integer times,
             HttpSession session) {
         User user = (User) shiroHelp.getLoginUser();
-        PageResult<ExercisePaper> p = exerciseService.list(page, size, structId, user != null ? user.getId():null, ExerciseLogic.ValueOf(logic), logicExtend, times);
+        Page<ExercisePaper> p = exerciseService.list(page, size, structId, user != null ? user.getId():null, ExerciseLogic.ValueOf(logic), logicExtend, times);
 
         List<UserExercisePaperDto> pr = Transform.convert(p, UserExercisePaperDto.class);
 

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

@@ -112,9 +112,11 @@ public class SentenceController
 
         // 评论: 通过资料的长难句分类获取该资料的评论
         CourseData courseData = courseDataService.getSentence();
-        List<Comment> commentList = commentService.list(1, 1, "course_data", courseData.getId().toString());
-        dto.setComments(Transform.convert(commentList, CommentExtendDto.class));
-
+        if(courseData != null){
+            List<Comment> commentList = commentService.list(1, 1, "course_data", courseData.getId().toString());
+            dto.setComments(Transform.convert(commentList, CommentExtendDto.class));
+        }
+        
         return ResponseHelp.success(dto);
     }
 

+ 13 - 3
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/MessageDto.java

@@ -1,12 +1,12 @@
 package com.qxgmat.dto.admin.request;
 
 import com.nuliji.tools.annotation.Dto;
-import com.qxgmat.data.dao.entity.Message;
+import com.qxgmat.data.dao.entity.MessageTemplate;
 
 import java.util.Date;
 
-@Dto(entity = Message.class)
-public class MessageDto {
+@Dto(entity = MessageTemplate.class)
+public class MessageTemplateDto {
     private Integer id;
 
     private String title;
@@ -17,6 +17,8 @@ public class MessageDto {
 
     private Date sendTime;
 
+    private Integer sendStatus;
+
     public Integer getId() {
         return id;
     }
@@ -56,4 +58,12 @@ public class MessageDto {
     public void setSendTime(Date sendTime) {
         this.sendTime = sendTime;
     }
+
+    public Integer getSendStatus() {
+        return sendStatus;
+    }
+
+    public void setSendStatus(Integer sendStatus) {
+        this.sendStatus = sendStatus;
+    }
 }

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

@@ -0,0 +1,58 @@
+package com.qxgmat.dto.admin.request;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.ReadyData;
+
+
+@Dto(entity = ReadyData.class)
+public class ReadyDataDto {
+    private Integer id;
+
+    private Integer isOfficial;
+
+    private String cover;
+
+    private String title;
+
+    private String content;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public Integer getIsOfficial() {
+        return isOfficial;
+    }
+
+    public void setIsOfficial(Integer isOfficial) {
+        this.isOfficial = isOfficial;
+    }
+
+    public String getCover() {
+        return cover;
+    }
+
+    public void setCover(String cover) {
+        this.cover = cover;
+    }
+}

+ 14 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/request/UserLoginDto.java

@@ -33,6 +33,12 @@ public class UserLoginDto {
     @ApiModelProperty(value = "邀请人手机/邀请码", required = false)
     private String inviteCode;
 
+    /**
+     * 绑定邮箱
+     */
+    @ApiModelProperty(value = "邮箱", required = false)
+    private String email;
+
     public String getMobile() {
         return mobile;
     }
@@ -64,4 +70,12 @@ public class UserLoginDto {
     public void setArea(String area) {
         this.area = area;
     }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
 }

+ 14 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/request/UserValidMobileDto.java

@@ -30,6 +30,12 @@ public class UserValidMobileDto {
     @ApiModelProperty(value = "邀请人手机/邀请码", required = false)
     private String inviteCode;
 
+    /**
+     * 绑定邮箱
+     */
+    @ApiModelProperty(value = "邮箱", required = false)
+    private String email;
+
     public String getMobile() {
         return mobile;
     }
@@ -61,4 +67,12 @@ public class UserValidMobileDto {
     public void setArea(String area) {
         this.area = area;
     }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
 }

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

@@ -37,24 +37,14 @@ public class MailHelp {
         this.mail = new SendCloudMail(apiUser, apiKey);
     }
 
-    public boolean sendBaseMail(String email, String subject, String body, Map<String, String> params){
-        // 替换模版
-        body = replaceBody(body, params);
+    public boolean sendBaseMail(String email, String subject, String body){
         SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, null);
         return response.getResult();
     }
 
-    public boolean sendAttachMail(String email, String subject, String body, Map<String, String> params, String filePath){
-        // 替换模版
-        body = replaceBody(body, params);
+    public boolean sendAttachMail(String email, String subject, String body, String filePath){
         SendCloudMail.Response response = mail.sendMail(email, subject, body, from, fromName, new FileSystemResource(filePath));
         return response.getResult();
     }
 
-    private String replaceBody(String body, Map<String, String> params){
-        for(String key :params.keySet()){
-            body = body.replaceAll("{"+key+"}", params.get(key));
-        }
-        return body;
-    }
 }

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

@@ -1,13 +1,19 @@
 package com.qxgmat.help;
 
 import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.third.OauthData;
 import com.nuliji.tools.third.wechat.MessageListener;
 import com.nuliji.tools.third.wechat.WechatClient;
+import com.qxgmat.data.constants.enums.MessageCategory;
+import com.qxgmat.data.constants.enums.MessageType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import java.util.HashMap;
+import java.util.Map;
+
 @Service
 public class WechatHelp {
     private WechatClient wechatPc;
@@ -26,6 +32,12 @@ public class WechatHelp {
         this.wechat = new WechatClient(appId, appSecret);
     }
 
+    @Value("${third.wechat.questionTemplate}")
+    private String questionTemplate;
+
+    @Value("${third.wechat.courseTemplate}")
+    private String courseTemplate;
+
     public OauthData oauthPc(String code) {
         return wechatPc.webAuthorize(code);
     }
@@ -63,4 +75,20 @@ public class WechatHelp {
         });
         return "";
     }
+
+    public boolean sendMessage(String openid, MessageCategory messageCategory, Map<String, String> dataMap){
+        String templateId = "";
+        switch (messageCategory){
+            case ASK_QUESTION:
+                templateId = this.questionTemplate;
+                break;
+            case ASK_COURSE:
+                templateId = this.courseTemplate;
+                break;
+            default:
+                throw new ParameterException("微信通知类型错误");
+        }
+        wechat.sendMessage(openid, templateId, "", dataMap);
+        return true;
+    }
 }

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

@@ -179,7 +179,7 @@ public class UsersService extends AbstractService {
      * @return
      */
     @Transactional
-    public User register(String area, String mobile, String inviteCode, User openUser, String registerIp, String[] registerInfo){
+    public User register(String area, String mobile, String inviteCode, String email, User openUser, String registerIp, String[] registerInfo){
         boolean n = false;
         User user = getByMobile(area, mobile);
         if (user != null){
@@ -197,7 +197,7 @@ public class UsersService extends AbstractService {
             }
         }else{
             // 注册,并且绑定邀请者
-            user = User.builder().area(area).mobile(mobile).build();
+            user = User.builder().area(area).mobile(mobile).email(email).build();
             n = true;
             if (inviteCode != null && !inviteCode.isEmpty()){
                 User origin = getByInviteCode(inviteCode);

+ 162 - 34
server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java

@@ -1,21 +1,23 @@
 package com.qxgmat.service.extend;
 
-import com.alibaba.fastjson.JSONObject;
-import com.nuliji.tools.MessageHelp;
+import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
-import com.qxgmat.data.constants.enums.user.MessageType;
+import com.qxgmat.data.constants.enums.MessageCategory;
+import com.qxgmat.data.constants.enums.MessageMethod;
+import com.qxgmat.data.constants.enums.MessageType;
+import com.qxgmat.data.dao.entity.MessageTemplate;
+import com.qxgmat.data.dao.entity.TextbookLibrary;
+import com.qxgmat.data.dao.entity.User;
 import com.qxgmat.data.dao.entity.UserMessage;
 import com.qxgmat.help.MailHelp;
-import com.qxgmat.service.inline.MessageService;
+import com.qxgmat.help.WechatHelp;
+import com.qxgmat.service.inline.MessageTemplateService;
 import com.qxgmat.service.inline.UserMessageService;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -25,50 +27,176 @@ public class MessageExtendService {
     private MailHelp mailHelp;
 
     @Resource
+    private WechatHelp wechatHelp;
+
+    @Resource
     private UserMessageService userMessageService;
 
     @Resource
-    private MessageService messageService;
+    private MessageTemplateService messageTemplateService;
 
     @Resource
     private ToolsService toolsService;
 
-    public UserMessage send(Integer userId, MessageType type, Map<String, String> params){
-        try {
-            // 根据模版创建
-            JSONObject template = toolsService.messageTemplate(type);
-            String content = template.getString("content");
-            String link = template.getString("link");
-            userMessageService.add(UserMessage.builder()
-                    .userId(userId)
-                    .title(type.title)
-                    .content(replaceBody(content, params))
-                    .link(link)
-                    .type(type.type)
-                    .isRead(0)
-                    .createTime(new Date())
-                    .build());
-            return null;
-        }catch (Exception e){
-            throw new SystemException("Message生成失败", e);
+    private void send(User user, MessageCategory category, Map<String, String>params){
+        List<MessageTemplate> templateList = messageTemplateService.listByCategory(category);
+        for(MessageTemplate template : templateList){
+            MessageMethod messageMethod = MessageMethod.ValueOf(template.getMessageMethod());
+            String title = replaceBody(template.getTitle(), params);
+            String content = replaceBody(template.getContent(), params);
+            switch(messageMethod){
+                case EMAIL:
+                    sendEmail(params.getOrDefault("emails", user.getEmail()), title, content);
+                    break;
+                case INSIDE:
+                    sendInside(user.getId(), title, content, template.getLink(), category);
+                    break;
+                case WECHAT:
+                    sendWechat(user.getWechatOpenidWechat(), category, params);
+                    break;
+                default:
+                    throw new ParameterException("消息发送方式错误");
+            }
         }
     }
 
+    private UserMessage sendInside(Integer userId, String title, String content, String link, MessageCategory category){
+        // 根据模版创建
+        return userMessageService.add(UserMessage.builder()
+                .userId(userId)
+                .title(title)
+                .content(content)
+                .link(link)
+                .type(MessageType.FromCategory(category).key)
+                .isRead(0)
+                .createTime(new Date())
+                .build());
+    }
+
+    private void sendEmail(String emails, String title, String body){
+        mailHelp.sendBaseMail(emails, title, body);
+    }
+
+    private void sendWechat(String openId, MessageCategory category, Map<String, String> params){
+        wechatHelp.sendMessage(openId, category, params);
+    }
+
     /**
      * 发送邀请邮件
      * @param emails
      */
-    public void sendInviteEmail(String[] emails, String code){
+    public void sendInviteEmail(User user, String[] emails, String code){
+        Map<String, String> map = new HashMap<>();
+        map.put("emails", Strings.join(Arrays.stream(emails).collect(Collectors.toList()), ';'));
+        map.put("code", code);
+        send(user, MessageCategory.INVITED, map);
+    }
+
+    /**
+     * 发送机经更新
+     */
+    public void sendTextbookUpdate(User user, TextbookLibrary textbookLibrary){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.TEXTBOOK_UPDATE, map);
+    }
+
+    /**
+     * 发送登录异常
+     */
+    public void sendLoginAbnormal(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.LOGIN_ABNORMAL, map);
+    }
+
+    /**
+     * 发送预习作业到期通知:6小时,去除夜间12-7点
+     * 课程名称:{courseTitle}
+     * 作业名称:{assignTitle}
+     */
+    public void sendPreviewNotice(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.PREVIEW_NOTICE, map);
+    }
+
+    /**
+     * 购买成功提醒
+     * 购买服务,课程名:{title}
+     * 支付金额:{money}
+     * 开通有效期:{expireTime}
+     */
+    public void sendPayed(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.PAYED, map);
+    }
+
+    /**
+     * 发送资料更新
+     * 资料名称:{title}
+     * 更新时间:{updateTime}
+     */
+    public void sendDataUpdate(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.DATA_UPDATE, map);
+    }
+
+    /**
+     * 提问回复
+     * 提问简介:{content}
+     * 提问时间:{askTime}
+     * 提问状态:{status}, 精选、隐藏
+     */
+    public void sendAskQuestion(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.ASK_QUESTION, map);
+    }
+
+    /**
+     * 提问回复
+     * 提问简介:{content}
+     * 提问时间:{askTime}
+     * 提问状态:{status}, 精选、隐藏
+     */
+    public void sendAskCourse(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.ASK_COURSE, map);
+    }
+
+    /**
+     * 咨询回复
+     * 咨询板块
+     * 咨询简介
+     */
+    public void sendFaqCallback(User user){
         Map<String, String> map = new HashMap<>();
-        mailHelp.sendBaseMail(
-                Strings.join(Arrays.stream(emails).collect(Collectors.toList()), ';'),
-                "邮件邀请",
-                "",
-                map);
+        send(user, MessageCategory.FAQ_CALLBACK, map);
     }
 
-    public void sendTextbookUpdate(){
+    /**
+     * 纠错回复
+     * 咨询板块
+     * 咨询简介
+     */
+    public void sendFeedbackAnswer(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.FEEDBACK_CALLBACK, map);
+    }
 
+    /**
+     * 注册成功通知
+     * @param user
+     */
+    public void sendRegister(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.REGISTER, map);
+    }
+
+    /**
+     * 邮箱变更
+     * @param user
+     */
+    public void sendEmailChange(User user){
+        Map<String, String> map = new HashMap<>();
+        send(user, MessageCategory.EMAIL_CHANGE, map);
     }
 
     private String replaceBody(String body, Map<String, String> params){

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

@@ -3,7 +3,6 @@ package com.qxgmat.service.extend;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.qxgmat.data.constants.enums.*;
-import com.qxgmat.data.constants.enums.user.MessageType;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.relation.entity.QuestionNoRelation;
 import com.qxgmat.data.relation.entity.SentenceQuestionRelation;
@@ -178,17 +177,6 @@ public class ToolsService {
     }
 
     /**
-     * 获取消息模版
-     * @param messageType
-     * @return
-     */
-    public JSONObject messageTemplate(MessageType messageType){
-        Setting setting = settingService.getByKey(SettingKey.MESSAGE_TEMPLATE);
-        JSONObject value = setting.getValue();
-        return value.getJSONObject(messageType.type);
-    }
-
-    /**
      * 根据考试设置,得到做题时间,做题数量
      *      setting: {subject: { time: "", number: "" }}
      * @param order

+ 0 - 104
server/gateway-api/src/main/java/com/qxgmat/service/inline/MessageService.java

@@ -1,104 +0,0 @@
-package com.qxgmat.service.inline;
-
-import com.github.pagehelper.Page;
-import com.nuliji.tools.AbstractService;
-import com.nuliji.tools.exception.ParameterException;
-import com.nuliji.tools.exception.SystemException;
-import com.nuliji.tools.mybatis.Example;
-import com.qxgmat.data.constants.enums.status.DirectionStatus;
-import com.qxgmat.data.dao.MessageMapper;
-import com.qxgmat.data.dao.entity.Message;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-@Service
-public class MessageService extends AbstractService {
-    private static final Logger logger = LoggerFactory.getLogger(MessageService.class);
-
-    @Resource
-    private MessageMapper messageMapper;
-
-    public Page<Message> listAdmin(int page, int pageSize, String order, DirectionStatus direction){
-        Example example = new Example(Message.class);
-        if(order == null || order.isEmpty()) order = "id";
-        switch(direction){
-            case ASC:
-                example.orderBy(order).asc();
-                break;
-            case DESC:
-            default:
-                example.orderBy(order).desc();
-        }
-        return select(messageMapper, example, page, pageSize);
-    }
-
-    /**
-     * 获取到期还未发送消息列表
-     * @return
-     */
-    public List<Message> listExpire(){
-        Example example = new Example(Message.class);
-        example.and(
-                example.createCriteria()
-                        .andEqualTo("sendNumber", 0)
-                        .andEqualTo("sendStatus", 0)
-                        .andLessThan("sendTime", new Date())
-        );
-        return select(messageMapper, example);
-    }
-
-    public Message add(Message message){
-        int result = insert(messageMapper, message);
-        message = one(messageMapper, message.getId());
-        if(message == null){
-            throw new SystemException("消息添加失败");
-        }
-        return message;
-    }
-
-    public Message edit(Message message){
-        Message in = one(messageMapper, message.getId());
-        if(in == null){
-            throw new ParameterException("消息不存在");
-        }
-        int result = update(messageMapper, message);
-        return message;
-    }
-
-    public boolean delete(Number id){
-        Message in = one(messageMapper, id);
-        if(in == null){
-            throw new ParameterException("消息不存在");
-        }
-        int result = delete(messageMapper, id);
-        return result > 0;
-    }
-
-    public Message get(Number id){
-        Message in = one(messageMapper, id);
-
-        if(in == null){
-            throw new ParameterException("消息不存在");
-        }
-        return in;
-    }
-
-    public Page<Message> select(int page, int pageSize){
-        return select(messageMapper, page, pageSize);
-    }
-
-    public Page<Message> select(Integer[] ids){
-        return page(()->select(messageMapper, ids), 1, ids.length);
-    }
-
-    public List<Message> select(Collection ids){
-        return select(messageMapper, ids);
-    }
-
-}

+ 140 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/MessageTemplateService.java

@@ -0,0 +1,140 @@
+package com.qxgmat.service.inline;
+
+import com.github.pagehelper.Page;
+import com.nuliji.tools.AbstractService;
+import com.nuliji.tools.exception.ParameterException;
+import com.nuliji.tools.exception.SystemException;
+import com.nuliji.tools.mybatis.Example;
+import com.qxgmat.data.constants.enums.MessageCategory;
+import com.qxgmat.data.constants.enums.MessageMethod;
+import com.qxgmat.data.constants.enums.MessageType;
+import com.qxgmat.data.constants.enums.status.DirectionStatus;
+import com.qxgmat.data.dao.MessageTemplateMapper;
+import com.qxgmat.data.dao.entity.MessageTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class MessageTemplateService extends AbstractService {
+    private static final Logger logger = LoggerFactory.getLogger(MessageTemplateService.class);
+
+    @Resource
+    private MessageTemplateMapper messageTemplateMapper;
+
+    public Page<MessageTemplate> listAdmin(int page, int pageSize, MessageCategory messageCategory, MessageMethod messageMethod, Boolean needCustom, String order, DirectionStatus direction){
+        Example example = new Example(MessageTemplate.class);
+        if(messageCategory != null)
+            example.and(
+                    example.createCriteria()
+                        .andEqualTo("messageCategory", messageCategory)
+            );
+        if(messageMethod != null)
+            example.and(
+                    example.createCriteria()
+                        .andEqualTo("messageMethod", messageMethod)
+            );
+        if(needCustom != null)
+            example.and(
+                    needCustom ? example.createCriteria()
+                            .andEqualTo("messageCategory", MessageCategory.CUSTOM.key):
+                            example.createCriteria()
+                            .andNotEqualTo("messageCategory", MessageCategory.CUSTOM.key)
+            );
+        if(order == null || order.isEmpty()) order = "id";
+        switch(direction){
+            case ASC:
+                example.orderBy(order).asc();
+                break;
+            case DESC:
+            default:
+                example.orderBy(order).desc();
+        }
+        return select(messageTemplateMapper, example, page, pageSize);
+    }
+
+    /**
+     * 可发送模版
+     * @param messageCategory
+     * @return
+     */
+    public List<MessageTemplate> listByCategory(MessageCategory messageCategory){
+        Example example = new Example(MessageTemplate.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("messageCategory", messageCategory)
+                        .andEqualTo("sendStatus", 1)
+        );
+        return select(messageTemplateMapper, example);
+    }
+
+    /**
+     * 获取到期还未发送消息列表
+     * @return
+     */
+    public List<MessageTemplate> listCustomerExpire(){
+        Example example = new Example(MessageTemplate.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("messageCategory", MessageCategory.CUSTOM.key)
+                        .andEqualTo("sendNumber", 0)
+                        .andEqualTo("sendStatus", 0)
+                        .andLessThan("sendTime", new Date())
+        );
+        return select(messageTemplateMapper, example);
+    }
+
+    public MessageTemplate add(MessageTemplate message){
+        int result = insert(messageTemplateMapper, message);
+        message = one(messageTemplateMapper, message.getId());
+        if(message == null){
+            throw new SystemException("消息添加失败");
+        }
+        return message;
+    }
+
+    public MessageTemplate edit(MessageTemplate message){
+        MessageTemplate in = one(messageTemplateMapper, message.getId());
+        if(in == null){
+            throw new ParameterException("消息不存在");
+        }
+        int result = update(messageTemplateMapper, message);
+        return message;
+    }
+
+    public boolean delete(Number id){
+        MessageTemplate in = one(messageTemplateMapper, id);
+        if(in == null){
+            throw new ParameterException("消息不存在");
+        }
+        int result = delete(messageTemplateMapper, id);
+        return result > 0;
+    }
+
+    public MessageTemplate get(Number id){
+        MessageTemplate in = one(messageTemplateMapper, id);
+
+        if(in == null){
+            throw new ParameterException("消息不存在");
+        }
+        return in;
+    }
+
+    public Page<MessageTemplate> select(int page, int pageSize){
+        return select(messageTemplateMapper, page, pageSize);
+    }
+
+    public Page<MessageTemplate> select(Integer[] ids){
+        return page(()->select(messageTemplateMapper, ids), 1, ids.length);
+    }
+
+    public List<MessageTemplate> select(Collection ids){
+        return select(messageTemplateMapper, ids);
+    }
+
+}

+ 105 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/ReadyDataService.java

@@ -0,0 +1,105 @@
+package com.qxgmat.service.inline;
+
+import com.github.pagehelper.Page;
+import com.nuliji.tools.AbstractService;
+import com.nuliji.tools.exception.ParameterException;
+import com.nuliji.tools.exception.SystemException;
+import com.nuliji.tools.mybatis.Example;
+import com.qxgmat.data.constants.enums.status.DirectionStatus;
+import com.qxgmat.data.dao.ReadyDataMapper;
+import com.qxgmat.data.dao.ReadyRoomMapper;
+import com.qxgmat.data.dao.entity.ReadyData;
+import com.qxgmat.data.dao.entity.ReadyRoom;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+@Service
+public class ReadyDataService extends AbstractService {
+    private static final Logger logger = LoggerFactory.getLogger(ReadyDataService.class);
+
+    @Resource
+    private ReadyDataMapper readyDataMapper;
+
+    public Page<ReadyData> listAdmin(int page, int size, Boolean isOfficial, String order, DirectionStatus direction){
+        Example example = new Example(ReadyData.class);
+        if (isOfficial != null)
+            example.and(
+                    example.createCriteria()
+                            .andEqualTo("isOfficial", isOfficial)
+            );
+        if(order == null || order.isEmpty()) order = "id";
+        switch(direction){
+            case ASC:
+                example.orderBy(order).asc();
+                break;
+            case DESC:
+            default:
+                example.orderBy(order).desc();
+        }
+        return select(readyDataMapper, example, page, size);
+    }
+
+    public List<ReadyData> all(Boolean isOfficial){
+        Example example = new Example(ReadyData.class);
+        if (isOfficial != null)
+            example.and(
+                    example.createCriteria()
+                            .andEqualTo("isOfficial", isOfficial)
+            );
+        return select(readyDataMapper, example);
+    }
+
+    public ReadyData add(ReadyData entity){
+        int result = insert(readyDataMapper, entity);
+        entity = one(readyDataMapper, entity.getId());
+        if(entity == null){
+            throw new SystemException("考场添加失败");
+        }
+        return entity;
+    }
+
+    public ReadyData edit(ReadyData entity){
+        ReadyData in = one(readyDataMapper, entity.getId());
+        if(in == null){
+            throw new ParameterException("考场不存在");
+        }
+        int result = update(readyDataMapper, entity);
+        return entity;
+    }
+
+    public boolean delete(Number id){
+        ReadyData in = one(readyDataMapper, id);
+        if(in == null){
+            throw new ParameterException("考场不存在");
+        }
+        int result = delete(readyDataMapper, id);
+        return result > 0;
+    }
+
+    public ReadyData get(Number id){
+        ReadyData in = one(readyDataMapper, id);
+
+        if(in == null){
+            throw new ParameterException("考场不存在");
+        }
+        return in;
+    }
+
+    public Page<ReadyData> select(int page, int pageSize){
+        return select(readyDataMapper, page, pageSize);
+    }
+
+    public Page<ReadyData> select(Integer[] ids){
+        return page(()->select(readyDataMapper, ids), 1, ids.length);
+    }
+
+    public List<ReadyData> select(Collection ids){
+        return select(readyDataMapper, ids);
+    }
+
+}

+ 19 - 0
server/gateway-api/src/main/java/com/qxgmat/service/inline/ReadyReadService.java

@@ -42,6 +42,25 @@ public class ReadyReadService extends AbstractService {
         return select(readyReadMapper, example, page, size);
     }
 
+    public Page<ReadyRead> list(int page, int size, String plate, String order, DirectionStatus direction){
+        Example example = new Example(ReadyRead.class);
+        if (plate != null)
+            example.and(
+                    example.createCriteria()
+                            .andEqualTo("plate", plate)
+            );
+        if(order == null || order.isEmpty()) order = "id";
+        switch(direction){
+            case ASC:
+                example.orderBy(order).asc();
+                break;
+            case DESC:
+            default:
+                example.orderBy(order).desc();
+        }
+        return select(readyReadMapper, example, page, size);
+    }
+
     public ReadyRead add(ReadyRead entity){
         int result = insert(readyReadMapper, entity);
         entity = one(readyReadMapper, entity.getId());

+ 18 - 18
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserMessageService.java

@@ -5,6 +5,7 @@ import com.nuliji.tools.AbstractService;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
+import com.qxgmat.data.constants.enums.MessageType;
 import com.qxgmat.data.dao.UserMessageMapper;
 import com.qxgmat.data.dao.entity.UserMessage;
 import org.slf4j.Logger;
@@ -22,6 +23,23 @@ public class UserMessageService  extends AbstractService {
     @Resource
     private UserMessageMapper userMessageMapper;
 
+    public Page<UserMessage> list(int page, int pageSize, Number userId, MessageType messageType, Integer read){
+        Example example = new Example(UserMessage.class);
+        if (userId != null)
+            example.and(
+                    example.createCriteria().andEqualTo("userId", userId)
+            );
+        if (messageType != null)
+            example.and(
+                    example.createCriteria().andEqualTo("type", messageType)
+            );
+        if (read != null)
+            example.and(
+                    example.createCriteria().andEqualTo("isRead", read > 0?1:0)
+            );
+        example.setOrderByClause("id desc");
+        return select(userMessageMapper, example, page, pageSize);
+    }
     /**
      * 合并用户信息,将old转移至new
      * @param oldUserId
@@ -102,24 +120,6 @@ public class UserMessageService  extends AbstractService {
         return select(userMessageMapper, page, pageSize);
     }
 
-    public Page<UserMessage> select(int page, int pageSize, Number userId, String type, Integer read){
-        Example example = new Example(UserMessage.class);
-        if (userId != null)
-            example.and(
-                    example.createCriteria().andEqualTo("userId", userId)
-            );
-        if (type != null)
-            example.and(
-                    example.createCriteria().andEqualTo("type", type)
-            );
-        if (read != null)
-            example.and(
-                    example.createCriteria().andEqualTo("isRead", read > 0?1:0)
-            );
-        example.setOrderByClause("id desc");
-        return select(userMessageMapper, example, page, pageSize);
-    }
-
     public Page<UserMessage> select(Integer[] ids){
         return page(()->select(userMessageMapper, ids), 1, ids.length);
     }

+ 13 - 9
server/gateway-api/src/main/java/com/qxgmat/task/ScheduledTask.java

@@ -6,14 +6,13 @@ import com.github.pagehelper.Page;
 import com.nuliji.tools.third.OauthData;
 import com.qxgmat.data.constants.enums.SettingKey;
 import com.qxgmat.data.constants.enums.status.MessageStatus;
-import com.qxgmat.data.dao.entity.Message;
+import com.qxgmat.data.dao.entity.MessageTemplate;
 import com.qxgmat.data.dao.entity.Setting;
 import com.qxgmat.data.dao.entity.User;
-import com.qxgmat.data.dao.entity.UserService;
 import com.qxgmat.data.relation.entity.UserPrepareRelation;
 import com.qxgmat.help.WechatHelp;
 import com.qxgmat.service.UsersService;
-import com.qxgmat.service.inline.MessageService;
+import com.qxgmat.service.inline.MessageTemplateService;
 import com.qxgmat.service.inline.SettingService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
-import javax.annotation.Resource;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -48,7 +46,7 @@ public class ScheduledTask {
     private WechatHelp wechatHelp;
 
     @Autowired
-    private MessageService messageService;
+    private MessageTemplateService messageTemplateService;
 
     /**
      * cron表达式:* * * * * *(共6位,使用空格隔开,具体如下)
@@ -139,15 +137,15 @@ public class ScheduledTask {
     @Scheduled(cron="0 0 * * * *")
     public void autoSendMessage(){
         logger.info("Start auto Send message");
-        List<Message> list = messageService.listExpire();
-        for(Message message : list){
-            messageService.edit(Message.builder()
+        List<MessageTemplate> list = messageTemplateService.listCustomerExpire();
+        for(MessageTemplate message : list){
+            messageTemplateService.edit(MessageTemplate.builder()
                     .id(message.getId())
                     .sendStatus(MessageStatus.SENDING.index)
                     .build());
             int number = 0;
 
-            messageService.edit(Message.builder()
+            messageTemplateService.edit(MessageTemplate.builder()
                     .id(message.getId())
                     .sendNumber(number)
                     .sendStatus(MessageStatus.SENDED.index)
@@ -155,6 +153,12 @@ public class ScheduledTask {
         }
     }
 
+    // 每小时判断发送预习作业消息
+    @Scheduled(cron="0 0 * * * *")
+    public void autoSendPreviewMessage(){
+        logger.info("Start auto Send Preview message");
+    }
+
     // 每天判断是否自动组卷
     @Scheduled(cron="0 1 0 * * *")
     public void autoExercisePaper() throws ParseException {

+ 16 - 1
server/gateway-api/src/main/profile/dev/application-runtime.yml

@@ -88,7 +88,22 @@ upload:
   web_url: /upload/
 
 third:
-  redirectUrl: http://127.0.0.1:8080
+  wechat:
+    pc:
+      appId: wx324965bb6800f9b9
+      appSecret: 51b8bf5029502d8eccb7a658529b1372
+
+    native:
+      appId: wxbee75af2ece94ed7
+      appSecret: efdef63acfae765b0b890072e12a0198
+
+    questionTemplate: 123123
+    courseTemplate: 1312
+
+  redirectUrl: http://www.qianxing.com/gateway/oauth
+
+pay:
+  notifyUrl: http://www.qianxing.com/gateway/pay
 
 video:
   ffmpeg: ffmpeg

+ 17 - 1
server/gateway-api/src/main/profile/prod/application-runtime.yml

@@ -88,7 +88,23 @@ upload:
   web_url: /upload/
 
 third:
-  redirectUrl: http://127.0.0.1:8080
+  wechat:
+    pc:
+      appId: wx324965bb6800f9b9
+      appSecret: 51b8bf5029502d8eccb7a658529b1372
+
+    native:
+      appId: wxbee75af2ece94ed7
+      appSecret: efdef63acfae765b0b890072e12a0198
+
+    questionTemplate: 123123
+    courseTemplate: 1312
+
+
+  redirectUrl: http://www.qianxing.com/gateway/oauth
+
+pay:
+  notifyUrl: http://www.qianxing.com/gateway/pay
 
 video:
   ffmpeg: ffmpeg

+ 17 - 1
server/gateway-api/src/main/profile/test/application-runtime.yml

@@ -88,7 +88,23 @@ upload:
   web_url: /upload/
 
 third:
-  redirectUrl: http://127.0.0.1:8080
+  wechat:
+    pc:
+      appId: wx324965bb6800f9b9
+      appSecret: 51b8bf5029502d8eccb7a658529b1372
+
+    native:
+      appId: wxbee75af2ece94ed7
+      appSecret: efdef63acfae765b0b890072e12a0198
+
+    questionTemplate: 123123
+    courseTemplate: 1312
+
+
+  redirectUrl: http://www.qianxing.com/gateway/oauth
+
+pay:
+  notifyUrl: http://www.qianxing.com/gateway/pay
 
 video:
   ffmpeg: ffmpeg

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

@@ -45,17 +45,6 @@ third:
     appKey: LD0Si697zIdSut8iv5GoPpE8
     appSecret: DhpsxWkTbI379Q2tUsnD5RG8jiGVGggo
 
-  wechat:
-    pc:
-      appId: wx324965bb6800f9b9
-      appSecret: 51b8bf5029502d8eccb7a658529b1372
-
-    native:
-      appId: wxbee75af2ece94ed7
-      appSecret: efdef63acfae765b0b890072e12a0198
-
-  redirectUrl: http://www.qianxing.com/gateway/oauth
-
 pay:
   alipay:
     appId: 12312

+ 3 - 3
server/tools/src/main/java/com/nuliji/tools/third/wechat/WechatClient.java

@@ -216,7 +216,7 @@ public class WechatClient {
      * @param dataMap
      * @return 返回数据,正常返回的例子: {"errcode":0,"errmsg":"ok","msgid":200228332}
      */
-    private JSONObject sendMessage(String openid, String templateId, String dataUrl, HashMap<String, String> dataMap) {
+    public JSONObject sendMessage(String openid, String templateId, String dataUrl, Map<String, String> dataMap) {
         String accessToken = getAccessToken(appId, appSecret);
         String url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
         Map<String, String> params = new HashMap<>();
@@ -228,12 +228,12 @@ public class WechatClient {
         if (dataUrl != null){
             body.put("url", dataUrl);
         }
-        body.put("data", null);
+        body.put("data", dataMap);
         JSONObject object = execute(url, params, body);
         return object;
     }
 
-    private JSONObject sendCustomMessage(String openid, String content) {
+    public JSONObject sendCustomMessage(String openid, String content) {
         String accessToken = getAccessToken(appId, appSecret);
         String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
         Map<String, String> params = new HashMap<>();