Browse Source

feat(server): 修正逻辑

Go 5 years ago
parent
commit
b406ae3e0e
76 changed files with 2152 additions and 549 deletions
  1. 5 1
      front/project/Constant.js
  2. 15 0
      front/project/admin/routes/show/deploy/index.js
  3. 3 0
      front/project/admin/routes/show/deploy/index.less
  4. 418 0
      front/project/admin/routes/show/deploy/page.js
  5. 2 1
      front/project/admin/routes/show/index.js
  6. 8 0
      front/project/admin/stores/system.js
  7. 3 2
      front/project/h5/routes/page/identity/page.js
  8. 16 11
      front/project/h5/routes/page/study/page.js
  9. 7 0
      front/project/h5/stores/main.js
  10. 4 3
      front/project/h5/stores/my.js
  11. 1 1
      front/project/www/components/Login/index.js
  12. 7 3
      front/project/www/local.json
  13. 99 141
      front/project/www/routes/paper/process/base/index.js
  14. 1 1
      server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java
  15. 1 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/SettingKey.java
  16. 17 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/logic/TextbookLogic.java
  17. 7 7
      server/data/src/main/java/com/qxgmat/data/dao/entity/ExaminationPaper.java
  18. 92 22
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibraryHistory.java
  19. 35 35
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookPaper.java
  20. 16 16
      server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookQuestion.java
  21. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserPaper.java
  22. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserReport.java
  23. 0 20
      server/data/src/main/java/com/qxgmat/data/dao/mapping/CourseStudentOnlineMapper.xml
  24. 4 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookLibraryHistoryMapper.xml
  25. 3 3
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookPaperMapper.xml
  26. 2 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/TextbookQuestionMapper.xml
  27. 4 3
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserPaperMapper.xml
  28. 2 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserReportMapper.xml
  29. 36 0
      server/data/src/main/java/com/qxgmat/data/relation/PreviewAssignRelationMapper.java
  30. 19 0
      server/data/src/main/java/com/qxgmat/data/relation/TextbookPaperRelationMapper.java
  31. 14 6
      server/data/src/main/java/com/qxgmat/data/relation/UserCourseRecordRelationMapper.java
  32. 8 0
      server/data/src/main/java/com/qxgmat/data/relation/UserOrderRecordRelationMapper.java
  33. 22 0
      server/data/src/main/java/com/qxgmat/data/relation/UserQuestionRelationMapper.java
  34. 7 2
      server/data/src/main/java/com/qxgmat/data/relation/UserSentenceRecordRelationMapper.java
  35. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/entity/UserCourseStatRelation.java
  36. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/entity/UserSentenceStatRelation.java
  37. 2 2
      server/data/src/main/java/com/qxgmat/data/relation/mapping/ExaminationPaperRelationMapper.xml
  38. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/ExercisePaperRelationMapper.xml
  39. 97 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/PreviewAssignRelationMapper.xml
  40. 40 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/TextbookPaperRelationMapper.xml
  41. 1 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/TextbookQuestionRelationMapper.xml
  42. 41 1
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserCourseRecordRelationMapper.xml
  43. 38 18
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml
  44. 56 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserQuestionRelationMapper.xml
  45. 21 4
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserSentenceRecordRelationMapper.xml
  46. 17 0
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  47. 3 3
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  48. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  49. 7 0
      server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java
  50. 16 4
      server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java
  51. 28 81
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  52. 5 5
      server/gateway-api/src/main/java/com/qxgmat/controller/api/QuestionController.java
  53. 1 5
      server/gateway-api/src/main/java/com/qxgmat/controller/api/SentenceController.java
  54. 158 2
      server/gateway-api/src/main/java/com/qxgmat/controller/api/TextbookController.java
  55. 30 10
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/TextbookLibraryHistoryDto.java
  56. 73 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserTextbookGroupExtendDto.java
  57. 18 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/PaperBaseDto.java
  58. 11 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserStudyDetailDto.java
  59. 118 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserTextbookGroupDto.java
  60. 70 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserTextbookPaperDto.java
  61. 6 2
      server/gateway-api/src/main/java/com/qxgmat/help/AiHelp.java
  62. 35 0
      server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java
  63. 17 2
      server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java
  64. 64 4
      server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java
  65. 16 10
      server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java
  66. 13 9
      server/gateway-api/src/main/java/com/qxgmat/service/extend/PreviewService.java
  67. 11 22
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java
  68. 32 6
      server/gateway-api/src/main/java/com/qxgmat/service/extend/SentenceService.java
  69. 2 2
      server/gateway-api/src/main/java/com/qxgmat/service/extend/TextbookService.java
  70. 27 42
      server/gateway-api/src/main/java/com/qxgmat/service/inline/PreviewAssignService.java
  71. 28 16
      server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookPaperService.java
  72. 16 2
      server/gateway-api/src/main/java/com/qxgmat/service/inline/TextbookQuestionService.java
  73. 33 3
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseRecordService.java
  74. 27 0
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserOrderRecordService.java
  75. 16 6
      server/gateway-api/src/main/java/com/qxgmat/service/inline/UserSentenceRecordService.java
  76. 6 0
      server/tools/src/main/java/com/nuliji/tools/Tools.java

+ 5 - 1
front/project/Constant.js

@@ -56,7 +56,11 @@ export const SentenceOption = [{ label: '平行', value: 'parallel' }, { label:
 
 export const ExaminationSubject = [{ short: 'V', value: 'verbal', label: '语文', english: 'Verbal' }, { short: 'Q', value: 'quant', label: '数学', english: 'Quant' }, { short: 'IR', value: 'ir', label: '综合推理', english: 'LR' }, { short: 'AWA', value: 'awa', label: '作文', english: 'AWA' }];
 
-export const ExaminationOrder = [{ list: ['awa', 'ir', 'quant', 'verbal'] }, { list: ['quant', 'verbal', 'ir', 'awa'] }, { list: ['verbal', 'quant', 'ir', 'awa'] }];
+export const ExaminationOrder = [
+  { label: 'ARQV', value: ['awa', 'ir', 'quant', 'verbal'], list: [{ label: 'Analytical Writing Analysis', value: 'awa' }, { label: 'Integrated Reasoning', value: 'ir' }, { label: 'Quantitative', value: 'quant' }, { label: 'Verbal', value: 'verbal' }] },
+  { label: 'VQRA', value: ['verbal', 'quant', 'ir', 'awa'], list: [{ label: 'Verbal', value: 'verbal' }, { label: 'Quantitative', value: 'quant' }, { label: 'Integrated Reasoning', value: 'ir' }, { label: 'Analytical Writing Analysis', value: 'awa' }] },
+  { label: 'QVRA', value: ['quant', 'verbal', 'ir', 'awa'], list: [{ label: 'Quantitative', value: 'quant' }, { label: 'Verbal', value: 'verbal' }, { label: 'Integrated Reasoning', value: 'ir' }, { label: 'Analytical Writing Analysis', value: 'awa' }] },
+];
 
 export const DataType = [{ label: '电子', value: 'electron' }, { label: '纸质', value: 'paper' }];
 

+ 15 - 0
front/project/admin/routes/show/deploy/index.js

@@ -0,0 +1,15 @@
+import module from '../../module';
+import group from '../group';
+
+export default {
+  path: '/show/deploy',
+  key: 'show-deploy',
+  title: '配置文案',
+  needLogin: true,
+  module,
+  group,
+  index: true,
+  component() {
+    return import('./page');
+  },
+};

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

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

+ 418 - 0
front/project/admin/routes/show/deploy/page.js

@@ -0,0 +1,418 @@
+import React from 'react';
+import { Tabs, Form, Row, Col, Input, InputNumber, Button, Upload, Icon } from 'antd';
+import './index.less';
+import Page from '@src/containers/Page';
+import Block from '@src/components/Block';
+import { flattenObject } from '@src/services/Tools';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { ServiceParamMap } from '../../../../Constant';
+import { System } from '../../../stores/system';
+
+export default class extends Page {
+  constructor(props) {
+    super(props);
+    this.state.tab = 'qx_cat';
+    this.vipList = ServiceParamMap.vip;
+  }
+
+  initData() {
+    this.refresh(this.state.tab);
+  }
+
+  refresh(tab) {
+    if (tab === 'qx_cat') {
+      return this.refreshQxCat();
+    }
+    if (tab === 'textbook') {
+      return this.refreshTextbook();
+    }
+    if (tab === 'vip') {
+      return this.refreshVip();
+    }
+    return Promise.reject();
+  }
+
+  refreshQxCat() {
+    return System.getServiceQxCat().then(result => {
+      this.setState({ qx_cat: result || {} });
+      const { form } = this.props;
+      form.setFieldsValue(flattenObject(result, 'qx_cat'));
+    });
+  }
+
+  refreshTextbook() {
+    return System.getServiceTextbook().then(result => {
+      this.setState({ textbook: result || {} });
+      const { form } = this.props;
+      form.setFieldsValue(flattenObject(result, 'textbook'));
+    });
+  }
+
+  refreshVip() {
+    return System.getServiceVip().then(result => {
+      this.setState({ vip: result || {} });
+      const { form } = this.props;
+      form.setFieldsValue(flattenObject(result, 'vip'));
+    });
+  }
+
+  changeMapValue(field, second, index, key, value) {
+    const data = this.state[field] || {};
+    data[second] = data[second] || [];
+    data[second][index] = data[second][index] || {};
+    data[second][index][key] = value;
+    this.setState({ [field]: data });
+  }
+
+  submit(tab) {
+    let handler;
+    if (tab === 'qx_cat') {
+      handler = this.submitQxCat();
+    }
+    if (tab === 'textbook') {
+      handler = this.submitTextbook();
+    }
+    if (tab === 'vip') {
+      handler = this.submitVip();
+    }
+    handler.then(() => {
+      asyncSMessage('保存成功');
+    });
+  }
+
+  submitQxCat() {
+    const { qx_cat } = this.state;
+    return System.setServiceQxCat(qx_cat);
+  }
+
+  submitTextbook() {
+    const { textbook } = this.state;
+    return System.setServiceTextbook(textbook);
+  }
+
+  submitVip() {
+    const { vip } = this.state;
+    return System.setServiceVip(vip);
+  }
+
+  renderQxCat() {
+    const { getFieldDecorator, setFieldsValue, getFieldValue } = this.props.form;
+    const image = getFieldValue('qx_cat.image') || null;
+    return <Form>
+      <Row>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='商品价格'>
+          {getFieldDecorator('qx_cat.package[0].price', {
+            rules: [
+              { required: true, message: '输入千行Cat价格' },
+            ],
+          })(
+            <InputNumber placeholder='请输入千行Cat价格' onChange={(value) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'price', value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='服务名称'>
+          {getFieldDecorator('qx_cat.package[0].title', {
+            rules: [
+              { required: true, message: '输入千行Cat名称' },
+            ],
+          })(
+            <Input placeholder='请输入千行Cat名称' onChange={(e) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'title', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='服务简介'>
+          {getFieldDecorator('qx_cat.package[0].description', {
+            rules: [
+              { required: true, message: '输入千行Cat服务简介' },
+            ],
+          })(
+            <Input placeholder='请输入千行Cat服务简介' onChange={(e) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'description', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='有效期说明'>
+          {getFieldDecorator('qx_cat.package[0].expire_info', {
+            rules: [
+              { required: true, message: '输入千行Cat有效期说明' },
+            ],
+          })(
+            <Input placeholder='请输入千行Cat有效期说明' onChange={(e) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'expire_info', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='退款政策'>
+          {getFieldDecorator('qx_cat.package[0].refund_policy', {
+            rules: [
+              { required: true, message: '输入千行Cat退款政策' },
+            ],
+          })(
+            <Input placeholder='请输入千行Cat退款政策' onChange={(e) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'refund_policy', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='版权说明'>
+          {getFieldDecorator('qx_cat.package[0].copyright_notes', {
+            rules: [
+              { required: true, message: '输入千行Cat版权说明' },
+            ],
+          })(
+            <Input placeholder='请输入千行Cat版权说明' onChange={(e) => {
+              this.changeMapValue('qx_cat', 'package', 0, 'copyright_notes', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='商品图片'>
+          {getFieldDecorator('qx_cat.image', {
+            rules: [
+              { required: true, message: '上传图片' },
+            ],
+          })(
+            <Upload
+              listType="picture-card"
+              showUploadList={false}
+              beforeUpload={(file) => System.uploadImage(file).then((result) => {
+                setFieldsValue({ 'qx_cat.image': result.url });
+                return Promise.reject();
+              })}
+            >
+              {image ? <img src={image} alt="avatar" /> : <div>
+                <Icon type={this.state.loading ? 'loading' : 'plus'} />
+                <div className="ant-upload-text">Upload</div>
+              </div>}
+            </Upload>,
+          )}
+        </Form.Item>
+      </Row>
+    </Form>;
+  }
+
+  renderTextbook() {
+    const { getFieldDecorator, setFieldsValue, getFieldValue } = this.props.form;
+    const image = getFieldValue('textbook.image') || null;
+    return <Form>
+      <Row>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='商品图片'>
+          {getFieldDecorator('textbook.image', {
+            rules: [
+              { required: true, message: '上传图片' },
+            ],
+          })(
+            <Upload
+              listType="picture-card"
+              showUploadList={false}
+              beforeUpload={(file) => System.uploadImage(file).then((result) => {
+                setFieldsValue({ 'textbook.image': result.url });
+                return Promise.reject();
+              })}
+            >
+              {image ? <img src={image} alt="avatar" /> : <div>
+                <Icon type={this.state.loading ? 'loading' : 'plus'} />
+                <div className="ant-upload-text">Upload</div>
+              </div>}
+            </Upload>,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='商品价格'>
+          {getFieldDecorator('textbook.package[0].price', {
+            rules: [
+              { required: true, message: '输入数学机经价格' },
+            ],
+          })(
+            <InputNumber placeholder='请输入数学机经价格' onChange={(value) => {
+              this.changeMapValue('textbook', 'package', 0, 'price', value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='服务名称'>
+          {getFieldDecorator('textbook.package[0].title', {
+            rules: [
+              { required: true, message: '输入数学机经名称' },
+            ],
+          })(
+            <Input placeholder='请输入数学机经名称' onChange={(e) => {
+              this.changeMapValue('textbook', 'package', 0, 'title', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='服务简介'>
+          {getFieldDecorator('textbook.package[0].description', {
+            rules: [
+              { required: true, message: '输入数学机经服务简介' },
+            ],
+          })(
+            <Input placeholder='请输入数学机经服务简介' onChange={(e) => {
+              this.changeMapValue('textbook', 'package', 0, 'description', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='有效期说明'>
+          {getFieldDecorator('textbook.package[0].expire_info', {
+            rules: [
+              { required: true, message: '输入数学机经有效期说明' },
+            ],
+          })(
+            <Input placeholder='请输入数学机经有效期说明' onChange={(e) => {
+              this.changeMapValue('textbook', 'package', 0, 'expire_info', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='退款政策'>
+          {getFieldDecorator('textbook.package[0].refund_policy', {
+            rules: [
+              { required: true, message: '输入数学机经退款政策' },
+            ],
+          })(
+            <Input placeholder='请输入数学机经退款政策' onChange={(e) => {
+              this.changeMapValue('textbook', 'package', 0, 'refund_policy', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+        <Form.Item labelCol={{ span: 4 }} wrapperCol={{ span: 16 }} label='版权说明'>
+          {getFieldDecorator('textbook.package[0].copyright_notes', {
+            rules: [
+              { required: true, message: '输入数学机经版权说明' },
+            ],
+          })(
+            <Input placeholder='请输入数学机经版权说明' onChange={(e) => {
+              this.changeMapValue('textbook', 'package', 0, 'copyright_notes', e.target.value);
+            }} style={{ width: '200px' }} />,
+          )}
+        </Form.Item>
+      </Row>
+    </Form>;
+  }
+
+  renderVip() {
+    const { getFieldDecorator, setFieldsValue, getFieldValue } = this.props.form;
+    const image = getFieldValue('vip.image') || null;
+    return <Form>
+      <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='商品图片'>
+        {getFieldDecorator('vip.image', {
+          rules: [
+            { required: true, message: '上传图片' },
+          ],
+        })(
+          <Upload
+            listType="picture-card"
+            showUploadList={false}
+            beforeUpload={(file) => System.uploadImage(file).then((result) => {
+              setFieldsValue({ 'vip.image': result.url });
+              return Promise.reject();
+            })}
+          >
+            {image ? <img src={image} alt="avatar" /> : <div>
+              <Icon type={this.state.loading ? 'loading' : 'plus'} />
+              <div className="ant-upload-text">Upload</div>
+            </div>}
+          </Upload>,
+        )}
+      </Form.Item>
+      <Row>
+        {this.vipList.map((row, index) => {
+          return <Col span={12}>
+            <h1>{row.label}</h1>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='商品价格'>
+              {getFieldDecorator(`vip.package[${index}].price`, {
+                rules: [
+                  { required: true, message: '输入价格' },
+                ],
+              })(
+                <InputNumber placeholder={'输入价格'} onChange={(value) => {
+                  this.changeMapValue('vip', 'package', index, 'price', value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='服务名称'>
+              {getFieldDecorator(`vip.package[${index}].title`, {
+                rules: [
+                  { required: true, message: '输入名称' },
+                ],
+              })(
+                <Input placeholder={'输入名称'} onChange={(e) => {
+                  this.changeMapValue('vip', 'package', index, 'title', e.target.value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='服务简介'>
+              {getFieldDecorator(`vip.package[${index}].description`, {
+                rules: [
+                  { required: true, message: '输入服务简介' },
+                ],
+              })(
+                <Input placeholder='请输入服务简介' onChange={(e) => {
+                  this.changeMapValue('vip', 'package', index, 'description', e.target.value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='有效期说明'>
+              {getFieldDecorator(`vip.package[${index}].expire_info`, {
+                rules: [
+                  { required: true, message: '输入有效期说明' },
+                ],
+              })(
+                <Input placeholder='请输入有效期说明' onChange={(e) => {
+                  this.changeMapValue('vip', 'package', index, 'expire_info', e.target.value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='退款政策'>
+              {getFieldDecorator(`vip.package[${index}].refund_policy`, {
+                rules: [
+                  { required: true, message: '输入退款政策' },
+                ],
+              })(
+                <Input placeholder='请输入退款政策' onChange={(e) => {
+                  this.changeMapValue('vip', 'package', index, 'refund_policy', e.target.value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+            <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} label='版权说明'>
+              {getFieldDecorator(`vip.package[${index}].copyright_notes`, {
+                rules: [
+                  { required: true, message: '输入版权说明' },
+                ],
+              })(
+                <Input placeholder='请输入版权说明' onChange={(e) => {
+                  this.changeMapValue('vip', 'package', index, 'copyright_notes', e.target.value);
+                }} style={{ width: '200px' }} />,
+              )}
+            </Form.Item>
+          </Col>;
+        })}
+
+      </Row>
+    </Form>;
+  }
+
+  renderView() {
+    const { tab } = this.state;
+    return <Block><Tabs activeKey={tab} onChange={(value) => {
+      this.setState({ tab: value, selectedKeys: [], checkedKeys: [] });
+      this.refresh(value);
+    }}>
+      <Tabs.TabPane tab="千行Cat" key="qx_cat">
+        {this.renderQxCat()}
+      </Tabs.TabPane>
+      <Tabs.TabPane tab="数学机经" key="textbook">
+        {this.renderTextbook()}
+      </Tabs.TabPane>
+      <Tabs.TabPane tab="Vip" key="vip">
+        {this.renderVip()}
+      </Tabs.TabPane>
+    </Tabs>
+      <Row type="flex" justify="center">
+        <Col>
+          <Button type="primary" onClick={() => {
+            this.submit(tab);
+          }}>保存</Button>
+        </Col>
+      </Row>
+    </Block>;
+  }
+}

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

@@ -6,5 +6,6 @@ import articleDetail from './articleDetail';
 import comment from './comment';
 import ad from './ad';
 import message from './message';
+import deploy from './deploy';
 
-export default [tips, faq, article, articleDetail, comment, ad, message];
+export default [tips, faq, article, articleDetail, comment, ad, message, deploy];

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

@@ -149,6 +149,14 @@ export default class SystemStore extends BaseStore {
     return this.apiPut('/setting/experience_info', params);
   }
 
+  getWechatInfo() {
+    return this.apiGet('/setting/wechat_info');
+  }
+
+  setWechatInfo(params) {
+    return this.apiPut('/setting/wechat_info', params);
+  }
+
   getTips() {
     return this.apiGet('/setting/tips');
   }

+ 3 - 2
front/project/h5/routes/page/identity/page.js

@@ -6,6 +6,7 @@ import { asyncSMessage } from '@src/services/AsyncTools';
 import { dataURLtoBlob, formatDate } from '@src/services/Tools';
 import Icon from '../../../components/Icon';
 import { Common } from '../../../stores/common';
+// import { Main } from '../../../stores/main';
 import { My } from '../../../stores/my';
 
 export default class extends Page {
@@ -107,7 +108,7 @@ export default class extends Page {
     return (
       <div>
         <div className="text">
-          请扫描您的居民身份证原件,领取6个月VIP权限。
+          请扫描您的居民身份证原件,领取90VIP权限。
           <br />
           千行将重视和保护您的隐私。
         </div>
@@ -145,7 +146,7 @@ export default class extends Page {
           <Icon type="check-circle" />
         </div>
         <div className="title">认证完成!</div>
-        <div className="desc">180天VIP权限已赠送至您的账户</div>
+        <div className="desc">90天VIP权限已赠送至您的账户</div>
         <div className="desc">生效时间:{formatDate(data.useStartTime, 'YYYY-MM-DD')}</div>
       </div>
     );

+ 16 - 11
front/project/h5/routes/page/study/page.js

@@ -1,7 +1,7 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
-import { formatDate, formatSeconds } from '@src/services/Tools';
+import { formatDate, formatSeconds, formatPercent } from '@src/services/Tools';
 import Icon from '../../../components/Icon';
 import { My } from '../../../stores/my';
 
@@ -12,13 +12,20 @@ export default class extends Page {
     My.getStudyTotal().then(total => {
       this.setState({ total });
     });
-    My.getStudyWeek().then(week => {
-      this.setState({ week });
+    My.getStudyWeek(0).then(latest => {
+      const diff = latest.time - latest.avgTime;
+      const diffPercent = diff > 0 ? formatPercent(latest.time - latest.avgTime, latest.avgTime, true) : formatPercent(latest.avgTime - latest.time, latest.avgTime, true);
+      this.setState({ latest, diff, diffPercent });
+      My.getStudyWeek(1).then(last => {
+        const diffLast = latest.time - last.time;
+        const diffLastPercent = diffLast > 0 ? formatPercent(latest.time - last.time, last.time, true) : formatPercent(last.time - latest.time, last.time, true);
+        this.setState({ last, diffLast, diffLastPercent });
+      });
     });
   }
 
   renderView() {
-    const { total } = this.state;
+    const { total, latest = {}, diff = 0, diffPercent = 0, diffLast = 0, diffLastPercent = 0 } = this.state;
     return (
       <div>
         <div className="block">
@@ -33,22 +40,20 @@ export default class extends Page {
         <div className="t-c">
           <div className="item">
             <div className="text">学习时间</div>
-            <div className="value">
-              <span>23</span>Hour
-            </div>
+            <div className="value" dangerouslySetInnerHTML={{ __html: formatSeconds(latest.time).replace(/([0-9]+)([msh])/g, '<span>$1</span>$2') }} />
           </div>
           <div className="item">
             <div className="text">同比上周</div>
             <div className="value">
-              <Icon type="caret-up" theme="filled" color="#6EC64B" />
-              <span>15</span>%
+              {diffLast > 0 ? <Icon type="caret-up" theme="filled" color="#6EC64B" /> : <Icon type="caret-down" theme="filled" color="#F36565" />}
+              <span>{diffLastPercent}</span>%
             </div>
           </div>
           <div className="item">
             <div className="text">同比全站</div>
             <div className="value">
-              <Icon type="caret-down" theme="filled" color="#F36565" />
-              <span>15</span>%
+              {diff > 0 ? <Icon type="caret-up" theme="filled" color="#6EC64B" /> : <Icon type="caret-down" theme="filled" color="#F36565" />}
+              <span>{diffPercent}</span>%
             </div>
           </div>
         </div>

+ 7 - 0
front/project/h5/stores/main.js

@@ -28,6 +28,13 @@ export default class MainStore extends BaseStore {
   }
 
   /**
+   * 获取微信信息
+   */
+  getWechat() {
+    return this.apiGet('/base/wechat');
+  }
+
+  /**
    * 获取对应位置的提示tips
    * @param {*} position
    */

+ 4 - 3
front/project/h5/stores/my.js

@@ -108,10 +108,11 @@ export default class MyStore extends BaseStore {
   }
 
   /**
-   * 获取本周学习记录
+   * 获取每周学习记录
+   * @param {*} week 0本周,1上周
    */
-  getStudyWeek() {
-    return this.apiGet('/my/study/week', {});
+  getStudyWeek(week) {
+    return this.apiGet('/my/study/week', { week });
   }
 
   /**

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

@@ -19,7 +19,7 @@ const BIND_WX_ERROR = 'BIND_WX_ERROR';
 export default class Login extends Component {
   constructor(props) {
     super(props);
-    this.state = { type: LOGIN_PHONE };
+    this.state = { type: LOGIN_PHONE, data: {} };
   }
 
   close() {

+ 7 - 3
front/project/www/local.json

@@ -14,9 +14,13 @@
     ]
   },
   "test": {
-    "scripts": ["http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"]
+    "scripts": [
+      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
+    ]
   },
   "production": {
-    "scripts": ["http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"]
+    "scripts": [
+      "http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"
+    ]
   }
-}
+}

+ 99 - 141
front/project/www/routes/paper/process/base/index.js

@@ -12,7 +12,7 @@ import Calculator from '../../../../components/Calculator';
 import AnswerSelect from '../../../../components/AnswerSelect';
 import AnswerTable from '../../../../components/AnswerTable';
 import Editor from '../../../../components/Editor';
-import { QuestionType } from '../../../../../Constant';
+import { QuestionType, ExaminationOrder } from '../../../../../Constant';
 
 const QuestionTypeMap = getMap(QuestionType, 'value');
 
@@ -262,51 +262,15 @@ export default class extends Component {
   }
 
   renderExaminationStart() {
-    const { disorder } = this.state;
+    // const { paper, userQuestion, singleTime, stageTime, flow } = this.props;
+    // const { showTime, showNo } = this.state;
     const { paper, flow } = this.props;
     return (
-      <div className="start">
-        <div className="bg" />
-        <div className="fixed-content">
-          <div className="title">{paper.title}</div>
-          <div className="desc">
-            <div className="block">
-              <div className="desc-title">
-                <Assets name="subject_icon" />
-                题目总数
-              </div>
-              <div className="desc-info">{paper.questionNumer}</div>
-            </div>
-            <div className="block">
-              <div className="desc-title">
-                <Assets name="time_icon" />
-                建议用时
-              </div>
-              <div className="desc-info">{formatSeconds(paper.time)}</div>
-            </div>
-          </div>
-          <div className="tip">
-            <Checkbox className="m-r-1" checked={disorder} onChange={() => this.setState({ disorder: !disorder })} />
-            题目选项乱序显示
-          </div>
-          <div className="submit">
-            <Button size="lager" radius onClick={() => flow.start({ disorder })}>
-              开始练习
-            </Button>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  renderExerciseStart() {
-    const { paper, userQuestion, singleTime, stageTime, flow } = this.props;
-    const { showTime, showNo } = this.state;
-    return (
       <div className="layout">
+        <div className="fixed" />
         <div className="layout-header">
           <div className="title">{paper.title}</div>
-          <div className="right">
+          {/* <div className="right">
             <div
               className="block"
               onClick={() => {
@@ -326,9 +290,9 @@ export default class extends Component {
               <Assets name="subjectnumber_icon" />
               {showNo && `${userQuestion.no} of ${paper.questionNumber}`}
             </div>
-          </div>
+          </div> */}
         </div>
-        <div className={'layout-body'}>{this.renderExerciseStartCAT()}</div>
+        <div className={'layout-body'}>{paper.isAdapt > 1 ? this.renderExaminationStartCAT() : this.renderExaminationStartDefault()}</div>
         <div className="layout-footer">
           <div className="help">
             <Assets name="help_icon" />
@@ -346,7 +310,47 @@ export default class extends Component {
     );
   }
 
-  renderExerciseStartDefault() {
+  renderExerciseStart() {
+    const { disorder } = this.state;
+    const { paper, flow } = this.props;
+    return (
+      <div className="start">
+        <div className="bg" />
+        <div className="fixed-content">
+          <div className="title">{paper.title}</div>
+          <div className="desc">
+            <div className="block">
+              <div className="desc-title">
+                <Assets name="subject_icon" />
+                题目总数
+              </div>
+              <div className="desc-info">{paper.questionNumer}</div>
+            </div>
+            <div className="block">
+              <div className="desc-title">
+                <Assets name="time_icon" />
+                建议用时
+              </div>
+              <div className="desc-info">{formatSeconds(paper.time)}</div>
+            </div>
+          </div>
+          {paper.times > 0 && <div className="tip">
+            <Checkbox className="m-r-1" checked={!disorder} onChange={() => this.setState({ disorder: !!disorder })} />
+            题目选项乱序显示
+          </div>}
+          <div className="submit">
+            <Button size="lager" radius onClick={() => flow.start({ disorder: paper.times > 0 ? !disorder : false })}>
+              开始练习
+            </Button>
+          </div>
+        </div>
+      </div>
+    );
+  }
+
+  renderExaminationStartCAT() {
+    const { disorder, order, orderIndex } = this.state;
+    const { paper, flow } = this.props;
     return (
       <div className="exercise-start default">
         <div className="title">Section Ordering</div>
@@ -361,50 +365,29 @@ export default class extends Component {
           selected, before moving on to the next section. You will NOT be able to return to this screen.
         </div>
         <div className="block-list">
-          <div className="block-item">
-            <div className="block-title">
-              <div className="block-title-border">
-                <AntDIcon type="check" />
-                <span>ARQV</span>
+          {ExaminationOrder.map((row, index) => {
+            return <div className="block-item">
+              <div className="block-title" onClick={() => {
+                this.setState({ order: row.value, orderIndex: index });
+              }}>
+                <div className="block-title-border">
+                  {orderIndex === index && <AntDIcon type="check" />}
+                  <span>{row.label}</span>
+                </div>
               </div>
-            </div>
-            <div className="block-text">1.Analytical Writing Analysis </div>
-            <div className="block-text">2.Integrated Reasoning </div>
-            <div className="block-text">3.Quantitative </div>
-            <div className="block-text">4.Verbal </div>
-          </div>
-          <div className="block-item">
-            <div className="block-title">
-              <div className="block-title-border">
-                <AntDIcon type="check" />
-                <span>VQRA </span>
-              </div>
-            </div>
-            <div className="block-text">1.Verbal </div>
-            <div className="block-text">2.Quantitative </div>
-            <div className="block-text">3.Integrated Reasoning </div>
-            <div className="block-text">4.Analytical Writing Analysis </div>
-          </div>
-          <div className="block-item">
-            <div className="block-title">
-              <div className="block-title-border">
-                <AntDIcon type="check" />
-                <span>QVRA</span>
-              </div>
-            </div>
-            <div className="block-text">1.Quantitative</div>
-            <div className="block-text">2.Verbal </div>
-            <div className="block-text">3.Integrated Reasoning </div>
-            <div className="block-text">4.Analytical Writing Analysis </div>
-          </div>
+              {row.list.map((r, i) => {
+                return <div className="block-text">{i + 1}.{r.label} </div>;
+              })}
+            </div>;
+          })}
         </div>
         <div className="bottom">
-          <div className="text">
-            <Checkbox checked /> 题目选项乱序显示
-          </div>
+          {paper.times > 0 && <div className="text">
+            <Checkbox checked={!disorder} onChange={() => this.setState({ disorder: !!disorder })} /> 题目选项乱序显示
+          </div>}
           <div className="text">
             Click{' '}
-            <div className="next" onClick={() => this.next()}>
+            <div className="next" onClick={() => flow.start({ disorder: paper.times > 0 ? !disorder : false, order })}>
               Next
               <Assets name="next_icon" />
             </div>{' '}
@@ -415,68 +398,44 @@ export default class extends Component {
     );
   }
 
-  renderExerciseStartCAT() {
+  renderExaminationStartDefault() {
+    const { disorder, order, orderIndex } = this.state;
+    const { paper, flow } = this.props;
     return (
       <div className="exercise-start cat">
         <div className="title">Section Ordering</div>
         <div className="block-list">
-          <div className="block-item">
-            <div className="block-item-body active">
-              <AntDIcon type="check" style={{ color: '#fff' }} />
-              <div className="block-text">
-                <Checkbox checked /> Analytical Writing Analysis{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox checked /> Integrated Reasoning{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox checked /> Quantitative{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox checked /> Verbal{' '}
-              </div>
-            </div>
-          </div>
-          <div className="block-item">
-            <div className="block-item-body">
-              <div className="block-text">
-                <Checkbox /> Quantitative
-              </div>
-              <div className="block-text">
-                <Checkbox /> Verbal{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox /> Integrated Reasoning{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox /> Analytical Writing Analysis{' '}
-              </div>
-            </div>
-          </div>
-          <div className="block-item">
-            <div className="block-item-body">
-              <div className="block-text">
-                <Checkbox /> Verbal{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox /> Quantitative{' '}
-              </div>
-              <div className="block-text">
-                <Checkbox /> Integrated Reasoning{' '}
+          {ExaminationOrder.map((row, index) => {
+            return <div className="block-item" onClick={() => {
+              this.setState({ order: row.value, orderIndex: index });
+            }}>
+              <div className={orderIndex === index ? 'block-item-body active' : 'block-item-body'}>
+                {orderIndex === index && <AntDIcon type="check" style={{ color: '#fff' }} />}
+                {row.list.map((r, i) => {
+                  return <div className="block-text" onClick={() => {
+                    if (order.indexOf(r.value) > 0) {
+                      // 取消
+                      order[i] = '';
+                    } else {
+                      // 选中
+                      order[i] = r.value;
+                    }
+                    this.setState({ order });
+                  }}>
+                    <Checkbox checked={orderIndex === index ? order.indexOf(r.value) >= 0 : false} /> {r.label}{' '}
+                  </div>;
+                })}
               </div>
-              <div className="block-text">
-                <Checkbox /> Analytical Writing Analysis{' '}
-              </div>
-            </div>
-          </div>
+            </div>;
+          })}
         </div>
         <div className="bottom">
-          <div className="text">
-            <Checkbox checked /> 题目选项乱序显示
-          </div>
+          {paper.times > 0 && <div className="text">
+            <Checkbox checked={!disorder} onChange={() => this.setState({ disorder: !!disorder })} /> 题目选项乱序显示
+          </div>}
           <div className="text">
             Click{' '}
-            <div className="next" onClick={() => this.next()}>
+            <div className="next" onClick={() => flow.start({ disorder: paper.times > 0 ? !disorder : false, order: order.filter(row => row) })}>
               Next
               <Assets name="next_icon" />
             </div>{' '}
@@ -565,12 +524,11 @@ export default class extends Component {
                 <span className="t-d-l">N</span>o
               </div>
             </div>
-          ) : (
-            <div className="btn-list">
-              <div className="btn" onClick={() => this.hideModal(true)}>
-                <span className="t-d-l">O</span>k
+          ) : (<div className="btn-list">
+            <div className="btn" onClick={() => this.hideModal(true)}>
+              <span className="t-d-l">O</span>k
               </div>
-            </div>
+          </div>
           )}
         </div>
       </div>

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

@@ -3,7 +3,7 @@ package com.qxgmat.data.constants.enums;
 public enum ServiceKey {
     VIP("vip", 0, 0), // 收藏和错题处的组卷、导出;笔记导出功能;部分解析只有VIP可以看;下载模考报告; 解锁完整版模考报告;“提问开放”期间有提问权限
     TEXTBOOK("textbook", 180, 30),
-    QX_CAT("qx_cat", 180, 0), // 可以考2次
+    QX_CAT("qx_cat", 180, 180), // 可以考2次
 
     ;
     public String key;

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

@@ -29,6 +29,7 @@ public enum SettingKey {
     PROMOTE("course_promote"), // 促销
     EXPERIENCE_INFO("experience_info"), // 心经信息
     SENTENCE_INFO("sentence_info"), // 长难句信息
+    WECHAT_INFO("wechat_info"), // 微信公众号信息
 
     TIPS("tips"); // 页面提示信息
 

+ 17 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/logic/TextbookLogic.java

@@ -1,5 +1,7 @@
 package com.qxgmat.data.constants.enums.logic;
 
+import com.qxgmat.data.constants.enums.QuestionType;
+
 public enum TextbookLogic {
     DS("ds"), PS("ps"), DS_PS("ds+ps");
     public String key;
@@ -11,4 +13,19 @@ public enum TextbookLogic {
         if (name == null) return null;
         return TextbookLogic.valueOf(name.toUpperCase());
     }
+
+    public static TextbookLogic[] all(){
+        return new TextbookLogic[]{DS, PS, DS_PS};
+    }
+
+    public boolean contain(QuestionType questionType){
+        switch(this){
+            case DS:
+                return questionType == QuestionType.DS;
+            case PS:
+                return questionType == QuestionType.PS;
+            default:
+                return questionType == QuestionType.DS || questionType == QuestionType.PS;
+        }
+    }
 }

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

@@ -23,7 +23,7 @@ public class ExaminationPaper implements Serializable {
     private Integer structThree;
 
     /**
-     * 是否适应难度
+     * 是否适应难度: 0非适应性,1适应性,2千行
      */
     @Column(name = "`is_adapt`")
     private Integer isAdapt;
@@ -123,18 +123,18 @@ public class ExaminationPaper implements Serializable {
     }
 
     /**
-     * 获取是否适应难度
+     * 获取是否适应难度: 0非适应性,1适应性,2千行
      *
-     * @return is_adapt - 是否适应难度
+     * @return is_adapt - 是否适应难度: 0非适应性,1适应性,2千行
      */
     public Integer getIsAdapt() {
         return isAdapt;
     }
 
     /**
-     * 设置是否适应难度
+     * 设置是否适应难度: 0非适应性,1适应性,2千行
      *
-     * @param isAdapt 是否适应难度
+     * @param isAdapt 是否适应难度: 0非适应性,1适应性,2千行
      */
     public void setIsAdapt(Integer isAdapt) {
         this.isAdapt = isAdapt;
@@ -327,9 +327,9 @@ public class ExaminationPaper implements Serializable {
         }
 
         /**
-         * 设置是否适应难度
+         * 设置是否适应难度: 0非适应性,1适应性,2千行
          *
-         * @param isAdapt 是否适应难度
+         * @param isAdapt 是否适应难度: 0非适应性,1适应性,2千行
          */
         public Builder isAdapt(Integer isAdapt) {
             obj.setIsAdapt(isAdapt);

+ 92 - 22
server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookLibraryHistory.java

@@ -57,10 +57,22 @@ public class TextbookLibraryHistory implements Serializable {
     private Date createTime;
 
     /**
-     * 更新日志
+     * 数学更新日志
      */
-    @Column(name = "`content`")
-    private String content;
+    @Column(name = "`quant_contennt`")
+    private String quantContennt;
+
+    /**
+     * 阅读更新日志
+     */
+    @Column(name = "`rc_content`")
+    private String rcContent;
+
+    /**
+     * 综合推理更新日志
+     */
+    @Column(name = "`ir_content`")
+    private String irContent;
 
     private static final long serialVersionUID = 1L;
 
@@ -219,21 +231,57 @@ public class TextbookLibraryHistory implements Serializable {
     }
 
     /**
-     * 获取更新日志
+     * 获取数学更新日志
+     *
+     * @return quant_contennt - 数学更新日志
+     */
+    public String getQuantContennt() {
+        return quantContennt;
+    }
+
+    /**
+     * 设置数学更新日志
+     *
+     * @param quantContennt 数学更新日志
+     */
+    public void setQuantContennt(String quantContennt) {
+        this.quantContennt = quantContennt;
+    }
+
+    /**
+     * 获取阅读更新日志
+     *
+     * @return rc_content - 阅读更新日志
+     */
+    public String getRcContent() {
+        return rcContent;
+    }
+
+    /**
+     * 设置阅读更新日志
+     *
+     * @param rcContent 阅读更新日志
+     */
+    public void setRcContent(String rcContent) {
+        this.rcContent = rcContent;
+    }
+
+    /**
+     * 获取综合推理更新日志
      *
-     * @return content - 更新日志
+     * @return ir_content - 综合推理更新日志
      */
-    public String getContent() {
-        return content;
+    public String getIrContent() {
+        return irContent;
     }
 
     /**
-     * 设置更新日志
+     * 设置综合推理更新日志
      *
-     * @param content 更新日志
+     * @param irContent 综合推理更新日志
      */
-    public void setContent(String content) {
-        this.content = content;
+    public void setIrContent(String irContent) {
+        this.irContent = irContent;
     }
 
     @Override
@@ -251,7 +299,9 @@ public class TextbookLibraryHistory implements Serializable {
         sb.append(", ir=").append(ir);
         sb.append(", irVersion=").append(irVersion);
         sb.append(", createTime=").append(createTime);
-        sb.append(", content=").append(content);
+        sb.append(", quantContennt=").append(quantContennt);
+        sb.append(", rcContent=").append(rcContent);
+        sb.append(", irContent=").append(irContent);
         sb.append("]");
         return sb.toString();
     }
@@ -296,6 +346,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置数学更新日志
+         *
+         * @param quantContennt 数学更新日志
+         */
+        public Builder quantContennt(String quantContennt) {
+            obj.setQuantContennt(quantContennt);
+            return this;
+        }
+
+        /**
          * 设置数学版本
          *
          * @param quantVersion 数学版本
@@ -316,6 +376,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置阅读更新日志
+         *
+         * @param rcContent 阅读更新日志
+         */
+        public Builder rcContent(String rcContent) {
+            obj.setRcContent(rcContent);
+            return this;
+        }
+
+        /**
          * 设置阅读版本
          *
          * @param rcVersion 阅读版本
@@ -336,6 +406,16 @@ public class TextbookLibraryHistory implements Serializable {
         }
 
         /**
+         * 设置综合推理更新日志
+         *
+         * @param irContent 综合推理更新日志
+         */
+        public Builder irContent(String irContent) {
+            obj.setIrContent(irContent);
+            return this;
+        }
+
+        /**
          * 设置综合推理版本
          *
          * @param irVersion 综合推理版本
@@ -353,16 +433,6 @@ public class TextbookLibraryHistory implements Serializable {
             return this;
         }
 
-        /**
-         * 设置更新日志
-         *
-         * @param content 更新日志
-         */
-        public Builder content(String content) {
-            obj.setContent(content);
-            return this;
-        }
-
         public TextbookLibraryHistory build() {
             return this.obj;
         }

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

@@ -12,12 +12,6 @@ public class TextbookPaper implements Serializable {
     private Integer id;
 
     /**
-     * 所属时间
-     */
-    @Column(name = "`time`")
-    private String time;
-
-    /**
      * 标题
      */
     @Column(name = "`title`")
@@ -59,6 +53,12 @@ public class TextbookPaper implements Serializable {
     @Column(name = "`status`")
     private Integer status;
 
+    /**
+     * 所属年份
+     */
+    @Column(name = "`year`")
+    private String year;
+
     private static final long serialVersionUID = 1L;
 
     /**
@@ -76,24 +76,6 @@ public class TextbookPaper implements Serializable {
     }
 
     /**
-     * 获取所属时间
-     *
-     * @return time - 所属时间
-     */
-    public String getTime() {
-        return time;
-    }
-
-    /**
-     * 设置所属时间
-     *
-     * @param time 所属时间
-     */
-    public void setTime(String time) {
-        this.time = time;
-    }
-
-    /**
      * 获取标题
      *
      * @return title - 标题
@@ -229,6 +211,24 @@ public class TextbookPaper implements Serializable {
         this.status = status;
     }
 
+    /**
+     * 获取所属年份
+     *
+     * @return year - 所属年份
+     */
+    public String getYear() {
+        return year;
+    }
+
+    /**
+     * 设置所属年份
+     *
+     * @param year 所属年份
+     */
+    public void setYear(String year) {
+        this.year = year;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -236,7 +236,6 @@ public class TextbookPaper implements Serializable {
         sb.append(" [");
         sb.append("Hash = ").append(hashCode());
         sb.append(", id=").append(id);
-        sb.append(", time=").append(time);
         sb.append(", title=").append(title);
         sb.append(", logic=").append(logic);
         sb.append(", no=").append(no);
@@ -245,6 +244,7 @@ public class TextbookPaper implements Serializable {
         sb.append(", questionNumber=").append(questionNumber);
         sb.append(", createTime=").append(createTime);
         sb.append(", status=").append(status);
+        sb.append(", year=").append(year);
         sb.append("]");
         return sb.toString();
     }
@@ -269,16 +269,6 @@ public class TextbookPaper implements Serializable {
         }
 
         /**
-         * 设置所属时间
-         *
-         * @param time 所属时间
-         */
-        public Builder time(String time) {
-            obj.setTime(time);
-            return this;
-        }
-
-        /**
          * 设置标题
          *
          * @param title 标题
@@ -354,6 +344,16 @@ public class TextbookPaper implements Serializable {
             return this;
         }
 
+        /**
+         * 设置所属年份
+         *
+         * @param year 所属年份
+         */
+        public Builder year(String year) {
+            obj.setYear(year);
+            return this;
+        }
+
         public TextbookPaper build() {
             return this.obj;
         }

+ 16 - 16
server/data/src/main/java/com/qxgmat/data/dao/entity/TextbookQuestion.java

@@ -53,10 +53,10 @@ public class TextbookQuestion implements Serializable {
     private Integer totalCorrect;
 
     /**
-     * 所属时间
+     * 所属年份
      */
-    @Column(name = "`time`")
-    private String time;
+    @Column(name = "`year`")
+    private String year;
 
     private static final long serialVersionUID = 1L;
 
@@ -201,21 +201,21 @@ public class TextbookQuestion implements Serializable {
     }
 
     /**
-     * 获取所属时间
+     * 获取所属年份
      *
-     * @return time - 所属时间
+     * @return year - 所属年份
      */
-    public String getTime() {
-        return time;
+    public String getYear() {
+        return year;
     }
 
     /**
-     * 设置所属时间
+     * 设置所属年份
      *
-     * @param time 所属时间
+     * @param year 所属年份
      */
-    public void setTime(String time) {
-        this.time = time;
+    public void setYear(String year) {
+        this.year = year;
     }
 
     @Override
@@ -232,7 +232,7 @@ public class TextbookQuestion implements Serializable {
         sb.append(", totalTime=").append(totalTime);
         sb.append(", totalNumber=").append(totalNumber);
         sb.append(", totalCorrect=").append(totalCorrect);
-        sb.append(", time=").append(time);
+        sb.append(", year=").append(year);
         sb.append("]");
         return sb.toString();
     }
@@ -327,12 +327,12 @@ public class TextbookQuestion implements Serializable {
         }
 
         /**
-         * 设置所属时间
+         * 设置所属年份
          *
-         * @param time 所属时间
+         * @param year 所属年份
          */
-        public Builder time(String time) {
-            obj.setTime(time);
+        public Builder year(String year) {
+            obj.setYear(year);
             return this;
         }
 

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

@@ -36,6 +36,12 @@ public class UserPaper implements Serializable {
     private String paperOrigin;
 
     /**
+     * 模考:是否适应难度: 0非适应性,1适应性,2千行
+     */
+    @Column(name = "`is_adapt`")
+    private Integer isAdapt;
+
+    /**
      * 对应来源id
      */
     @Column(name = "`origin_id`")
@@ -196,6 +202,24 @@ public class UserPaper implements Serializable {
     }
 
     /**
+     * 获取模考:是否适应难度: 0非适应性,1适应性,2千行
+     *
+     * @return is_adapt - 模考:是否适应难度: 0非适应性,1适应性,2千行
+     */
+    public Integer getIsAdapt() {
+        return isAdapt;
+    }
+
+    /**
+     * 设置模考:是否适应难度: 0非适应性,1适应性,2千行
+     *
+     * @param isAdapt 模考:是否适应难度: 0非适应性,1适应性,2千行
+     */
+    public void setIsAdapt(Integer isAdapt) {
+        this.isAdapt = isAdapt;
+    }
+
+    /**
      * 获取对应来源id
      *
      * @return origin_id - 对应来源id
@@ -422,6 +446,7 @@ public class UserPaper implements Serializable {
         sb.append(", title=").append(title);
         sb.append(", paperModule=").append(paperModule);
         sb.append(", paperOrigin=").append(paperOrigin);
+        sb.append(", isAdapt=").append(isAdapt);
         sb.append(", originId=").append(originId);
         sb.append(", recordId=").append(recordId);
         sb.append(", questionNoIds=").append(questionNoIds);
@@ -498,6 +523,16 @@ public class UserPaper implements Serializable {
         }
 
         /**
+         * 设置模考:是否适应难度: 0非适应性,1适应性,2千行
+         *
+         * @param isAdapt 模考:是否适应难度: 0非适应性,1适应性,2千行
+         */
+        public Builder isAdapt(Integer isAdapt) {
+            obj.setIsAdapt(isAdapt);
+            return this;
+        }
+
+        /**
          * 设置对应来源id
          *
          * @param originId 对应来源id

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

@@ -105,6 +105,12 @@ public class UserReport implements Serializable {
     @Column(name = "`is_finish`")
     private Integer isFinish;
 
+    /**
+     * 是否完成报告
+     */
+    @Column(name = "`is_stat`")
+    private Integer isStat;
+
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -412,6 +418,24 @@ public class UserReport implements Serializable {
     }
 
     /**
+     * 获取是否完成报告
+     *
+     * @return is_stat - 是否完成报告
+     */
+    public Integer getIsStat() {
+        return isStat;
+    }
+
+    /**
+     * 设置是否完成报告
+     *
+     * @param isStat 是否完成报告
+     */
+    public void setIsStat(Integer isStat) {
+        this.isStat = isStat;
+    }
+
+    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -462,6 +486,7 @@ public class UserReport implements Serializable {
         sb.append(", score=").append(score);
         sb.append(", detail=").append(detail);
         sb.append(", isFinish=").append(isFinish);
+        sb.append(", isStat=").append(isStat);
         sb.append(", createTime=").append(createTime);
         sb.append(", updateTime=").append(updateTime);
         sb.append("]");
@@ -646,6 +671,16 @@ public class UserReport implements Serializable {
         }
 
         /**
+         * 设置是否完成报告
+         *
+         * @param isStat 是否完成报告
+         */
+        public Builder isStat(Integer isStat) {
+            obj.setIsStat(isStat);
+            return this;
+        }
+
+        /**
          * @param createTime
          */
         public Builder createTime(Date createTime) {

+ 0 - 20
server/data/src/main/java/com/qxgmat/data/dao/mapping/CourseStudentOnlineMapper.xml

@@ -1,20 +0,0 @@
-<?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.CourseStudentOnlineMapper">
-  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.CourseStudentOnline">
-    <!--
-      WARNING - @mbg.generated
-    -->
-    <id column="id" jdbcType="INTEGER" property="id" />
-    <result column="course_id" jdbcType="INTEGER" property="courseId" />
-    <result column="time_id" jdbcType="INTEGER" property="timeId" />
-    <result column="user_id" jdbcType="INTEGER" property="userId" />
-    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
-  </resultMap>
-  <sql id="Base_Column_List">
-    <!--
-      WARNING - @mbg.generated
-    -->
-    `id`, `course_id`, `time_id`, `user_id`, `create_time`
-  </sql>
-</mapper>

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

@@ -19,7 +19,9 @@
     <!--
       WARNING - @mbg.generated
     -->
-    <result column="content" jdbcType="LONGVARCHAR" property="content" />
+    <result column="quant_contennt" jdbcType="LONGVARCHAR" property="quantContennt" />
+    <result column="rc_content" jdbcType="LONGVARCHAR" property="rcContent" />
+    <result column="ir_content" jdbcType="LONGVARCHAR" property="irContent" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
@@ -32,6 +34,6 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `content`
+    `quant_contennt`, `rc_content`, `ir_content`
   </sql>
 </mapper>

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

@@ -6,7 +6,6 @@
       WARNING - @mbg.generated
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
-    <result column="time" jdbcType="VARCHAR" property="time" />
     <result column="title" jdbcType="VARCHAR" property="title" />
     <result column="logic" jdbcType="VARCHAR" property="logic" />
     <result column="no" jdbcType="INTEGER" property="no" />
@@ -15,12 +14,13 @@
     <result column="question_number" jdbcType="INTEGER" property="questionNumber" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="status" jdbcType="INTEGER" property="status" />
+    <result column="year" jdbcType="VARCHAR" property="year" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `time`, `title`, `logic`, `no`, `library_id`, `question_no_ids`, `question_number`, 
-    `create_time`, `status`
+    `id`, `title`, `logic`, `no`, `library_id`, `question_no_ids`, `question_number`, 
+    `create_time`, `status`, `year`
   </sql>
 </mapper>

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

@@ -13,13 +13,13 @@
     <result column="total_time" jdbcType="INTEGER" property="totalTime" />
     <result column="total_number" jdbcType="INTEGER" property="totalNumber" />
     <result column="total_correct" jdbcType="INTEGER" property="totalCorrect" />
-    <result column="time" jdbcType="VARCHAR" property="time" />
+    <result column="year" jdbcType="VARCHAR" property="year" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
     `id`, `title`, `no`, `library_id`, `question_id`, `total_time`, `total_number`, `total_correct`, 
-    `time`
+    `year`
   </sql>
 </mapper>

+ 4 - 3
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserPaperMapper.xml

@@ -10,6 +10,7 @@
     <result column="title" jdbcType="VARCHAR" property="title" />
     <result column="paper_module" jdbcType="VARCHAR" property="paperModule" />
     <result column="paper_origin" jdbcType="VARCHAR" property="paperOrigin" />
+    <result column="is_adapt" jdbcType="INTEGER" property="isAdapt" />
     <result column="origin_id" jdbcType="INTEGER" property="originId" />
     <result column="record_id" jdbcType="INTEGER" property="recordId" />
     <result column="question_no_ids" jdbcType="VARCHAR" property="questionNoIds" typeHandler="com.nuliji.tools.mybatis.handler.IntegerArrayWithJsonHandler" />
@@ -27,8 +28,8 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `title`, `paper_module`, `paper_origin`, `origin_id`, `record_id`, 
-    `question_no_ids`, `question_number`, `times`, `time`, `latest_time`, `total_time`, 
-    `total_number`, `total_correct`, `delete_time`, `is_reset`
+    `id`, `user_id`, `title`, `paper_module`, `paper_origin`, `is_adapt`, `origin_id`, 
+    `record_id`, `question_no_ids`, `question_number`, `times`, `time`, `latest_time`, 
+    `total_time`, `total_number`, `total_correct`, `delete_time`, `is_reset`
   </sql>
 </mapper>

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

@@ -22,6 +22,7 @@
     <result column="score" jdbcType="VARCHAR" property="score" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="detail" jdbcType="VARCHAR" property="detail" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="is_finish" jdbcType="INTEGER" property="isFinish" />
+    <result column="is_stat" jdbcType="INTEGER" property="isStat" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
   </resultMap>
@@ -31,6 +32,6 @@
     -->
     `id`, `user_id`, `paper_id`, `paper_module`, `paper_origin`, `origin_id`, `question_no_ids`, 
     `question_number`, `time`, `user_number`, `user_time`, `user_correct`, `finish_time`, 
-    `setting`, `score`, `detail`, `is_finish`, `create_time`, `update_time`
+    `setting`, `score`, `detail`, `is_finish`, `is_stat`, `create_time`, `update_time`
   </sql>
 </mapper>

+ 36 - 0
server/data/src/main/java/com/qxgmat/data/relation/PreviewAssignRelationMapper.java

@@ -0,0 +1,36 @@
+package com.qxgmat.data.relation;
+
+import com.qxgmat.data.dao.entity.ExercisePaper;
+import com.qxgmat.data.dao.entity.PreviewAssign;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface PreviewAssignRelationMapper {
+
+    List<PreviewAssign> listByCourse(
+            @Param("courseId") Number courseId,
+            @Param("userId") Number userId,
+            @Param("times") Integer times
+    );
+
+    List<PreviewAssign> listByAppointment(
+            @Param("courseId") Number courseId,
+            @Param("appointmentIds") Collection appointmentIds,
+            @Param("userId") Number userId,
+            @Param("endTime") String endTime,
+            @Param("times") Integer times
+    );
+
+    List<PreviewAssign> listByTime(
+            @Param("courseId") Number courseId,
+            @Param("timeId") Number timeId,
+            @Param("userId") Number userId,
+            @Param("endTime") String endTime,
+            @Param("times") Integer times
+    );
+}

+ 19 - 0
server/data/src/main/java/com/qxgmat/data/relation/TextbookPaperRelationMapper.java

@@ -0,0 +1,19 @@
+package com.qxgmat.data.relation;
+
+import com.qxgmat.data.dao.entity.ExercisePaper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface TextbookPaperRelationMapper {
+
+    List<ExercisePaper> listWithUser(
+            @Param("libraryId") Number libraryId,
+            @Param("userId") Number userId,
+            @Param("logic") String logic,
+            @Param("times") Integer times
+    );
+}

+ 14 - 6
server/data/src/main/java/com/qxgmat/data/relation/UserCourseRecordRelationMapper.java

@@ -1,20 +1,28 @@
 package com.qxgmat.data.relation;
 
-import com.qxgmat.data.dao.entity.UserReport;
-import com.qxgmat.data.relation.entity.UserCourseStatRelation;
+import com.qxgmat.data.relation.entity.UserModuleRecordStatRelation;
 import com.qxgmat.data.relation.entity.UserRankStatRelation;
-import com.qxgmat.data.relation.entity.UserReportLimitRelation;
-import com.qxgmat.data.relation.entity.UserStudyStatRelation;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import org.apache.ibatis.annotations.Param;
 
-import java.util.Collection;
 import java.util.List;
 
 /**
  * Created by gaojie on 2017/11/9.
  */
 public interface UserCourseRecordRelationMapper {
-    List<UserCourseStatRelation> statGroupType(
+    List<UserRecordStatRelation> stat(
+            @Param("userId") Integer userId,
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime
+    );
+
+    List<UserRecordStatRelation> statAvg(
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime
+    );
+
+    List<UserModuleRecordStatRelation> statGroupType(
             @Param("userId") Integer userId,
             @Param("startTime") String startTime,
             @Param("endTime") String endTime

+ 8 - 0
server/data/src/main/java/com/qxgmat/data/relation/UserOrderRecordRelationMapper.java

@@ -22,6 +22,14 @@ public interface UserOrderRecordRelationMapper {
             @Param("direction") String direction
     );
 
+    List<UserOrderRecord> listWithVs(
+            @Param("vsType") String vsType,
+            @Param("courseId") Integer courseId,
+            @Param("userId") Integer userId,
+            @Param("order") String order,
+            @Param("direction") String direction
+    );
+
     List<CourseStudentNumberRelation> groupByTime(
             @Param("ids") Collection ids
     );

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

@@ -0,0 +1,22 @@
+package com.qxgmat.data.relation;
+
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface UserQuestionRelationMapper {
+    List<UserRecordStatRelation> stat(
+            @Param("userId") Integer userId,
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime
+    );
+
+    List<UserRecordStatRelation> statAvg(
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime
+    );
+}

+ 7 - 2
server/data/src/main/java/com/qxgmat/data/relation/UserSentenceRecordRelationMapper.java

@@ -1,6 +1,6 @@
 package com.qxgmat.data.relation;
 
-import com.qxgmat.data.relation.entity.UserSentenceStatRelation;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
@@ -9,9 +9,14 @@ import java.util.List;
  * Created by gaojie on 2017/11/9.
  */
 public interface UserSentenceRecordRelationMapper {
-    List<UserSentenceStatRelation> stat(
+    List<UserRecordStatRelation> stat(
             @Param("userId") Integer userId,
             @Param("startTime") String startTime,
             @Param("endTime") String endTime
     );
+
+    List<UserRecordStatRelation> statAvg(
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime
+    );
 }

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/entity/UserCourseStatRelation.java

@@ -2,7 +2,7 @@ package com.qxgmat.data.relation.entity;
 
 import javax.persistence.Column;
 
-public class UserCourseStatRelation {
+public class UserModuleRecordStatRelation {
     /**
      * 对应模块或题型
      */

+ 1 - 1
server/data/src/main/java/com/qxgmat/data/relation/entity/UserSentenceStatRelation.java

@@ -2,7 +2,7 @@ package com.qxgmat.data.relation.entity;
 
 import javax.persistence.Column;
 
-public class UserSentenceStatRelation {
+public class UserRecordStatRelation {
     /**
      * 用户做题时间
      */

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

@@ -31,10 +31,10 @@
     </if>
     where 1
     <if test="structId != null">
-      and ep.`struct_three` = #{structId,jdbcType=VARCHAR} or ep.`struct_four` = #{structId,jdbcType=VARCHAR}
+      and (ep.`struct_three` = #{structId,jdbcType=VARCHAR} or ep.`struct_four` = #{structId,jdbcType=VARCHAR})
     </if>
     <if test="userId != null">
-      and up.`id` != null
+      and up.`id` &gt; 0
     </if>
   </select>
 </mapper>

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

@@ -43,7 +43,7 @@
     </if>
     where 1
     <if test="structId != null">
-      and ep.`struct_three` = #{structId,jdbcType=VARCHAR} or ep.`struct_four` = #{structId,jdbcType=VARCHAR}
+      and (ep.`struct_three` = #{structId,jdbcType=VARCHAR} or ep.`struct_four` = #{structId,jdbcType=VARCHAR})
     </if>
     <if test="userId != null">
       and up.`id` &gt; 0

+ 97 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/PreviewAssignRelationMapper.xml

@@ -0,0 +1,97 @@
+<?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.relation.PreviewAssignRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.PreviewAssign">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    ep.`id`
+  </sql>
+
+  <!--
+   视频课程作业列表
+  -->
+  <select id="listByCourse" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `preview_assign` pa
+    <if test="userId != null">
+    left join `user_paper` up on ep.`id` = up.`origin_id`
+      and up.`paper_origin` = 'preview'
+      and up.`user_id` = #{userId,jdbcType=VARCHAR}
+      <if test="times != null">
+        and up.`times` >= #{times,jdbcType=VARCHAR}
+      </if>
+    </if>
+    where 1
+    <if test="courseId != null">
+      and pa.`course_id` = #{courseId,jdbcType=VARCHAR}
+    </if>
+    <if test="userId != null">
+      and up.`id` &gt; 0
+    </if>
+  </select>
+
+  <!--
+   1v1课程作业列表
+  -->
+  <select id="listByAppointment" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `preview_assign` pa
+    <if test="userId != null">
+      left join `user_paper` up on ep.`id` = up.`origin_id`
+      and up.`paper_origin` = 'preview'
+      and up.`user_id` = #{userId,jdbcType=VARCHAR}
+      <if test="times != null">
+        and up.`times` >= #{times,jdbcType=VARCHAR}
+      </if>
+    </if>
+    where 1
+    <if test="courseId != null">
+      and pa.`course_id` = #{courseId,jdbcType=VARCHAR}
+    </if>
+    <if test="appointmentIds != null">
+      pa.`course_appointment` IN
+      <foreach collection="appointmentIds" item="item" index="index" open="(" close=")" separator=",">
+        #{item}
+      </foreach>
+    </if>
+    <if test="userId != null">
+      and up.`id` &gt; 0
+    </if>
+  </select>
+
+  <!--
+   小班课程作业列表
+  -->
+  <select id="listByAppointment" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `preview_assign` pa
+    <if test="userId != null">
+      left join `user_paper` up on ep.`id` = up.`origin_id`
+      and up.`paper_origin` = 'preview'
+      and up.`user_id` = #{userId,jdbcType=VARCHAR}
+      <if test="times != null">
+        and up.`times` >= #{times,jdbcType=VARCHAR}
+      </if>
+    </if>
+    where 1
+    <if test="courseId != null">
+      and pa.`course_id` = #{courseId,jdbcType=VARCHAR}
+    </if>
+    <if test="timeId != null">
+      and pa.`course_time` = #{timeId,jdbcType=VARCHAR}
+    </if>
+    <if test="userId != null">
+      and up.`id` &gt; 0
+    </if>
+  </select>
+</mapper>

+ 40 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/TextbookPaperRelationMapper.xml

@@ -0,0 +1,40 @@
+<?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.relation.TextbookPaperRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.TextbookPaper">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    ep.`id`
+  </sql>
+
+  <!--
+    用户机经-练习册列表: 用户端
+  -->
+  <select id="listWithUser" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `textbook_paper` tp
+    <if test="userId != null">
+    left join `user_paper` up on ep.`id` = up.`origin_id`
+      and up.`paper_origin` = 'textbook'
+      and up.`user_id` = #{userId,jdbcType=VARCHAR}
+      <if test="times != null">
+        and up.`times` >= #{times,jdbcType=VARCHAR}
+      </if>
+    </if>
+    where 1
+    <if test="libraryId != null">
+      and tp.`library_id` = #{libraryId,jdbcType=VARCHAR}
+    </if>
+    <if test="userId != null">
+      and up.`id` &gt; 0
+    </if>
+  </select>
+</mapper>

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

@@ -35,7 +35,7 @@
     </if>
     where 1
     <if test="paperId != null">
-      and tp.`id` != null
+      and tp.`id` &gt; 0
     </if>
     <if test="questionNoId != null">
       and tq.`id` =#{questionNoId,jdbcType=VARCHAR}

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

@@ -7,7 +7,13 @@
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
   </resultMap>
-  <resultMap id="studyMap" type="com.qxgmat.data.relation.entity.UserCourseStatRelation">
+  <resultMap id="studyMap" type="com.qxgmat.data.relation.entity.UserRecordStatRelation">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="user_time" jdbcType="INTEGER" property="user_time" />
+  </resultMap>
+  <resultMap id="studyModuleMap" type="com.qxgmat.data.relation.entity.UserModuleRecordStatRelation">
     <!--
       WARNING - @mbg.generated
     -->
@@ -29,6 +35,40 @@
   </sql>
 
   <!--
+    用户听课记录统计
+  -->
+  <select id="stat" resultMap="studyMap">
+    select
+    sum(ucr.`user_time`) as `user_time`
+    from `user_course_record` ucr
+    where
+    ucr.`user_id` = #{userId,jdbcType=VARCHAR}
+    <if test="startTime != null">
+      and ucr.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
+    </if>
+    <if test="endTime != null">
+      and ucr.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
+    </if>
+  </select>
+
+  <!--
+    全站听课记录统计
+  -->
+  <select id="statAvg" resultMap="studyMap">
+    select
+    sum(ucr.`user_time`) / count(distance(ucr.`user_id`)) as `user_time`
+    from `user_course_record` ucr
+    where
+    1
+    <if test="startTime != null">
+      and ucr.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
+    </if>
+    <if test="endTime != null">
+      and ucr.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
+    </if>
+  </select>
+
+  <!--
     用户听课记录统计,分题型
   -->
   <select id="statGroupType" resultMap="studyMap">

+ 38 - 18
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserOrderRecordRelationMapper.xml

@@ -1,6 +1,6 @@
 <?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.relation.UserNoteQuestionRelationMapper">
+<mapper namespace="com.qxgmat.data.relation.UserOrderRecordRelationMapper">
   <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.UserNoteQuestion">
     <!--
       WARNING - @mbg.generated
@@ -34,34 +34,54 @@
       #{item}
     </foreach>
   </select>
+
   <!--
-    用户笔记题目列表
+    获取用户学习记录
   -->
-  <select id="list" resultMap="IdMap">
+  <select id="listWithStudyAdmin" resultMap="IdMap">
     select
     <include refid="Id_Column_List" />
-    from `user_note_question` ucq
-    left join `question_no` qn on qn.`id` = ucq.`question_no_id`
-    <if test="module != null">
-      and qn.`module` = #{module,jdbcType=VARCHAR}
+    from `user_order_record` uor
+    left join `course` c on c.`id` = uor.`product_id` and uor.`product_type` = 'course'
+    <if test="courseModules != null">
+      c.`course_module` IN
+      <foreach collection="courseModules" item="item" index="index" open="(" close=")" separator=",">
+        #{item, jdbcType=VARCHAR}
+      </foreach>
     </if>
-    left join `question` q on q.`id` = ucq.`question_id`
-    <if test="questionType != null">
-      and q.`question_type` = #{questionType,jdbcType=VARCHAR}
+    <if test="structId != null">
+      and (c.`struct_id` = #{structId,jdbcType=VARCHAR} or c.`parent_struct_id` = #{structId,jdbcType=VARCHAR})
+    </if>
+    <if test="courseId != null">
+      and c.`id` = #{courseId,jdbcType=VARCHAR}
     </if>
     where
-    qn.`id` &gt; 0
-    and q.`id` &gt; 0
+    c.`id` &gt; 0
     <if test="userId != null">
-      and ucq.`user_id` = #{userId,jdbcType=VARCHAR}
+      and uor.`user_id` = #{userId,jdbcType=VARCHAR}
     </if>
-    <if test="startTime != null">
-      and ucq.`createTime` &gt; #{startTime,jdbcType=VARCHAR}
+    order by ${order} ${direction}
+  </select>
+
+  <!--
+    获取用户VS学习记录
+  -->
+  <select id="listWithVs" resultMap="IdMap">
+    select
+    <include refid="Id_Column_List" />
+    from `user_order_record` uor
+    left join `course` c on c.`id` = uor.`product_id` and uor.`product_type` = 'course'
+    <if test="vsType != null">
+      and c.`vs_type` = #{vsType,jdbcType=VARCHAR}
     </if>
-    <if test="endTime != null">
-      and ucq.`createTime` &lt; #{endTime,jdbcType=VARCHAR}
+    <if test="courseId != null">
+      and c.`id` = #{courseId,jdbcType=VARCHAR}
+    </if>
+    where
+    c.`id` &gt; 0
+    <if test="userId != null">
+      and uor.`user_id` = #{userId,jdbcType=VARCHAR}
     </if>
-
     order by ${order} ${direction}
   </select>
 

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

@@ -0,0 +1,56 @@
+<?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.relation.UserQuestionRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.UserQuestion">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+  <resultMap id="studyMap" type="com.qxgmat.data.relation.entity.UserRecordStatRelation">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="user_time" jdbcType="INTEGER" property="user_time" />
+  </resultMap>
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    ur.`id`
+  </sql>
+
+  <!--
+    用户做题记录统计
+  -->
+  <select id="stat" resultMap="studyMap">
+    select
+    sum(uq.`user_time`) as `user_time`
+    from `user_question` uq
+    where
+    uq.`user_id` = #{userId,jdbcType=VARCHAR}
+    <if test="startTime != null">
+      and uq.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
+    </if>
+    <if test="endTime != null">
+      and uq.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
+    </if>
+  </select>
+
+  <!--
+    全站做题记录统计
+  -->
+  <select id="statAvg" resultMap="studyMap">
+    select
+    sum(uq.`user_time`) /count(distance(uq.`user_id`)) as `user_time`
+    from `user_question` uq
+    where
+    1
+    <if test="startTime != null">
+      and uq.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
+    </if>
+    <if test="endTime != null">
+      and uq.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
+    </if>
+  </select>
+</mapper>

+ 21 - 4
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserSentenceRecordRelationMapper.xml

@@ -7,7 +7,7 @@
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
   </resultMap>
-  <resultMap id="studyMap" type="com.qxgmat.data.relation.entity.UserSentenceStatRelation">
+  <resultMap id="studyMap" type="com.qxgmat.data.relation.entity.UserRecordStatRelation">
     <!--
       WARNING - @mbg.generated
     -->
@@ -28,12 +28,29 @@
     sum(usr.`user_time`) as `user_time`
     from `user_sentence_record` usr
     where
-    ucr.`user_id` = #{userId,jdbcType=VARCHAR}
+    usr.`user_id` = #{userId,jdbcType=VARCHAR}
     <if test="startTime != null">
-      and ur.`update_time` &gt; #{startTime,jdbcType=VARCHAR}
+      and usr.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
     </if>
     <if test="endTime != null">
-      and ur.`update_time` &lt; #{endTime,jdbcType=VARCHAR}
+      and usr.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
+    </if>
+  </select>
+
+  <!--
+    全站阅读记录统计
+  -->
+  <select id="statAvg" resultMap="studyMap">
+    select
+    sum(usr.`user_time`) / count(distance(usr.`user_id`)) as `user_time`
+    from `user_sentence_record` usr
+    where
+    1
+    <if test="startTime != null">
+      and usr.`create_time` &gt; #{startTime,jdbcType=VARCHAR}
+    </if>
+    <if test="endTime != null">
+      and usr.`create_time` &lt; #{endTime,jdbcType=VARCHAR}
     </if>
   </select>
 </mapper>

+ 17 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java

@@ -365,6 +365,23 @@ public class SettingController {
         return ResponseHelp.success(entity.getValue());
     }
 
+    @RequestMapping(value = "/wechat_info", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改长难句信息", httpMethod = "PUT")
+    private Response<Boolean> editWechatInfo(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.WECHAT_INFO);
+        entity.setValue(dto);
+        settingService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/wechat_info", method = RequestMethod.GET)
+    @ApiOperation(value = "获取长难句信息", httpMethod = "GET")
+    private Response<JSONObject> getWechatInfo(){
+        Setting entity = settingService.getByKey(SettingKey.WECHAT_INFO);
+
+        return ResponseHelp.success(entity.getValue());
+    }
+
     @RequestMapping(value = "/tips", method = RequestMethod.PUT)
     @ApiOperation(value = "修改结构说明", httpMethod = "PUT")
     private Response<Boolean> editTips(@RequestBody @Validated JSONObject dto){

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

@@ -158,9 +158,9 @@ public class UserController {
         UserDetailDto dto = Transform.convert(entity, UserDetailDto.class);
         Integer time = 0;
 
-        time += courseExtendService.studyTime(id);
-        time += sentenceService.studyTime(id);
-        time += questionFlowService.studyTime(id);
+        time += courseExtendService.studyTime(id, null, null);
+        time += sentenceService.studyTime(id, null, null);
+        time += questionFlowService.studyTime(id, null, null);
         dto.setTotalTime(time);
 
         return ResponseHelp.success(dto);

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

@@ -73,7 +73,7 @@ public class AuthController {
         }else{
             user = usersService.getUserByToken(token);
             // 用该token登录
-            shiroHelp.getSession().login(shiroHelp.user(user.getMobile(), ""));
+            shiroHelp.getSession().login(shiroHelp.user(user.getArea()+":"+user.getMobile(), ""));
         }
 
         User entity = usersService.get(user.getId());

+ 7 - 0
server/gateway-api/src/main/java/com/qxgmat/controller/api/BaseController.java

@@ -101,6 +101,13 @@ public class BaseController {
         return ResponseHelp.success(entity.getValue());
     }
 
+    @RequestMapping(value = "/wechat", method = RequestMethod.GET)
+    @ApiOperation(value = "获取心经信息", notes = "获取心经信息", httpMethod = "GET")
+    public Response<JSONObject> wechat()  {
+        Setting entity = settingService.getByKey(SettingKey.WECHAT_INFO);
+        return ResponseHelp.success(entity.getValue());
+    }
+
     @RequestMapping(value = "/score", method = RequestMethod.GET)
     @ApiOperation(value = "考分计算", notes = "获取考分排行信息", httpMethod = "GET")
     public Response<Rank> score(

+ 16 - 4
server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java

@@ -7,6 +7,7 @@ import com.nuliji.tools.exception.ParameterException;
 import com.qxgmat.data.constants.enums.ExperienceDayRange;
 import com.qxgmat.data.constants.enums.ExperienceScoreRange;
 import com.qxgmat.data.constants.enums.module.CourseModule;
+import com.qxgmat.data.constants.enums.module.VsCourseType;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.constants.enums.user.DataType;
 import com.qxgmat.data.dao.entity.*;
@@ -239,12 +240,22 @@ public class CourseController {
     @RequestMapping(value = "/progress", method = RequestMethod.GET)
     @ApiOperation(value = "获取课程进度", notes = "获取所有课程及状态进度", httpMethod = "GET")
     public Response<List<UserCourseProgressDto>> progress(
-            @RequestParam(required = false) String[] courseModules,
+            @RequestParam(required = false) String courseModule,
             @RequestParam(required = false) Integer structId,
             @RequestParam(required = false) Integer courseId
     )  {
         User user = (User) shiroHelp.getLoginUser();
-        List<UserOrderRecord> userOrderRecordList = userOrderRecordService.listWithStudyAdmin(1, 1000, courseModules, structId, courseId, user.getId(), null,null, null);
+        List<UserOrderRecord> userOrderRecordList;
+        CourseModule module = CourseModule.ValueOf(courseModule);
+        if (module == CourseModule.VIDEO){
+            // 视频课程包含:小班课程
+            userOrderRecordList = userOrderRecordService.listWithStudyAdmin(1, 1000, new String[]{CourseModule.VIDEO.key, CourseModule.ONLINE.key}, structId, courseId, user.getId(), null,null, null);
+        } else if (module == CourseModule.VS){
+            // 1v1课程:只有系统授课有作业
+            userOrderRecordList = userOrderRecordService.listWithVs(1, 1000, VsCourseType.COACH, courseId, user.getId(), null, null);
+        }else{
+            throw new ParameterException("课程类型错误");
+        }
         List<UserCourseProgressDto> dtos = Transform.convert(userOrderRecordList, UserCourseProgressDto.class);
 
         // 绑定课程
@@ -265,14 +276,15 @@ public class CourseController {
     @RequestMapping(value = "/preview/list", method = RequestMethod.GET)
     @ApiOperation(value = "获取预习作业列表", notes = "获取预习作业列表", httpMethod = "GET")
     public Response<List<UserExercisePaperDto>> listPreview(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) Integer recordId,
             @RequestParam(required = false) String endTime,
             @RequestParam(required = false) Integer times
     )  {
         User user = (User) shiroHelp.getLoginUser();
 
-        List<UserPreviewPaperRelation> p = previewService.list(recordId, user.getId(), endTime, times, null);
-
+        List<UserPreviewPaperRelation> p = previewService.list(page, size, recordId, user.getId(), endTime, times);
         List<UserExercisePaperDto> pr = Transform.convert(p, UserExercisePaperDto.class);
 
         // 获取试卷统计信息

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

@@ -25,8 +25,10 @@ import com.qxgmat.help.AiHelp;
 import com.qxgmat.help.MailHelp;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.*;
+import com.qxgmat.service.extend.CourseExtendService;
 import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.extend.QuestionFlowService;
+import com.qxgmat.service.extend.SentenceService;
 import com.qxgmat.service.inline.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -147,6 +149,12 @@ public class MyController {
     private QuestionFlowService questionFlowService;
 
     @Autowired
+    private SentenceService sentenceService;
+
+    @Autowired
+    private CourseExtendService courseExtendService;
+
+    @Autowired
     private OrderFlowService orderFlowService;
 
     @RequestMapping(value = "/email", method = RequestMethod.POST)
@@ -459,92 +467,31 @@ public class MyController {
 
     @RequestMapping(value = "/study/week", method = RequestMethod.GET)
     @ApiOperation(value = "获取本周记录", notes = "获取本周学习记录", httpMethod = "GET")
-    public Response<UserStudyDetailDto> studyWeekTime()  {
+    public Response<UserStudyDetailDto> studyWeekTime(
+            @RequestParam(required = false) Integer week
+    )  {
         User user = (User) shiroHelp.getLoginUser();
         UserStudyDetailDto dto = new UserStudyDetailDto();
         dto.setCreateTime(user.getCreateTime());
         dto.setDays((int)((user.getCreateTime().getTime() - new Date().getTime()) / (1000*3600*24)));
-        Integer totalTime = 0;
-        Map<String, Integer> categoryMap = new HashMap<>();
-        // 按模块来源分组查询: module=> sentence, examination, collect+error, 忽略exercise,preview
-        List<UserStudyStatRelation> moduleList = userReportService.statGroupModule(user.getId());
-        for(UserStudyStatRelation module:moduleList){
-            // 练习时间过滤
-            if (module.getModule().equals(PaperModule.EXERCISE.key)){
-                continue;
-            }
-            Integer time = module.getUserTime();
-            String key = module.getModule();
-            totalTime += time;
-            // 收藏及错误组卷合并
-            if (module.getModule().equals(PaperOrigin.COLLECT.key)
-                    || module.getModule().equals(PaperOrigin.ERROR.key)){
-                key = "freedom";
-                time += categoryMap.getOrDefault(key, 0);
-            }else if (module.getModule().equals(PaperOrigin.PREVIEW.key)){
-                key = PaperOrigin.EXERCISE.key;
-            }
-            categoryMap.put(key, time);
-        }
-        // 按题型统计练习
-        List<UserStudyStatRelation> exerciseList = userReportService.statGroupExerciseType(user.getId(), null, null);
-        for(UserStudyStatRelation type:exerciseList){
-            totalTime += type.getUserTime();
-            categoryMap.put(type.getModule(), type.getUserTime());
-        }
-        // 按题型统计预习作业
-        List<UserStudyStatRelation> previewList = userReportService.statGroupExerciseType(user.getId(), null, null);
-        for(UserStudyStatRelation type:previewList){
-            totalTime += type.getUserTime();
-            categoryMap.put(type.getModule(), type.getUserTime());
-        }
-        // 按题型统计课程
-        List<UserCourseStatRelation> recordList = userCourseRecordService.statGroupType(user.getId(), null, null);
-        for (UserCourseStatRelation record : recordList){
-            totalTime += record.getUserTime();
-            // 累加同类型时间
-            Integer time = categoryMap.getOrDefault(record.getModule(), 0);
-            categoryMap.put(record.getModule(), time);
-        }
-        // 获取长难句阅读统计
-        UserSentenceStatRelation sentenceStatRelation = userSentenceRecordService.stat(user.getId(), null, null);
-        if (sentenceStatRelation != null){
-            Integer sentenceTime = categoryMap.getOrDefault(PaperModule.SENTENCE.key, 0);
-            categoryMap.put(PaperModule.SENTENCE.key, sentenceTime + sentenceStatRelation.getUserTime());
-        }
 
-        List<ExerciseStruct> p = exerciseStructService.main();
-        Map<String, String> m = new HashMap<>();
-        for (ExerciseStruct struct : p){
-            if (struct.getExtend() == null || struct.getExtend().isEmpty()) continue;
-            m.put(struct.getExtend(), struct.getTitleZh() + (struct.getTitleEn().isEmpty() ? "":" "+struct.getTitleEn()));
-        }
+        Date now = Tools.today();
+        int day = Tools.getDayOfWeek(now);
+        Date start = Tools.addDate(now, -1 * (day + week * 7));
+        Date end = Tools.addDate(start, 7);
 
-        // 组装数据
-        List<UserStudyExtendDto> categorys = new ArrayList<>();
-        if (categoryMap.containsKey(PaperModule.SENTENCE.key)) categorys.add(new UserStudyExtendDto(m.get(PaperModule.SENTENCE.key), categoryMap.get(PaperModule.SENTENCE.key)));
-        if (categoryMap.containsKey(QuestionType.SC.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionType.SC.key), categoryMap.get(QuestionType.SC.key)));
-        if (categoryMap.containsKey(QuestionType.RC.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionType.RC.key), categoryMap.get(QuestionType.RC.key)));
-        if (categoryMap.containsKey(QuestionType.CR.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionType.CR.key), categoryMap.get(QuestionType.CR.key)));
-        if (categoryMap.containsKey(QuestionType.PS.key)){
-            // 累加数学
-            Integer time = categoryMap.getOrDefault(QuestionSubject.QUANT.key, 0);
-            categoryMap.put(QuestionSubject.QUANT.key, time + categoryMap.get(QuestionType.PS.key));
-        }
-        if (categoryMap.containsKey(QuestionType.DS.key)){
-            // 累加数学
-            Integer time = categoryMap.getOrDefault(QuestionSubject.QUANT.key, 0);
-            categoryMap.put(QuestionSubject.QUANT.key, time + categoryMap.get(QuestionType.DS.key));
-        }
-        if (categoryMap.containsKey(QuestionSubject.QUANT.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionSubject.QUANT.key), categoryMap.get(QuestionSubject.QUANT.key)));
+        Integer time = 0;
+        time += courseExtendService.studyTime(user.getId(), start, end);
+        time += sentenceService.studyTime(user.getId(), start, end);
+        time += questionFlowService.studyTime(user.getId(), start, end);
+        dto.setTime(time);
 
-        if (categoryMap.containsKey(QuestionType.IR.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionType.IR.key), categoryMap.get(QuestionType.IR.key)));
-        if (categoryMap.containsKey(QuestionType.AWA.key)) categorys.add(new UserStudyExtendDto(m.get(QuestionType.AWA.key), categoryMap.get(QuestionType.AWA.key)));
-        if (categoryMap.containsKey(PaperModule.EXAMINATION.key)) categorys.add(new UserStudyExtendDto("模考", categoryMap.get(PaperModule.EXAMINATION.key)));
-        if (categoryMap.containsKey("freedom")) categorys.add(new UserStudyExtendDto("自由组卷", categoryMap.get("freedom")));
+        Integer avgTime = 0;
+        avgTime += courseExtendService.studyAvgTime(start, end);
+        avgTime += sentenceService.studyAvgTime(start, end);
+        avgTime += questionFlowService.studyAvgTime(start, end);
+        dto.setAvgTime(avgTime);
 
-        dto.setTime(totalTime);
-        dto.setCategorys(categorys);
         return ResponseHelp.success(dto);
     }
 
@@ -590,15 +537,15 @@ public class MyController {
             categoryMap.put(type.getModule(), type.getUserTime());
         }
         // 按题型统计课程
-        List<UserCourseStatRelation> recordList = userCourseRecordService.statGroupType(user.getId(), null, null);
-        for (UserCourseStatRelation record : recordList){
+        List<UserModuleRecordStatRelation> recordList = userCourseRecordService.statGroupType(user.getId(), null, null);
+        for (UserModuleRecordStatRelation record : recordList){
             totalTime += record.getUserTime();
             // 累加同类型时间
             Integer time = categoryMap.getOrDefault(record.getModule(), 0);
             categoryMap.put(record.getModule(), time);
         }
         // 获取长难句阅读统计
-        UserSentenceStatRelation sentenceStatRelation = userSentenceRecordService.stat(user.getId(), null, null);
+        UserRecordStatRelation sentenceStatRelation = userSentenceRecordService.stat(user.getId(), null, null);
         if (sentenceStatRelation != null){
             Integer sentenceTime = categoryMap.getOrDefault(PaperModule.SENTENCE.key, 0);
             categoryMap.put(PaperModule.SENTENCE.key, sentenceTime + sentenceStatRelation.getUserTime());

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

@@ -196,12 +196,12 @@ public class QuestionController {
                 List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXERCISE, ids, null);
                 // 绑定userPaperId,用于关联report
                 Map userPaperMap = Transform.getMap(userPaperList, UserPaper.class, "originId", "id");
-                Transform.combine(childrenDtos, userPaperMap, UserSentencePaperDto.class, "id", "userPaperId");
+                Transform.combine(childrenDtos, userPaperMap, UserExerciseGroupExtendDto.class, "id", "userPaperId");
 
                 // 获取最后一次作业结果
                 Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
                 List<UserReport> reportList = userReportService.listWithLater(paperIds);
-                Transform.combine(childrenDtos, reportList, UserSentencePaperDto.class, "userPaperId", "report", UserReport.class, "paperId", UserReportExtendDto.class);
+                Transform.combine(childrenDtos, reportList, UserExerciseGroupExtendDto.class, "userPaperId", "report", UserReport.class, "paperId", UserReportExtendDto.class);
 
                 dto.setChildren(childrenDtos);
             }else{
@@ -747,9 +747,9 @@ public class QuestionController {
         return ResponseHelp.success(true);
     }
 
-    @RequestMapping(value = "/restart/examination", method = RequestMethod.POST)
+    @RequestMapping(value = "/reset/cat", method = RequestMethod.POST)
     @ApiOperation(value = "重置整套模拟卷", notes = "重置考试", httpMethod = "POST")
-    public Response<Boolean> restartExamination(@RequestBody @Validated ExaminationRestartDto dto)  {
+    public Response<Boolean> resetCat()  {
         User user = (User) shiroHelp.getLoginUser();
 
         UserService userService = userServiceService.getService(user.getId(), ServiceKey.QX_CAT);
@@ -760,7 +760,7 @@ public class QuestionController {
             throw new ParameterException("已重置,请再次购买服务");
         }
         // reset当前考卷的所有状态
-        questionFlowService.restart(dto.getStructId(), user.getId());
+        examinationService.resetCat(user.getId(), false);
         userServiceService.edit(UserService.builder().id(userService.getId()).isReset(1).build());
         return ResponseHelp.success(true);
     }

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

@@ -114,11 +114,7 @@ public class SentenceController
     public Response<Boolean> active(@RequestBody @Validated UserSentenceCodeDto dto) {
         User user = (User) shiroHelp.getLoginUser();
         if (user == null) throw new AuthException("需要登录");
-
-        if (sentenceCodeService.isActive(user.getId()) == null){
-            sentenceCodeService.active(user.getId(), dto.getCode());
-        }
-
+        sentenceService.active(user.getId(), dto.getCode());
         return ResponseHelp.success(true);
     }
 

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

@@ -5,12 +5,24 @@ import com.nuliji.tools.*;
 import com.nuliji.tools.exception.AuthException;
 import com.nuliji.tools.exception.ParameterException;
 import com.qxgmat.data.constants.enums.QuestionSubject;
+import com.qxgmat.data.constants.enums.QuestionType;
 import com.qxgmat.data.constants.enums.ServiceKey;
+import com.qxgmat.data.constants.enums.logic.TextbookLogic;
+import com.qxgmat.data.constants.enums.module.PaperOrigin;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.dao.entity.*;
+import com.qxgmat.data.inline.UserQuestionStat;
+import com.qxgmat.data.relation.entity.TextbookQuestionRelation;
+import com.qxgmat.dto.extend.UserPaperBaseExtendDto;
+import com.qxgmat.dto.extend.UserReportExtendDto;
 import com.qxgmat.dto.extend.UserServiceRecordExtendDto;
+import com.qxgmat.dto.extend.UserTextbookGroupExtendDto;
+import com.qxgmat.dto.response.UserTextbookGroupDto;
 import com.qxgmat.dto.response.UserTextbookInfoDto;
+import com.qxgmat.dto.response.UserTextbookPaperDto;
 import com.qxgmat.help.ShiroHelp;
+import com.qxgmat.service.UserPaperService;
+import com.qxgmat.service.UserQuestionService;
 import com.qxgmat.service.UserServiceService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.QuestionFlowService;
@@ -23,8 +35,8 @@ import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.HttpSession;
 import java.text.DateFormat;
 import java.text.ParseException;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/api/textbook")
@@ -44,6 +56,15 @@ public class TextbookController
     private UsersService usersService;
 
     @Autowired
+    private UserPaperService userPaperService;
+
+    @Autowired
+    private UserQuestionService userQuestionService;
+
+    @Autowired
+    private UserReportService userReportService;
+
+    @Autowired
     private UserServiceService userServiceService;
 
     @Autowired
@@ -53,6 +74,12 @@ public class TextbookController
     private QuestionFlowService questionFlowService;
 
     @Autowired
+    private TextbookPaperService textbookPaperService;
+
+    @Autowired
+    private TextbookQuestionService textbookQuestionService;
+
+    @Autowired
     private TextbookLibraryService textbookLibraryService;
 
     @Autowired
@@ -61,6 +88,82 @@ public class TextbookController
     @Autowired
     private TextbookTopicService textbookTopicService;
 
+    @RequestMapping(value = "/progress", method = RequestMethod.GET)
+    @ApiOperation(value = "练习进度", httpMethod = "GET")
+    public Response<List<UserTextbookGroupDto>> progress(HttpSession session) {
+        User user = (User) shiroHelp.getLoginUser();
+
+        TextbookLibrary latest = textbookLibraryService.getLatest();
+        TextbookLibrary second = textbookLibraryService.getSecond();
+        List<UserTextbookGroupDto> p = new ArrayList<>(2);
+
+        for(TextbookLibrary library : new ArrayList<TextbookLibrary>(2){{add(latest);add(second);}}){
+            UserTextbookGroupDto dto = Transform.convert(library, UserTextbookGroupDto.class);
+            // 获取第三层所有题目,并获取题目统计
+            List<TextbookQuestion> list = textbookQuestionService.listByLibrary(library.getId());
+            List<TextbookQuestionRelation> relations = textbookQuestionService.relation(list);
+            dto.setStat(textbookQuestionService.statPaper(list));
+            dto.setQuestionNumber(list.size());
+            Map<Object, UserQuestionStat> userQuestionStatMap = null;
+            if(user != null){
+                Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
+                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
+                userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);
+
+                dto.setUserStat(userQuestionService.statQuestion(userQuestionList));
+
+                if (list.size() > userQuestionStatMap.size()){
+                    dto.setUserNumber(userQuestionStatMap.size());
+                    dto.setMinTimes(0);
+                }else{
+                    int minTimes = 0;
+                    // 统计最小轮的已做题数
+                    for(UserQuestionStat stat : userQuestionStatMap.values()){
+                        if(stat.getUserNumber() < minTimes || minTimes == 0) minTimes = stat.getUserNumber();
+                    }
+                    int userNumber = 0;
+                    for(UserQuestionStat stat : userQuestionStatMap.values()){
+                        if(stat.getUserNumber() > minTimes) userNumber += 1;
+                    }
+                    dto.setMinTimes(minTimes);
+                    dto.setUserNumber(userNumber);
+                }
+            }
+
+            List<UserTextbookGroupExtendDto> childrenDtos = new ArrayList<>(TextbookLogic.all().length);
+            for(TextbookLogic logic : TextbookLogic.all()){
+                UserTextbookGroupExtendDto extendDto = new UserTextbookGroupExtendDto();
+                extendDto.setLogic(logic.key);
+                List<TextbookQuestionRelation> childQuestionList = relations.stream().filter((q)-> logic.contain(QuestionType.ValueOf(q.getQuestion().getQuestionType()))).collect(Collectors.toList());
+                extendDto.setQuestionNumber(childQuestionList.size());
+                if (user != null){
+                    int minTimes = 0;
+                    int userQuestionNumber = 0;
+                    boolean flag = true;
+                    for(TextbookQuestion questionNo : childQuestionList){
+                        UserQuestionStat stat = userQuestionStatMap.get(questionNo.getId());
+                        if (stat == null) {
+                            flag = false;
+                            break;
+                        }
+                        if (stat.getUserNumber() < minTimes || minTimes == 0) minTimes = stat.getUserNumber();
+                    }
+                    if (!flag) minTimes = 0;
+                    for(TextbookQuestion questionNo : childQuestionList){
+                        UserQuestionStat stat = userQuestionStatMap.get(questionNo.getId());
+                        if (stat != null && stat.getUserNumber() > minTimes)  userQuestionNumber += 1;
+                    }
+                    extendDto.setUserNumber(userQuestionNumber);
+                    extendDto.setMinTimes(minTimes);
+                }
+                childrenDtos.add(extendDto);
+            }
+            dto.setChildren(childrenDtos);
+            p.add(dto);
+        }
+
+        return ResponseHelp.success(p);
+    }
 
     @RequestMapping(value = "/info", method = RequestMethod.GET)
     @ApiOperation(value = "机经信息", httpMethod = "GET")
@@ -150,4 +253,57 @@ public class TextbookController
 
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
+
+
+    @RequestMapping(value = "/paper/list", method = RequestMethod.GET)
+    @ApiOperation(value = "机经组卷列表", httpMethod = "GET")
+    public Response<List<UserTextbookPaperDto>> listPaper(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = true) boolean latest,
+            @RequestParam(required = true) String logic,
+            @RequestParam(required = false)  Integer times,
+            HttpSession session) {
+        User user = (User) shiroHelp.getLoginUser();
+        TextbookLibrary library;
+        if (latest){
+            if (user == null){
+                throw new AuthException("请先登录");
+            }
+            if (!userServiceService.hasService(user.getId(), ServiceKey.TEXTBOOK)){
+                throw new ParameterException("没有机经查看权限");
+            }
+            library = textbookLibraryService.getLatest();
+        }else{
+            // 获取往期:倒数第二
+            library = textbookLibraryService.getSecond();
+        }
+        List<TextbookPaper> p = textbookPaperService.list(page, size, library.getId(), user != null ? user.getId():null, TextbookLogic.ValueOf(logic), times);
+        List<UserTextbookPaperDto> pr = Transform.convert(p, UserTextbookPaperDto.class);
+
+        // 获取试卷统计信息
+        Map<Integer, Integer[]> questionNoIdsMap = new HashMap<>();
+        for(TextbookPaper paper : p){
+            questionNoIdsMap.put(paper.getId(), paper.getQuestionNoIds());
+        }
+        Map statMap = textbookQuestionService.statPaperMap(questionNoIdsMap);
+        Transform.combine(pr, statMap, UserTextbookPaperDto.class, "id", "stat");
+
+        if (user != null){
+            // 获取做题记录
+            Collection ids = Transform.getIds(p, TextbookPaper.class, "id");
+            List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.TEXTBOOK, ids, null);
+            Transform.combine(pr, userPaperList, UserTextbookPaperDto.class, "id", "paper", UserPaper.class, "originId", UserPaperBaseExtendDto.class);
+            // 绑定userPaperId,用于关联report
+            Map userPaperMap = Transform.getMap(userPaperList, UserPaper.class, "originId", "id");
+            Transform.combine(pr, userPaperMap, UserTextbookPaperDto.class, "id", "userPaperId");
+
+            // 获取最后一次作业结果
+            Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
+            List<UserReport> reportList = userReportService.listWithLater(paperIds);
+            Transform.combine(pr, reportList, UserTextbookPaperDto.class, "userPaperId", "report", UserReport.class, "paperId", UserReportExtendDto.class);
+        }
+
+        return ResponseHelp.success(pr);
+    }
 }

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

@@ -7,14 +7,18 @@ import com.qxgmat.data.dao.entity.TextbookLibraryHistory;
 public class TextbookLibraryHistoryDto {
     private Integer libraryId;
 
-    private String content;
-
     private String quant = "";
 
+    private String quantContent = "";
+
     private String ir = "";
 
+    private String irContent = "";
+
     private String rc = "";
 
+    private String rcContent = "";
+
     public Integer getLibraryId() {
         return libraryId;
     }
@@ -23,14 +27,6 @@ public class TextbookLibraryHistoryDto {
         this.libraryId = libraryId;
     }
 
-    public String getContent() {
-        return content;
-    }
-
-    public void setContent(String content) {
-        this.content = content;
-    }
-
     public String getQuant() {
         return quant;
     }
@@ -54,4 +50,28 @@ public class TextbookLibraryHistoryDto {
     public void setRc(String rc) {
         this.rc = rc;
     }
+
+    public String getQuantContent() {
+        return quantContent;
+    }
+
+    public void setQuantContent(String quantContent) {
+        this.quantContent = quantContent;
+    }
+
+    public String getIrContent() {
+        return irContent;
+    }
+
+    public void setIrContent(String irContent) {
+        this.irContent = irContent;
+    }
+
+    public String getRcContent() {
+        return rcContent;
+    }
+
+    public void setRcContent(String rcContent) {
+        this.rcContent = rcContent;
+    }
 }

+ 73 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/UserTextbookGroupExtendDto.java

@@ -0,0 +1,73 @@
+package com.qxgmat.dto.extend;
+
+public class UserTextbookGroupExtendDto {
+    private String logic;
+
+    private String title;
+
+    private Integer questionNumber;
+
+    private Integer userNumber;
+
+    private Integer minTimes;
+
+    private Integer userPaperId;
+
+    private UserReportExtendDto report;
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Integer getQuestionNumber() {
+        return questionNumber;
+    }
+
+    public void setQuestionNumber(Integer questionNumber) {
+        this.questionNumber = questionNumber;
+    }
+
+    public Integer getUserNumber() {
+        return userNumber;
+    }
+
+    public void setUserNumber(Integer userNumber) {
+        this.userNumber = userNumber;
+    }
+
+    public Integer getMinTimes() {
+        return minTimes;
+    }
+
+    public void setMinTimes(Integer minTimes) {
+        this.minTimes = minTimes;
+    }
+
+    public UserReportExtendDto getReport() {
+        return report;
+    }
+
+    public void setReport(UserReportExtendDto report) {
+        this.report = report;
+    }
+
+    public Integer getUserPaperId() {
+        return userPaperId;
+    }
+
+    public void setUserPaperId(Integer userPaperId) {
+        this.userPaperId = userPaperId;
+    }
+
+    public String getLogic() {
+        return logic;
+    }
+
+    public void setLogic(String logic) {
+        this.logic = logic;
+    }
+}

+ 18 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/PaperBaseDto.java

@@ -11,6 +11,8 @@ public class PaperBaseDto {
     private String title;
     private Integer time;
     private Integer questionNumber;
+    private Integer times;
+    private Integer isAdapt;
 
     public Integer getQuestionNumber() {
         return questionNumber;
@@ -59,4 +61,20 @@ public class PaperBaseDto {
     public void setTime(Integer time) {
         this.time = time;
     }
+
+    public Integer getIsAdapt() {
+        return isAdapt;
+    }
+
+    public void setIsAdapt(Integer isAdapt) {
+        this.isAdapt = isAdapt;
+    }
+
+    public Integer getTimes() {
+        return times;
+    }
+
+    public void setTimes(Integer times) {
+        this.times = times;
+    }
 }

+ 11 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserStudyDetailDto.java

@@ -19,6 +19,9 @@ public class UserStudyDetailDto {
     // 各部分时长
     private List<UserStudyExtendDto> categorys;
 
+    // 全站平均时长
+    private Integer avgTime;
+
     public Integer getDays() {
         return days;
     }
@@ -50,4 +53,12 @@ public class UserStudyDetailDto {
     public void setCategorys(List<UserStudyExtendDto> categorys) {
         this.categorys = categorys;
     }
+
+    public Integer getAvgTime() {
+        return avgTime;
+    }
+
+    public void setAvgTime(Integer avgTime) {
+        this.avgTime = avgTime;
+    }
 }

+ 118 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserTextbookGroupDto.java

@@ -0,0 +1,118 @@
+package com.qxgmat.dto.response;
+
+import com.qxgmat.data.inline.PaperStat;
+import com.qxgmat.data.inline.UserQuestionStat;
+import com.qxgmat.dto.extend.UserTextbookGroupExtendDto;
+
+import java.util.List;
+public class UserTextbookGroupDto {
+    private Integer id;
+
+    private String titleZh;
+
+    private String titleEn;
+
+    private String description;
+
+    private String extend;
+
+    private Integer questionNumber;
+
+    private Integer minTimes;
+
+    private Integer userNumber;
+
+    private PaperStat stat;
+
+    private UserQuestionStat userStat;
+
+    private List<UserTextbookGroupExtendDto> children;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getTitleZh() {
+        return titleZh;
+    }
+
+    public void setTitleZh(String titleZh) {
+        this.titleZh = titleZh;
+    }
+
+    public String getTitleEn() {
+        return titleEn;
+    }
+
+    public void setTitleEn(String titleEn) {
+        this.titleEn = titleEn;
+    }
+
+    public String getExtend() {
+        return extend;
+    }
+
+    public void setExtend(String extend) {
+        this.extend = extend;
+    }
+
+    public Integer getQuestionNumber() {
+        return questionNumber;
+    }
+
+    public void setQuestionNumber(Integer questionNumber) {
+        this.questionNumber = questionNumber;
+    }
+
+    public Integer getUserNumber() {
+        return userNumber;
+    }
+
+    public void setUserNumber(Integer userNumber) {
+        this.userNumber = userNumber;
+    }
+
+    public PaperStat getStat() {
+        return stat;
+    }
+
+    public void setStat(PaperStat stat) {
+        this.stat = stat;
+    }
+
+    public UserQuestionStat getUserStat() {
+        return userStat;
+    }
+
+    public void setUserStat(UserQuestionStat userStat) {
+        this.userStat = userStat;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getMinTimes() {
+        return minTimes;
+    }
+
+    public void setMinTimes(Integer minTimes) {
+        this.minTimes = minTimes;
+    }
+
+    public List<UserTextbookGroupExtendDto> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<UserTextbookGroupExtendDto> children) {
+        this.children = children;
+    }
+}

+ 70 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserTextbookPaperDto.java

@@ -0,0 +1,70 @@
+package com.qxgmat.dto.response;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.TextbookPaper;
+import com.qxgmat.data.inline.PaperStat;
+import com.qxgmat.dto.extend.UserPaperBaseExtendDto;
+import com.qxgmat.dto.extend.UserReportExtendDto;
+
+@Dto(entity = TextbookPaper.class)
+public class UserTextbookPaperDto {
+    private Integer id;
+
+    private String title;
+
+    private PaperStat stat;
+
+    private UserPaperBaseExtendDto paper;
+
+    private UserReportExtendDto report;
+
+    private Integer userPaperId;
+
+    public PaperStat getStat() {
+        return stat;
+    }
+
+    public void setStat(PaperStat stat) {
+        this.stat = stat;
+    }
+
+    public UserPaperBaseExtendDto getPaper() {
+        return paper;
+    }
+
+    public void setPaper(UserPaperBaseExtendDto paper) {
+        this.paper = paper;
+    }
+
+    public UserReportExtendDto getReport() {
+        return report;
+    }
+
+    public void setReport(UserReportExtendDto report) {
+        this.report = report;
+    }
+
+    public Integer getUserPaperId() {
+        return userPaperId;
+    }
+
+    public void setUserPaperId(Integer userPaperId) {
+        this.userPaperId = userPaperId;
+    }
+
+    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;
+    }
+}

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

@@ -32,8 +32,12 @@ public class AiHelp {
         // City类可用于IPDB格式的IPv4免费库,IPv4与IPv6的每周高级版、每日标准版、每日高级版、每日专业版、每日旗舰版
         if (path.startsWith(".")){
             // 相对路径,从resource中获取
-            logger.debug(this.getClass().getClassLoader().getResource(path).toString());
-            cityDb = new City(this.getClass().getClassLoader().getResourceAsStream(path));
+            logger.debug("{}, {}", path, this.getClass().getClassLoader().getResource(path));
+            try {
+                cityDb = new City(this.getClass().getClassLoader().getResourceAsStream(path));
+            }catch (Exception e){
+                logger.error(e.getLocalizedMessage());
+            }
         }else{
             cityDb = new City(path);
         }

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

@@ -14,6 +14,8 @@ import com.qxgmat.data.dao.UserQuestionMapper;
 import com.qxgmat.data.dao.entity.UserQuestion;
 import com.qxgmat.data.dao.entity.UserReport;
 import com.qxgmat.data.inline.UserQuestionStat;
+import com.qxgmat.data.relation.UserQuestionRelationMapper;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import com.qxgmat.service.annotation.InitQuestion;
 import com.qxgmat.service.inline.QuestionNoService;
 import com.qxgmat.service.inline.SentenceQuestionService;
@@ -34,6 +36,9 @@ public class UserQuestionService extends AbstractService {
     @Resource
     private UserQuestionMapper userQuestionMapper;
 
+    @Resource
+    private UserQuestionRelationMapper userQuestionRelationMapper;
+
     /**
      * 用户错题记录:相同题目的最后一次做题记录及其编号
      * @param page
@@ -220,6 +225,36 @@ public class UserQuestionService extends AbstractService {
         return select(userQuestionMapper, example);
     }
 
+
+    /**
+     * 统计用户的做题记录
+     * @param userId
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public UserRecordStatRelation stat(Integer userId, String startTime, String endTime){
+        List<UserRecordStatRelation> list = userQuestionRelationMapper.stat(userId, startTime, endTime);
+        if (list!=null && list.size() > 0){
+            return list.get(1);
+        }
+        return null;
+    }
+
+    /**
+     * 统计全站的做题记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public UserRecordStatRelation statAvg(String startTime, String endTime){
+        List<UserRecordStatRelation> list = userQuestionRelationMapper.statAvg(startTime, endTime);
+        if (list!=null && list.size() > 0){
+            return list.get(1);
+        }
+        return null;
+    }
+
     public UserQuestion add(UserQuestion question){
         int result = insert(userQuestionMapper, question);
         question = one(userQuestionMapper, question.getId());

+ 17 - 2
server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java

@@ -4,7 +4,9 @@ import com.nuliji.tools.Tools;
 import com.qxgmat.data.constants.enums.module.ProductType;
 import com.qxgmat.data.dao.entity.Course;
 import com.qxgmat.data.dao.entity.UserOrderRecord;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import com.qxgmat.service.inline.CourseService;
+import com.qxgmat.service.inline.UserCourseRecordService;
 import com.qxgmat.service.inline.UserOrderRecordService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -20,6 +22,9 @@ public class CourseExtendService {
     @Autowired
     private UserOrderRecordService userOrderRecordService;
 
+    @Autowired
+    private UserCourseRecordService userCourseRecordService;
+
     /**
      * 计算vs课程有效期
      * @param vsNumber
@@ -53,7 +58,17 @@ public class CourseExtendService {
      * @param userId
      * @return
      */
-    public Integer studyTime(Integer userId){
-        return 0;
+    public Integer studyTime(Integer userId, Date startTime, Date endTime){
+        UserRecordStatRelation record = userCourseRecordService.stat(userId, startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
+    }
+
+    /**
+     * 全站平均听课时间
+     * @return
+     */
+    public Integer studyAvgTime(Date startTime, Date endTime){
+        UserRecordStatRelation record = userCourseRecordService.statAvg(startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
     }
 }

+ 64 - 4
server/gateway-api/src/main/java/com/qxgmat/service/extend/ExaminationService.java

@@ -5,13 +5,15 @@ import com.github.pagehelper.Page;
 import com.nuliji.tools.AbstractService;
 import com.nuliji.tools.PageResult;
 import com.nuliji.tools.Transform;
+import com.nuliji.tools.exception.ParameterException;
+import com.nuliji.tools.exception.SystemException;
 import com.qxgmat.data.constants.enums.QuestionDifficult;
 import com.qxgmat.data.constants.enums.QuestionSubject;
 import com.qxgmat.data.constants.enums.QuestionType;
+import com.qxgmat.data.constants.enums.ServiceKey;
+import com.qxgmat.data.constants.enums.module.PaperOrigin;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
-import com.qxgmat.data.dao.entity.ExaminationPaper;
-import com.qxgmat.data.dao.entity.ExaminationStruct;
-import com.qxgmat.data.dao.entity.UserQuestion;
+import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.relation.ExaminationPaperRelationMapper;
 import com.qxgmat.data.relation.QuestionNoRelationMapper;
 import com.qxgmat.data.relation.UserPaperRelationMapper;
@@ -178,7 +180,7 @@ public class ExaminationService extends AbstractService {
     }
 
     /**
-     * 查找练习组卷
+     * 查找模考组卷
      * @param page
      * @param size
      * @param structId
@@ -199,6 +201,64 @@ public class ExaminationService extends AbstractService {
     }
 
     /**
+     * cat模考是否已经完成
+     * @param userId
+     * @return
+     */
+    public boolean isFinishCat(Integer userId){
+        ExaminationStruct struct = getCat();
+        List<ExaminationPaper> paperList = examinationPaperService.listByTwo(struct.getId());
+        Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
+        List<UserPaper> userPaperList = userPaperService.listWithOrigin(userId, PaperOrigin.EXAMINATION, ids, null);
+        if (paperList.size() != userPaperList.size()){
+            return false;
+        }
+        Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
+        List<UserReport> reportList = userReportService.listWithLater(paperIds);
+        for(UserReport report: reportList){
+            if(report.getIsFinish() == 0){
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 重置Cat模考系列
+     * @param userId
+     * @return
+     */
+    @Transactional
+    public Boolean resetCat(Integer userId, boolean force){
+        ExaminationStruct struct = getCat();
+        List<ExaminationPaper> paperList = examinationPaperService.listByTwo(struct.getId());
+        Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
+        List<UserPaper> userPaperList = userPaperService.listWithOrigin(userId, PaperOrigin.EXAMINATION, ids, null);
+        if (!force && paperList.size() != userPaperList.size()){
+            throw new ParameterException("未完成所有");
+        }
+        Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
+        if(!force){
+            List<UserReport> reportList = userReportService.listWithLater(paperIds);
+            for(UserReport report: reportList){
+                if(report.getIsFinish() == 0){
+                    throw new ParameterException("未完成所有");
+                }
+            }
+        }
+        return userPaperService.reset(paperIds, userId);
+    }
+
+    public ExaminationStruct getCat(){
+        List<ExaminationStruct> list = examinationStructService.main();
+        for (ExaminationStruct struct : list){
+            if (struct.getLevel() == 1) continue;
+            if (struct.getExtend().equals(ServiceKey.QX_CAT.key)) return struct;
+        }
+        throw new SystemException("没有找到cat模考节点");
+    }
+
+    /**
      * 获取下一阶段难度分
      * @param currentLevel
      * @param number

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

@@ -61,6 +61,9 @@ public class OrderFlowService {
     @Resource
     private UsersService usersService;
 
+    @Resource
+    private ExaminationService examinationService;
+
     private TradeService tradeService;
 
     @Resource
@@ -449,19 +452,22 @@ public class OrderFlowService {
                         .build();
                 userService = userServiceService.add(userService);
             }else{
-                if (userService.getExpireTime() != null && userService.getExpireTime().before(time)){
+                if (serviceKey == ServiceKey.QX_CAT){
+                    // cat逻辑独立
+                    if (userService.getExpireTime().after(time)){
+                        // 有效期内
+                        if (userService.getIsReset() == 0) throw new ParameterException("已开通当前服务");
+                        // 第二次还未做完
+                        if (userService.getIsReset() == 1 && examinationService.isFinishCat(record.getUserId()))  throw new ParameterException("已开通当前服务");
+                    }
+                    // 重置
+                    userService.setIsReset(0);
+                    examinationService.resetCat(record.getUserId(), true);
+                }else if (userService.getExpireTime() != null && userService.getExpireTime().before(time)){
                     // 已到期 - 续期
                     userService.setStartTime(startTime);
                     userService.setExpireTime(endTime);
                 }else{
-                    if (serviceKey == ServiceKey.QX_CAT){
-                        if (userService.getIsReset() == 0){
-                            // 未到期 - 报错
-                            throw new ParameterException("已开通当前服务");
-                        }
-                        // 重置
-                        userService.setIsReset(0);
-                    }
                     // 未到期 - 延长有效期
                     startTime = userService.getExpireTime();
                     if (expireDay > 0){
@@ -850,7 +856,7 @@ public class OrderFlowService {
         UserOrderRecord record = UserOrderRecord.builder()
                 .userId(userId)
                 .service(ServiceKey.VIP.key)
-                .param(ServiceVipKey.MONTH6.key)
+                .param(ServiceVipKey.MONTH3.key)
                 .source(RecordSource.REAL.key)
                 .build();
         // 虚拟order

+ 13 - 9
server/gateway-api/src/main/java/com/qxgmat/service/extend/PreviewService.java

@@ -57,6 +57,9 @@ public class PreviewService extends AbstractService {
     @Resource
     private UserCourseProgressService userCourseProgressService;
 
+    @Resource
+    private UserCourseService userCourseService;
+
     /**
      * 获取用户分组作业列表
      * @param userId
@@ -67,7 +70,7 @@ public class PreviewService extends AbstractService {
         Map<Object, Collection<UserPreviewPaperRelation>> relationMap = new HashMap<>();
         for(Object id : recordIds){
             Integer recordId = (Integer)id;
-            relationMap.put(recordId, list(userId, recordId, null, 0, top));
+            relationMap.put(recordId, list(1, top, userId, recordId, null, 0));
         }
         return relationMap;
     }
@@ -80,7 +83,7 @@ public class PreviewService extends AbstractService {
      * @param times
      * @return
      */
-    public List<UserPreviewPaperRelation> list(Integer recordId, Integer userId, String endTime, Integer times, Integer top){
+    public List<UserPreviewPaperRelation> list(int page, int size, Integer recordId, Integer userId, String endTime, Integer times){
         // 根据不同的课程,执行不同的查询方案
         UserOrderRecord record = userOrderRecordService.get(recordId);
         if (record == null){
@@ -100,18 +103,19 @@ public class PreviewService extends AbstractService {
                 // 查询记录对应预约情况
                 List<UserCourseAppointment> appointmentList = userCourseAppointmentService.listByRecord(recordId);
                 Collection appointmentIds = Transform.getIds(appointmentList, UserCourseAppointment.class, "id");
-                previewAssignList = previewAssignService.listByAppointment(appointmentIds, endTime, top);
+                previewAssignList = previewAssignService.listByAppointment(page, size, course.getId(), appointmentIds, userId, endTime, times);
                 break;
             case ONLINE:
                 // 查询记录对应时间段内
-                previewAssignList = previewAssignService.listByTime(record.getTimeId(), endTime, top);
+                previewAssignList = previewAssignService.listByTime(page, size, course.getId(), record.getTimeId(), userId, endTime, times);
                 replaceTitle(previewAssignList);
                 break;
             case VIDEO:
                 // 获取课时,并关联当前记录的paper
                 if (endTime != null){
                     // 无论列表还是卡片,都只显示2条
-                    top = 2;
+                    size = 2;
+                    page = 1;
                     List<CourseNo> courseNoList = courseNoService.allCourse(course.getId());
                     // 查询课时进度
                     List<UserCourseProgress> progressList =userCourseProgressService.listCourse(record.getId(), course.getId());
@@ -131,12 +135,12 @@ public class PreviewService extends AbstractService {
                         }
                         break;
                     }
-                    int max = min+top -1;
+                    int max = min+size -1;
                     int finalMin = min;
                     List<CourseNo> select = courseNoList.stream().filter(row->row.getNo() >= finalMin && row.getNo()<=max).collect(Collectors.toList());
                     previewAssignList = previewAssignService.listByCourseNos(course.getId(), getIds(select, CourseNo.class, "id"));
                 }else{
-                    previewAssignList = previewAssignService.listByCourse(course.getId());
+                    previewAssignList = previewAssignService.listByCourse(page, size, course.getId(), userId, times);
                 }
                 replaceTitle(previewAssignList);
                 break;
@@ -176,8 +180,8 @@ public class PreviewService extends AbstractService {
                 return timeRecord.getId();
             case VIDEO:
                 // 获取当前该课程记录
-                UserOrderRecord record = userOrderRecordService.getByUserAndCourse(userId, assign.getCourseId(), true);
-                return record != null ? record.getId() : 0;
+                UserCourse record = userCourseService.getCourse(userId, assign.getCourseId());
+                return record != null ? record.getRecordId() : 0;
             default:
                 return 0;
         }

+ 11 - 22
server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java

@@ -187,6 +187,7 @@ public class QuestionFlowService {
         initPaperCallback.put(PaperModule.EXAMINATION, (userPaper, id)->{
             ExaminationPaper paper = examinationPaperService.get(id);
             userPaper.setTitle(paper.getTitle());
+            userPaper.setIsAdapt(paper.getIsAdapt());
         });
 
         initReportCallback.put(PaperModule.EXERCISE, (report, paper)->{
@@ -649,6 +650,7 @@ public class QuestionFlowService {
         // 分析做题结果
         StatReport callback = finishCallback.get(PaperModule.ValueOf(userReport.getPaperModule()));
         callback.callback(userReport, userQuestionList);
+        userReport.setIsStat(1);
         userReportService.edit(userReport);
 
         // 统计: 更新对应paper记录
@@ -671,42 +673,29 @@ public class QuestionFlowService {
         // 分析做题结果
         StatReport callback = finishCallback.get(PaperModule.ValueOf(userReport.getPaperModule()));
         callback.callback(userReport, userQuestionList);
+        userReport.setIsStat(1);
         userReportService.edit(userReport);
 
         return userPaperService.reset(userPaperId, userId);
     }
 
     /**
-     * 重置模考系列
-     * @param structId
+     * 累计考试学习时间
      * @param userId
      * @return
      */
-    @Transactional
-    public Boolean restartExamination(Integer structId, Integer userId){
-        List<ExaminationPaper> paperList = examinationPaperService.listByTwo(structId);
-        Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
-        List<UserPaper> userPaperList = userPaperService.listWithOrigin(userId, PaperOrigin.EXAMINATION, ids, null);
-        if (paperList.size() != userPaperList.size()){
-            throw new ParameterException("未完成所有");
-        }
-        Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
-        List<UserReport> reportList = userReportService.listWithLater(paperIds);
-        for(UserReport report: reportList){
-            if(report.getIsFinish() == 0){
-                throw new ParameterException("未完成所有");
-            }
-        }
-        return userPaperService.reset(paperIds, userId);
+    public Integer studyTime(Integer userId, Date startTime, Date endTime){
+        UserRecordStatRelation record = userQuestionService.stat(userId, startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
     }
 
     /**
-     * 累计考试学习时间
-     * @param userId
+     * 平均考试时间
      * @return
      */
-    public Integer studyTime(Integer userId, Date startTime, Date endTime){
-        return 0;
+    public Integer studyAvgTime(Date startTime, Date endTime){
+        UserRecordStatRelation record = userQuestionService.statAvg(startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
     }
 
     /**

+ 32 - 6
server/gateway-api/src/main/java/com/qxgmat/service/extend/SentenceService.java

@@ -7,12 +7,10 @@ import com.qxgmat.data.constants.enums.logic.SentenceLogic;
 import com.qxgmat.data.dao.entity.*;
 import com.qxgmat.data.relation.UserReportRelationMapper;
 import com.qxgmat.data.relation.entity.SentenceQuestionRelation;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import com.qxgmat.data.relation.entity.UserSentencePaperRelation;
 import com.qxgmat.service.UserPaperService;
-import com.qxgmat.service.inline.SentencePaperService;
-import com.qxgmat.service.inline.QuestionService;
-import com.qxgmat.service.inline.SentenceQuestionService;
-import com.qxgmat.service.inline.UserReportService;
+import com.qxgmat.service.inline.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -20,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 
 @Service
@@ -43,6 +42,12 @@ public class SentenceService {
     @Resource
     private UserPaperService userPaperService;
 
+    @Resource
+    private UserSentenceRecordService userSentenceRecordService;
+
+    @Resource
+    private SentenceCodeService sentenceCodeService;
+
 
     /**
      * 添加长难句题目:加入题库,关联题目,并关联长难句paper
@@ -177,7 +182,28 @@ public class SentenceService {
      * @param userId
      * @return
      */
-    public Integer studyTime(Integer userId){
-        return 0;
+    public Integer studyTime(Integer userId, Date startTime, Date endTime){
+        UserRecordStatRelation record = userSentenceRecordService.stat(userId, startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
+    }
+
+    /**
+     * 全站平均时间
+     * @return
+     */
+    public Integer studyAvgTime(Date startTime, Date endTime){
+        UserRecordStatRelation record = userSentenceRecordService.statAvg(startTime.toString(), endTime.toString());
+        return record == null ? record.getUserTime() : 0;
+    }
+
+    /**
+     * 激活长难句
+     * @param userId
+     * @param code
+     */
+    public void active(Integer userId, String code){
+        if (sentenceCodeService.isActive(userId) == null){
+            sentenceCodeService.active(userId, code);
+        }
     }
 }

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

@@ -83,7 +83,7 @@ public class TextbookService {
 
         relation.setTitle(textbookQuestionService.generateTitle(prefixTitle, relation.getNo()));
         // 保存年份
-        relation.setTime(String.valueOf(Tools.year(library.getStartDate())));
+        relation.setYear(String.valueOf(Tools.year(library.getStartDate())));
 
         TextbookQuestion textbookQuestion =  textbookQuestionService.add(relation);
 
@@ -128,7 +128,7 @@ public class TextbookService {
             }
             paper = textbookPaperService.add(TextbookPaper.builder()
                     .logic(SentenceLogic.NO.key)
-                    .time(question.getTime())
+                    .year(question.getYear())
                     .libraryId(question.getLibraryId())
                     .title(textbookPaperService.generateTitle(prefixTitle, paperLength, no, 1))
                     .no(no+1)

+ 27 - 42
server/gateway-api/src/main/java/com/qxgmat/service/inline/PreviewAssignService.java

@@ -2,6 +2,7 @@ package com.qxgmat.service.inline;
 
 import com.github.pagehelper.Page;
 import com.nuliji.tools.AbstractService;
+import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
@@ -11,6 +12,7 @@ import com.qxgmat.data.dao.PreviewAssignMapper;
 import com.qxgmat.data.dao.PreviewPaperMapper;
 import com.qxgmat.data.dao.entity.PreviewAssign;
 import com.qxgmat.data.dao.entity.PreviewPaper;
+import com.qxgmat.data.relation.PreviewAssignRelationMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -27,6 +29,9 @@ public class PreviewAssignService extends AbstractService {
     @Resource
     private PreviewAssignMapper previewAssignMapper;
 
+    @Resource
+    private PreviewAssignRelationMapper previewAssignRelationMapper;
+
     public Page<PreviewAssign> listAdmin(int page, int size, Integer paperId, String order, DirectionStatus direction){
         Example example = new Example(PreviewAssign.class);
         if(paperId != null){
@@ -73,13 +78,13 @@ public class PreviewAssignService extends AbstractService {
      * @param courseId
      * @return
      */
-    public List<PreviewAssign> listByCourse(Integer courseId){
-        Example example = new Example(PreviewAssign.class);
-        example.and(
-                example.createCriteria()
-                        .andEqualTo("courseId", courseId)
-        );
-        return select(previewAssignMapper, example);
+    public List<PreviewAssign> listByCourse(int page, int size, Integer courseId, Integer userId, Integer times){
+        Page<PreviewAssign> p = page(()->previewAssignRelationMapper.listByCourse(courseId, userId, times), page, size);
+        Collection ids = Transform.getIds(p, PreviewAssign.class, "id");
+        // 获取详细数据
+        List<PreviewAssign> list = select(ids);
+        Transform.replace(p, list, PreviewAssign.class, "id");
+        return p;
     }
 
     /**
@@ -99,26 +104,16 @@ public class PreviewAssignService extends AbstractService {
 
     /**
      * 用户查询1v1相对的预约作业
-     * @param ids
+     * @param appointmentIds
      * @return
      */
-    public List<PreviewAssign> listByAppointment(Collection ids, String endTime, Integer top){
-        Example example = new Example(PreviewAssign.class);
-        example.and(
-                example.createCriteria()
-                        .andIn("courseAppointment", ids)
-        );
-        if (endTime != null){
-            example.and(
-                    example.createCriteria()
-                        .andLessThanOrEqualTo("endTime", endTime)
-            );
-        }
-        example.orderBy("startTime").asc();
-        if (top != null){
-            return select(previewAssignMapper, example, 1, top);
-        }
-        return select(previewAssignMapper, example);
+    public List<PreviewAssign> listByAppointment(int page, int size, Integer courseId, Collection appointmentIds, Integer userId, String endTime, Integer times){
+        Page<PreviewAssign> p = page(()->previewAssignRelationMapper.listByAppointment(courseId, appointmentIds, userId, endTime, times), page, size);
+        Collection ids = Transform.getIds(p, PreviewAssign.class, "id");
+        // 获取详细数据
+        List<PreviewAssign> list = select(ids);
+        Transform.replace(p, list, PreviewAssign.class, "id");
+        return p;
     }
 
     public boolean removeCourseAppointment(Integer courseId, Integer id){
@@ -136,23 +131,13 @@ public class PreviewAssignService extends AbstractService {
      * @param timeId
      * @return
      */
-    public List<PreviewAssign> listByTime(Integer timeId, String endTime, Integer top){
-        Example example = new Example(PreviewAssign.class);
-        example.and(
-                example.createCriteria()
-                        .andEqualTo("courseTime", timeId)
-        );
-        if (endTime != null){
-            example.and(
-                    example.createCriteria()
-                            .andLessThanOrEqualTo("endTime", endTime)
-            );
-        }
-        example.orderBy("startTime").asc();
-        if (top != null){
-            return select(previewAssignMapper, example, 1, top);
-        }
-        return select(previewAssignMapper, example);
+    public List<PreviewAssign> listByTime(int page, int size, Integer courseId, Integer timeId, Integer userId, String endTime, Integer times){
+        Page<PreviewAssign> p = page(()->previewAssignRelationMapper.listByTime(courseId, timeId, userId, endTime, times), page, size);
+        Collection ids = Transform.getIds(p, PreviewAssign.class, "id");
+        // 获取详细数据
+        List<PreviewAssign> list = select(ids);
+        Transform.replace(p, list, PreviewAssign.class, "id");
+        return p;
     }
 
     public boolean removeCourseTime(Integer courseId, Integer timeId){

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

@@ -2,12 +2,15 @@ package com.qxgmat.service.inline;
 
 import com.github.pagehelper.Page;
 import com.nuliji.tools.AbstractService;
+import com.nuliji.tools.PageResult;
+import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.exception.SystemException;
 import com.nuliji.tools.mybatis.Example;
 import com.qxgmat.data.constants.enums.logic.TextbookLogic;
 import com.qxgmat.data.dao.TextbookPaperMapper;
 import com.qxgmat.data.dao.entity.*;
+import com.qxgmat.data.relation.TextbookPaperRelationMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -22,6 +25,31 @@ public class TextbookPaperService extends AbstractService {
     @Resource
     private TextbookPaperMapper textbookPaperMapper;
 
+    @Resource
+    private TextbookPaperRelationMapper textbookPaperRelationMapper;
+
+    /**
+     * 查找组卷
+     * @param page
+     * @param size
+     * @param libraryId
+     * @param userId
+     * @param logic
+     * @param times
+     * @return
+     */
+    public Page<TextbookPaper> list(int page, int size, Number libraryId, Number userId, TextbookLogic logic, Integer times){
+        String logicKey = logic != null ? logic.key : "";
+        Page<TextbookPaper> p = page(()->{
+            textbookPaperRelationMapper.listWithUser(libraryId, userId, logicKey, times);
+        },page, size);
+
+        Collection ids = Transform.getIds(p, TextbookPaper.class, "id");
+        // 获取详细数据
+        List<TextbookPaper> list = select(ids);
+        Transform.replace(p, list, TextbookPaper.class, "id");
+        return p;
+    }
     /**
      * 更新所有组卷title
      * @param libraryId
@@ -68,22 +96,6 @@ public class TextbookPaperService extends AbstractService {
         return page(()->select(textbookPaperMapper, example), page, size);
     }
 
-    /**
-     * 获取可用的试卷列表:单个分组逻辑
-     * @param logic
-     * @return
-     */
-    public List<TextbookPaper> listByLogic(TextbookLogic logic){
-        Example example = new Example(SentencePaper.class);
-        example.and(
-                example.createCriteria()
-                        .andEqualTo("logic", logic.key)
-                        .andEqualTo("status", 1)
-        );
-        example.orderBy("no").asc();
-        return select(textbookPaperMapper, example);
-    }
-
     public List<TextbookPaper> listByQuestion(TextbookQuestion question){
         Example example = new Example(SentencePaper.class);
         example.and(

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

@@ -119,6 +119,20 @@ public class TextbookQuestionService extends AbstractService {
     }
 
     /**
+     * 获取换库表中的题目
+     * @param libraryId
+     * @return
+     */
+    public List<TextbookQuestion> listByLibrary(Integer libraryId){
+        Example example = new Example(TextbookQuestion.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("libraryId", libraryId)
+        );
+        return select(textbookQuestionMapper, example);
+    }
+
+    /**
      * 获取换库表中的最后一题
      * @param libraryId
      * @return
@@ -232,7 +246,7 @@ public class TextbookQuestionService extends AbstractService {
     }
 
     /**
-     * 根据长难句题目关系,获取完整题目:列表
+     * 根据题目关系,获取完整题目:列表
      * @param p
      * @return
      */
@@ -246,7 +260,7 @@ public class TextbookQuestionService extends AbstractService {
     }
 
     /**
-     * 根据长难句题目关系,获取完整题目:单个
+     * 根据题目关系,获取完整题目:单个
      * @param p
      * @return
      */

+ 33 - 3
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserCourseRecordService.java

@@ -8,8 +8,9 @@ import com.nuliji.tools.mybatis.Example;
 import com.qxgmat.data.dao.UserCourseRecordMapper;
 import com.qxgmat.data.dao.entity.UserCourseRecord;
 import com.qxgmat.data.relation.UserCourseRecordRelationMapper;
-import com.qxgmat.data.relation.entity.UserCourseStatRelation;
+import com.qxgmat.data.relation.entity.UserModuleRecordStatRelation;
 import com.qxgmat.data.relation.entity.UserRankStatRelation;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -28,7 +29,6 @@ public class UserCourseRecordService extends AbstractService {
     @Resource
     private UserCourseRecordRelationMapper userCourseRecordRelationMapper;
 
-
     /**
      * 获取指定时间内的听课记录
      * @param userId
@@ -46,6 +46,36 @@ public class UserCourseRecordService extends AbstractService {
         );
         return select(userCourseRecordMapper, example);
     }
+
+    /**
+     * 统计用户的听课记录
+     * @param userId
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public UserRecordStatRelation stat(Integer userId, String startTime, String endTime){
+        List<UserRecordStatRelation> list = userCourseRecordRelationMapper.stat(userId, startTime, endTime);
+        if (list!=null && list.size() > 0){
+            return list.get(1);
+        }
+        return null;
+    }
+
+    /**
+     * 统计全站的听课记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public UserRecordStatRelation statAvg(String startTime, String endTime){
+        List<UserRecordStatRelation> list = userCourseRecordRelationMapper.statAvg(startTime, endTime);
+        if (list!=null && list.size() > 0){
+            return list.get(1);
+        }
+        return null;
+    }
+
     /**
      * 统计不同题型用户的听课记录
      * @param userId
@@ -53,7 +83,7 @@ public class UserCourseRecordService extends AbstractService {
      * @param endTime
      * @return
      */
-    public List<UserCourseStatRelation> statGroupType(Integer userId, String startTime, String endTime){
+    public List<UserModuleRecordStatRelation> statGroupType(Integer userId, String startTime, String endTime){
         return userCourseRecordRelationMapper.statGroupType(userId, startTime, endTime);
     }
 

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

@@ -9,6 +9,7 @@ import com.nuliji.tools.mybatis.Example;
 import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.constants.enums.module.CourseModule;
 import com.qxgmat.data.constants.enums.module.ProductType;
+import com.qxgmat.data.constants.enums.module.VsCourseType;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.dao.UserOrderRecordMapper;
 import com.qxgmat.data.dao.entity.UserOrder;
@@ -313,6 +314,32 @@ public class UserOrderRecordService extends AbstractService {
     }
 
     /**
+     * 列出用户学习记录
+     * @param page
+     * @param size
+     * @param courseId
+     * @param userId
+     * @return
+     */
+    public Page<UserOrderRecord> listWithVs(int page, int size, VsCourseType vsType, Integer courseId, Integer userId, String order, DirectionStatus direction){
+        if(order == null || order.isEmpty()) order = "id";
+        if (direction == null){
+            direction = DirectionStatus.DESC;
+        }
+        String finalOrder = order;
+        DirectionStatus finalDirection = direction;
+        Page<UserOrderRecord> p = page(
+                ()-> userOrderRecordRelationMapper.listWithVs(vsType != null ? vsType.key : null, courseId, userId, finalOrder, finalDirection.key)
+                , page, size);
+
+        Collection ids = Transform.getIds(p, UserOrderRecord.class, "id");
+        Transform.replace(p, select(ids), UserOrderRecord.class, "id");
+
+        return p;
+    }
+
+
+    /**
      * 获取最大vs课程编号
      * @param userId
      * @return

+ 16 - 6
server/gateway-api/src/main/java/com/qxgmat/service/inline/UserSentenceRecordService.java

@@ -5,14 +5,11 @@ 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.dao.UserCourseRecordMapper;
 import com.qxgmat.data.dao.UserSentenceRecordMapper;
 import com.qxgmat.data.dao.entity.UserCourseRecord;
 import com.qxgmat.data.dao.entity.UserSentenceRecord;
-import com.qxgmat.data.relation.UserCourseRecordRelationMapper;
 import com.qxgmat.data.relation.UserSentenceRecordRelationMapper;
-import com.qxgmat.data.relation.entity.UserCourseStatRelation;
-import com.qxgmat.data.relation.entity.UserSentenceStatRelation;
+import com.qxgmat.data.relation.entity.UserRecordStatRelation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -38,8 +35,8 @@ public class UserSentenceRecordService extends AbstractService {
      * @param endTime
      * @return
      */
-    public UserSentenceStatRelation stat(Integer userId, String startTime, String endTime){
-        List<UserSentenceStatRelation> list = userSentenceRecordRelationMapper.stat(userId, startTime, endTime);
+    public UserRecordStatRelation stat(Integer userId, String startTime, String endTime){
+        List<UserRecordStatRelation> list = userSentenceRecordRelationMapper.stat(userId, startTime, endTime);
         if (list!=null && list.size() > 0){
             return list.get(1);
         }
@@ -47,6 +44,19 @@ public class UserSentenceRecordService extends AbstractService {
     }
 
     /**
+     * 统计全站的阅读记录
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    public UserRecordStatRelation statAvg(String startTime, String endTime){
+        List<UserRecordStatRelation> list = userSentenceRecordRelationMapper.statAvg(startTime, endTime);
+        if (list!=null && list.size() > 0){
+            return list.get(1);
+        }
+        return null;
+    }
+    /**
      * 获取指定时间内的阅读记录
      * @param userId
      * @param startTime

+ 6 - 0
server/tools/src/main/java/com/nuliji/tools/Tools.java

@@ -69,6 +69,12 @@ public class Tools {
         return calendar.get(Calendar.DAY_OF_MONTH);
     }
 
+    public static int getDayOfWeek(Date date){
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        return calendar.get(Calendar.DAY_OF_WEEK);
+    }
+
     public static byte[] getMd5(String str) {
         try {
             MessageDigest md = MessageDigest.getInstance("MD5");