Browse Source

feat(server): 订单流程

Go 5 years ago
parent
commit
86a500b6b6
100 changed files with 3501 additions and 819 deletions
  1. 8 4
      front/project/Constant.js
  2. 67 21
      front/project/admin/routes/course/experience/page.js
  3. 22 5
      front/project/admin/routes/course/experienceDetail/page.js
  4. 19 12
      front/project/admin/routes/course/student/page.js
  5. 2 2
      front/project/admin/routes/course/studyDetail/page.js
  6. 14 14
      front/project/admin/routes/setting/promote/page.js
  7. 4 4
      front/project/admin/stores/system.js
  8. 5 4
      front/project/h5/routes/page/identity/page.js
  9. 38 4
      front/project/h5/routes/page/open/page.js
  10. 2 1
      front/project/h5/routes/product/courseDetail/page.js
  11. 5 2
      front/project/h5/routes/product/coursePackage/page.js
  12. 2 1
      front/project/h5/routes/product/courseVs/page.js
  13. 2 1
      front/project/h5/routes/product/dataDetail/page.js
  14. 6 3
      front/project/h5/routes/product/serviceDetail/page.js
  15. 2 1
      front/project/h5/stores/index.js
  16. 0 24
      front/project/h5/stores/my.js
  17. 66 0
      front/project/h5/stores/order.js
  18. 2 1
      front/project/www/stores/index.js
  19. 38 0
      front/project/www/stores/order.js
  20. 1 0
      server/build.gradle
  21. 24 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/ExperienceDayRange.java
  22. 24 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/ExperienceScoreRange.java
  23. 10 7
      server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceKey.java
  24. 25 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/ServiceVipKey.java
  25. 1 1
      server/data/src/main/java/com/qxgmat/data/constants/enums/SettingKey.java
  26. 8 4
      server/data/src/main/java/com/qxgmat/data/constants/enums/module/CourseModule.java
  27. 1 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/module/ProductType.java
  28. 10 10
      server/data/src/main/java/com/qxgmat/data/constants/enums/trade/PayType.java
  29. 20 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/trade/PayModule.java
  30. 36 0
      server/data/src/main/java/com/qxgmat/data/constants/enums/trade/RecordSource.java
  31. 0 22
      server/data/src/main/java/com/qxgmat/data/constants/enums/user/ServiceSource.java
  32. 7 0
      server/data/src/main/java/com/qxgmat/data/dao/PreviewAssignMapper.java
  33. 62 27
      server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java
  34. 300 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/PreviewAssign.java
  35. 0 210
      server/data/src/main/java/com/qxgmat/data/dao/entity/PreviewPaper.java
  36. 140 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/User.java
  37. 51 16
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserAbnormal.java
  38. 35 35
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourse.java
  39. 19 54
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseAppointment.java
  40. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseProgress.java
  41. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseRecord.java
  42. 122 7
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrder.java
  43. 141 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrderCheckout.java
  44. 245 35
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrderRecord.java
  45. 35 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/UserQuestion.java
  46. 6 4
      server/data/src/main/java/com/qxgmat/data/dao/mapping/CourseExperienceMapper.xml
  47. 24 0
      server/data/src/main/java/com/qxgmat/data/dao/mapping/PreviewAssignMapper.xml
  48. 1 8
      server/data/src/main/java/com/qxgmat/data/dao/mapping/PreviewPaperMapper.xml
  49. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserAbnormalMapper.xml
  50. 3 4
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseAppointmentMapper.xml
  51. 2 13
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseMapper.xml
  52. 2 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseProgressMapper.xml
  53. 2 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserCourseRecordMapper.xml
  54. 6 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserMapper.xml
  55. 6 1
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderCheckoutMapper.xml
  56. 5 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderMapper.xml
  57. 12 5
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderRecordMapper.xml
  58. 3 2
      server/data/src/main/java/com/qxgmat/data/dao/mapping/UserQuestionMapper.xml
  59. 6 0
      server/data/src/main/java/com/qxgmat/data/relation/CourseDataRelationMapper.java
  60. 17 0
      server/data/src/main/java/com/qxgmat/data/relation/CourseExperienceRelationMapper.java
  61. 15 0
      server/data/src/main/java/com/qxgmat/data/relation/CoursePackageRelationMapper.java
  62. 17 0
      server/data/src/main/java/com/qxgmat/data/relation/CourseRelationMapper.java
  63. 5 0
      server/data/src/main/java/com/qxgmat/data/relation/UserRelationMapper.java
  64. 11 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseDataRelationMapper.xml
  65. 28 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseExperienceRelationMapper.xml
  66. 27 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/CoursePackageRelationMapper.xml
  67. 29 0
      server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseRelationMapper.xml
  68. 12 3
      server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml
  69. 1 0
      server/data/src/main/resources/mybatis-generator.xml
  70. 22 14
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/CourseController.java
  71. 7 7
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  72. 20 7
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/UserController.java
  73. 52 50
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  74. 5 3
      server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java
  75. 8 47
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  76. 158 26
      server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java
  77. 60 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CourseExperienceDto.java
  78. 10 20
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/UserCourseRecordDto.java
  79. 9 29
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/UserServiceRecordDto.java
  80. 50 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/CourseExperienceListDto.java
  81. 5 5
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/UserCourseStudyRecordInfoDto.java
  82. 49 0
      server/gateway-api/src/main/java/com/qxgmat/dto/extend/CoursePackageExtendDto.java
  83. 45 0
      server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordAddDto.java
  84. 1 1
      server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordOpenDto.java
  85. 82 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderPreDto.java
  86. 42 1
      server/gateway-api/src/main/java/com/qxgmat/help/AiHelp.java
  87. 2 0
      server/gateway-api/src/main/java/com/qxgmat/service/UserQuestionService.java
  88. 16 0
      server/gateway-api/src/main/java/com/qxgmat/service/UserServiceService.java
  89. 28 15
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java
  90. 10 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitCheckout.java
  91. 9 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitCourseRecord.java
  92. 10 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitOrder.java
  93. 8 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitRecord.java
  94. 0 8
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/PayModule.java
  95. 10 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/RemoveCheckout.java
  96. 7 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/StopRecord.java
  97. 7 0
      server/gateway-api/src/main/java/com/qxgmat/service/annotation/UseRecord.java
  98. 10 7
      server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java
  99. 825 0
      server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java
  100. 0 0
      server/gateway-api/src/main/java/com/qxgmat/service/extend/QuestionFlowService.java

+ 8 - 4
front/project/Constant.js

@@ -23,12 +23,12 @@ export const PreviewMode = [{ label: '指定作业', value: 0 }, { label: '系
 export const ServiceKey = [{ label: 'VIP', value: 'vip' }, { label: '机经', value: 'textbook' }, { label: '千行CAT', value: 'qx_cat' }];
 
 export const ServiceParamMap = {
-  vip: [{ label: '1个月', value: '1month' }, { label: '3个月', value: '3month' }, { label: '6个月', value: '6month' }],
+  vip: [{ label: '1个月', value: 'month1' }, { label: '3个月', value: 'month3' }, { label: '6个月', value: 'month6' }],
 };
 
-export const ServiceSource = [{ label: '线下支付', value: 'offline' }, { label: '线上支付', value: 'online' }, { label: '实名认证', value: 'real' }, { label: '邀请好友', value: 'invite' }, { label: '课程赠送', value: 'gift_course' }, { label: '活动赠送', value: 'gift_activity' }];
+export const ServiceSource = [{ label: '银行转账', value: 'bank' }, { label: '微信', value: 'wechat' }, { label: '支付宝', value: 'alipay' }, { label: '实名认证', value: 'real' }, { label: '邀请好友', value: 'invite' }, { label: '备考设置', value: 'prepare' }, { label: '课程赠送', value: 'gift_course' }, { label: '活动赠送', value: 'gift_activity' }];
 
-export const CourseSource = [{ label: '线下报名', value: 'offline' }, { label: '线上报名', value: 'online' }];
+export const CourseSource = [{ label: '网上报名', value: 'bank' }, { label: '网上报名', value: 'wechat' }, { label: '网上报名', value: 'alipay' }, { label: '线下报名', value: 'offline' }];
 
 export const SwitchSelect = [{ value: 0, label: '否' }, { value: 1, label: '是' }];
 
@@ -56,7 +56,11 @@ export const ExaminationOrder = [{ list: ['awa', 'ir', 'quant', 'verbal'] }, { l
 
 export const DataType = [{ label: '电子', value: 'electron' }, { label: '纸质', value: 'paper' }];
 
-export const ExperienceScore = [{ label: '650+', value: '_650' }, { label: '700+', value: '_700' }, { label: '750+', value: '_750' }];
+export const ExperienceScore = [{ label: '680+', value: '_680' }, { label: '700+', value: '_700' }, { label: '730+', value: '_730' }, { label: '750+', value: '_750' }];
+
+export const ExperienceScoreRange = [680, 800, 10];
+
+export const ExperienceDay = [{ label: '<=1个月', value: 'lt1month' }, { label: '1-3个月', value: 'b13month' }, { label: '>=3个月', value: 'gt3month' }];
 
 export const ExperiencePercent = [{ label: '50+', value: '_50' }, { label: '100+', value: '_100' }, { label: '150+', value: '_150' }, { label: '200+', value: '_200' }];
 

+ 67 - 21
front/project/admin/routes/course/experience/page.js

@@ -7,33 +7,61 @@ import Block from '@src/components/Block';
 import FilterLayout from '@src/layouts/FilterLayout';
 import ActionLayout from '@src/layouts/ActionLayout';
 import TableLayout from '@src/layouts/TableLayout';
-import { formatDate, bindSearch } from '@src/services/Tools';
+import { formatDate, bindSearch, getMap } from '@src/services/Tools';
 import { asyncSMessage } from '@src/services/AsyncTools';
-import { ExperienceScore, ExperiencePercent } from '../../../../Constant';
+import { ExperienceScore, ExperiencePercent, PrepareStatus, ExperienceDay } from '../../../../Constant';
 import { System } from '../../../stores/system';
 import { Course } from '../../../stores/course';
 import { User } from '../../../stores/user';
 
+const PrepareStatusMap = getMap(PrepareStatus, 'value', 'label');
+const ExperiencePercentMap = getMap(ExperiencePercent, 'value', 'label');
 
 export default class extends Page {
   init() {
-    this.filterForm = [
-      {
-        key: 'keyword',
-        type: 'input',
-        allowClear: true,
-        name: '搜索',
-        placeholder: '标题或正文',
-      }, {
-        key: 'userId',
-        type: 'select',
-        name: '用户',
-        allowClear: true,
-        select: [],
-        number: true,
-        placeholder: '请输入',
-      },
-    ];
+    this.filterForm = [{
+      key: 'keyword',
+      type: 'input',
+      allowClear: true,
+      name: '搜索',
+      placeholder: '标题或正文',
+    }, {
+      key: 'userId',
+      type: 'select',
+      name: '用户',
+      allowClear: true,
+      select: [],
+      number: true,
+      placeholder: '请输入',
+    }, {
+      key: 'prepareStatus',
+      type: 'select',
+      allowClear: true,
+      name: '身份',
+      select: PrepareStatus,
+      placeholder: '请选择',
+    }, {
+      key: 'experienceDay',
+      type: 'select',
+      allowClear: true,
+      name: '备考周期',
+      select: ExperienceDay,
+      placeholder: '请选择',
+    }, {
+      key: 'experienceScore',
+      type: 'select',
+      allowClear: true,
+      name: '分手成绩',
+      select: ExperienceScore,
+      placeholder: '请选择',
+    }, {
+      key: 'experiencePercent',
+      type: 'select',
+      allowClear: true,
+      name: '提分比例',
+      select: ExperiencePercent,
+      placeholder: '请选择',
+    }];
     this.actionList = [{
       key: 'info',
       name: '数据管理',
@@ -51,8 +79,8 @@ export default class extends Page {
     }, {
       title: '作者',
       dataIndex: 'user',
-      render: (text) => {
-        return (text || {}).nickname;
+      render: (text, record) => {
+        return text ? text.nickname : record.nickname;
       },
     }, {
       title: '更新时间',
@@ -61,6 +89,24 @@ export default class extends Page {
         return formatDate(text);
       },
     }, {
+      title: '身份',
+      dataIndex: 'prepareStatus',
+      render: (text) => {
+        return PrepareStatusMap[text] || text;
+      },
+    }, {
+      title: '备考周期',
+      dataIndex: 'experienceDay',
+    }, {
+      title: '分手成绩',
+      dataIndex: 'experienceScore',
+    }, {
+      title: '提分比例',
+      dataIndex: 'experiencePercent',
+      render: (text) => {
+        return ExperiencePercentMap[text] || text;
+      },
+    }, {
       title: '阅读数',
       dataIndex: 'viewNumber',
     }, {

+ 22 - 5
front/project/admin/routes/course/experienceDetail/page.js

@@ -1,5 +1,5 @@
 import React from 'react';
-import { Form, Input, Button, Row, Col } from 'antd';
+import { Form, Input, Button, Row, Col, InputNumber } from 'antd';
 import './index.less';
 import Editor from '@src/components/Editor';
 import Page from '@src/containers/Page';
@@ -8,10 +8,18 @@ import Select from '@src/components/Select';
 // import FileUpload from '@src/components/FileUpload';
 import { formatFormError, generateSearch } from '@src/services/Tools';
 import { asyncSMessage } from '@src/services/AsyncTools';
-import { PrepareStatus, PrepareExaminationTime, ExperienceScore, ExperiencePercent } from '../../../../Constant';
+import { PrepareStatus, ExperienceScoreRange, ExperiencePercent } from '../../../../Constant';
 import { Course } from '../../../stores/course';
 import { User } from '../../../stores/user';
 
+const [minScore, maxScore, step] = ExperienceScoreRange;
+let tmp = minScore;
+const ExperienceScore = [];
+while (tmp <= maxScore) {
+  ExperienceScore.push({ label: `${tmp}`, value: tmp });
+  tmp += step;
+}
+
 export default class extends Page {
   initData() {
     const { id } = this.params;
@@ -81,6 +89,15 @@ export default class extends Page {
             <Select {...this.state.userId} placeholder='请选择作者' />,
           )}
         </Form.Item>
+        <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='昵称'>
+          {getFieldDecorator('nickname', {
+            rules: [
+              { required: true, message: '请输入昵称' },
+            ],
+          })(
+            <Input placeholder='输入昵称' />,
+          )}
+        </Form.Item>
         <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='作者信息'>
           <Row>
             <Col span={6}>
@@ -93,12 +110,12 @@ export default class extends Page {
               )}
             </Col>
             <Col span={6}>
-              {getFieldDecorator('prepareExaminationTime', {
+              {getFieldDecorator('experienceDay', {
                 rules: [{
-                  required: true, message: '请选择',
+                  required: true, message: '请输入备考天数',
                 }],
               })(
-                <Select select={PrepareExaminationTime} placeholder='备考周期' />,
+                <InputNumber placeholder='备考周期: 天' />,
               )}
             </Col>
             <Col span={6}>

+ 19 - 12
front/project/admin/routes/course/student/page.js

@@ -120,9 +120,13 @@ export default class extends Page {
       select: [],
       name: '老师',
     }, {
-      key: 'vsNumber',
+      key: 'number',
       type: 'number',
       name: '课时数',
+    }, {
+      key: 'cctalkName',
+      type: 'input',
+      name: 'CCTalk用户名',
     }];
     this.vsColumns = [{
       title: '学员名称',
@@ -156,16 +160,19 @@ export default class extends Page {
         }
         return CourseStatusMap[status] || status;
       },
-      // }, {
-      //   title: '操作',
-      //   dataIndex: 'handler',
-      //   render: (text, record) => {
-      //     return <div className="table-button">
-      //       {<a onClick={() => {
-      //         this.changeVs(record);
-      //       }}>修改</a>}
-      //     </div>;
-      //   },
+    }, {
+      title: 'CCTalk用户名',
+      dataIndex: 'cctalkName',
+    }, {
+      title: '操作',
+      dataIndex: 'handler',
+      render: (text, record) => {
+        return <div className="table-button">
+          {<a onClick={() => {
+            this.changeVs(record);
+          }}>修改</a>}
+        </div>;
+      },
     }];
   }
 
@@ -304,7 +311,7 @@ export default class extends Page {
     asyncForm('添加', this.vsList, {}, info => {
       if (data.vsType === 'novice') {
         // 写死:新手每次1课时
-        info.vsNumber = 1;
+        info.number = 1;
       }
       // ([info.useStartTime, info.useEndTime] = info.time);
       return Course.addStudentVs(Object.assign({ courseId: id }, info)).then(() => {

+ 2 - 2
front/project/admin/routes/course/studyDetail/page.js

@@ -70,7 +70,7 @@ export default class extends Page {
       dataIndex: 'no',
       render: (text) => {
         const { data } = this.state;
-        return `${text}/${data.vsNumber}`;
+        return `${text}/${data.number}`;
       },
     }, {
       title: '上课时间',
@@ -226,7 +226,7 @@ export default class extends Page {
   renderVs() {
     const { data, page } = this.state;
     return <Block flex>
-      {data.vsNumber > page.total && <ActionLayout
+      {data.number > page.total && <ActionLayout
         itemList={this.vsAction}
         selectedKeys={this.state.selectedKeys}
         onAction={key => this.onAction(key)}

+ 14 - 14
front/project/admin/routes/setting/promote/page.js

@@ -19,9 +19,9 @@ export default class extends Page {
         form.getFieldDecorator(`vs_list[${index}].number`);
         form.getFieldDecorator(`vs_list[${index}].percent`);
       });
-      (result.gifts || []).forEach((row, index) => {
-        form.getFieldDecorator(`gifts[${index}].money`);
-        form.getFieldDecorator(`gifts[${index}].hour`);
+      (result.ask_time || []).forEach((row, index) => {
+        form.getFieldDecorator(`ask_time[${index}].money`);
+        form.getFieldDecorator(`ask_time[${index}].hour`);
       });
       form.setFieldsValue(flattenObject(result));
       this.setState({ load: true, data: result });
@@ -70,7 +70,7 @@ export default class extends Page {
         data.vs_list = (data.vs_list || []).map(row => {
           return { number: Number(row.number), percent: Number(row.percent) };
         });
-        data.gifts = (data.gifts || []).map(row => {
+        data.ask_time = (data.ask_time || []).map(row => {
           return { money: Number(row.money), hour: Number(row.hour) };
         });
         System.setCoursePromote(data)
@@ -218,51 +218,51 @@ export default class extends Page {
     </Block>;
   }
 
-  renderGift() {
+  renderAskTime() {
     const { getFieldDecorator } = this.props.form;
     const { data } = this.state;
-    const gifts = data.gifts || [];
+    const ask_time = data.ask_time || [];
     return <Block>
       <h1>课程赠品</h1>
       <Form>
         <Form.Item label='满额送回复时长服务' />
-        {gifts.map((row, index) => {
+        {ask_time.map((row, index) => {
           return <Row>
             <Col span={6}>
               <Form.Item labelCol={{ span: 10 }} wrapperCol={{ span: 14 }} label='实付金额'>
-                {getFieldDecorator(`gifts[${index}].money`, {
+                {getFieldDecorator(`ask_time[${index}].money`, {
                   rules: [
                     { required: true, message: '输入金额' },
                   ],
                 })(
                   <Input placeholder={'输入金额'} onChange={(value) => {
-                    this.changeMapValue('gifts', index, 'money', value);
+                    this.changeMapValue('ask_time', index, 'money', value);
                   }} />,
                 )}
               </Form.Item>
             </Col>
             <Col span={6}>
               <Form.Item labelCol={{ span: 10 }} wrapperCol={{ span: 14 }} label='赠送'>
-                {getFieldDecorator(`gifts[${index}].hour`, {
+                {getFieldDecorator(`ask_time[${index}].hour`, {
                   rules: [
                     { required: true, message: '输入小时' },
                   ],
                 })(
                   <Input placeholder={'输入小时'} onChange={(value) => {
-                    this.changeMapValue('gifts', index, 'hour', value);
+                    this.changeMapValue('ask_time', index, 'hour', value);
                   }} />,
                 )}
               </Form.Item>
             </Col>
             <Col span={1} onClick={() => {
-              this.deleteLength('gifts', index, 1);
+              this.deleteLength('ask_time', index, 1);
             }}>
               <Button><Icon type="minus" /></Button>
             </Col>
           </Row>;
         })}
         <Button onClick={() => {
-          this.addLength('gifts', { money: 0, hour: 0 });
+          this.addLength('ask_time', { money: 0, hour: 0 });
         }}><Icon type={'plus'} /></Button>
       </Form>
     </Block>;
@@ -273,7 +273,7 @@ export default class extends Page {
     return <div>
       {this.renderVideo()}
       {this.renderVs()}
-      {this.renderGift()}
+      {this.renderAskTime()}
       <Row type="flex" justify="center">
         <Col>
           <Button type="primary" onClick={() => {

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

@@ -133,12 +133,12 @@ export default class SystemStore extends BaseStore {
     return this.apiPut('/setting/course_index', params);
   }
 
-  getCoursePromote() {
-    return this.apiGet('/setting/course_promote');
+  getPromote() {
+    return this.apiGet('/setting/promote');
   }
 
-  setCoursePromote(params) {
-    return this.apiPut('/setting/course_promote', params);
+  setPromote(params) {
+    return this.apiPut('/setting/promote', params);
   }
 
   getExperienceInfo() {

+ 5 - 4
front/project/h5/routes/page/identity/page.js

@@ -3,7 +3,7 @@ import './index.less';
 import Page from '@src/containers/Page';
 import Assets from '@src/components/Assets';
 import { asyncSMessage } from '@src/services/AsyncTools';
-import { dataURLtoBlob } from '@src/services/Tools';
+import { dataURLtoBlob, formatDate } from '@src/services/Tools';
 import Icon from '../../../components/Icon';
 import { Common } from '../../../stores/common';
 import { My } from '../../../stores/my';
@@ -98,8 +98,8 @@ export default class extends Page {
     if (!this.front || !this.back) {
       return;
     }
-    My.realFinish().then(() => {
-      this.setState({ state: 'finish' });
+    My.realFinish().then((result) => {
+      this.setState({ state: 'finish', data: result });
     });
   }
 
@@ -138,6 +138,7 @@ export default class extends Page {
   }
 
   renderFinish() {
+    const { data } = this.state;
     return (
       <div className="finish">
         <div className="icon">
@@ -145,7 +146,7 @@ export default class extends Page {
         </div>
         <div className="title">认证完成!</div>
         <div className="desc">180天VIP权限已赠送至您的账户</div>
-        <div className="desc">生效时间:2019-06-15</div>
+        <div className="desc">生效时间:{formatDate(data.useStartTime, 'YYYY-MM-DD')}</div>
       </div>
     );
   }

+ 38 - 4
front/project/h5/routes/page/open/page.js

@@ -5,26 +5,47 @@ import Assets from '@src/components/Assets';
 import { getMap, formatDate } from '@src/services/Tools';
 import Checkbox from '../../../components/CheckBox';
 import Button from '../../../components/Button';
-import { My } from '../../../stores/my';
+import Icon from '../../../components/Icon';
+import { Order } from '../../../stores/order';
 import { ServiceKey } from '../../../../Constant';
 
 const ServiceKeyMap = getMap(ServiceKey, 'value', 'label');
 
 export default class extends Page {
+  initState() {
+    return { state: 'open' };
+  }
+
   initData() {
     const { id } = this.params;
-    My.getRecord(id)
+    Order.getRecord(id)
       .then(result => {
+        if (result.isUse) {
+          this.setState({ state: 'finish', data: result });
+          return;
+        }
         this.setState(result);
       });
   }
 
   submit() {
-    My.useRecord(this.params.id)
-      .then();
+    Order.useRecord(this.params.id)
+      .then((result) => {
+        this.setState({ state: 'finish', data: result });
+      });
   }
 
   renderView() {
+    const { state } = this.state;
+    switch (state) {
+      case 'finish':
+        return this.renderFinish();
+      default:
+        return this.renderOpen();
+    }
+  }
+
+  renderOpen() {
     const { productType } = this.state;
     if (productType === 'service') {
       return this.renderService();
@@ -82,4 +103,17 @@ export default class extends Page {
       </div>
     );
   }
+
+  renderFinish() {
+    const { data } = this.state;
+    return (
+      <div className="finish">
+        <div className="icon">
+          <Icon type="check-circle" />
+        </div>
+        <div className="title">开通成功!</div>
+        <div className="desc">生效时间:{formatDate(data.useStartTime, 'YYYY-MM-DD')}</div>
+      </div>
+    );
+  }
 }

+ 2 - 1
front/project/h5/routes/product/courseDetail/page.js

@@ -5,6 +5,7 @@ import Page from '@src/containers/Page';
 import Money from '../../../components/Money';
 import Button from '../../../components/Button';
 import { Course } from '../../../stores/course';
+import { Order } from '../../../stores/order';
 import { UserUrl } from '../../../../Constant';
 
 export default class extends Page {
@@ -20,7 +21,7 @@ export default class extends Page {
   }
 
   buy() {
-
+    Order.speedPay({ productType: 'course', productId: this.params.id });
   }
 
   renderView() {

+ 5 - 2
front/project/h5/routes/product/coursePackage/page.js

@@ -7,10 +7,11 @@ import Button from '../../../components/Button';
 import Tag from '../../../components/Tag';
 // import Avatar from '../../../components/Avatar';
 import { Course } from '../../../stores/course';
+import { Order } from '../../../stores/order';
 import { ServiceKey, ServiceParamMap } from '../../../../Constant';
 
 export default class extends Page {
-  init() {}
+  init() { }
 
   initData() {
     const { id } = this.params;
@@ -20,7 +21,9 @@ export default class extends Page {
     });
   }
 
-  buy() {}
+  buy() {
+    Order.speedPay({ productType: 'course_package', productId: this.params.id });
+  }
 
   renderView() {
     const { data = {} } = this.state;

+ 2 - 1
front/project/h5/routes/product/courseVs/page.js

@@ -5,6 +5,7 @@ import Page from '@src/containers/Page';
 import Money from '../../../components/Money';
 import Button from '../../../components/Button';
 import { Course } from '../../../stores/course';
+import { Order } from '../../../stores/order';
 
 export default class extends Page {
   initState() {
@@ -19,7 +20,7 @@ export default class extends Page {
   }
 
   buy() {
-
+    Order.speedPay({ productType: 'course', productId: this.params.id, number: 1 });
   }
 
   renderView() {

+ 2 - 1
front/project/h5/routes/product/dataDetail/page.js

@@ -8,6 +8,7 @@ import Money from '../../../components/Money';
 import Button from '../../../components/Button';
 import Tag from '../../../components/Tag';
 import { Course } from '../../../stores/course';
+import { Order } from '../../../stores/order';
 import { DataType } from '../../../../Constant';
 
 const DataTypeMap = getMap(DataType, 'value', 'label');
@@ -26,7 +27,7 @@ export default class extends Page {
   }
 
   buy() {
-
+    Order.speedPay({ productType: 'data', productId: this.params.id });
   }
 
   renderView() {

+ 6 - 3
front/project/h5/routes/product/serviceDetail/page.js

@@ -5,6 +5,8 @@ import Money from '../../../components/Money';
 import Button from '../../../components/Button';
 import { SpecialRadioGroup } from '../../../components/Radio';
 import { Main } from '../../../stores/main';
+import { Order } from '../../../stores/order';
+import { ServiceParamMap } from '../../../../Constant';
 
 export default class extends Page {
   initState() {
@@ -27,9 +29,10 @@ export default class extends Page {
   }
 
   buy() {
-    const { data, index } = this.state;
-    const item = data.package[index] || {};
-    console.log(item);
+    const { service } = this.params;
+    const { index } = this.state;
+    const param = ServiceParamMap[service] ? ServiceParamMap[service][index].value : '';
+    Order.speedPay({ productType: 'service', service, param });
   }
 
   //   expire_info: '',

+ 2 - 1
front/project/h5/stores/index.js

@@ -3,6 +3,7 @@ import { Common } from './common';
 import { Main } from './main';
 import { Textbook } from './textbook';
 import { Course } from './course';
+import { Order } from './order';
 import { My } from './my';
 
-export default [User, Common, Main, Textbook, Course, My];
+export default [User, Common, Main, Textbook, Course, Order, My];

+ 0 - 24
front/project/h5/stores/my.js

@@ -280,30 +280,6 @@ export default class MyStore extends BaseStore {
   }
 
   /**
-   * 获取所有已购记录
-   * @param {*} param0
-   */
-  listRecord({ page, size }) {
-    return this.apiGet('/my/record/list', { page, size });
-  }
-
-  /**
-   * 获取订单记录
-   * @param {*} id
-   */
-  getRecord(id) {
-    return this.apiGet('/my/record/detail', { id });
-  }
-
-  /**
-   * 开通服务、课程等
-   * @param {*} id
-   */
-  useRecord(id, isSubscribe) {
-    return this.apiPost('/my/record/use', { id, isSubscribe });
-  }
-
-  /**
    * 获取数据订阅更新
    * @param {*} param0
    */

+ 66 - 0
front/project/h5/stores/order.js

@@ -0,0 +1,66 @@
+import BaseStore from '@src/stores/base';
+// import * as querystring from 'querystring';
+
+export default class OrderStore extends BaseStore {
+  allCheckout() {
+    return this.apiGet('/order/checkout/all');
+  }
+
+  addCheckout({ productType, productId, service, param, number }) {
+    return this.apiPost('/order/checkout/add', { productType, productId, service, param, number });
+  }
+
+  removeCheckout(checkoutId) {
+    return this.apiDelete('/order/checkout/delete', { checkoutId });
+  }
+
+  confirmPay() {
+    return this.apiPost('/order/pay/confirm');
+  }
+
+  speedPay({ productType, productId, service, param, number }) {
+    return this.apiPost('/order/pay/speed', { productType, productId, service, param, number });
+  }
+
+  wechatQr(orderId) {
+    return this.apiPost('/order/wechat/qr', { orderId });
+  }
+
+  wechatJs(orderId) {
+    return this.apiPost('/order/wechat/js', { orderId });
+  }
+
+  alipayQr(orderId) {
+    return this.apiPost('/order/alipay/qr', { orderId });
+  }
+
+  query(orderId) {
+    return this.apiGet('/order/pay/query', { orderId });
+  }
+
+  /**
+   * 获取所有已购记录
+   * @param {*} param0
+   */
+  listRecord({ page, size }) {
+    return this.apiGet('/my/record/list', { page, size });
+  }
+
+  /**
+   * 获取订单记录
+   * @param {*} id
+   */
+  getRecord(id) {
+    return this.apiGet('/my/record/detail', { id });
+  }
+
+  /**
+   * 开通服务、课程等
+   * @param {*} id
+   */
+  useRecord(id, isSubscribe) {
+    return this.apiPost('/my/record/use', { id, isSubscribe });
+  }
+}
+
+export const Order = new OrderStore({ key: 'order' });

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

@@ -5,6 +5,7 @@ import { Textbook } from './textbook';
 import { My } from './my';
 import { Question } from './question';
 import { Sentence } from './sentence';
+import { Order } from './order';
 import { User } from './user';
 
-export default [Common, Main, Course, Textbook, My, Question, Sentence, User];
+export default [Common, Main, Course, Textbook, My, Question, Sentence, Order, User];

+ 38 - 0
front/project/www/stores/order.js

@@ -0,0 +1,38 @@
+import BaseStore from '@src/stores/base';
+// import * as querystring from 'querystring';
+
+export default class OrderStore extends BaseStore {
+  allCheckout() {
+    return this.apiGet('/order/checkout/all');
+  }
+
+  addCheckout(productType, productId, service, param) {
+    return this.apiPost('/order/checkout/add', { productType, productId, service, param });
+  }
+
+  removeCheckout(checkoutId) {
+    return this.apiDelete('/order/checkout/delete', { checkoutId });
+  }
+
+  confirmPay() {
+    return this.apiPost('/order/pay/confirm');
+  }
+
+  speedPay(productType, productId, service, param) {
+    return this.apiPost('/order/pay/speed', { productType, productId, service, param });
+  }
+
+  wechatQr(orderId) {
+    return this.apiPost('/order/wechat/qr', { orderId });
+  }
+
+  wechatJs(orderId) {
+    return this.apiPost('/order/wechat/js', { orderId });
+  }
+
+  alipayQr(orderId) {
+    return this.apiPost('/order/alipay/qr', { orderId });
+  }
+}
+
+export const Order = new OrderStore({ key: 'order' });

+ 1 - 0
server/build.gradle

@@ -90,6 +90,7 @@ subprojects {
         compileClasspath group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.9'
         compileClasspath group: 'com.alibaba', name: 'fastjson', version: '1.2.46'
         compileClasspath 'javax.persistence:javax.persistence-api:2.2'
+        compileClasspath group: 'net.ipip', name: 'ipdb', version: '1.1.2'
 
         compileClasspath fileTree(dir:'libs',include:['*.jar'])
     }

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

@@ -0,0 +1,24 @@
+package com.qxgmat.data.constants.enums;
+
+
+/**
+ * Created by gaojie on 2017/11/20.
+ */
+public enum ExperienceDayRange {
+    LT1MONTH(0,30), B13MONTH(30,90), GT3MONTH(90, Integer.MAX_VALUE)
+
+    ;
+    final static public String message = "筛选分数";
+
+    public int min ;
+    public int max ;
+    private ExperienceDayRange(int min, int max){
+        this.min = min;
+        this.max = max;
+    }
+    public static ExperienceDayRange ValueOf(String value){
+        if (value == null) return null;
+        return ExperienceDayRange.valueOf(value.toUpperCase());
+    }
+
+}

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

@@ -0,0 +1,24 @@
+package com.qxgmat.data.constants.enums;
+
+
+/**
+ * Created by gaojie on 2017/11/20.
+ */
+public enum ExperienceScoreRange {
+    SCORE_680(680,700), SCORE_700(700,730), SCORE_730(730,750), SCORE_750(750, 800)
+
+    ;
+    final static public String message = "筛选分数";
+
+    public int min ;
+    public int max ;
+    private ExperienceScoreRange(int min, int max){
+        this.min = min;
+        this.max = max;
+    }
+    public static ExperienceScoreRange ValueOf(String value){
+        if (value == null) return null;
+        return ExperienceScoreRange.valueOf("SCORE"+value);
+    }
+
+}

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

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

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

@@ -0,0 +1,25 @@
+package com.qxgmat.data.constants.enums;
+
+public enum ServiceVipKey {
+    MONTH1("month1", 0, 30),
+    MONTH3("month3", 1, 90),
+    MONTH6("month6", 2, 180),
+    DAY7("day7", -1, 7),
+
+    ;
+    public String key;
+
+    public Integer index;
+
+    public Integer useExpireDay;
+    private ServiceVipKey(String key, Integer index, Integer useExpireDay){
+        this.key = key;
+        this.index = index;
+        this.useExpireDay = useExpireDay;
+    }
+
+    public static ServiceVipKey ValueOf(String name){
+        if (name == null || name.isEmpty()) return null;
+        return ServiceVipKey.valueOf(name.toUpperCase());
+    }
+}

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

@@ -26,7 +26,7 @@ public enum SettingKey {
     SERVICE_TEXTBOOK("service_textbook"), // 数学机经服务信息
     SERVICE_VIP("service_vip"), // vip服务信息
     COURSE_INDEX("course_index"), // 课程首页设置
-    COURSE_PROMOTE("course_promote"), // 课程促销
+    PROMOTE("course_promote"), // 促销
     EXPERIENCE_INFO("experience_info"), // 心经信息
 
     TIPS("tips"); // 页面提示信息

+ 8 - 4
server/data/src/main/java/com/qxgmat/data/constants/enums/module/CourseModule.java

@@ -2,13 +2,17 @@ package com.qxgmat.data.constants.enums.module;
 
 // 课程模块
 public enum CourseModule {
-    VIDEO("video"),
-    ONLINE("online"),
-    VS("vs"),
+    VIDEO("video", 180),
+    ONLINE("online", 0),// 后台添加
+    VS("vs", 7),
     ;
     public String key;
-    private CourseModule(String key){
+
+    public Integer expireDay;
+
+    private CourseModule(String key, Integer expireDay){
         this.key = key;
+        this.expireDay = expireDay;
     }
 
     public static CourseModule ValueOf(String name){

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

@@ -3,6 +3,7 @@ package com.qxgmat.data.constants.enums.module;
 public enum ProductType {
     SERVICE("service"),
     COURSE("course"),
+    COURSE_PACKAGE("course_package"),
     DATA("data"),
     ;
     public String key;

+ 10 - 10
server/data/src/main/java/com/qxgmat/data/constants/enums/trade/PayType.java

@@ -3,35 +3,35 @@ package com.qxgmat.data.constants.enums.trade;
 /**
  * Created by gaojie on 2017/11/20.
  */
-public enum PayType {
-    CASH("cash"),
+public enum PayMethod {
     ALIPAY("alipay"),
-    WECHAT("wechat")
+    WECHAT("wechat"),
+    BANK("bank"),
     ;
     final static public String message = "支付类型";
 
     public String key ;
-    private PayType(String value){
+    private PayMethod(String value){
         this.key = value;
     }
 
-    public static PayType ValueOf(String name){
+    public static PayMethod ValueOf(String name){
         if (name == null) return null;
-        return PayType.valueOf(name.toUpperCase());
+        return PayMethod.valueOf(name.toUpperCase());
     }
 
-    public static PayType FromChannel(PayChannel channel){
+    public static PayMethod FromChannel(PayChannel channel){
         if (channel == null) return null;
         switch (channel){
             case ALIPAY_APP:
             case ALIPAY_QR:
-                return PayType.ALIPAY;
+                return PayMethod.ALIPAY;
             case WECHAT_APP:
             case WECHAT_QR:
-                return PayType.WECHAT;
+                return PayMethod.WECHAT;
             case OFFLINE:
             default:
-                return PayType.CASH;
+                return PayMethod.BANK;
         }
     }
 }

+ 20 - 0
server/data/src/main/java/com/qxgmat/data/constants/enums/trade/PayModule.java

@@ -0,0 +1,20 @@
+package com.qxgmat.data.constants.enums.trade;
+
+/**
+ * Created by gaojie on 2017/11/20.
+ */
+public enum PayModule {
+    ORDER("order"),
+    ;
+    final static public String message = "支付模块";
+
+    public String key ;
+    private PayModule(String value){
+        this.key = value;
+    }
+
+    public static PayModule ValueOf(String name){
+        if (name == null) return null;
+        return PayModule.valueOf(name.toUpperCase());
+    }
+}

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

@@ -0,0 +1,36 @@
+package com.qxgmat.data.constants.enums.trade;
+
+
+public enum RecordSource {
+    BANK("bank"),
+    WECHAT("wechat"),
+    ALIPAY("alipay"),
+    REAL("real"),
+    INVITE("invite"),
+    PREPARE("prepare"),
+    GIFT_COURSE("gift_course"),
+    GIFT_ACTIVITY("gift_activity"),
+
+    ;
+    public String key;
+    private RecordSource(String key){
+        this.key = key;
+    }
+
+    public static RecordSource ValueOf(String name){
+        if (name == "") return null;
+        return RecordSource.valueOf(name.toUpperCase());
+    }
+
+    public static RecordSource FromPayModule(PayMethod payMethod){
+        switch (payMethod){
+            case WECHAT:
+                return WECHAT;
+            case ALIPAY:
+                return ALIPAY;
+            case BANK:
+            default:
+                return BANK;
+        }
+    }
+}

+ 0 - 22
server/data/src/main/java/com/qxgmat/data/constants/enums/user/ServiceSource.java

@@ -1,22 +0,0 @@
-package com.qxgmat.data.constants.enums.user;
-
-
-public enum ServiceSource {
-    OFFLINE("offline"),
-    ONLINE("online"),
-    REAL("real"),
-    INVITE("invite"),
-    GIFT_COURSE("gift_course"),
-    GIFT_ACTIVITY("gift_activity"),
-
-    ;
-    public String key;
-    private ServiceSource(String key){
-        this.key = key;
-    }
-
-    public static ServiceSource ValueOf(String name){
-        if (name == "") return null;
-        return ServiceSource.valueOf(name.toUpperCase());
-    }
-}

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

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

+ 62 - 27
server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java

@@ -18,6 +18,12 @@ public class CourseExperience implements Serializable {
     private Integer userId;
 
     /**
+     * 用户昵称
+     */
+    @Column(name = "`nickname`")
+    private String nickname;
+
+    /**
      * 题目
      */
     @Column(name = "`title`")
@@ -36,16 +42,16 @@ public class CourseExperience implements Serializable {
     private String prepareStatus;
 
     /**
-     * 备考周期
+     * 备考周期: 天
      */
-    @Column(name = "`prepare_examination_time`")
-    private String prepareExaminationTime;
+    @Column(name = "`experience_day`")
+    private Integer experienceDay;
 
     /**
-     * 分手成绩
+     * 分手成绩: 具体分数
      */
     @Column(name = "`experience_score`")
-    private String experienceScore;
+    private Integer experienceScore;
 
     /**
      * 提分幅度
@@ -112,6 +118,24 @@ public class CourseExperience implements Serializable {
     }
 
     /**
+     * 获取用户昵称
+     *
+     * @return nickname - 用户昵称
+     */
+    public String getNickname() {
+        return nickname;
+    }
+
+    /**
+     * 设置用户昵称
+     *
+     * @param nickname 用户昵称
+     */
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    /**
      * 获取题目
      *
      * @return title - 题目
@@ -166,38 +190,38 @@ public class CourseExperience implements Serializable {
     }
 
     /**
-     * 获取备考周期
+     * 获取备考周期: 天
      *
-     * @return prepare_examination_time - 备考周期
+     * @return experience_day - 备考周期: 天
      */
-    public String getPrepareExaminationTime() {
-        return prepareExaminationTime;
+    public Integer getExperienceDay() {
+        return experienceDay;
     }
 
     /**
-     * 设置备考周期
+     * 设置备考周期: 天
      *
-     * @param prepareExaminationTime 备考周期
+     * @param experienceDay 备考周期: 天
      */
-    public void setPrepareExaminationTime(String prepareExaminationTime) {
-        this.prepareExaminationTime = prepareExaminationTime;
+    public void setExperienceDay(Integer experienceDay) {
+        this.experienceDay = experienceDay;
     }
 
     /**
-     * 获取分手成绩
+     * 获取分手成绩: 具体分数
      *
-     * @return experience_score - 分手成绩
+     * @return experience_score - 分手成绩: 具体分数
      */
-    public String getExperienceScore() {
+    public Integer getExperienceScore() {
         return experienceScore;
     }
 
     /**
-     * 设置分手成绩
+     * 设置分手成绩: 具体分数
      *
-     * @param experienceScore 分手成绩
+     * @param experienceScore 分手成绩: 具体分数
      */
-    public void setExperienceScore(String experienceScore) {
+    public void setExperienceScore(Integer experienceScore) {
         this.experienceScore = experienceScore;
     }
 
@@ -309,10 +333,11 @@ public class CourseExperience implements Serializable {
         sb.append("Hash = ").append(hashCode());
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
+        sb.append(", nickname=").append(nickname);
         sb.append(", title=").append(title);
         sb.append(", link=").append(link);
         sb.append(", prepareStatus=").append(prepareStatus);
-        sb.append(", prepareExaminationTime=").append(prepareExaminationTime);
+        sb.append(", experienceDay=").append(experienceDay);
         sb.append(", experienceScore=").append(experienceScore);
         sb.append(", experiencePercent=").append(experiencePercent);
         sb.append(", viewNumber=").append(viewNumber);
@@ -354,6 +379,16 @@ public class CourseExperience implements Serializable {
         }
 
         /**
+         * 设置用户昵称
+         *
+         * @param nickname 用户昵称
+         */
+        public Builder nickname(String nickname) {
+            obj.setNickname(nickname);
+            return this;
+        }
+
+        /**
          * 设置题目
          *
          * @param title 题目
@@ -384,21 +419,21 @@ public class CourseExperience implements Serializable {
         }
 
         /**
-         * 设置备考周期
+         * 设置备考周期: 天
          *
-         * @param prepareExaminationTime 备考周期
+         * @param experienceDay 备考周期: 天
          */
-        public Builder prepareExaminationTime(String prepareExaminationTime) {
-            obj.setPrepareExaminationTime(prepareExaminationTime);
+        public Builder experienceDay(Integer experienceDay) {
+            obj.setExperienceDay(experienceDay);
             return this;
         }
 
         /**
-         * 设置分手成绩
+         * 设置分手成绩: 具体分数
          *
-         * @param experienceScore 分手成绩
+         * @param experienceScore 分手成绩: 具体分数
          */
-        public Builder experienceScore(String experienceScore) {
+        public Builder experienceScore(Integer experienceScore) {
             obj.setExperienceScore(experienceScore);
             return this;
         }

+ 300 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/PreviewAssign.java

@@ -0,0 +1,300 @@
+package com.qxgmat.data.dao.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import javax.persistence.*;
+
+@Table(name = "preview_assign")
+public class PreviewAssign implements Serializable {
+    @Id
+    @Column(name = "`id`")
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    /**
+     * 预习作业id
+     */
+    @Column(name = "`paper_id`")
+    private Integer paperId;
+
+    /**
+     * 课程id
+     */
+    @Column(name = "`course_id`")
+    private Integer courseId;
+
+    /**
+     * 课程课时
+     */
+    @Column(name = "`course_no`")
+    private Integer courseNo;
+
+    /**
+     * 课程时间段
+     */
+    @Column(name = "`course_time`")
+    private Integer courseTime;
+
+    /**
+     * 对应预约id
+     */
+    @Column(name = "`course_appointment`")
+    private Integer courseAppointment;
+
+    /**
+     * 做题完成时间
+     */
+    @Column(name = "`target_time`")
+    private Date targetTime;
+
+    @Column(name = "`create_time`")
+    private Date createTime;
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @return id
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取预习作业id
+     *
+     * @return paper_id - 预习作业id
+     */
+    public Integer getPaperId() {
+        return paperId;
+    }
+
+    /**
+     * 设置预习作业id
+     *
+     * @param paperId 预习作业id
+     */
+    public void setPaperId(Integer paperId) {
+        this.paperId = paperId;
+    }
+
+    /**
+     * 获取课程id
+     *
+     * @return course_id - 课程id
+     */
+    public Integer getCourseId() {
+        return courseId;
+    }
+
+    /**
+     * 设置课程id
+     *
+     * @param courseId 课程id
+     */
+    public void setCourseId(Integer courseId) {
+        this.courseId = courseId;
+    }
+
+    /**
+     * 获取课程课时
+     *
+     * @return course_no - 课程课时
+     */
+    public Integer getCourseNo() {
+        return courseNo;
+    }
+
+    /**
+     * 设置课程课时
+     *
+     * @param courseNo 课程课时
+     */
+    public void setCourseNo(Integer courseNo) {
+        this.courseNo = courseNo;
+    }
+
+    /**
+     * 获取课程时间段
+     *
+     * @return course_time - 课程时间段
+     */
+    public Integer getCourseTime() {
+        return courseTime;
+    }
+
+    /**
+     * 设置课程时间段
+     *
+     * @param courseTime 课程时间段
+     */
+    public void setCourseTime(Integer courseTime) {
+        this.courseTime = courseTime;
+    }
+
+    /**
+     * 获取对应预约id
+     *
+     * @return course_appointment - 对应预约id
+     */
+    public Integer getCourseAppointment() {
+        return courseAppointment;
+    }
+
+    /**
+     * 设置对应预约id
+     *
+     * @param courseAppointment 对应预约id
+     */
+    public void setCourseAppointment(Integer courseAppointment) {
+        this.courseAppointment = courseAppointment;
+    }
+
+    /**
+     * 获取做题完成时间
+     *
+     * @return target_time - 做题完成时间
+     */
+    public Date getTargetTime() {
+        return targetTime;
+    }
+
+    /**
+     * 设置做题完成时间
+     *
+     * @param targetTime 做题完成时间
+     */
+    public void setTargetTime(Date targetTime) {
+        this.targetTime = targetTime;
+    }
+
+    /**
+     * @return create_time
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * @param createTime
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", paperId=").append(paperId);
+        sb.append(", courseId=").append(courseId);
+        sb.append(", courseNo=").append(courseNo);
+        sb.append(", courseTime=").append(courseTime);
+        sb.append(", courseAppointment=").append(courseAppointment);
+        sb.append(", targetTime=").append(targetTime);
+        sb.append(", createTime=").append(createTime);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static PreviewAssign.Builder builder() {
+        return new PreviewAssign.Builder();
+    }
+
+    public static class Builder {
+        private PreviewAssign obj;
+
+        public Builder() {
+            this.obj = new PreviewAssign();
+        }
+
+        /**
+         * @param id
+         */
+        public Builder id(Integer id) {
+            obj.setId(id);
+            return this;
+        }
+
+        /**
+         * 设置预习作业id
+         *
+         * @param paperId 预习作业id
+         */
+        public Builder paperId(Integer paperId) {
+            obj.setPaperId(paperId);
+            return this;
+        }
+
+        /**
+         * 设置课程id
+         *
+         * @param courseId 课程id
+         */
+        public Builder courseId(Integer courseId) {
+            obj.setCourseId(courseId);
+            return this;
+        }
+
+        /**
+         * 设置课程课时
+         *
+         * @param courseNo 课程课时
+         */
+        public Builder courseNo(Integer courseNo) {
+            obj.setCourseNo(courseNo);
+            return this;
+        }
+
+        /**
+         * 设置课程时间段
+         *
+         * @param courseTime 课程时间段
+         */
+        public Builder courseTime(Integer courseTime) {
+            obj.setCourseTime(courseTime);
+            return this;
+        }
+
+        /**
+         * 设置对应预约id
+         *
+         * @param courseAppointment 对应预约id
+         */
+        public Builder courseAppointment(Integer courseAppointment) {
+            obj.setCourseAppointment(courseAppointment);
+            return this;
+        }
+
+        /**
+         * 设置做题完成时间
+         *
+         * @param targetTime 做题完成时间
+         */
+        public Builder targetTime(Date targetTime) {
+            obj.setTargetTime(targetTime);
+            return this;
+        }
+
+        /**
+         * @param createTime
+         */
+        public Builder createTime(Date createTime) {
+            obj.setCreateTime(createTime);
+            return this;
+        }
+
+        public PreviewAssign build() {
+            return this.obj;
+        }
+    }
+}

+ 0 - 210
server/data/src/main/java/com/qxgmat/data/dao/entity/PreviewPaper.java

@@ -36,18 +36,6 @@ public class PreviewPaper implements Serializable {
     private Integer courseNo;
 
     /**
-     * 课程时间段
-     */
-    @Column(name = "`course_time`")
-    private Integer courseTime;
-
-    /**
-     * 预约id
-     */
-    @Column(name = "`appointment_id`")
-    private Integer appointmentId;
-
-    /**
      * 题目编号id:json
      */
     @Column(name = "`question_no_ids`")
@@ -59,30 +47,6 @@ public class PreviewPaper implements Serializable {
     @Column(name = "`paper_module`")
     private String paperModule;
 
-    /**
-     * 制定作业用户id:json
-     */
-    @Column(name = "`user_ids`")
-    private Integer[] userIds;
-
-    /**
-     * 作业开始时间
-     */
-    @Column(name = "`start_time`")
-    private Date startTime;
-
-    /**
-     * 作业结束时间
-     */
-    @Column(name = "`end_time`")
-    private Date endTime;
-
-    /**
-     * 完成人数
-     */
-    @Column(name = "`finish`")
-    private Integer finish;
-
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -178,42 +142,6 @@ public class PreviewPaper implements Serializable {
     }
 
     /**
-     * 获取课程时间段
-     *
-     * @return course_time - 课程时间段
-     */
-    public Integer getCourseTime() {
-        return courseTime;
-    }
-
-    /**
-     * 设置课程时间段
-     *
-     * @param courseTime 课程时间段
-     */
-    public void setCourseTime(Integer courseTime) {
-        this.courseTime = courseTime;
-    }
-
-    /**
-     * 获取预约id
-     *
-     * @return appointment_id - 预约id
-     */
-    public Integer getAppointmentId() {
-        return appointmentId;
-    }
-
-    /**
-     * 设置预约id
-     *
-     * @param appointmentId 预约id
-     */
-    public void setAppointmentId(Integer appointmentId) {
-        this.appointmentId = appointmentId;
-    }
-
-    /**
      * 获取题目编号id:json
      *
      * @return question_no_ids - 题目编号id:json
@@ -250,78 +178,6 @@ public class PreviewPaper implements Serializable {
     }
 
     /**
-     * 获取制定作业用户id:json
-     *
-     * @return user_ids - 制定作业用户id:json
-     */
-    public Integer[] getUserIds() {
-        return userIds;
-    }
-
-    /**
-     * 设置制定作业用户id:json
-     *
-     * @param userIds 制定作业用户id:json
-     */
-    public void setUserIds(Integer[] userIds) {
-        this.userIds = userIds;
-    }
-
-    /**
-     * 获取作业开始时间
-     *
-     * @return start_time - 作业开始时间
-     */
-    public Date getStartTime() {
-        return startTime;
-    }
-
-    /**
-     * 设置作业开始时间
-     *
-     * @param startTime 作业开始时间
-     */
-    public void setStartTime(Date startTime) {
-        this.startTime = startTime;
-    }
-
-    /**
-     * 获取作业结束时间
-     *
-     * @return end_time - 作业结束时间
-     */
-    public Date getEndTime() {
-        return endTime;
-    }
-
-    /**
-     * 设置作业结束时间
-     *
-     * @param endTime 作业结束时间
-     */
-    public void setEndTime(Date endTime) {
-        this.endTime = endTime;
-    }
-
-    /**
-     * 获取完成人数
-     *
-     * @return finish - 完成人数
-     */
-    public Integer getFinish() {
-        return finish;
-    }
-
-    /**
-     * 设置完成人数
-     *
-     * @param finish 完成人数
-     */
-    public void setFinish(Integer finish) {
-        this.finish = finish;
-    }
-
-    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -360,14 +216,8 @@ public class PreviewPaper implements Serializable {
         sb.append(", courseId=").append(courseId);
         sb.append(", courseModule=").append(courseModule);
         sb.append(", courseNo=").append(courseNo);
-        sb.append(", courseTime=").append(courseTime);
-        sb.append(", appointmentId=").append(appointmentId);
         sb.append(", questionNoIds=").append(questionNoIds);
         sb.append(", paperModule=").append(paperModule);
-        sb.append(", userIds=").append(userIds);
-        sb.append(", startTime=").append(startTime);
-        sb.append(", endTime=").append(endTime);
-        sb.append(", finish=").append(finish);
         sb.append(", createTime=").append(createTime);
         sb.append(", updateTime=").append(updateTime);
         sb.append("]");
@@ -434,26 +284,6 @@ public class PreviewPaper implements Serializable {
         }
 
         /**
-         * 设置课程时间段
-         *
-         * @param courseTime 课程时间段
-         */
-        public Builder courseTime(Integer courseTime) {
-            obj.setCourseTime(courseTime);
-            return this;
-        }
-
-        /**
-         * 设置预约id
-         *
-         * @param appointmentId 预约id
-         */
-        public Builder appointmentId(Integer appointmentId) {
-            obj.setAppointmentId(appointmentId);
-            return this;
-        }
-
-        /**
          * 设置题目编号id:json
          *
          * @param questionNoIds 题目编号id:json
@@ -474,46 +304,6 @@ public class PreviewPaper implements Serializable {
         }
 
         /**
-         * 设置制定作业用户id:json
-         *
-         * @param userIds 制定作业用户id:json
-         */
-        public Builder userIds(Integer[] userIds) {
-            obj.setUserIds(userIds);
-            return this;
-        }
-
-        /**
-         * 设置作业开始时间
-         *
-         * @param startTime 作业开始时间
-         */
-        public Builder startTime(Date startTime) {
-            obj.setStartTime(startTime);
-            return this;
-        }
-
-        /**
-         * 设置作业结束时间
-         *
-         * @param endTime 作业结束时间
-         */
-        public Builder endTime(Date endTime) {
-            obj.setEndTime(endTime);
-            return this;
-        }
-
-        /**
-         * 设置完成人数
-         *
-         * @param finish 完成人数
-         */
-        public Builder finish(Integer finish) {
-            obj.setFinish(finish);
-            return this;
-        }
-
-        /**
          * @param createTime
          */
         public Builder createTime(Date createTime) {

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

@@ -196,6 +196,30 @@ public class User implements Serializable {
     private String registerIp;
 
     /**
+     * 注册地信息
+     */
+    @Column(name = "`register_city`")
+    private String registerCity;
+
+    /**
+     * 最后一次登录ip
+     */
+    @Column(name = "`latest_login_ip`")
+    private String latestLoginIp;
+
+    /**
+     * 最后登录时间
+     */
+    @Column(name = "`latest_login_time`")
+    private Date latestLoginTime;
+
+    /**
+     * 累计警告次数
+     */
+    @Column(name = "`total_alert`")
+    private Integer totalAlert;
+
+    /**
      * 是否冻结
      */
     @Column(name = "`is_frozen`")
@@ -787,6 +811,78 @@ public class User implements Serializable {
     }
 
     /**
+     * 获取注册地信息
+     *
+     * @return register_city - 注册地信息
+     */
+    public String getRegisterCity() {
+        return registerCity;
+    }
+
+    /**
+     * 设置注册地信息
+     *
+     * @param registerCity 注册地信息
+     */
+    public void setRegisterCity(String registerCity) {
+        this.registerCity = registerCity;
+    }
+
+    /**
+     * 获取最后一次登录ip
+     *
+     * @return latest_login_ip - 最后一次登录ip
+     */
+    public String getLatestLoginIp() {
+        return latestLoginIp;
+    }
+
+    /**
+     * 设置最后一次登录ip
+     *
+     * @param latestLoginIp 最后一次登录ip
+     */
+    public void setLatestLoginIp(String latestLoginIp) {
+        this.latestLoginIp = latestLoginIp;
+    }
+
+    /**
+     * 获取最后登录时间
+     *
+     * @return latest_login_time - 最后登录时间
+     */
+    public Date getLatestLoginTime() {
+        return latestLoginTime;
+    }
+
+    /**
+     * 设置最后登录时间
+     *
+     * @param latestLoginTime 最后登录时间
+     */
+    public void setLatestLoginTime(Date latestLoginTime) {
+        this.latestLoginTime = latestLoginTime;
+    }
+
+    /**
+     * 获取累计警告次数
+     *
+     * @return total_alert - 累计警告次数
+     */
+    public Integer getTotalAlert() {
+        return totalAlert;
+    }
+
+    /**
+     * 设置累计警告次数
+     *
+     * @param totalAlert 累计警告次数
+     */
+    public void setTotalAlert(Integer totalAlert) {
+        this.totalAlert = totalAlert;
+    }
+
+    /**
      * 获取是否冻结
      *
      * @return is_frozen - 是否冻结
@@ -892,6 +988,10 @@ public class User implements Serializable {
         sb.append(", totalMoney=").append(totalMoney);
         sb.append(", inviteNumber=").append(inviteNumber);
         sb.append(", registerIp=").append(registerIp);
+        sb.append(", registerCity=").append(registerCity);
+        sb.append(", latestLoginIp=").append(latestLoginIp);
+        sb.append(", latestLoginTime=").append(latestLoginTime);
+        sb.append(", totalAlert=").append(totalAlert);
         sb.append(", isFrozen=").append(isFrozen);
         sb.append(", createTime=").append(createTime);
         sb.append(", dataEmailSubscribe=").append(dataEmailSubscribe);
@@ -1228,6 +1328,46 @@ public class User implements Serializable {
         }
 
         /**
+         * 设置注册地信息
+         *
+         * @param registerCity 注册地信息
+         */
+        public Builder registerCity(String registerCity) {
+            obj.setRegisterCity(registerCity);
+            return this;
+        }
+
+        /**
+         * 设置最后一次登录ip
+         *
+         * @param latestLoginIp 最后一次登录ip
+         */
+        public Builder latestLoginIp(String latestLoginIp) {
+            obj.setLatestLoginIp(latestLoginIp);
+            return this;
+        }
+
+        /**
+         * 设置最后登录时间
+         *
+         * @param latestLoginTime 最后登录时间
+         */
+        public Builder latestLoginTime(Date latestLoginTime) {
+            obj.setLatestLoginTime(latestLoginTime);
+            return this;
+        }
+
+        /**
+         * 设置累计警告次数
+         *
+         * @param totalAlert 累计警告次数
+         */
+        public Builder totalAlert(Integer totalAlert) {
+            obj.setTotalAlert(totalAlert);
+            return this;
+        }
+
+        /**
          * 设置是否冻结
          *
          * @param isFrozen 是否冻结

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

@@ -24,16 +24,22 @@ public class UserAbnormal implements Serializable {
     private Integer isAlert;
 
     /**
+     * 是否忽略该ip
+     */
+    @Column(name = "`is_ignore`")
+    private Integer isIgnore;
+
+    /**
      * 登录ip
      */
     @Column(name = "`login_ip`")
     private String loginIp;
 
     /**
-     * 已累计警告
+     * 登录地信息
      */
-    @Column(name = "`total_alert`")
-    private Integer totalAlert;
+    @Column(name = "`login_city`")
+    private String loginCity;
 
     @Column(name = "`create_time`")
     private Date createTime;
@@ -91,6 +97,24 @@ public class UserAbnormal implements Serializable {
     }
 
     /**
+     * 获取是否忽略该ip
+     *
+     * @return is_ignore - 是否忽略该ip
+     */
+    public Integer getIsIgnore() {
+        return isIgnore;
+    }
+
+    /**
+     * 设置是否忽略该ip
+     *
+     * @param isIgnore 是否忽略该ip
+     */
+    public void setIsIgnore(Integer isIgnore) {
+        this.isIgnore = isIgnore;
+    }
+
+    /**
      * 获取登录ip
      *
      * @return login_ip - 登录ip
@@ -109,21 +133,21 @@ public class UserAbnormal implements Serializable {
     }
 
     /**
-     * 获取已累计警告
+     * 获取登录地信息
      *
-     * @return total_alert - 已累计警告
+     * @return login_city - 登录地信息
      */
-    public Integer getTotalAlert() {
-        return totalAlert;
+    public String getLoginCity() {
+        return loginCity;
     }
 
     /**
-     * 设置已累计警告
+     * 设置登录地信息
      *
-     * @param totalAlert 已累计警告
+     * @param loginCity 登录地信息
      */
-    public void setTotalAlert(Integer totalAlert) {
-        this.totalAlert = totalAlert;
+    public void setLoginCity(String loginCity) {
+        this.loginCity = loginCity;
     }
 
     /**
@@ -149,8 +173,9 @@ public class UserAbnormal implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", isAlert=").append(isAlert);
+        sb.append(", isIgnore=").append(isIgnore);
         sb.append(", loginIp=").append(loginIp);
-        sb.append(", totalAlert=").append(totalAlert);
+        sb.append(", loginCity=").append(loginCity);
         sb.append(", createTime=").append(createTime);
         sb.append("]");
         return sb.toString();
@@ -196,6 +221,16 @@ public class UserAbnormal implements Serializable {
         }
 
         /**
+         * 设置是否忽略该ip
+         *
+         * @param isIgnore 是否忽略该ip
+         */
+        public Builder isIgnore(Integer isIgnore) {
+            obj.setIsIgnore(isIgnore);
+            return this;
+        }
+
+        /**
          * 设置登录ip
          *
          * @param loginIp 登录ip
@@ -206,12 +241,12 @@ public class UserAbnormal implements Serializable {
         }
 
         /**
-         * 设置已累计警告
+         * 设置登录地信息
          *
-         * @param totalAlert 已累计警告
+         * @param loginCity 登录地信息
          */
-        public Builder totalAlert(Integer totalAlert) {
-            obj.setTotalAlert(totalAlert);
+        public Builder loginCity(String loginCity) {
+            obj.setLoginCity(loginCity);
             return this;
         }
 

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

@@ -24,6 +24,12 @@ public class UserCourse implements Serializable {
     private Integer courseId;
 
     /**
+     * 当前课程记录id
+     */
+    @Column(name = "`record_id`")
+    private Integer recordId;
+
+    /**
      * 开通时间
      */
     @Column(name = "`start_time`")
@@ -35,12 +41,6 @@ public class UserCourse implements Serializable {
     @Column(name = "`expire_time`")
     private Date expireTime;
 
-    /**
-     * 课程扩展信息:json
-     */
-    @Column(name = "`extend`")
-    private String extend;
-
     private static final long serialVersionUID = 1L;
 
     /**
@@ -94,6 +94,24 @@ public class UserCourse implements Serializable {
     }
 
     /**
+     * 获取当前课程记录id
+     *
+     * @return record_id - 当前课程记录id
+     */
+    public Integer getRecordId() {
+        return recordId;
+    }
+
+    /**
+     * 设置当前课程记录id
+     *
+     * @param recordId 当前课程记录id
+     */
+    public void setRecordId(Integer recordId) {
+        this.recordId = recordId;
+    }
+
+    /**
      * 获取开通时间
      *
      * @return start_time - 开通时间
@@ -129,24 +147,6 @@ public class UserCourse implements Serializable {
         this.expireTime = expireTime;
     }
 
-    /**
-     * 获取课程扩展信息:json
-     *
-     * @return extend - 课程扩展信息:json
-     */
-    public String getExtend() {
-        return extend;
-    }
-
-    /**
-     * 设置课程扩展信息:json
-     *
-     * @param extend 课程扩展信息:json
-     */
-    public void setExtend(String extend) {
-        this.extend = extend;
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -156,9 +156,9 @@ public class UserCourse implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", courseId=").append(courseId);
+        sb.append(", recordId=").append(recordId);
         sb.append(", startTime=").append(startTime);
         sb.append(", expireTime=").append(expireTime);
-        sb.append(", extend=").append(extend);
         sb.append("]");
         return sb.toString();
     }
@@ -203,6 +203,16 @@ public class UserCourse implements Serializable {
         }
 
         /**
+         * 设置当前课程记录id
+         *
+         * @param recordId 当前课程记录id
+         */
+        public Builder recordId(Integer recordId) {
+            obj.setRecordId(recordId);
+            return this;
+        }
+
+        /**
          * 设置开通时间
          *
          * @param startTime 开通时间
@@ -222,16 +232,6 @@ public class UserCourse implements Serializable {
             return this;
         }
 
-        /**
-         * 设置课程扩展信息:json
-         *
-         * @param extend 课程扩展信息:json
-         */
-        public Builder extend(String extend) {
-            obj.setExtend(extend);
-            return this;
-        }
-
         public UserCourse build() {
             return this.obj;
         }

+ 19 - 54
server/data/src/main/java/com/qxgmat/data/dao/entity/UserCourseAppointment.java

@@ -45,14 +45,8 @@ public class UserCourseAppointment implements Serializable {
     /**
      * 频道号
      */
-    @Column(name = "`channel`")
-    private String channel;
-
-    /**
-     * 对应用户名
-     */
-    @Column(name = "`channel_username`")
-    private String channelUsername;
+    @Column(name = "`cctalk_channel`")
+    private String cctalkChannel;
 
     /**
      * 预约开始时间
@@ -67,7 +61,7 @@ public class UserCourseAppointment implements Serializable {
     private Date endTime;
 
     /**
-     * 作业id
+     * 用户作业id
      */
     @Column(name = "`paper_id`")
     private Integer paperId;
@@ -202,37 +196,19 @@ public class UserCourseAppointment implements Serializable {
     /**
      * 获取频道号
      *
-     * @return channel - 频道号
+     * @return cctalk_channel - 频道号
      */
-    public String getChannel() {
-        return channel;
+    public String getCctalkChannel() {
+        return cctalkChannel;
     }
 
     /**
      * 设置频道号
      *
-     * @param channel 频道号
-     */
-    public void setChannel(String channel) {
-        this.channel = channel;
-    }
-
-    /**
-     * 获取对应用户名
-     *
-     * @return channel_username - 对应用户名
+     * @param cctalkChannel 频道号
      */
-    public String getChannelUsername() {
-        return channelUsername;
-    }
-
-    /**
-     * 设置对应用户名
-     *
-     * @param channelUsername 对应用户名
-     */
-    public void setChannelUsername(String channelUsername) {
-        this.channelUsername = channelUsername;
+    public void setCctalkChannel(String cctalkChannel) {
+        this.cctalkChannel = cctalkChannel;
     }
 
     /**
@@ -272,18 +248,18 @@ public class UserCourseAppointment implements Serializable {
     }
 
     /**
-     * 获取作业id
+     * 获取用户作业id
      *
-     * @return paper_id - 作业id
+     * @return paper_id - 用户作业id
      */
     public Integer getPaperId() {
         return paperId;
     }
 
     /**
-     * 设置作业id
+     * 设置用户作业id
      *
-     * @param paperId 作业id
+     * @param paperId 用户作业id
      */
     public void setPaperId(Integer paperId) {
         this.paperId = paperId;
@@ -369,8 +345,7 @@ public class UserCourseAppointment implements Serializable {
         sb.append(", title=").append(title);
         sb.append(", recordId=").append(recordId);
         sb.append(", courseId=").append(courseId);
-        sb.append(", channel=").append(channel);
-        sb.append(", channelUsername=").append(channelUsername);
+        sb.append(", cctalkChannel=").append(cctalkChannel);
         sb.append(", startTime=").append(startTime);
         sb.append(", endTime=").append(endTime);
         sb.append(", paperId=").append(paperId);
@@ -464,20 +439,10 @@ public class UserCourseAppointment implements Serializable {
         /**
          * 设置频道号
          *
-         * @param channel 频道号
-         */
-        public Builder channel(String channel) {
-            obj.setChannel(channel);
-            return this;
-        }
-
-        /**
-         * 设置对应用户名
-         *
-         * @param channelUsername 对应用户名
+         * @param cctalkChannel 频道号
          */
-        public Builder channelUsername(String channelUsername) {
-            obj.setChannelUsername(channelUsername);
+        public Builder cctalkChannel(String cctalkChannel) {
+            obj.setCctalkChannel(cctalkChannel);
             return this;
         }
 
@@ -502,9 +467,9 @@ public class UserCourseAppointment implements Serializable {
         }
 
         /**
-         * 设置作业id
+         * 设置用户作业id
          *
-         * @param paperId 作业id
+         * @param paperId 用户作业id
          */
         public Builder paperId(Integer paperId) {
             obj.setPaperId(paperId);

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

@@ -29,6 +29,12 @@ public class UserCourseProgress implements Serializable {
     private Integer courseNoId;
 
     /**
+     * 课程记录id
+     */
+    @Column(name = "`record_id`")
+    private Integer recordId;
+
+    /**
      * 进度
      */
     @Column(name = "`progress`")
@@ -111,6 +117,24 @@ public class UserCourseProgress implements Serializable {
     }
 
     /**
+     * 获取课程记录id
+     *
+     * @return record_id - 课程记录id
+     */
+    public Integer getRecordId() {
+        return recordId;
+    }
+
+    /**
+     * 设置课程记录id
+     *
+     * @param recordId 课程记录id
+     */
+    public void setRecordId(Integer recordId) {
+        this.recordId = recordId;
+    }
+
+    /**
      * 获取进度
      *
      * @return progress - 进度
@@ -156,6 +180,7 @@ public class UserCourseProgress implements Serializable {
         sb.append(", userId=").append(userId);
         sb.append(", courseId=").append(courseId);
         sb.append(", courseNoId=").append(courseNoId);
+        sb.append(", recordId=").append(recordId);
         sb.append(", progress=").append(progress);
         sb.append(", times=").append(times);
         sb.append("]");
@@ -212,6 +237,16 @@ public class UserCourseProgress implements Serializable {
         }
 
         /**
+         * 设置课程记录id
+         *
+         * @param recordId 课程记录id
+         */
+        public Builder recordId(Integer recordId) {
+            obj.setRecordId(recordId);
+            return this;
+        }
+
+        /**
          * 设置进度
          *
          * @param progress 进度

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

@@ -24,6 +24,12 @@ public class UserCourseRecord implements Serializable {
     private Integer courseId;
 
     /**
+     * 课程记录id
+     */
+    @Column(name = "`record_id`")
+    private Integer recordId;
+
+    /**
      * 访问时长
      */
     @Column(name = "`user_time`")
@@ -91,6 +97,24 @@ public class UserCourseRecord implements Serializable {
     }
 
     /**
+     * 获取课程记录id
+     *
+     * @return record_id - 课程记录id
+     */
+    public Integer getRecordId() {
+        return recordId;
+    }
+
+    /**
+     * 设置课程记录id
+     *
+     * @param recordId 课程记录id
+     */
+    public void setRecordId(Integer recordId) {
+        this.recordId = recordId;
+    }
+
+    /**
      * 获取访问时长
      *
      * @return user_time - 访问时长
@@ -149,6 +173,7 @@ public class UserCourseRecord implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", courseId=").append(courseId);
+        sb.append(", recordId=").append(recordId);
         sb.append(", userTime=").append(userTime);
         sb.append(", courseNoId=").append(courseNoId);
         sb.append(", createTime=").append(createTime);
@@ -196,6 +221,16 @@ public class UserCourseRecord implements Serializable {
         }
 
         /**
+         * 设置课程记录id
+         *
+         * @param recordId 课程记录id
+         */
+        public Builder recordId(Integer recordId) {
+            obj.setRecordId(recordId);
+            return this;
+        }
+
+        /**
          * 设置访问时长
          *
          * @param userTime 访问时长

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

@@ -1,6 +1,7 @@
 package com.qxgmat.data.dao.entity;
 
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.util.Date;
@@ -49,8 +50,23 @@ public class UserOrder implements Serializable {
     @Column(name = "`invoice_money`")
     private BigDecimal invoiceMoney;
 
+    /**
+     * 优惠信息
+     */
+    @Column(name = "`promote`")
+    private JSONObject promote;
+
+    /**
+     * 回复时长
+     */
+    @Column(name = "`ask_time`")
+    private Integer askTime;
+
+    /**
+     * 支付id
+     */
     @Column(name = "`pay_id`")
-    private String payId;
+    private Long payId;
 
     /**
      * 支付状态
@@ -64,6 +80,12 @@ public class UserOrder implements Serializable {
     @Column(name = "`pay_time`")
     private Date payTime;
 
+    /**
+     * 交易流水号
+     */
+    @Column(name = "`transaction_no`")
+    private String transactionNo;
+
     private static final long serialVersionUID = 1L;
 
     /**
@@ -189,16 +211,56 @@ public class UserOrder implements Serializable {
     }
 
     /**
-     * @return pay_id
+     * 获取优惠信息
+     *
+     * @return promote - 优惠信息
+     */
+    public JSONObject getPromote() {
+        return promote;
+    }
+
+    /**
+     * 设置优惠信息
+     *
+     * @param promote 优惠信息
+     */
+    public void setPromote(JSONObject promote) {
+        this.promote = promote;
+    }
+
+    /**
+     * 获取回复时长
+     *
+     * @return ask_time - 回复时长
+     */
+    public Integer getAskTime() {
+        return askTime;
+    }
+
+    /**
+     * 设置回复时长
+     *
+     * @param askTime 回复时长
+     */
+    public void setAskTime(Integer askTime) {
+        this.askTime = askTime;
+    }
+
+    /**
+     * 获取支付id
+     *
+     * @return pay_id - 支付id
      */
-    public String getPayId() {
+    public Long getPayId() {
         return payId;
     }
 
     /**
-     * @param payId
+     * 设置支付id
+     *
+     * @param payId 支付id
      */
-    public void setPayId(String payId) {
+    public void setPayId(Long payId) {
         this.payId = payId;
     }
 
@@ -248,6 +310,24 @@ public class UserOrder implements Serializable {
         this.payTime = payTime;
     }
 
+    /**
+     * 获取交易流水号
+     *
+     * @return transaction_no - 交易流水号
+     */
+    public String getTransactionNo() {
+        return transactionNo;
+    }
+
+    /**
+     * 设置交易流水号
+     *
+     * @param transactionNo 交易流水号
+     */
+    public void setTransactionNo(String transactionNo) {
+        this.transactionNo = transactionNo;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -261,10 +341,13 @@ public class UserOrder implements Serializable {
         sb.append(", money=").append(money);
         sb.append(", originMoney=").append(originMoney);
         sb.append(", invoiceMoney=").append(invoiceMoney);
+        sb.append(", promote=").append(promote);
+        sb.append(", askTime=").append(askTime);
         sb.append(", payId=").append(payId);
         sb.append(", payStatus=").append(payStatus);
         sb.append(", createTime=").append(createTime);
         sb.append(", payTime=").append(payTime);
+        sb.append(", transactionNo=").append(transactionNo);
         sb.append("]");
         return sb.toString();
     }
@@ -349,9 +432,31 @@ public class UserOrder implements Serializable {
         }
 
         /**
-         * @param payId
+         * 设置优惠信息
+         *
+         * @param promote 优惠信息
          */
-        public Builder payId(String payId) {
+        public Builder promote(JSONObject promote) {
+            obj.setPromote(promote);
+            return this;
+        }
+
+        /**
+         * 设置回复时长
+         *
+         * @param askTime 回复时长
+         */
+        public Builder askTime(Integer askTime) {
+            obj.setAskTime(askTime);
+            return this;
+        }
+
+        /**
+         * 设置支付id
+         *
+         * @param payId 支付id
+         */
+        public Builder payId(Long payId) {
             obj.setPayId(payId);
             return this;
         }
@@ -382,6 +487,16 @@ public class UserOrder implements Serializable {
             return this;
         }
 
+        /**
+         * 设置交易流水号
+         *
+         * @param transactionNo 交易流水号
+         */
+        public Builder transactionNo(String transactionNo) {
+            obj.setTransactionNo(transactionNo);
+            return this;
+        }
+
         public UserOrder build() {
             return this.obj;
         }

+ 141 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/UserOrderCheckout.java

@@ -1,6 +1,7 @@
 package com.qxgmat.data.dao.entity;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.util.Date;
 import javax.persistence.*;
 
@@ -24,6 +25,12 @@ public class UserOrderCheckout implements Serializable {
     private Integer orderId;
 
     /**
+     * 套餐id
+     */
+    @Column(name = "`parent_id`")
+    private Integer parentId;
+
+    /**
      * 产品类型
      */
     @Column(name = "`product_type`")
@@ -47,6 +54,24 @@ public class UserOrderCheckout implements Serializable {
     @Column(name = "`param`")
     private String param;
 
+    /**
+     * 数量:购买的课时数
+     */
+    @Column(name = "`number`")
+    private Integer number;
+
+    /**
+     * 购买金额
+     */
+    @Column(name = "`money`")
+    private BigDecimal money;
+
+    /**
+     * 原价
+     */
+    @Column(name = "`origin_money`")
+    private BigDecimal originMoney;
+
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -103,6 +128,24 @@ public class UserOrderCheckout implements Serializable {
     }
 
     /**
+     * 获取套餐id
+     *
+     * @return parent_id - 套餐id
+     */
+    public Integer getParentId() {
+        return parentId;
+    }
+
+    /**
+     * 设置套餐id
+     *
+     * @param parentId 套餐id
+     */
+    public void setParentId(Integer parentId) {
+        this.parentId = parentId;
+    }
+
+    /**
      * 获取产品类型
      *
      * @return product_type - 产品类型
@@ -175,6 +218,60 @@ public class UserOrderCheckout implements Serializable {
     }
 
     /**
+     * 获取数量:购买的课时数
+     *
+     * @return number - 数量:购买的课时数
+     */
+    public Integer getNumber() {
+        return number;
+    }
+
+    /**
+     * 设置数量:购买的课时数
+     *
+     * @param number 数量:购买的课时数
+     */
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    /**
+     * 获取购买金额
+     *
+     * @return money - 购买金额
+     */
+    public BigDecimal getMoney() {
+        return money;
+    }
+
+    /**
+     * 设置购买金额
+     *
+     * @param money 购买金额
+     */
+    public void setMoney(BigDecimal money) {
+        this.money = money;
+    }
+
+    /**
+     * 获取原价
+     *
+     * @return origin_money - 原价
+     */
+    public BigDecimal getOriginMoney() {
+        return originMoney;
+    }
+
+    /**
+     * 设置原价
+     *
+     * @param originMoney 原价
+     */
+    public void setOriginMoney(BigDecimal originMoney) {
+        this.originMoney = originMoney;
+    }
+
+    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -197,10 +294,14 @@ public class UserOrderCheckout implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", orderId=").append(orderId);
+        sb.append(", parentId=").append(parentId);
         sb.append(", productType=").append(productType);
         sb.append(", productId=").append(productId);
         sb.append(", service=").append(service);
         sb.append(", param=").append(param);
+        sb.append(", number=").append(number);
+        sb.append(", money=").append(money);
+        sb.append(", originMoney=").append(originMoney);
         sb.append(", createTime=").append(createTime);
         sb.append("]");
         return sb.toString();
@@ -246,6 +347,16 @@ public class UserOrderCheckout implements Serializable {
         }
 
         /**
+         * 设置套餐id
+         *
+         * @param parentId 套餐id
+         */
+        public Builder parentId(Integer parentId) {
+            obj.setParentId(parentId);
+            return this;
+        }
+
+        /**
          * 设置产品类型
          *
          * @param productType 产品类型
@@ -286,6 +397,36 @@ public class UserOrderCheckout implements Serializable {
         }
 
         /**
+         * 设置数量:购买的课时数
+         *
+         * @param number 数量:购买的课时数
+         */
+        public Builder number(Integer number) {
+            obj.setNumber(number);
+            return this;
+        }
+
+        /**
+         * 设置购买金额
+         *
+         * @param money 购买金额
+         */
+        public Builder money(BigDecimal money) {
+            obj.setMoney(money);
+            return this;
+        }
+
+        /**
+         * 设置原价
+         *
+         * @param originMoney 原价
+         */
+        public Builder originMoney(BigDecimal originMoney) {
+            obj.setOriginMoney(originMoney);
+            return this;
+        }
+
+        /**
          * @param createTime
          */
         public Builder createTime(Date createTime) {

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

@@ -26,8 +26,8 @@ public class UserOrderRecord implements Serializable {
     /**
      * 套餐id
      */
-    @Column(name = "`parent_record_id`")
-    private Integer parentRecordId;
+    @Column(name = "`parent_id`")
+    private Integer parentId;
 
     /**
      * 产品类型
@@ -66,18 +66,24 @@ public class UserOrderRecord implements Serializable {
     private Integer teacherId;
 
     /**
-     * vs课时数量
+     * 购买的数量:vs课时数量
      */
-    @Column(name = "`vs_number`")
-    private Integer vsNumber;
+    @Column(name = "`number`")
+    private Integer number;
 
     /**
-     * 回复时
+     * 回复时
      */
     @Column(name = "`ask_time`")
     private Integer askTime;
 
     /**
+     * cctalk用户名
+     */
+    @Column(name = "`cctalk_name`")
+    private String cctalkName;
+
+    /**
      * 订阅:千行、资料等
      */
     @Column(name = "`is_subscribe`")
@@ -114,11 +120,41 @@ public class UserOrderRecord implements Serializable {
     private Integer isUsed;
 
     /**
+     * 开通时间
+     */
+    @Column(name = "`use_time`")
+    private Date useTime;
+
+    /**
      * 是否停用
      */
     @Column(name = "`is_stop`")
     private Integer isStop;
 
+    /**
+     * 停用时间
+     */
+    @Column(name = "`stop_time`")
+    private Date stopTime;
+
+    /**
+     * 是否挂起:停课
+     */
+    @Column(name = "`is_suspend`")
+    private Integer isSuspend;
+
+    /**
+     * 挂起时间
+     */
+    @Column(name = "`suspend_time`")
+    private Date suspendTime;
+
+    /**
+     * 恢复时间
+     */
+    @Column(name = "`restore_time`")
+    private Date restoreTime;
+
     @Column(name = "`create_time`")
     private Date createTime;
 
@@ -177,19 +213,19 @@ public class UserOrderRecord implements Serializable {
     /**
      * 获取套餐id
      *
-     * @return parent_record_id - 套餐id
+     * @return parent_id - 套餐id
      */
-    public Integer getParentRecordId() {
-        return parentRecordId;
+    public Integer getParentId() {
+        return parentId;
     }
 
     /**
      * 设置套餐id
      *
-     * @param parentRecordId 套餐id
+     * @param parentId 套餐id
      */
-    public void setParentRecordId(Integer parentRecordId) {
-        this.parentRecordId = parentRecordId;
+    public void setParentId(Integer parentId) {
+        this.parentId = parentId;
     }
 
     /**
@@ -301,42 +337,60 @@ public class UserOrderRecord implements Serializable {
     }
 
     /**
-     * 获取vs课时数量
+     * 获取购买的数量:vs课时数量
      *
-     * @return vs_number - vs课时数量
+     * @return number - 购买的数量:vs课时数量
      */
-    public Integer getVsNumber() {
-        return vsNumber;
+    public Integer getNumber() {
+        return number;
     }
 
     /**
-     * 设置vs课时数量
+     * 设置购买的数量:vs课时数量
      *
-     * @param vsNumber vs课时数量
+     * @param number 购买的数量:vs课时数量
      */
-    public void setVsNumber(Integer vsNumber) {
-        this.vsNumber = vsNumber;
+    public void setNumber(Integer number) {
+        this.number = number;
     }
 
     /**
-     * 获取回复时
+     * 获取回复时
      *
-     * @return ask_time - 回复时
+     * @return ask_time - 回复时
      */
     public Integer getAskTime() {
         return askTime;
     }
 
     /**
-     * 设置回复时
+     * 设置回复时
      *
-     * @param askTime 回复时
+     * @param askTime 回复时
      */
     public void setAskTime(Integer askTime) {
         this.askTime = askTime;
     }
 
     /**
+     * 获取cctalk用户名
+     *
+     * @return cctalk_name - cctalk用户名
+     */
+    public String getCctalkName() {
+        return cctalkName;
+    }
+
+    /**
+     * 设置cctalk用户名
+     *
+     * @param cctalkName cctalk用户名
+     */
+    public void setCctalkName(String cctalkName) {
+        this.cctalkName = cctalkName;
+    }
+
+    /**
      * 获取订阅:千行、资料等
      *
      * @return is_subscribe - 订阅:千行、资料等
@@ -445,6 +499,24 @@ public class UserOrderRecord implements Serializable {
     }
 
     /**
+     * 获取开通时间
+     *
+     * @return use_time - 开通时间
+     */
+    public Date getUseTime() {
+        return useTime;
+    }
+
+    /**
+     * 设置开通时间
+     *
+     * @param useTime 开通时间
+     */
+    public void setUseTime(Date useTime) {
+        this.useTime = useTime;
+    }
+
+    /**
      * 获取是否停用
      *
      * @return is_stop - 是否停用
@@ -463,6 +535,78 @@ public class UserOrderRecord implements Serializable {
     }
 
     /**
+     * 获取停用时间
+     *
+     * @return stop_time - 停用时间
+     */
+    public Date getStopTime() {
+        return stopTime;
+    }
+
+    /**
+     * 设置停用时间
+     *
+     * @param stopTime 停用时间
+     */
+    public void setStopTime(Date stopTime) {
+        this.stopTime = stopTime;
+    }
+
+    /**
+     * 获取是否挂起:停课
+     *
+     * @return is_suspend - 是否挂起:停课
+     */
+    public Integer getIsSuspend() {
+        return isSuspend;
+    }
+
+    /**
+     * 设置是否挂起:停课
+     *
+     * @param isSuspend 是否挂起:停课
+     */
+    public void setIsSuspend(Integer isSuspend) {
+        this.isSuspend = isSuspend;
+    }
+
+    /**
+     * 获取挂起时间
+     *
+     * @return suspend_time - 挂起时间
+     */
+    public Date getSuspendTime() {
+        return suspendTime;
+    }
+
+    /**
+     * 设置挂起时间
+     *
+     * @param suspendTime 挂起时间
+     */
+    public void setSuspendTime(Date suspendTime) {
+        this.suspendTime = suspendTime;
+    }
+
+    /**
+     * 获取恢复时间
+     *
+     * @return restore_time - 恢复时间
+     */
+    public Date getRestoreTime() {
+        return restoreTime;
+    }
+
+    /**
+     * 设置恢复时间
+     *
+     * @param restoreTime 恢复时间
+     */
+    public void setRestoreTime(Date restoreTime) {
+        this.restoreTime = restoreTime;
+    }
+
+    /**
      * @return create_time
      */
     public Date getCreateTime() {
@@ -485,22 +629,28 @@ public class UserOrderRecord implements Serializable {
         sb.append(", id=").append(id);
         sb.append(", userId=").append(userId);
         sb.append(", orderId=").append(orderId);
-        sb.append(", parentRecordId=").append(parentRecordId);
+        sb.append(", parentId=").append(parentId);
         sb.append(", productType=").append(productType);
         sb.append(", productId=").append(productId);
         sb.append(", service=").append(service);
         sb.append(", param=").append(param);
         sb.append(", source=").append(source);
         sb.append(", teacherId=").append(teacherId);
-        sb.append(", vsNumber=").append(vsNumber);
+        sb.append(", number=").append(number);
         sb.append(", askTime=").append(askTime);
+        sb.append(", cctalkName=").append(cctalkName);
         sb.append(", isSubscribe=").append(isSubscribe);
         sb.append(", startTime=").append(startTime);
         sb.append(", endTime=").append(endTime);
         sb.append(", useStartTime=").append(useStartTime);
         sb.append(", useEndTime=").append(useEndTime);
         sb.append(", isUsed=").append(isUsed);
+        sb.append(", useTime=").append(useTime);
         sb.append(", isStop=").append(isStop);
+        sb.append(", stopTime=").append(stopTime);
+        sb.append(", isSuspend=").append(isSuspend);
+        sb.append(", suspendTime=").append(suspendTime);
+        sb.append(", restoreTime=").append(restoreTime);
         sb.append(", createTime=").append(createTime);
         sb.append("]");
         return sb.toString();
@@ -548,10 +698,10 @@ public class UserOrderRecord implements Serializable {
         /**
          * 设置套餐id
          *
-         * @param parentRecordId 套餐id
+         * @param parentId 套餐id
          */
-        public Builder parentRecordId(Integer parentRecordId) {
-            obj.setParentRecordId(parentRecordId);
+        public Builder parentId(Integer parentId) {
+            obj.setParentId(parentId);
             return this;
         }
 
@@ -616,19 +766,19 @@ public class UserOrderRecord implements Serializable {
         }
 
         /**
-         * 设置vs课时数量
+         * 设置购买的数量:vs课时数量
          *
-         * @param vsNumber vs课时数量
+         * @param number 购买的数量:vs课时数量
          */
-        public Builder vsNumber(Integer vsNumber) {
-            obj.setVsNumber(vsNumber);
+        public Builder number(Integer number) {
+            obj.setNumber(number);
             return this;
         }
 
         /**
-         * 设置回复时
+         * 设置回复时
          *
-         * @param askTime 回复时
+         * @param askTime 回复时
          */
         public Builder askTime(Integer askTime) {
             obj.setAskTime(askTime);
@@ -636,6 +786,16 @@ public class UserOrderRecord implements Serializable {
         }
 
         /**
+         * 设置cctalk用户名
+         *
+         * @param cctalkName cctalk用户名
+         */
+        public Builder cctalkName(String cctalkName) {
+            obj.setCctalkName(cctalkName);
+            return this;
+        }
+
+        /**
          * 设置订阅:千行、资料等
          *
          * @param isSubscribe 订阅:千行、资料等
@@ -696,6 +856,16 @@ public class UserOrderRecord implements Serializable {
         }
 
         /**
+         * 设置开通时间
+         *
+         * @param useTime 开通时间
+         */
+        public Builder useTime(Date useTime) {
+            obj.setUseTime(useTime);
+            return this;
+        }
+
+        /**
          * 设置是否停用
          *
          * @param isStop 是否停用
@@ -706,6 +876,46 @@ public class UserOrderRecord implements Serializable {
         }
 
         /**
+         * 设置停用时间
+         *
+         * @param stopTime 停用时间
+         */
+        public Builder stopTime(Date stopTime) {
+            obj.setStopTime(stopTime);
+            return this;
+        }
+
+        /**
+         * 设置是否挂起:停课
+         *
+         * @param isSuspend 是否挂起:停课
+         */
+        public Builder isSuspend(Integer isSuspend) {
+            obj.setIsSuspend(isSuspend);
+            return this;
+        }
+
+        /**
+         * 设置挂起时间
+         *
+         * @param suspendTime 挂起时间
+         */
+        public Builder suspendTime(Date suspendTime) {
+            obj.setSuspendTime(suspendTime);
+            return this;
+        }
+
+        /**
+         * 设置恢复时间
+         *
+         * @param restoreTime 恢复时间
+         */
+        public Builder restoreTime(Date restoreTime) {
+            obj.setRestoreTime(restoreTime);
+            return this;
+        }
+
+        /**
          * @param createTime
          */
         public Builder createTime(Date createTime) {

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

@@ -55,6 +55,12 @@ public class UserQuestion implements Serializable {
     private Integer no;
 
     /**
+     * 子阶段序号
+     */
+    @Column(name = "`stage_no`")
+    private Integer stageNo;
+
+    /**
      * 系统定义时间
      */
     @Column(name = "`time`")
@@ -236,6 +242,24 @@ public class UserQuestion implements Serializable {
     }
 
     /**
+     * 获取子阶段序号
+     *
+     * @return stage_no - 子阶段序号
+     */
+    public Integer getStageNo() {
+        return stageNo;
+    }
+
+    /**
+     * 设置子阶段序号
+     *
+     * @param stageNo 子阶段序号
+     */
+    public void setStageNo(Integer stageNo) {
+        this.stageNo = stageNo;
+    }
+
+    /**
      * 获取系统定义时间
      *
      * @return time - 系统定义时间
@@ -371,6 +395,7 @@ public class UserQuestion implements Serializable {
         sb.append(", questionId=").append(questionId);
         sb.append(", questionNoId=").append(questionNoId);
         sb.append(", no=").append(no);
+        sb.append(", stageNo=").append(stageNo);
         sb.append(", time=").append(time);
         sb.append(", userTime=").append(userTime);
         sb.append(", userAnswer=").append(userAnswer);
@@ -472,6 +497,16 @@ public class UserQuestion implements Serializable {
         }
 
         /**
+         * 设置子阶段序号
+         *
+         * @param stageNo 子阶段序号
+         */
+        public Builder stageNo(Integer stageNo) {
+            obj.setStageNo(stageNo);
+            return this;
+        }
+
+        /**
          * 设置系统定义时间
          *
          * @param time 系统定义时间

+ 6 - 4
server/data/src/main/java/com/qxgmat/data/dao/mapping/CourseExperienceMapper.xml

@@ -7,11 +7,12 @@
     -->
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
+    <result column="nickname" jdbcType="VARCHAR" property="nickname" />
     <result column="title" jdbcType="VARCHAR" property="title" />
     <result column="link" jdbcType="VARCHAR" property="link" />
     <result column="prepare_status" jdbcType="VARCHAR" property="prepareStatus" />
-    <result column="prepare_examination_time" jdbcType="VARCHAR" property="prepareExaminationTime" />
-    <result column="experience_score" jdbcType="VARCHAR" property="experienceScore" />
+    <result column="experience_day" jdbcType="INTEGER" property="experienceDay" />
+    <result column="experience_score" jdbcType="INTEGER" property="experienceScore" />
     <result column="experience_percent" jdbcType="VARCHAR" property="experiencePercent" />
     <result column="view_number" jdbcType="INTEGER" property="viewNumber" />
     <result column="collect_number" jdbcType="INTEGER" property="collectNumber" />
@@ -28,8 +29,9 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `title`, `link`, `prepare_status`, `prepare_examination_time`, `experience_score`, 
-    `experience_percent`, `view_number`, `collect_number`, `create_time`, `update_time`
+    `id`, `user_id`, `nickname`, `title`, `link`, `prepare_status`, `experience_day`, 
+    `experience_score`, `experience_percent`, `view_number`, `collect_number`, `create_time`, 
+    `update_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--

+ 24 - 0
server/data/src/main/java/com/qxgmat/data/dao/mapping/PreviewAssignMapper.xml

@@ -0,0 +1,24 @@
+<?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.PreviewAssignMapper">
+  <resultMap id="BaseResultMap" type="com.qxgmat.data.dao.entity.PreviewAssign">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+    <result column="paper_id" jdbcType="INTEGER" property="paperId" />
+    <result column="course_id" jdbcType="INTEGER" property="courseId" />
+    <result column="course_no" jdbcType="INTEGER" property="courseNo" />
+    <result column="course_time" jdbcType="INTEGER" property="courseTime" />
+    <result column="course_appointment" jdbcType="INTEGER" property="courseAppointment" />
+    <result column="target_time" jdbcType="TIMESTAMP" property="targetTime" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    `id`, `paper_id`, `course_id`, `course_no`, `course_time`, `course_appointment`, 
+    `target_time`, `create_time`
+  </sql>
+</mapper>

+ 1 - 8
server/data/src/main/java/com/qxgmat/data/dao/mapping/PreviewPaperMapper.xml

@@ -10,14 +10,8 @@
     <result column="course_id" jdbcType="INTEGER" property="courseId" />
     <result column="course_module" jdbcType="VARCHAR" property="courseModule" />
     <result column="course_no" jdbcType="INTEGER" property="courseNo" />
-    <result column="course_time" jdbcType="INTEGER" property="courseTime" />
-    <result column="appointment_id" jdbcType="INTEGER" property="appointmentId" />
     <result column="question_no_ids" jdbcType="VARCHAR" property="questionNoIds" typeHandler="com.nuliji.tools.mybatis.handler.IntegerArrayWithJsonHandler" />
     <result column="paper_module" jdbcType="VARCHAR" property="paperModule" />
-    <result column="user_ids" jdbcType="VARCHAR" property="userIds" typeHandler="com.nuliji.tools.mybatis.handler.IntegerArrayWithJsonHandler" />
-    <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
-    <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
-    <result column="finish" jdbcType="INTEGER" property="finish" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
   </resultMap>
@@ -25,8 +19,7 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `title`, `course_id`, `course_module`, `course_no`, `course_time`, `appointment_id`, 
-    `question_no_ids`, `paper_module`, `user_ids`, `start_time`, `end_time`, `finish`, 
+    `id`, `title`, `course_id`, `course_module`, `course_no`, `question_no_ids`, `paper_module`, 
     `create_time`, `update_time`
   </sql>
 </mapper>

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

@@ -8,14 +8,15 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="is_alert" jdbcType="INTEGER" property="isAlert" />
+    <result column="is_ignore" jdbcType="INTEGER" property="isIgnore" />
     <result column="login_ip" jdbcType="VARCHAR" property="loginIp" />
-    <result column="total_alert" jdbcType="INTEGER" property="totalAlert" />
+    <result column="login_city" jdbcType="VARCHAR" property="loginCity" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `is_alert`, `login_ip`, `total_alert`, `create_time`
+    `id`, `user_id`, `is_alert`, `is_ignore`, `login_ip`, `login_city`, `create_time`
   </sql>
 </mapper>

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

@@ -11,8 +11,7 @@
     <result column="title" jdbcType="VARCHAR" property="title" />
     <result column="record_id" jdbcType="INTEGER" property="recordId" />
     <result column="course_id" jdbcType="INTEGER" property="courseId" />
-    <result column="channel" jdbcType="VARCHAR" property="channel" />
-    <result column="channel_username" jdbcType="VARCHAR" property="channelUsername" />
+    <result column="cctalk_channel" jdbcType="VARCHAR" property="cctalkChannel" />
     <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
     <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
     <result column="paper_id" jdbcType="INTEGER" property="paperId" />
@@ -25,7 +24,7 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `no`, `title`, `record_id`, `course_id`, `channel`, `channel_username`, 
-    `start_time`, `end_time`, `paper_id`, `is_finish`, `supply_list`, `note_list`, `create_time`
+    `id`, `user_id`, `no`, `title`, `record_id`, `course_id`, `cctalk_channel`, `start_time`, 
+    `end_time`, `paper_id`, `is_finish`, `supply_list`, `note_list`, `create_time`
   </sql>
 </mapper>

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

@@ -8,25 +8,14 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="course_id" jdbcType="INTEGER" property="courseId" />
+    <result column="record_id" jdbcType="INTEGER" property="recordId" />
     <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
     <result column="expire_time" jdbcType="TIMESTAMP" property="expireTime" />
   </resultMap>
-  <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.qxgmat.data.dao.entity.UserCourse">
-    <!--
-      WARNING - @mbg.generated
-    -->
-    <result column="extend" jdbcType="LONGVARCHAR" property="extend" />
-  </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `course_id`, `start_time`, `expire_time`
-  </sql>
-  <sql id="Blob_Column_List">
-    <!--
-      WARNING - @mbg.generated
-    -->
-    `extend`
+    `id`, `user_id`, `course_id`, `record_id`, `start_time`, `expire_time`
   </sql>
 </mapper>

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

@@ -9,6 +9,7 @@
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="course_id" jdbcType="INTEGER" property="courseId" />
     <result column="course_no_id" jdbcType="INTEGER" property="courseNoId" />
+    <result column="record_id" jdbcType="INTEGER" property="recordId" />
     <result column="progress" jdbcType="INTEGER" property="progress" />
     <result column="times" jdbcType="INTEGER" property="times" />
   </resultMap>
@@ -16,6 +17,6 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `course_id`, `course_no_id`, `progress`, `times`
+    `id`, `user_id`, `course_id`, `course_no_id`, `record_id`, `progress`, `times`
   </sql>
 </mapper>

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

@@ -8,6 +8,7 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="course_id" jdbcType="INTEGER" property="courseId" />
+    <result column="record_id" jdbcType="INTEGER" property="recordId" />
     <result column="user_time" jdbcType="INTEGER" property="userTime" />
     <result column="course_no_id" jdbcType="INTEGER" property="courseNoId" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -16,6 +17,6 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `course_id`, `user_time`, `course_no_id`, `create_time`
+    `id`, `user_id`, `course_id`, `record_id`, `user_time`, `course_no_id`, `create_time`
   </sql>
 </mapper>

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

@@ -37,6 +37,10 @@
     <result column="total_money" jdbcType="DECIMAL" property="totalMoney" />
     <result column="invite_number" jdbcType="INTEGER" property="inviteNumber" />
     <result column="register_ip" jdbcType="VARCHAR" property="registerIp" />
+    <result column="register_city" jdbcType="VARCHAR" property="registerCity" />
+    <result column="latest_login_ip" jdbcType="VARCHAR" property="latestLoginIp" />
+    <result column="latest_login_time" jdbcType="TIMESTAMP" property="latestLoginTime" />
+    <result column="total_alert" jdbcType="INTEGER" property="totalAlert" />
     <result column="is_frozen" jdbcType="INTEGER" property="isFrozen" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="data_email_subscribe" jdbcType="INTEGER" property="dataEmailSubscribe" />
@@ -52,6 +56,7 @@
     `real_photo_front`, `real_photo_back`, `real_status`, `prepare_time`, `prepare_status`, 
     `prepare_goal`, `prepare_examination_time`, `prepare_score_time`, `latest_exercise`, 
     `latest_error`, `origin_id`, `invite_code`, `total_money`, `invite_number`, `register_ip`, 
-    `is_frozen`, `create_time`, `data_email_subscribe`, `textbook_email_subscribe`
+    `register_city`, `latest_login_ip`, `latest_login_time`, `total_alert`, `is_frozen`, 
+    `create_time`, `data_email_subscribe`, `textbook_email_subscribe`
   </sql>
 </mapper>

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

@@ -8,16 +8,21 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="order_id" jdbcType="INTEGER" property="orderId" />
+    <result column="parent_id" jdbcType="INTEGER" property="parentId" />
     <result column="product_type" jdbcType="VARCHAR" property="productType" />
     <result column="product_id" jdbcType="INTEGER" property="productId" />
     <result column="service" jdbcType="VARCHAR" property="service" />
     <result column="param" jdbcType="VARCHAR" property="param" />
+    <result column="number" jdbcType="INTEGER" property="number" />
+    <result column="money" jdbcType="DECIMAL" property="money" />
+    <result column="origin_money" jdbcType="DECIMAL" property="originMoney" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `order_id`, `product_type`, `product_id`, `service`, `param`, `create_time`
+    `id`, `user_id`, `order_id`, `parent_id`, `product_type`, `product_id`, `service`, 
+    `param`, `number`, `money`, `origin_money`, `create_time`
   </sql>
 </mapper>

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

@@ -12,16 +12,19 @@
     <result column="money" jdbcType="DECIMAL" property="money" />
     <result column="origin_money" jdbcType="DECIMAL" property="originMoney" />
     <result column="invoice_money" jdbcType="DECIMAL" property="invoiceMoney" />
-    <result column="pay_id" jdbcType="VARCHAR" property="payId" />
+    <result column="promote" jdbcType="VARCHAR" property="promote" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
+    <result column="ask_time" jdbcType="INTEGER" property="askTime" />
+    <result column="pay_id" jdbcType="BIGINT" property="payId" />
     <result column="pay_status" jdbcType="INTEGER" property="payStatus" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
     <result column="pay_time" jdbcType="TIMESTAMP" property="payTime" />
+    <result column="transaction_no" jdbcType="VARCHAR" property="transactionNo" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `product_types`, `pay_method`, `money`, `origin_money`, `invoice_money`, 
-    `pay_id`, `pay_status`, `create_time`, `pay_time`
+    `promote`, `ask_time`, `pay_id`, `pay_status`, `create_time`, `pay_time`, `transaction_no`
   </sql>
 </mapper>

+ 12 - 5
server/data/src/main/java/com/qxgmat/data/dao/mapping/UserOrderRecordMapper.xml

@@ -8,30 +8,37 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="order_id" jdbcType="INTEGER" property="orderId" />
-    <result column="parent_record_id" jdbcType="INTEGER" property="parentRecordId" />
+    <result column="parent_id" jdbcType="INTEGER" property="parentId" />
     <result column="product_type" jdbcType="VARCHAR" property="productType" />
     <result column="product_id" jdbcType="INTEGER" property="productId" />
     <result column="service" jdbcType="VARCHAR" property="service" />
     <result column="param" jdbcType="VARCHAR" property="param" />
     <result column="source" jdbcType="VARCHAR" property="source" />
     <result column="teacher_id" jdbcType="INTEGER" property="teacherId" />
-    <result column="vs_number" jdbcType="INTEGER" property="vsNumber" />
+    <result column="number" jdbcType="INTEGER" property="number" />
     <result column="ask_time" jdbcType="INTEGER" property="askTime" />
+    <result column="cctalk_name" jdbcType="VARCHAR" property="cctalkName" />
     <result column="is_subscribe" jdbcType="INTEGER" property="isSubscribe" />
     <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
     <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
     <result column="use_start_time" jdbcType="TIMESTAMP" property="useStartTime" />
     <result column="use_end_time" jdbcType="TIMESTAMP" property="useEndTime" />
     <result column="is_used" jdbcType="INTEGER" property="isUsed" />
+    <result column="use_time" jdbcType="TIMESTAMP" property="useTime" />
     <result column="is_stop" jdbcType="INTEGER" property="isStop" />
+    <result column="stop_time" jdbcType="TIMESTAMP" property="stopTime" />
+    <result column="is_suspend" jdbcType="INTEGER" property="isSuspend" />
+    <result column="suspend_time" jdbcType="TIMESTAMP" property="suspendTime" />
+    <result column="restore_time" jdbcType="TIMESTAMP" property="restoreTime" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `order_id`, `parent_record_id`, `product_type`, `product_id`, `service`, 
-    `param`, `source`, `teacher_id`, `vs_number`, `ask_time`, `is_subscribe`, `start_time`, 
-    `end_time`, `use_start_time`, `use_end_time`, `is_used`, `is_stop`, `create_time`
+    `id`, `user_id`, `order_id`, `parent_id`, `product_type`, `product_id`, `service`, 
+    `param`, `source`, `teacher_id`, `number`, `ask_time`, `cctalk_name`, `is_subscribe`, 
+    `start_time`, `end_time`, `use_start_time`, `use_end_time`, `is_used`, `use_time`, 
+    `is_stop`, `stop_time`, `is_suspend`, `suspend_time`, `restore_time`, `create_time`
   </sql>
 </mapper>

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

@@ -13,6 +13,7 @@
     <result column="question_id" jdbcType="INTEGER" property="questionId" />
     <result column="question_no_id" jdbcType="INTEGER" property="questionNoId" />
     <result column="no" jdbcType="INTEGER" property="no" />
+    <result column="stage_no" jdbcType="INTEGER" property="stageNo" />
     <result column="time" jdbcType="INTEGER" property="time" />
     <result column="user_time" jdbcType="INTEGER" property="userTime" />
     <result column="user_answer" jdbcType="VARCHAR" property="userAnswer" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
@@ -26,7 +27,7 @@
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `report_id`, `question_module`, `question_type`, `question_id`, 
-    `question_no_id`, `no`, `time`, `user_time`, `user_answer`, `is_correct`, `setting`, 
-    `detail`, `create_time`
+    `question_no_id`, `no`, `stage_no`, `time`, `user_time`, `user_answer`, `is_correct`, 
+    `setting`, `detail`, `create_time`
   </sql>
 </mapper>

+ 6 - 0
server/data/src/main/java/com/qxgmat/data/relation/CourseDataRelationMapper.java

@@ -18,4 +18,10 @@ public interface CourseDataRelationMapper {
             String order,
             String direction
     );
+
+    void accumulation(
+            @Param("id") Number dataId,
+            @Param("view") int view,
+            @Param("sale") int sale
+    );
 }

+ 17 - 0
server/data/src/main/java/com/qxgmat/data/relation/CourseExperienceRelationMapper.java

@@ -0,0 +1,17 @@
+package com.qxgmat.data.relation;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface CourseExperienceRelationMapper {
+
+    void accumulation(
+            @Param("id") Number experienceId,
+            @Param("view") int view,
+            @Param("collect") int collect
+    );
+}

+ 15 - 0
server/data/src/main/java/com/qxgmat/data/relation/CoursePackageRelationMapper.java

@@ -0,0 +1,15 @@
+package com.qxgmat.data.relation;
+
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface CoursePackageRelationMapper {
+
+    void accumulation(
+            @Param("id") Number packageId,
+            @Param("sale") int sale
+    );
+}

+ 17 - 0
server/data/src/main/java/com/qxgmat/data/relation/CourseRelationMapper.java

@@ -0,0 +1,17 @@
+package com.qxgmat.data.relation;
+
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * Created by gaojie on 2017/11/9.
+ */
+public interface CourseRelationMapper {
+
+    void accumulation(
+            @Param("id") Number courseId,
+            @Param("trail") int trail,
+            @Param("sale") int sale,
+            @Param("packageSale") int packageSale
+    );
+}

+ 5 - 0
server/data/src/main/java/com/qxgmat/data/relation/UserRelationMapper.java

@@ -5,6 +5,7 @@ import com.qxgmat.data.dao.entity.UserReport;
 import com.qxgmat.data.relation.entity.UserPrepareRelation;
 import org.apache.ibatis.annotations.Param;
 
+import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.List;
 
@@ -25,4 +26,8 @@ public interface UserRelationMapper {
             @Param("courseId") Number courseId
     );
 
+    void accumulation(
+            @Param("id") Number userId,
+            @Param("money") BigDecimal money
+    );
 }

+ 11 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseDataRelationMapper.xml

@@ -14,6 +14,17 @@
     -->
     cd.`id`
   </sql>
+
+  <!--累加记录-->
+  <update id="accumulation">
+    UPDATE `course_data`
+    <trim prefix="set" suffixOverrides=",">
+      `view_number`=`view_number`+#{view, jdbcType=INTEGER},
+      `sale_number`=`sale_number`+#{sale, jdbcType=INTEGER},
+    </trim>
+    WHERE `id` = #{id, jdbcType=VARCHAR}
+  </update>
+
   <!--
     获取用户购买的资料记录
   -->

+ 28 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseExperienceRelationMapper.xml

@@ -0,0 +1,28 @@
+<?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.CourseExperienceRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.CourseExperience">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    cd.`id`
+  </sql>
+
+  <!--累加记录-->
+  <update id="accumulation">
+    UPDATE `course_experience`
+    <trim prefix="set" suffixOverrides=",">
+      `view_number`=`view_number`+#{view, jdbcType=INTEGER},
+      `collect_number`=`collect_number`+#{collect, jdbcType=INTEGER},
+    </trim>
+    WHERE `id` = #{id, jdbcType=VARCHAR}
+  </update>
+
+</mapper>

+ 27 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/CoursePackageRelationMapper.xml

@@ -0,0 +1,27 @@
+<?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.CoursePackageRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.CoursePackage">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    cd.`id`
+  </sql>
+
+  <!--累加记录-->
+  <update id="accumulation">
+    UPDATE `course_package`
+    <trim prefix="set" suffixOverrides=",">
+      `sale_number`=`sale_number`+#{sale, jdbcType=INTEGER},
+    </trim>
+    WHERE `id` = #{id, jdbcType=VARCHAR}
+  </update>
+
+</mapper>

+ 29 - 0
server/data/src/main/java/com/qxgmat/data/relation/mapping/CourseRelationMapper.xml

@@ -0,0 +1,29 @@
+<?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.CoursePackageRelationMapper">
+  <resultMap id="IdMap" type="com.qxgmat.data.dao.entity.CoursePackage">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    <id column="id" jdbcType="INTEGER" property="id" />
+  </resultMap>
+
+  <sql id="Id_Column_List">
+    <!--
+      WARNING - @mbg.generated
+    -->
+    cd.`id`
+  </sql>
+
+  <!--累加记录-->
+  <update id="accumulation">
+    UPDATE `course`
+    <trim prefix="set" suffixOverrides=",">
+      `trail_number`=`trail_number`+#{trail, jdbcType=INTEGER},
+      `sale_number`=`sale_number`+#{sale, jdbcType=INTEGER},
+      `package_sale_number`=`package_sale_number`+#{packageSale, jdbcType=INTEGER},
+    </trim>
+    WHERE `id` = #{id, jdbcType=VARCHAR}
+  </update>
+
+</mapper>

+ 12 - 3
server/data/src/main/java/com/qxgmat/data/relation/mapping/UserRelationMapper.xml

@@ -22,6 +22,15 @@
     ur.`id`
   </sql>
 
+  <!--累加支付-->
+  <update id="accumulation">
+    UPDATE `user`
+    <trim prefix="set" suffixOverrides=",">
+      `total_money`=`total_money`+#{monney, jdbcType=DECIMAL},
+    </trim>
+    WHERE `id` = #{userId, jdbcType=VARCHAR}
+  </update>
+
   <!--
     字符串字段统计
   -->
@@ -56,11 +65,11 @@
     select
     <include refid="Id_Column_List" />
     from `user` u
-    left join `user_course` uc on uc.`user_id` = u.`id`
+    left join `user_order_record` ur on ur.`user_id` = u.`id` and ur.`product_type` = 'course'
     <if test="courseId != null">
-      and uc.`course_id` = #{courseId,jdbcType=VARCHAR}
+      and ur.`product_id` = #{courseId,jdbcType=VARCHAR}
     </if>
-    where uc.`id` &gt; 0
+    where ur.`id` &gt; 0
     <if test="keyword != null">
       and (u.`mobile` like #{keywordLike,jdbcType=VARCHAR}
         or u.`id` like #{keywordLike,jdbcType=VARCHAR})

+ 1 - 0
server/data/src/main/resources/mybatis-generator.xml

@@ -169,6 +169,7 @@
         <table schema="qianxing" tableName="user_order" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false" delimitAllColumns="true">
             <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
             <columnOverride column="product_types" javaType="com.alibaba.fastjson.JSONArray" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonArrayHandler"/>
+            <columnOverride column="promote" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
         </table>
     </context>
 </generatorConfiguration>

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

@@ -1,10 +1,9 @@
 package com.qxgmat.controller.admin;
 
 import com.github.pagehelper.Page;
-import com.nuliji.tools.PageMessage;
-import com.nuliji.tools.Response;
-import com.nuliji.tools.ResponseHelp;
-import com.nuliji.tools.Transform;
+import com.nuliji.tools.*;
+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.ProductType;
 import com.qxgmat.data.constants.enums.status.AskStatus;
@@ -20,6 +19,7 @@ import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.ManagerService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.CourseExtendService;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.inline.*;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -85,6 +85,9 @@ public class CourseController {
     @Autowired
     private CourseExtendService courseExtendService;
 
+    @Autowired
+    private OrderFlowService orderFlowService;
+
 
     @RequestMapping(value = "/add", method = RequestMethod.POST)
     @ApiOperation(value = "添加课程", httpMethod = "POST")
@@ -282,7 +285,7 @@ public class CourseController {
 
     @RequestMapping(value = "/experience/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "编辑心经", httpMethod = "PUT")
-    public Response<Boolean> editExperience(@RequestBody @Validated CourseDataDto dto, HttpServletRequest request) {
+    public Response<Boolean> editExperience(@RequestBody @Validated CourseExperienceDto dto, HttpServletRequest request) {
         CourseExperience entity = Transform.dtoToEntity(dto);
         entity = courseExperienceService.edit(entity);
         managerLogService.log(request);
@@ -311,10 +314,14 @@ public class CourseController {
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) Integer userId,
             @RequestParam(required = false) String keyword,
+            @RequestParam(required = false) String prepareStatus,
+            @RequestParam(required = false) String experienceScore,
+            @RequestParam(required = false) String experienceDay,
+            @RequestParam(required = false) String experiencePercent,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
-        Page<CourseExperience> p = courseExperienceService.listAdmin(page, size, userId, keyword, order, DirectionStatus.ValueOf(direction));
+        Page<CourseExperience> p = courseExperienceService.listAdmin(page, size, userId, keyword, prepareStatus, ExperienceScoreRange.ValueOf(experienceScore), ExperienceDayRange.ValueOf(experienceDay), experiencePercent, order, DirectionStatus.ValueOf(direction));
         List<CourseExperienceListDto> pr = Transform.convert(p, CourseExperienceListDto.class);
 
         // 绑定用户
@@ -541,17 +548,14 @@ public class CourseController {
     @ApiOperation(value = "添加vs学员", httpMethod = "POST")
     private Response<Boolean> addStudentVs(@RequestBody @Validated UserCourseRecordDto dto){
         UserOrderRecord entity = Transform.dtoToEntity(dto);
-        Date startTime = new Date();
-        Date endTime = courseExtendService.computeExpire(startTime, dto.getVsNumber(), dto.getCourseId());
         entity.setProductType(ProductType.COURSE.key);
         entity.setOrderId(0);
         entity.setProductId(dto.getCourseId());
         entity.setTeacherId(dto.getTeacherId());
         entity.setSource("offline");
-        entity.setIsUsed(1);
-        entity.setStartTime(startTime);
-        entity.setEndTime(endTime);
-        userOrderRecordService.add(entity);
+        // 预约后开通: 有效期开通后计算
+        orderFlowService.addRecord(entity);
+
         return ResponseHelp.success(true);
     }
 
@@ -560,8 +564,12 @@ public class CourseController {
     private Response<Boolean> editStudentVs(@RequestBody @Validated UserCourseRecordDto dto){
         UserOrderRecord entity = Transform.dtoToEntity(dto);
         UserOrderRecord in = userOrderRecordService.get(entity.getId());
-        Date endTime = courseExtendService.computeExpire(in.getStartTime(), dto.getVsNumber(), dto.getCourseId());
-        entity.setEndTime(endTime);
+        if (in.getIsUsed() > 0){
+            // 已开通,重新计算有效期
+            Course course = courseService.get(dto.getCourseId());
+            Integer expireDay = courseExtendService.computeExpire(dto.getNumber(), course);
+            entity.setEndTime(Tools.addDate(in.getStartTime(), expireDay));
+        }
         userOrderRecordService.edit(entity);
         return ResponseHelp.success(true);
     }

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

@@ -304,19 +304,19 @@ public class SettingController {
         return ResponseHelp.success(entity.getValue());
     }
 
-    @RequestMapping(value = "/course_promote", method = RequestMethod.PUT)
-    @ApiOperation(value = "修改课程促销", httpMethod = "PUT")
-    private Response<Boolean> editCoursePromote(@RequestBody @Validated JSONObject dto){
-        Setting entity = settingService.getByKey(SettingKey.COURSE_PROMOTE);
+    @RequestMapping(value = "/promote", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改促销", httpMethod = "PUT")
+    private Response<Boolean> editPromote(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.PROMOTE);
         entity.setValue(dto);
         settingService.edit(entity);
         return ResponseHelp.success(true);
     }
 
-    @RequestMapping(value = "/course_promote", method = RequestMethod.GET)
+    @RequestMapping(value = "/promote", method = RequestMethod.GET)
     @ApiOperation(value = "获取课程促销", httpMethod = "GET")
-    private Response<JSONObject> getCoursePromote(){
-        Setting entity = settingService.getByKey(SettingKey.COURSE_PROMOTE);
+    private Response<JSONObject> getPromote(){
+        Setting entity = settingService.getByKey(SettingKey.PROMOTE);
 
         return ResponseHelp.success(entity.getValue());
     }

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

@@ -2,6 +2,7 @@ package com.qxgmat.controller.admin;
 
 import com.github.pagehelper.Page;
 import com.nuliji.tools.*;
+import com.nuliji.tools.exception.ParameterException;
 import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.constants.enums.module.FeedbackModule;
 import com.qxgmat.data.constants.enums.module.ProductType;
@@ -20,6 +21,7 @@ import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.ManagerService;
 import com.qxgmat.service.UserPaperService;
 import com.qxgmat.service.UserServiceService;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.extend.PreviewService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.inline.*;
@@ -110,6 +112,9 @@ public class UserController {
     private UserOrderRecordService userOrderRecordService;
 
     @Autowired
+    private OrderFlowService orderFlowService;
+
+    @Autowired
     private UserInvoiceService userInvoiceService;
 
 //    @RequestMapping(value = "/add", method = RequestMethod.POST)
@@ -383,22 +388,20 @@ public class UserController {
             User user = usersService.getByMobile(dto.getArea(), dto.getMobile());
             if (user == null){
                 // 创建user
-                user = usersService.register(dto.getArea(), dto.getMobile(), null, null);
+                user = usersService.register(dto.getArea(), dto.getMobile(), null, null,"", null);
             }
             entity.setUserId(user.getId());
         }
-        userOrderRecordService.add(entity);
+        orderFlowService.addRecord(entity);
         return ResponseHelp.success(true);
     }
 
     @RequestMapping(value = "/service/edit", method = RequestMethod.PUT)
     @ApiOperation(value = "修改服务记录", httpMethod = "PUT")
     private Response<Boolean> editService(@RequestBody @Validated UserServiceRecordDto dto){
-        UserOrderRecord entity = Transform.dtoToEntity(dto);
-        entity.setOrderId(0);
-        entity.setProductId(0);
-        entity.setProductType(ProductType.SERVICE.key);
-        userOrderRecordService.edit(entity);
+        if (dto.getIsStop() > 0){
+            orderFlowService.stopRecord(dto.getId());
+        }
         return ResponseHelp.success(true);
     }
 
@@ -432,6 +435,16 @@ public class UserController {
     @RequestMapping(value = "/course/appointment/add", method = RequestMethod.POST)
     @ApiOperation(value = "添加课程预约", httpMethod = "POST")
     public Response<UserCourseAppointment> addCourseAppointment(@RequestBody @Validated UserCourseAppointmentDto dto, HttpServletRequest request) {
+        // 判断课程是否已开通,否则先开通课程
+        UserOrderRecord record = userOrderRecordService.get(dto.getRecordId());
+        if (record==null){
+            throw new ParameterException("记录不存在");
+        }
+        if (record.getIsUsed()==0){
+            orderFlowService.useRecord(dto.getUserId(), dto.getRecordId());
+        }else if(record.getIsStop() > 0){
+            throw new ParameterException("已停用");
+        }
         UserCourseAppointment entity = Transform.dtoToEntity(dto);
         entity = userCourseAppointmentService.addAppointment(entity);
         managerLogService.log(request);

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

@@ -2,6 +2,7 @@ package com.qxgmat.controller.api;
 
 import com.nuliji.tools.Response;
 import com.nuliji.tools.ResponseHelp;
+import com.nuliji.tools.Tools;
 import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.AuthException;
 import com.nuliji.tools.exception.ParameterException;
@@ -10,11 +11,13 @@ import com.qxgmat.data.constants.enums.ServiceKey;
 import com.qxgmat.data.dao.entity.User;
 import com.qxgmat.dto.request.*;
 import com.qxgmat.dto.response.MyDto;
+import com.qxgmat.help.AiHelp;
 import com.qxgmat.help.CaptchaHelp;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.help.SmsHelp;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.UserServiceService;
+import com.qxgmat.service.inline.UserAbnormalService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +29,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import javax.validation.Validator;
+import java.util.Date;
 
 /**
  * Created by GaoJie on 2017/10/31.
@@ -36,15 +40,15 @@ import javax.validation.Validator;
 public class AuthController {
 
     @Autowired
-    private Validator validator;
-
-    @Autowired
     private CaptchaHelp captchaHelp;
 
     @Autowired
     private SmsHelp smsHelp;
 
     @Autowired
+    private AiHelp aiHelp;
+
+    @Autowired
     private ShiroHelp shiroHelp;
 
     @Autowired
@@ -53,6 +57,9 @@ public class AuthController {
     @Autowired
     private UserServiceService userServiceService;
 
+    @Autowired
+    private UserAbnormalService userAbnormalService;
+
 
     @RequestMapping(value = "/token", method = RequestMethod.POST)
     @ApiOperation(value = "验证token", httpMethod = "POST")
@@ -94,27 +101,15 @@ public class AuthController {
             throw new ParameterException("手机验证码错误!");
         }
         try {
-            User user = usersService.register(userLoginDto.getArea(), userLoginDto.getMobile(), userLoginDto.getInviteCode(), null);
+            String ip = Tools.getClientIp(request);
+            User user = usersService.register(userLoginDto.getArea(), userLoginDto.getMobile(), userLoginDto.getInviteCode(), null, ip, aiHelp.parseIp(ip));
         }catch (ParameterException e){
             // 忽略已注册信息
         }
         shiroHelp.getSession().login(shiroHelp.user(userLoginDto.getArea()+":"+userLoginDto.getMobile(), ""));
 
         User entity = shiroHelp.getLoginUser();
-        MyDto dto = Transform.convert(entity, MyDto.class);
-        if (!entity.getMobile().isEmpty()){
-            dto.setBindMobile(true);
-        }
-        if (!entity.getWechatUnionid().isEmpty()){
-            dto.setBindWechat(true);
-        }
-        if (entity.getRealStatus() > 0){
-            dto.setBindReal(true);
-        }
-        if(!entity.getPrepareStatus().isEmpty()){
-            dto.setBindPrepare(true);
-        }
-        dto.setVip(userServiceService.hasService(entity.getId(), ServiceKey.VIP));
+        MyDto dto = processUser(entity, request);
         return ResponseHelp.success(dto);
     }
 
@@ -123,7 +118,7 @@ public class AuthController {
     @ApiOperation(value = "直接微信二维码登录", httpMethod = "GET")
     public Response<MyDto> directWechatPc(
             @RequestParam(required = false, defaultValue = "") String code,
-            HttpSession session, HttpServletResponse response) {
+            HttpSession session, HttpServletRequest request) {
         User user = (User) shiroHelp.getLoginUser();
         if (user!=null){
             // 已登录用户,绑定
@@ -132,20 +127,7 @@ public class AuthController {
             shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_pc"));
         }
         User entity = shiroHelp.getLoginUser();
-        MyDto dto = Transform.convert(entity, MyDto.class);
-        if (!entity.getMobile().isEmpty()){
-            dto.setBindMobile(true);
-        }
-        if (!entity.getWechatUnionid().isEmpty()){
-            dto.setBindWechat(true);
-        }
-        if (entity.getRealStatus() > 0){
-            dto.setBindReal(true);
-        }
-        if(!entity.getPrepareStatus().isEmpty()){
-            dto.setBindPrepare(true);
-        }
-        dto.setVip(userServiceService.hasService(entity.getId(), ServiceKey.VIP));
+        MyDto dto = processUser(entity, request);
         return ResponseHelp.success(dto);
     }
 
@@ -153,7 +135,7 @@ public class AuthController {
     @ApiOperation(value = "直接微信二维码登录", httpMethod = "GET")
     public Response<MyDto> directWechat(
             @RequestParam(required = false, defaultValue = "") String code,
-            HttpSession session, HttpServletResponse response) {
+            HttpSession session, HttpServletRequest request) {
         User user = (User) shiroHelp.getLoginUser();
         if (user!=null){
             // 已登录用户,绑定
@@ -162,20 +144,7 @@ public class AuthController {
             shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_native"));
         }
         User entity = shiroHelp.getLoginUser();
-        MyDto dto = Transform.convert(entity, MyDto.class);
-        if (!entity.getMobile().isEmpty()){
-            dto.setBindMobile(true);
-        }
-        if (!entity.getWechatUnionid().isEmpty()){
-            dto.setBindWechat(true);
-        }
-        if (entity.getRealStatus() > 0){
-            dto.setBindReal(true);
-        }
-        if(!entity.getPrepareStatus().isEmpty()){
-            dto.setBindPrepare(true);
-        }
-        dto.setVip(userServiceService.hasService(entity.getId(), ServiceKey.VIP));
+        MyDto dto = processUser(entity, request);
         return ResponseHelp.success(dto);
     }
 
@@ -188,7 +157,7 @@ public class AuthController {
 
     @RequestMapping(value = "/bind", method = RequestMethod.POST)
     @ApiOperation(value = "绑定手机号", notes="第三方登录后可执行", httpMethod = "POST")
-    public Response<Boolean> bind(@RequestBody @Validated UserValidMobileDto userValidMobileDto, HttpSession session) {
+    public Response<Boolean> bind(@RequestBody @Validated UserValidMobileDto userValidMobileDto, HttpSession session, HttpServletRequest request) {
         if (!smsHelp.verifyCode(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getMobileVerifyCode(), session)) {
             throw new ParameterException("验证码有误,请重新获取!");
         }
@@ -200,7 +169,8 @@ public class AuthController {
 
         try{
             // 创建新的账号,设定手机号,绑定第三方登录
-            User user = usersService.register(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getInviteCode(), openUser);
+            String ip = Tools.getClientIp(request);
+            User user = usersService.register(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getInviteCode(), openUser, ip, aiHelp.parseIp(ip));
         }catch (ParameterException e){
             throw new ParameterException("该手机号绑定其他账号,请更换手机号码!");
         }
@@ -245,4 +215,36 @@ public class AuthController {
         }
         return ResponseHelp.success(true);
     }
+
+    private MyDto processUser(User user, HttpServletRequest request){
+        MyDto dto = Transform.convert(user, MyDto.class);
+        if (user.getId() == null || user.getId() == 0) return dto;
+        String ip = Tools.getClientIp(request);
+        User entity = User.builder().id(user.getId()).build();
+        entity.setLatestLoginTime(new Date());
+        if (!user.getRegisterIp().equals(ip) && !user.getLatestLoginIp().equals(ip)){
+            entity.setLatestLoginIp(ip);
+            // 登录异常处理
+            if(!aiHelp.compareIp(user.getRegisterIp(), ip)){
+                String[] info = aiHelp.parseIp(ip);
+                userAbnormalService.push(user.getId(), ip, info);
+            }
+        }
+        // 更新登录信息
+        usersService.edit(entity);
+        if (!user.getMobile().isEmpty()){
+            dto.setBindMobile(true);
+        }
+        if (!user.getWechatUnionid().isEmpty()){
+            dto.setBindWechat(true);
+        }
+        if (user.getRealStatus() > 0){
+            dto.setBindReal(true);
+        }
+        if(!user.getPrepareStatus().isEmpty()){
+            dto.setBindPrepare(true);
+        }
+        dto.setVip(userServiceService.hasService(user.getId(), ServiceKey.VIP));
+        return dto;
+    }
 }

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

@@ -3,6 +3,8 @@ package com.qxgmat.controller.api;
 
 import com.github.pagehelper.Page;
 import com.nuliji.tools.*;
+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.ProductType;
 import com.qxgmat.data.constants.enums.status.DirectionStatus;
@@ -193,14 +195,14 @@ public class CourseController {
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) String prepareStatus,
-            @RequestParam(required = false) String prepareExaminationTime,
+            @RequestParam(required = false) String experienceDay,
             @RequestParam(required = false) String experienceScore,
             @RequestParam(required = false) String experiencePercent,
             @RequestParam(required = false, defaultValue = "id") String order,
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
 
-        Page<CourseExperience> p = courseExperienceService.list(page, size, prepareStatus, prepareExaminationTime, experienceScore, experiencePercent, order, DirectionStatus.ValueOf(direction));
+        Page<CourseExperience> p = courseExperienceService.list(page, size, prepareStatus, ExperienceScoreRange.ValueOf(experienceScore), ExperienceDayRange.ValueOf(experienceDay), experiencePercent, order, DirectionStatus.ValueOf(direction));
 
         return ResponseHelp.success(p, page, size, p.getTotal());
     }
@@ -209,7 +211,7 @@ public class CourseController {
     @ApiOperation(value = "获取课程进度", notes = "获取所有课程及状态进度", httpMethod = "GET")
     public Response<List<UserCourseDetailDto>> progress()  {
         User user = (User) shiroHelp.getLoginUser();
-        List<UserCourse> userCourseList = userCourseService.getByUser(user.getId());
+        List<UserCourse> userCourseList = userOrderRecordService.getByUser(user.getId());
         List<UserCourseDetailDto> dtos = Transform.convert(userCourseList, UserCourseDetailDto.class);
 
         // 获取每个科目的最后2次作业

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

@@ -25,6 +25,7 @@ import com.qxgmat.help.AiHelp;
 import com.qxgmat.help.MailHelp;
 import com.qxgmat.help.ShiroHelp;
 import com.qxgmat.service.*;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.extend.QuestionFlowService;
 import com.qxgmat.service.inline.*;
 import io.swagger.annotations.Api;
@@ -143,10 +144,10 @@ public class MyController {
     private UserPaperQuestionService userPaperQuestionService;
 
     @Autowired
-    private UserOrderRecordService userOrderRecordService;
+    private QuestionFlowService questionFlowService;
 
     @Autowired
-    private QuestionFlowService questionFlowService;
+    private OrderFlowService orderFlowService;
 
     @RequestMapping(value = "/email", method = RequestMethod.POST)
     @ApiOperation(value = "绑定邮箱", httpMethod = "POST")
@@ -263,7 +264,7 @@ public class MyController {
                 .realStatus(1)
                 .realTime(new Date())
                 .build());
-        // todo 180天vip
+        orderFlowService.giveReal(user.getId());
         return ResponseHelp.success(dto);
     }
 
@@ -332,6 +333,10 @@ public class MyController {
         User entity = Transform.dtoToEntity(dto);
         User user = (User) shiroHelp.getLoginUser();
         entity.setId(user.getId());
+        if (user.getPrepareTime() == null){
+            // 邀请奖励
+            orderFlowService.givePrepare(user.getId());
+        }
         entity.setPrepareTime(new Date());
         usersService.edit(entity);
 
@@ -985,50 +990,6 @@ public class MyController {
         return ResponseHelp.success(true);
     }
 
-    @RequestMapping(value = "/record/list", method = RequestMethod.GET)
-    @ApiOperation(value = "获取订单记录列表", notes = "获取订单记录列表", httpMethod = "GET")
-    public Response<PageMessage<UserOrderRecord>> listRecord(
-            @RequestParam(required = false, defaultValue = "1") int page,
-            @RequestParam(required = false, defaultValue = "100") int size,
-            @RequestParam(required = false, defaultValue = "id") String order,
-            @RequestParam(required = false, defaultValue = "desc") String direction,
-            HttpSession session)  {
-        User user = (User) shiroHelp.getLoginUser();
-
-        Page<UserOrderRecord> p = userOrderRecordService.list(page, size, user.getId(), order, DirectionStatus.ValueOf(direction));
-
-        return ResponseHelp.success(p, page, size, p.getTotal());
-    }
-
-    @RequestMapping(value = "/record/detail", method = RequestMethod.GET)
-    @ApiOperation(value = "获取订单记录", notes = "获取订单记录", httpMethod = "GET")
-    public Response<UserOrderRecord> getOrderRecord(
-            @RequestParam(required = true) Integer id
-    )  {
-        User user = (User) shiroHelp.getLoginUser();
-        UserOrderRecord record = userOrderRecordService.get(id);
-        if (!record.getUserId().equals(user.getId())){
-            throw new ParameterException("记录不存在");
-        }
-        // todo
-
-        return ResponseHelp.success(record);
-    }
-
-    @RequestMapping(value = "/record/use", method = RequestMethod.POST)
-    @ApiOperation(value = "开通", notes = "开通", httpMethod = "POST")
-    public Response<UserOrderRecord> useRecord(@RequestBody @Validated RecordOpenDto dto)  {
-        User user = (User) shiroHelp.getLoginUser();
-        UserOrderRecord record = userOrderRecordService.get(dto.getRecordId());
-        if (!record.getUserId().equals(user.getId())){
-            throw new ParameterException("记录不存在");
-        }
-
-        // todo
-
-        return ResponseHelp.success(record);
-    }
-
     @RequestMapping(value = "/data/history", method = RequestMethod.GET)
     @ApiOperation(value = "资料更新记录", httpMethod = "GET")
     public Response<PageMessage<CourseDataHistoryInfoDto>> listDataHistory(

+ 158 - 26
server/gateway-api/src/main/java/com/qxgmat/controller/api/OrderController.java

@@ -1,15 +1,26 @@
 package com.qxgmat.controller.api;
 
-import com.alibaba.fastjson.JSONObject;
+import com.github.pagehelper.Page;
+import com.nuliji.tools.PageMessage;
 import com.nuliji.tools.Response;
 import com.nuliji.tools.ResponseHelp;
+import com.nuliji.tools.Transform;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.pay.data.PayResponseData;
+import com.qxgmat.data.constants.enums.module.ProductType;
+import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.constants.enums.trade.PayChannel;
+import com.qxgmat.data.constants.enums.trade.PayModule;
 import com.qxgmat.data.dao.entity.*;
+import com.qxgmat.dto.extend.CourseDataExtendDto;
+import com.qxgmat.dto.extend.CourseExtendDto;
+import com.qxgmat.dto.extend.CoursePackageExtendDto;
 import com.qxgmat.dto.request.PayOrderDto;
 import com.qxgmat.dto.request.RecordAddDto;
+import com.qxgmat.dto.request.RecordUseDto;
+import com.qxgmat.dto.response.UserOrderPreDto;
 import com.qxgmat.help.ShiroHelp;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.extend.TradeService;
 import com.qxgmat.service.inline.*;
 import io.swagger.annotations.Api;
@@ -20,7 +31,11 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/api/order")
@@ -40,6 +55,9 @@ public class OrderController {
     private PayService payService;
 
     @Autowired
+    private OrderFlowService orderFlowService;
+
+    @Autowired
     private UserOrderService userOrderService;
 
     @Autowired
@@ -48,51 +66,74 @@ public class OrderController {
     @Autowired
     private UserOrderCheckoutService userOrderCheckoutService;
 
+    @Autowired
+    private CourseService courseService;
+
+    @Autowired
+    private CoursePackageService coursePackageService;
+
+    @Autowired
+    private CourseDataService courseDataService;
+
     @RequestMapping(value = "/checkout/all", method = RequestMethod.GET)
     @ApiOperation(value = "全部购物车", notes = "全部购物车", httpMethod = "GET")
-    public Response<List<>> allCheckout(HttpServletRequest request) throws Exception {
+    public Response<UserOrderPreDto> allCheckout(HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
-        return ResponseHelp.success(data);
+        //
+        List<UserOrderCheckout> list = userOrderCheckoutService.allByUser(user.getId(), 0);
+        UserOrder order = orderFlowService.preOrderWithCheckout(user.getId(), list);
+
+        return ResponseHelp.success(detail(user.getId(), order, list));
     }
 
     @RequestMapping(value = "/checkout/add", method = RequestMethod.POST)
     @ApiOperation(value = "添加购物车", notes = "添加购物车", httpMethod = "POST")
-    public Response<PayResponseData> addCheckout(@RequestBody @Validated RecordAddDto dto, HttpServletRequest request) throws Exception {
+    public Response<Integer> addCheckout(@RequestBody @Validated RecordAddDto dto, HttpServletRequest request)  {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
-        return ResponseHelp.success(data);
+        UserOrderCheckout checkout = Transform.dtoToEntity(dto);
+        int number = orderFlowService.addCheckout(user.getId(), checkout);
+        return ResponseHelp.success(number);
     }
 
     @RequestMapping(value = "/checkout/delete", method = RequestMethod.DELETE)
     @ApiOperation(value = "删除购物车", notes = "删除购物车", httpMethod = "DELETE")
-    public Response<PayResponseData> deleteCheckout(@RequestParam int id, HttpServletRequest request) throws Exception {
+    public Response<Integer> deleteCheckout(@RequestParam int checkoutId, HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
-        return ResponseHelp.success(data);
+        int number = orderFlowService.removeCheckout(checkoutId, user.getId());
+        return ResponseHelp.success(number);
     }
 
     @RequestMapping(value = "/pay/confirm", method = RequestMethod.POST)
-    @ApiOperation(value = "确认支付", notes = "确认支付", httpMethod = "POST")
-    public Response<PayResponseData> addCheckout(@RequestBody @Validated RecordAddDto dto, HttpServletRequest request) throws Exception {
+    @ApiOperation(value = "确认支付:创建订单", notes = "创建订单", httpMethod = "POST")
+    public Response<UserOrderPreDto> confirmPay(HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
-        return ResponseHelp.success(data);
+        UserOrder order = orderFlowService.makeOrder(user.getId());
+
+        return ResponseHelp.success(detail(user.getId(), order, null));
     }
 
     @RequestMapping(value = "/pay/speed", method = RequestMethod.POST)
-    @ApiOperation(value = "确认支付", notes = "确认支付", httpMethod = "POST")
-    public Response<PayResponseData> addCheckout(@RequestBody @Validated RecordAddDto dto, HttpServletRequest request) throws Exception {
+    @ApiOperation(value = "确认支付:单条记录创建订单", notes = "单条记录创建订单", httpMethod = "POST")
+    public Response<UserOrderPreDto> speedPay(@RequestBody @Validated RecordAddDto dto, HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
-        return ResponseHelp.success(data);
+        UserOrderCheckout checkout = Transform.dtoToEntity(dto);
+        UserOrder order = orderFlowService.makeOrderWithProduct(user.getId(), checkout);
+
+        return ResponseHelp.success(detail(user.getId(), order, null));
     }
 
     @RequestMapping(value = "/pay/wechat_qr", method = RequestMethod.POST)
     @ApiOperation(value = "通过微信二维码支付", notes = "通过微信二维码支付", httpMethod = "POST")
     public Response<PayResponseData> wechatQr(@RequestBody @Validated PayOrderDto dto, HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
+        UserOrder order = userOrderService.get(dto.getOrderId());
+        if (order == null) {
+            throw new ParameterException("订单不存在");
+        }
+        if (!order.getUserId().equals(user.getId())){
+            throw new ParameterException("订单不存在");
+        }
+        PayResponseData data = tradeService.pay(user.getId(), "", "", PayModule.ORDER, order.getId(), BigDecimal.valueOf(0), PayChannel.WECHAT_QR, request);
         return ResponseHelp.success(data);
     }
 
@@ -100,7 +141,14 @@ public class OrderController {
     @ApiOperation(value = "通过微信内支付", notes = "通过微信内支付", httpMethod = "POST")
     public Response<PayResponseData> wechatJs(@RequestBody @Validated PayOrderDto dto, HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.WECHAT_JS, request);
+        UserOrder order = userOrderService.get(dto.getOrderId());
+        if (order == null) {
+            throw new ParameterException("订单不存在");
+        }
+        if (!order.getUserId().equals(user.getId())){
+            throw new ParameterException("订单不存在");
+        }
+        PayResponseData data = tradeService.pay(user.getId(), "", "", PayModule.ORDER, order.getId(), BigDecimal.valueOf(0), PayChannel.WECHAT_JS, request);
         return ResponseHelp.success(data);
     }
 
@@ -108,21 +156,105 @@ public class OrderController {
     @ApiOperation(value = "通过支付宝二维码支付", notes = "通过支付宝二维码支付", httpMethod = "POST")
     public Response<PayResponseData> alipayQr(@RequestBody @Validated PayOrderDto dto, HttpServletRequest request) throws Exception {
         User user = (User) shiroHelp.getLoginUser();
-        PayResponseData data = tradeService.pay(user.getId(), "", "", dto.getModule(), JSONObject.parseObject(JSONObject.toJSONString(dto.getModuleExtend())), BigDecimal.valueOf(0), PayChannel.ALIPAY_QR, request);
+        UserOrder order = userOrderService.get(dto.getOrderId());
+        if (order == null) {
+            throw new ParameterException("订单不存在");
+        }
+        if (!order.getUserId().equals(user.getId())){
+            throw new ParameterException("订单不存在");
+        }
+        PayResponseData data = tradeService.pay(user.getId(), "", "", PayModule.ORDER, order.getId(), BigDecimal.valueOf(0), PayChannel.ALIPAY_QR, request);
         return ResponseHelp.success(data);
     }
 
     @RequestMapping(value = "/pay/query", method = RequestMethod.GET)
     @ApiOperation(value = "支付结果查询", notes = "支付结果查询", httpMethod = "GET")
     public Response<Boolean> response(
-            @RequestParam(required = true, name="id") Long payId
+            @RequestParam(required = true, name="id") Long orderId
     ) {
-        Pay pay = payService.get(payId);
         User user = (User) shiroHelp.getLoginUser();
-
-        if (!pay.getUserId().equals(user.getId())){
+        UserOrder order = userOrderService.get(orderId);
+        if (order == null) {
+            throw new ParameterException("订单不存在");
+        }
+        if (!order.getUserId().equals(user.getId())){
             throw new ParameterException("支付信息不存在");
         }
-        return ResponseHelp.success(payService.isPayed(pay));
+        return ResponseHelp.success(order.getPayStatus() > 0);
+    }
+
+
+    @RequestMapping(value = "/record/list", method = RequestMethod.GET)
+    @ApiOperation(value = "获取订单记录列表", notes = "获取订单记录列表", httpMethod = "GET")
+    public Response<PageMessage<UserOrderRecord>> listRecord(
+            @RequestParam(required = false, defaultValue = "1") int page,
+            @RequestParam(required = false, defaultValue = "100") int size,
+            @RequestParam(required = false, defaultValue = "id") String order,
+            @RequestParam(required = false, defaultValue = "desc") String direction,
+            HttpSession session)  {
+        User user = (User) shiroHelp.getLoginUser();
+
+        Page<UserOrderRecord> p = userOrderRecordService.list(page, size, user.getId(), order, DirectionStatus.ValueOf(direction));
+
+        return ResponseHelp.success(p, page, size, p.getTotal());
+    }
+
+    @RequestMapping(value = "/record/detail", method = RequestMethod.GET)
+    @ApiOperation(value = "获取订单记录", notes = "获取订单记录", httpMethod = "GET")
+    public Response<UserOrderRecord> getOrderRecord(
+            @RequestParam(required = true) Integer id
+    )  {
+        User user = (User) shiroHelp.getLoginUser();
+        UserOrderRecord record = userOrderRecordService.get(id);
+        if (!record.getUserId().equals(user.getId())){
+            throw new ParameterException("记录不存在");
+        }
+        // todo
+
+        return ResponseHelp.success(record);
+    }
+
+    @RequestMapping(value = "/record/use", method = RequestMethod.POST)
+    @ApiOperation(value = "开通", notes = "开通", httpMethod = "POST")
+    public Response<UserOrderRecord> useRecord(@RequestBody @Validated RecordUseDto dto)  {
+        User user = (User) shiroHelp.getLoginUser();
+
+        UserOrderRecord record = orderFlowService.useRecord(user.getId(), dto.getRecordId());
+        return ResponseHelp.success(record);
+    }
+
+    /**
+     * 统一处理订单以及完整购物车返回信息
+     * @param userId
+     * @param order
+     * @param list
+     * @return
+     */
+    public UserOrderPreDto detail(Integer userId, UserOrder order, List<UserOrderCheckout> list)  {
+        UserOrderPreDto dto = Transform.convert(order, UserOrderPreDto.class);
+        if (list == null){
+            list = userOrderCheckoutService.allByUser(userId, order.getId());
+        }
+        dto.setCheckouts(list);
+
+        // 获取所有课程信息
+        List<UserOrderCheckout> courseCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.COURSE.key)).collect(Collectors.toList());
+        Collection courseIds = Transform.getIds(courseCheckout, UserOrderCheckout.class, "productId");
+        List<Course> courseList = courseService.select(courseIds);
+        dto.setCourses(Transform.convert(courseList, CourseExtendDto.class));
+
+        // 获取所有资料信息
+        List<UserOrderCheckout> dataCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.DATA.key)).collect(Collectors.toList());
+        Collection dataIds = Transform.getIds(dataCheckout, UserOrderCheckout.class, "productId");
+        List<CourseData> dataList = courseDataService.select(dataIds);
+        dto.setDatas(Transform.convert(dataList, CourseDataExtendDto.class));
+
+        // 获取所有套餐信息
+        List<UserOrderCheckout> packageCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.COURSE_PACKAGE.key)).collect(Collectors.toList());
+        Collection packageIds = Transform.getIds(packageCheckout, UserOrderCheckout.class, "productId");
+        List<CoursePackage> packageList = coursePackageService.select(packageIds);
+        dto.setPackages(Transform.convert(packageList, CoursePackageExtendDto.class));
+
+        return dto;
     }
 }

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

@@ -11,8 +11,20 @@ public class CourseExperienceDto {
 
     private Integer userId;
 
+    private String nickname;
+
     private String title;
 
+    private String link;
+
+    private String prepareStatus;
+
+    private Integer experienceDay;
+
+    private Integer experienceScore;
+
+    private String experiencePercent;
+
     private String content;
 
     public Integer getId() {
@@ -46,4 +58,52 @@ public class CourseExperienceDto {
     public void setUserId(Integer userId) {
         this.userId = userId;
     }
+
+    public String getNickname() {
+        return nickname;
+    }
+
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+    public String getPrepareStatus() {
+        return prepareStatus;
+    }
+
+    public void setPrepareStatus(String prepareStatus) {
+        this.prepareStatus = prepareStatus;
+    }
+
+    public Integer getExperienceDay() {
+        return experienceDay;
+    }
+
+    public void setExperienceDay(Integer experienceDay) {
+        this.experienceDay = experienceDay;
+    }
+
+    public Integer getExperienceScore() {
+        return experienceScore;
+    }
+
+    public void setExperienceScore(Integer experienceScore) {
+        this.experienceScore = experienceScore;
+    }
+
+    public String getExperiencePercent() {
+        return experiencePercent;
+    }
+
+    public void setExperiencePercent(String experiencePercent) {
+        this.experiencePercent = experiencePercent;
+    }
 }

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

@@ -15,11 +15,9 @@ public class UserCourseRecordDto {
 
     private Integer teacherId;
 
-    private Integer vsNumber;
+    private Integer number;
 
-    private Date useStartTime;
-
-    private Date useEndTime;
+    private String cctalkName;
 
     public Integer getId() {
         return id;
@@ -53,27 +51,19 @@ public class UserCourseRecordDto {
         this.teacherId = teacherId;
     }
 
-    public Date getUseStartTime() {
-        return useStartTime;
-    }
-
-    public void setUseStartTime(Date useStartTime) {
-        this.useStartTime = useStartTime;
-    }
-
-    public Date getUseEndTime() {
-        return useEndTime;
+    public Integer getNumber() {
+        return number;
     }
 
-    public void setUseEndTime(Date useEndTime) {
-        this.useEndTime = useEndTime;
+    public void setNumber(Integer number) {
+        this.number = number;
     }
 
-    public Integer getVsNumber() {
-        return vsNumber;
+    public String getCctalkName() {
+        return cctalkName;
     }
 
-    public void setVsNumber(Integer vsNumber) {
-        this.vsNumber = vsNumber;
+    public void setCctalkName(String cctalkName) {
+        this.cctalkName = cctalkName;
     }
 }

+ 9 - 29
server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/UserServiceRecordDto.java

@@ -21,11 +21,7 @@ public class UserServiceRecordDto {
 
     private String source;
 
-    private Integer isUsed;
-
-    private Date startTime;
-
-    private Date endTime;
+    private Integer isStop;
 
     public Integer getId() {
         return id;
@@ -67,28 +63,20 @@ public class UserServiceRecordDto {
         this.param = param;
     }
 
-    public Integer getIsUsed() {
-        return isUsed;
-    }
-
-    public void setIsUsed(Integer isUsed) {
-        this.isUsed = isUsed;
-    }
-
-    public Date getStartTime() {
-        return startTime;
+    public String getArea() {
+        return area;
     }
 
-    public void setStartTime(Date startTime) {
-        this.startTime = startTime;
+    public void setArea(String area) {
+        this.area = area;
     }
 
-    public Date getEndTime() {
-        return endTime;
+    public Integer getIsStop() {
+        return isStop;
     }
 
-    public void setEndTime(Date endTime) {
-        this.endTime = endTime;
+    public void setIsStop(Integer isStop) {
+        this.isStop = isStop;
     }
 
     public String getSource() {
@@ -98,12 +86,4 @@ public class UserServiceRecordDto {
     public void setSource(String source) {
         this.source = source;
     }
-
-    public String getArea() {
-        return area;
-    }
-
-    public void setArea(String area) {
-        this.area = area;
-    }
 }

+ 50 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/CourseExperienceListDto.java

@@ -14,8 +14,18 @@ public class CourseExperienceListDto {
 
     private UserExtendDto user;
 
+    private String nickname;
+
     private String title;
 
+    private String prepareStatus;
+
+    private Integer experienceDay;
+
+    private Integer experienceScore;
+
+    private String experiencePercent;
+
     private Integer viewNumber;
 
     private Integer collectNumber;
@@ -87,4 +97,44 @@ public class CourseExperienceListDto {
     public void setUpdateTime(Date updateTime) {
         this.updateTime = updateTime;
     }
+
+    public String getNickname() {
+        return nickname;
+    }
+
+    public void setNickname(String nickname) {
+        this.nickname = nickname;
+    }
+
+    public String getPrepareStatus() {
+        return prepareStatus;
+    }
+
+    public void setPrepareStatus(String prepareStatus) {
+        this.prepareStatus = prepareStatus;
+    }
+
+    public Integer getExperienceDay() {
+        return experienceDay;
+    }
+
+    public void setExperienceDay(Integer experienceDay) {
+        this.experienceDay = experienceDay;
+    }
+
+    public Integer getExperienceScore() {
+        return experienceScore;
+    }
+
+    public void setExperienceScore(Integer experienceScore) {
+        this.experienceScore = experienceScore;
+    }
+
+    public String getExperiencePercent() {
+        return experiencePercent;
+    }
+
+    public void setExperiencePercent(String experiencePercent) {
+        this.experiencePercent = experiencePercent;
+    }
 }

+ 5 - 5
server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/UserCourseStudyRecordInfoDto.java

@@ -24,7 +24,7 @@ public class UserCourseStudyRecordInfoDto {
 
     private Integer teacherId;
 
-    private Integer vsNumber;
+    private Integer number;
 
     private String source;
 
@@ -142,11 +142,11 @@ public class UserCourseStudyRecordInfoDto {
         this.teacherId = teacherId;
     }
 
-    public Integer getVsNumber() {
-        return vsNumber;
+    public Integer getNumber() {
+        return number;
     }
 
-    public void setVsNumber(Integer vsNumber) {
-        this.vsNumber = vsNumber;
+    public void setNumber(Integer number) {
+        this.number = number;
     }
 }

+ 49 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/extend/CoursePackageExtendDto.java

@@ -0,0 +1,49 @@
+package com.qxgmat.dto.extend;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.CoursePackage;
+
+import java.math.BigDecimal;
+
+@Dto(entity = CoursePackage.class)
+public class CoursePackageExtendDto {
+    private Integer id;
+
+    private String title;
+
+    private Integer[] courseIds;
+
+    private BigDecimal price;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public BigDecimal getPrice() {
+        return price;
+    }
+
+    public void setPrice(BigDecimal price) {
+        this.price = price;
+    }
+
+    public Integer[] getCourseIds() {
+        return courseIds;
+    }
+
+    public void setCourseIds(Integer[] courseIds) {
+        this.courseIds = courseIds;
+    }
+}

+ 45 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordAddDto.java

@@ -1,5 +1,9 @@
 package com.qxgmat.dto.request;
 
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+
+@Dto(entity = UserOrderCheckout.class)
 public class RecordAddDto {
     private String productType;
 
@@ -9,4 +13,45 @@ public class RecordAddDto {
 
     private String param;
 
+    private Integer number;
+
+    public String getProductType() {
+        return productType;
+    }
+
+    public void setProductType(String productType) {
+        this.productType = productType;
+    }
+
+    public Integer getProductId() {
+        return productId;
+    }
+
+    public void setProductId(Integer productId) {
+        this.productId = productId;
+    }
+
+    public String getService() {
+        return service;
+    }
+
+    public void setService(String service) {
+        this.service = service;
+    }
+
+    public String getParam() {
+        return param;
+    }
+
+    public void setParam(String param) {
+        this.param = param;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
 }

+ 1 - 1
server/gateway-api/src/main/java/com/qxgmat/dto/request/RecordOpenDto.java

@@ -1,6 +1,6 @@
 package com.qxgmat.dto.request;
 
-public class RecordOpenDto {
+public class RecordUseDto {
     private Integer recordId;
 
     private Integer isSubscribe;

+ 82 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/UserOrderPreDto.java

@@ -0,0 +1,82 @@
+package com.qxgmat.dto.response;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+import com.qxgmat.dto.extend.CourseDataExtendDto;
+import com.qxgmat.dto.extend.CourseExtendDto;
+import com.qxgmat.dto.extend.CoursePackageExtendDto;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public class UserOrderPreDto {
+    private BigDecimal money;
+
+    private BigDecimal originMoney;
+
+    private JSONObject promote;
+
+    private List<UserOrderCheckout> checkouts;
+
+    private List<CourseExtendDto> courses;
+
+    private List<CourseDataExtendDto> datas;
+
+    private List<CoursePackageExtendDto> packages;
+
+    public BigDecimal getMoney() {
+        return money;
+    }
+
+    public void setMoney(BigDecimal money) {
+        this.money = money;
+    }
+
+    public BigDecimal getOriginMoney() {
+        return originMoney;
+    }
+
+    public void setOriginMoney(BigDecimal originMoney) {
+        this.originMoney = originMoney;
+    }
+
+    public JSONObject getPromote() {
+        return promote;
+    }
+
+    public void setPromote(JSONObject promote) {
+        this.promote = promote;
+    }
+
+    public List<UserOrderCheckout> getCheckouts() {
+        return checkouts;
+    }
+
+    public void setCheckouts(List<UserOrderCheckout> checkouts) {
+        this.checkouts = checkouts;
+    }
+
+    public List<CourseExtendDto> getCourses() {
+        return courses;
+    }
+
+    public void setCourses(List<CourseExtendDto> courses) {
+        this.courses = courses;
+    }
+
+    public List<CourseDataExtendDto> getDatas() {
+        return datas;
+    }
+
+    public void setDatas(List<CourseDataExtendDto> datas) {
+        this.datas = datas;
+    }
+
+    public List<CoursePackageExtendDto> getPackages() {
+        return packages;
+    }
+
+    public void setPackages(List<CoursePackageExtendDto> packages) {
+        this.packages = packages;
+    }
+}

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

@@ -3,7 +3,7 @@ package com.qxgmat.help;
 import com.alibaba.fastjson.JSONObject;
 import com.nuliji.tools.exception.ParameterException;
 import com.nuliji.tools.third.baidu.BaiduAi;
-import com.nuliji.tools.third.sendcloud.SendCloudMail;
+import net.ipip.ipdb.City;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -11,6 +11,8 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.io.FileSystemResource;
 import org.springframework.stereotype.Service;
 
+import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -23,6 +25,19 @@ public class AiHelp {
 
     private BaiduAi ai;
 
+    private City cityDb;
+
+    @Autowired
+    private void getDb(@Value("${ip.database.path}") String path) throws IOException {
+        // City类可用于IPDB格式的IPv4免费库,IPv4与IPv6的每周高级版、每日标准版、每日高级版、每日专业版、每日旗舰版
+        if (path.startsWith(".")){
+            // 相对路径,从resource中获取
+            cityDb = new City(this.getClass().getClassLoader().getResourceAsStream(path.replaceFirst(".", "")));
+        }else{
+            cityDb = new City(path);
+        }
+    }
+
     @Autowired
     private void getSms(@Value("${third.baiduai.appKey}") String appKey,
                    @Value("${third.baiduai.appSecret}") String appSecret) {
@@ -77,4 +92,30 @@ public class AiHelp {
                 throw new ParameterException("图片识别失败");
         }
     }
+
+    public boolean compareIp(String ip1, String ip2){
+        try {
+            // db.find(address, language) 返回索引数组
+            String[] ip1info = cityDb.find(ip1, "CN");
+            String[] ip2info = cityDb.find(ip2, "CN");
+            // 只考虑国家和省级
+            for(int i = 0; i < 2; i++){
+                if (!ip1info[i].equals(ip2info[i])) return false;
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    public String[] parseIp(String ip){
+        try {
+            // db.find(address, language) 返回索引数组
+            return cityDb.find(ip, "CN");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new String[0];
+    }
 }

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

@@ -181,6 +181,8 @@ public class UserQuestionService extends AbstractService {
                 .userId(report.getUserId())
                 // 添加题目序号
                 .no(lastQuestion != null ? lastQuestion.getNo() + 1: 1)
+                // 子阶段序号:外部切换stage重置
+                .stageNo(lastQuestion != null ? lastQuestion.getStageNo() + 1: 1)
                 .build();
         return question;
     }

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

@@ -46,6 +46,22 @@ public class UserServiceService extends AbstractService {
     }
 
     /**
+     * 获取用户服务
+     * @param userId
+     * @param key
+     * @return
+     */
+    public UserService getService(Integer userId, ServiceKey key){
+        Example example = new Example(UserService.class);
+        example.and(
+                example.createCriteria()
+                        .andEqualTo("userId", userId)
+                        .andEqualTo("service", key.key)
+        );
+        return one(userServiceMapper, example);
+    }
+
+    /**
      * 给用户添加服务:系统自动
      * @param userId
      * @param key

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

@@ -15,13 +15,16 @@ import com.qxgmat.data.constants.enums.status.DirectionStatus;
 import com.qxgmat.data.constants.enums.user.PrepareExaminationTime;
 import com.qxgmat.data.dao.UserMapper;
 import com.qxgmat.data.dao.entity.User;
+import com.qxgmat.data.dao.entity.UserOrder;
 import com.qxgmat.data.inline.UserToken;
 import com.qxgmat.data.relation.UserRelationMapper;
 import com.qxgmat.data.relation.entity.UserPrepareRelation;
 import com.qxgmat.help.WechatHelp;
+import com.qxgmat.service.extend.OrderFlowService;
 import com.qxgmat.service.inline.UserCourseService;
 import com.qxgmat.service.inline.UserMessageService;
 import com.qxgmat.service.inline.UserOrderService;
+import org.hibernate.criterion.Order;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -50,19 +53,10 @@ public class UsersService extends AbstractService {
     private WechatHelp wechatHelp;
 
     @Resource
-    private UserMessageService userMessageService;
-
-    @Resource
-    private UserOrderService userOrderService;
-
-    @Resource
-    private UserCourseService userCourseService;
+    private UserRelationMapper userRelationMapper;
 
     @Resource
-    private UserServiceService userServiceService;
-
-    @Resource
-    private UserRelationMapper userRelationMapper;
+    private OrderFlowService orderFlowService;
 
     /**
      * 生成有效期token
@@ -184,15 +178,22 @@ public class UsersService extends AbstractService {
      * @return
      */
     @Transactional
-    public User register(String area, String mobile, String inviteCode, User openUser){
+    public User register(String area, String mobile, String inviteCode, User openUser, String registerIp, String[] registerInfo){
         boolean n = false;
         User user = getByMobile(area, mobile);
         if (user != null){
-            if (openUser == null)
+            if (openUser == null) {
+                // 直接手机登录,异常抛出,根据情况忽略
                 throw new ParameterException("手机号已注册");
-            else if(user.getWechatUnionid() != null && !user.getWechatUnionid().equals("")){
+            }else if(user.getWechatUnionid() != null && !user.getWechatUnionid().equals("")){
+                // openUser不为空,则用于绑定微信
                 throw new ParameterException("该手机已绑定其他账号,请更换手机号码");
             }
+            if (user.getRegisterIp() == null || user.getRegisterIp().isEmpty()){
+                user.setRegisterIp(registerIp);
+                user.setRegisterCity(registerInfo != null ? String.join(",",registerInfo) : "");
+                user.setLatestLoginIp(registerIp);
+            }
         }else{
             // 注册,并且绑定邀请者
             user = User.builder().mobile(mobile).build();
@@ -200,11 +201,15 @@ public class UsersService extends AbstractService {
             if (inviteCode != null && !inviteCode.isEmpty()){
                 User origin = getByInviteCode(inviteCode);
                 user.setOriginId(origin.getId());
-                // todo 按逻辑进行奖励
                 edit(User.builder().id(origin.getId()).inviteNumber(origin.getInviteNumber() + 1).build());
+                // 邀请奖励
+                orderFlowService.giveInvite(origin.getId());
             }
             // 生成邀请码: 10位字符串
             user.setInviteCode(Tools.getRandomString(10));
+            user.setRegisterIp(registerIp);
+            user.setRegisterCity(registerInfo != null ? String.join(",",registerInfo) : "");
+            user.setLatestLoginIp(registerIp);
         }
         // 绑定第三方登录信息
         if (openUser != null){
@@ -473,6 +478,14 @@ public class UsersService extends AbstractService {
         return count(userMapper, example);
     }
 
+    /**
+     * 累加支付金额
+     * @param order
+     */
+    public void accumulation(UserOrder order){
+        userRelationMapper.accumulation(order.getUserId(), order.getMoney());
+    }
+
     public boolean equalsPassword(User user, String password){
         return Objects.equals(user.getPassword(), Tools.stringMD5(Tools.stringMD5(password)));
     }

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitCheckout.java

@@ -0,0 +1,10 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+
+import java.util.List;
+
+
+public interface InitCheckout {
+    UserOrderCheckout callback(UserOrderCheckout checkout, List<UserOrderCheckout> list);
+}

+ 9 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitCourseRecord.java

@@ -0,0 +1,9 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.Course;
+import com.qxgmat.data.dao.entity.UserOrderRecord;
+
+
+public interface InitCourseRecord {
+    void callback(UserOrderRecord record, Course course);
+}

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitOrder.java

@@ -0,0 +1,10 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrder;
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+
+import java.util.List;
+
+public interface InitOrder {
+    void callback(UserOrder order, List<UserOrderCheckout> userOrderCheckoutList);
+}

+ 8 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/InitRecord.java

@@ -0,0 +1,8 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrderRecord;
+
+
+public interface InitRecord {
+    UserOrderRecord callback(UserOrderRecord record);
+}

+ 0 - 8
server/gateway-api/src/main/java/com/qxgmat/service/annotation/PayModule.java

@@ -1,8 +0,0 @@
-package com.qxgmat.service.annotation;
-
-/**
- * Created by gaojie on 2017/5/11.
- */
-public interface PayModule {
-    String getModuleId();
-}

+ 10 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/RemoveCheckout.java

@@ -0,0 +1,10 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrderCheckout;
+
+import java.util.List;
+
+
+public interface RemoveCheckout {
+    void callback(UserOrderCheckout checkout, List<UserOrderCheckout> list);
+}

+ 7 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/StopRecord.java

@@ -0,0 +1,7 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrderRecord;
+
+public interface StopRecord {
+    UserOrderRecord callback(UserOrderRecord record);
+}

+ 7 - 0
server/gateway-api/src/main/java/com/qxgmat/service/annotation/UseRecord.java

@@ -0,0 +1,7 @@
+package com.qxgmat.service.annotation;
+
+import com.qxgmat.data.dao.entity.UserOrderRecord;
+
+public interface UseRecord {
+    UserOrderRecord callback(UserOrderRecord record);
+}

+ 10 - 7
server/gateway-api/src/main/java/com/qxgmat/service/extend/CourseExtendService.java

@@ -22,17 +22,20 @@ public class CourseExtendService {
 
     /**
      * 计算vs课程有效期
-     * @param startTime
      * @param vsNumber
-     * @param courseId
      */
-    public Date computeExpire(Date startTime, Integer vsNumber, Integer courseId){
-        // 计算有效期
-        Course course = courseService.get(courseId);
+    public int computeExpire(Integer vsNumber, Course course){
         // day / 10
         Integer expireDays = course.getExpireDays();
 
-        int day = Math.min(vsNumber / 10, 0)  * expireDays;
-        return Tools.addDate(startTime, day);
+        return Math.min(vsNumber / 10, 0)  * expireDays;
+    }
+
+    public void suspendCourse(Integer userId, Integer courseId){
+
+    }
+
+    public void restoreCourse(Integer userId, Integer courseId){
+
     }
 }

+ 825 - 0
server/gateway-api/src/main/java/com/qxgmat/service/extend/OrderFlowService.java

@@ -0,0 +1,825 @@
+package com.qxgmat.service.extend;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.nuliji.tools.Tools;
+import com.nuliji.tools.Transform;
+import com.nuliji.tools.exception.ParameterException;
+import com.qxgmat.data.constants.enums.ServiceKey;
+import com.qxgmat.data.constants.enums.ServiceVipKey;
+import com.qxgmat.data.constants.enums.module.CourseModule;
+import com.qxgmat.data.constants.enums.module.ProductType;
+import com.qxgmat.data.constants.enums.trade.PayChannel;
+import com.qxgmat.data.constants.enums.trade.PayMethod;
+import com.qxgmat.data.constants.enums.trade.PayModule;
+import com.qxgmat.data.constants.enums.trade.RecordSource;
+import com.qxgmat.data.dao.entity.*;
+import com.qxgmat.service.UserServiceService;
+import com.qxgmat.service.UsersService;
+import com.qxgmat.service.annotation.*;
+import com.qxgmat.service.inline.*;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class OrderFlowService {
+    @Resource
+    private ToolsService toolsService;
+
+    @Resource
+    private CourseService courseService;
+
+    @Resource
+    private CourseExtendService courseExtendService;
+
+    @Resource
+    private CoursePackageService coursePackageService;
+
+    @Resource
+    private CourseDataService courseDataService;
+
+    @Resource
+    private UserCourseService userCourseService;
+
+    @Resource
+    private UserServiceService userServiceService;
+
+    @Resource
+    private UserOrderService userOrderService;
+
+    @Resource
+    private UserOrderCheckoutService userOrderCheckoutService;
+
+    @Resource
+    private UserOrderRecordService userOrderRecordService;
+
+    @Resource
+    private UsersService usersService;
+
+    private TradeService tradeService;
+
+    @Resource
+    private void registerTrade(TradeService tradeService) {
+        this.tradeService = tradeService;
+        this.tradeService.register(PayModule.ORDER, (obj)->{
+            Pay pay = (Pay)obj;
+            Integer userId = pay.getUserId();
+            Integer orderId = pay.getModuleExtend();
+            return this.payed(orderId, userId, pay.getId(), pay.getPayTime(), PayMethod.FromChannel(PayChannel.ValueOf(pay.getChannel())), pay.getTransactionNo());
+        });
+    }
+
+    private Map<ProductType, InitCheckout> initCheckoutCallback = new HashMap<>();
+
+    private Map<ProductType, RemoveCheckout> removeCheckoutCallback = new HashMap<>();
+
+    private Map<ProductType, InitOrder> initOrderCallback = new HashMap<>();
+
+    private Map<ProductType, InitRecord> initRecordCallback = new HashMap<>();
+
+    private Map<ProductType, UseRecord> useRecordCallback = new HashMap<>();
+
+    private Map<ProductType, StopRecord> stopRecordCallback = new HashMap<>();
+
+    public OrderFlowService(){
+        initCheckoutCallback.put(ProductType.COURSE, ((checkout, userOrderCheckoutList) -> {
+            // 判断课程是否已经存在
+            UserOrderCheckout tmp=null;
+            for(UserOrderCheckout in: userOrderCheckoutList){
+                if (!in.getProductType().equals(ProductType.COURSE.key)) continue;
+                if (in.getProductId().equals(checkout.getProductId())){
+                    tmp = in;
+                    break;
+                }
+            }
+            Course mainCourse = courseService.get(checkout.getProductId());
+
+            // 判断是否是1v1课程
+            if (mainCourse.getCourseModule().equals(CourseModule.VS.key)){
+                if (tmp != null){
+                    // 累加课时
+                    checkout.setId(tmp.getId());
+                    checkout.setNumber(tmp.getNumber() + checkout.getNumber());
+                }
+                checkout.setOriginMoney(mainCourse.getPrice().multiply(BigDecimal.valueOf(checkout.getNumber())));
+                int percent = toolsService.computeVsMoney(checkout);
+                checkout.setMoney(checkout.getOriginMoney().multiply(BigDecimal.valueOf(percent)).divide(BigDecimal.valueOf(100), BigDecimal.ROUND_HALF_UP));
+                return checkout;
+            }else{
+                // 课程已添加,无需添加
+                if(tmp != null){
+                    return null;
+                }
+            }
+
+            // 填充金额
+            checkout.setMoney(mainCourse.getPrice());
+            checkout.setOriginMoney(mainCourse.getPrice());
+
+            // 先加入列表,统一处理
+            userOrderCheckoutList.add(checkout);
+            // 判断是否符合套餐情况: 剩余的独立课程
+            List<UserOrderCheckout> courseCheckout = userOrderCheckoutList.stream().filter((in)-> in.getProductType().equals(ProductType.COURSE.key) && in.getParentId() == 0).collect(Collectors.toList());
+            Collection courseIds = Transform.getIds(courseCheckout, UserOrderCheckout.class, "productId");
+            CoursePackage coursePackage = coursePackageService.combineCourse(courseIds);
+            if(coursePackage != null){
+                List<Course> courseList = courseService.select(coursePackage.getCourseIds());
+                BigDecimal originMoney = BigDecimal.valueOf(0);
+                for(Course course : courseList){
+                    originMoney = originMoney.add(course.getPrice());
+                }
+                // 加入套餐
+                UserOrderCheckout packageCheckout = UserOrderCheckout.builder()
+                        .productType(ProductType.COURSE_PACKAGE.key)
+                        .productId(coursePackage.getId())
+                        .money(coursePackage.getPrice())
+                        .originMoney(originMoney)
+                        .build();
+                packageCheckout = userOrderCheckoutService.add(packageCheckout);
+                // 绑定所属
+                List<Integer> ids = Arrays.stream(coursePackage.getCourseIds()).collect(Collectors.toList());
+                for(UserOrderCheckout c:courseCheckout){
+                    if (ids.contains(c.getProductId())){
+                        if (c.getId() !=null && c.getId() > 0){
+                            userOrderCheckoutService.edit(UserOrderCheckout.builder()
+                                    .id(c.getId())
+                                    .parentId(packageCheckout.getId())
+                                    .build());
+                        }else{
+                            c.setParentId(packageCheckout.getId());
+                            userOrderCheckoutService.add(c);
+                        }
+                    }
+                }
+
+                return null;
+            }
+            // 正常添加
+            return checkout;
+        }));
+        initCheckoutCallback.put(ProductType.COURSE_PACKAGE, ((checkout, userOrderCheckoutList)->{
+            for(UserOrderCheckout in: userOrderCheckoutList){
+                if (!in.getProductType().equals(ProductType.COURSE_PACKAGE.key)) continue;
+                if (in.getProductId().equals(checkout.getProductId())) return null;
+            }
+            CoursePackage coursePackage = coursePackageService.get(checkout.getProductId());
+            List<Course> courseList = courseService.select(coursePackage.getCourseIds());
+            Map<Integer, Course> courseMap = new HashMap<>();
+            for(Course course : courseList){
+                courseMap.put(course.getId(), course);
+            }
+            BigDecimal originMoney = BigDecimal.valueOf(0);
+            for(Course course : courseList){
+                originMoney = originMoney.add(course.getPrice());
+            }
+            checkout.setMoney(coursePackage.getPrice());
+            checkout.setOriginMoney(originMoney);
+            checkout = userOrderCheckoutService.add(checkout);
+
+            // 转移课程包含的独立课程
+            List<Integer> ids = Arrays.stream(coursePackage.getCourseIds()).collect(Collectors.toList());
+            List<UserOrderCheckout> courseCheckout = userOrderCheckoutList.stream().filter((in)-> in.getProductType().equals(ProductType.COURSE.key) && in.getParentId() == 0).collect(Collectors.toList());
+            for(UserOrderCheckout in : courseCheckout){
+                if (ids.contains(in.getProductId())){
+                    // 转移该购物车记录
+                    userOrderCheckoutService.edit(UserOrderCheckout.builder()
+                            .id(in.getId())
+                            .parentId(checkout.getId())
+                            .build());
+                    ids.remove(in.getProductId());
+                }
+            }
+            // 添加还未添加的课程记录
+            for(Integer id : ids){
+                Course course = courseMap.get(id);
+                userOrderCheckoutService.add(UserOrderCheckout.builder()
+                        .parentId(checkout.getId())
+                        .productType(ProductType.COURSE.key)
+                        .productId(id)
+                        .money(course.getPrice())
+                        .originMoney(course.getPrice())
+                        .build());
+            }
+            return null;
+        }));
+        initCheckoutCallback.put(ProductType.DATA, ((checkout, userOrderCheckoutList)->{
+            for(UserOrderCheckout in: userOrderCheckoutList){
+                if (!in.getProductType().equals(ProductType.DATA.key)) continue;
+                if (in.getProductId().equals(checkout.getProductId())) return null;
+            }
+            CourseData courseData = courseDataService.get(checkout.getProductId());
+            checkout.setMoney(courseData.getPrice());
+            checkout.setOriginMoney(courseData.getPrice());
+            return checkout;
+        }));
+        initCheckoutCallback.put(ProductType.SERVICE, ((checkout, userOrderCheckoutList)->{
+            for(UserOrderCheckout in: userOrderCheckoutList){
+                if (!in.getProductType().equals(ProductType.SERVICE.key)) continue;
+                if (in.getService().equals(checkout.getService())){
+                    checkout.setId(in.getId());
+                    break;
+                }
+            }
+            BigDecimal money = BigDecimal.valueOf(toolsService.getServicePrice(checkout.getService(), checkout.getParam()));
+            checkout.setMoney(money);
+            checkout.setOriginMoney(money);
+            return checkout;
+        }));
+
+        removeCheckoutCallback.put(ProductType.COURSE, ((checkout, userOrderCheckoutList)->{
+            // 子项目不允许参数,则无需处理
+        }));
+        removeCheckoutCallback.put(ProductType.COURSE_PACKAGE, ((checkout, userOrderCheckoutList)->{
+            // 删除对应子项目
+        }));
+        removeCheckoutCallback.put(ProductType.DATA, ((checkout, userOrderCheckoutList)->{
+            // miss
+        }));
+        removeCheckoutCallback.put(ProductType.SERVICE, ((checkout, userOrderCheckoutList)->{
+            // miss
+        }));
+
+        initOrderCallback.put(ProductType.SERVICE, ((order, userOrderCheckoutList) -> {
+            // 累计金额
+            BigDecimal money = order.getMoney();
+            BigDecimal originMoney = order.getOriginMoney();
+            for(UserOrderCheckout checkout:userOrderCheckoutList){
+                money = money.add(checkout.getMoney());
+                originMoney = originMoney.add(checkout.getMoney());
+            }
+            order.setMoney(money);
+            order.setOriginMoney(originMoney);
+        }));
+        initOrderCallback.put(ProductType.DATA, ((order, userOrderCheckoutList) -> {
+            // 累计金额
+            BigDecimal money = order.getMoney();
+            BigDecimal originMoney = order.getOriginMoney();
+            for(UserOrderCheckout checkout:userOrderCheckoutList){
+                money = money.add(checkout.getMoney());
+                originMoney = originMoney.add(checkout.getMoney());
+            }
+            order.setMoney(money);
+            order.setOriginMoney(originMoney);
+        }));
+        initOrderCallback.put(ProductType.COURSE, ((order, userOrderCheckoutList) -> {
+            BigDecimal money = order.getMoney();
+            BigDecimal originMoney = order.getOriginMoney();
+
+            // 独立课程信息: 排除套餐内的
+            List<UserOrderCheckout> courseCheckout = userOrderCheckoutList.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.COURSE.key) && checkout.getParentId() == 0).collect(Collectors.toList());
+            Collection courseIds = Transform.getIds(courseCheckout, UserOrderCheckout.class, "productId");
+            List<Course> courseList = courseService.select(courseIds);
+            Map<Integer, Course> courseMap = new HashMap<>();
+            for(Course course : courseList){
+                courseMap.put(course.getId(), course);
+            }
+            BigDecimal courseMoney = BigDecimal.valueOf(0);
+
+            // 视频课程优惠
+            List<UserOrderCheckout> videoCheckout = courseCheckout.stream().filter((checkout)-> courseMap.get(checkout.getProductId()).getCourseModule().equals(CourseModule.VIDEO.key)).collect(Collectors.toList());
+            BigDecimal videoMoney = BigDecimal.valueOf(0);
+            for(UserOrderCheckout checkout : videoCheckout){
+                videoMoney = videoMoney.add(checkout.getMoney());
+            }
+            int percent = toolsService.computeVideoMoney(courseCheckout);
+            if (percent < 100){
+                BigDecimal promoteVideoMoney = videoMoney.multiply(BigDecimal.valueOf(percent)).divide(BigDecimal.valueOf(100), BigDecimal.ROUND_HALF_UP);
+                courseMoney = courseMoney.add(promoteVideoMoney);
+                money = money.add(promoteVideoMoney);
+
+                // 添加视频优惠记录
+                JSONObject promoteVideo = new JSONObject();
+                JSONObject promote = order.getPromote();
+                promote.put("video", promoteVideo);
+            }else{
+                courseMoney = courseMoney.add(videoMoney);
+                money = money.add(videoMoney);
+            }
+            originMoney = originMoney.add(videoMoney);
+
+            // 1v1课程优惠: 添加时计算
+            List<UserOrderCheckout> vsCheckout = courseCheckout.stream().filter((checkout)-> courseMap.get(checkout.getProductId()).getCourseModule().equals(CourseModule.VS.key)).collect(Collectors.toList());
+            for(UserOrderCheckout checkout: vsCheckout){
+                courseMoney = courseMoney.add(checkout.getMoney());
+                money = money.add(checkout.getMoney());
+                originMoney = originMoney.add(checkout.getMoney());
+            }
+
+            // 套餐费用
+            List<UserOrderCheckout> packageCheckout = userOrderCheckoutList.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.COURSE_PACKAGE.key)).collect(Collectors.toList());
+            for(UserOrderCheckout checkout : packageCheckout){
+                courseMoney = courseMoney.add(checkout.getMoney());
+                money = money.add(checkout.getMoney());
+                originMoney = originMoney.add(checkout.getMoney());
+            }
+
+            // 满金额处理
+            Integer ask = toolsService.getAskTime(courseMoney);
+            order.setAskTime(ask);
+
+            order.setMoney(money);
+            order.setOriginMoney(originMoney);
+        }));
+
+        initRecordCallback.put(ProductType.COURSE, (record -> {
+            Course course = courseService.get(record.getId());
+            CourseModule courseModule = CourseModule.ValueOf(course.getCourseModule());
+            // 小班课程不会存在记录,在线视频和1v1授课都有有效期
+            Date startTime = new Date();
+            Date endTime = Tools.addDate(startTime, courseModule.expireDay);
+            record.setStartTime(startTime);
+            record.setEndTime(endTime);
+            return record;
+        }));
+        initRecordCallback.put(ProductType.COURSE_PACKAGE, (record->{
+            // 资料无需开启,也没有有效期
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+            return record;
+        }));
+        initRecordCallback.put(ProductType.DATA, (record->{
+            // 资料无需开启,也没有有效期
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+            return record;
+        }));
+        initRecordCallback.put(ProductType.SERVICE, (record->{
+            if(record.getService().equals(ServiceKey.VIP.key)){
+                // VIP直接开通
+                record = useRecordCallback.get(ProductType.SERVICE).callback(record);
+            }else{
+                ServiceKey serviceKey = ServiceKey.ValueOf(record.getService());
+                Date startTime = new Date();
+                Date endTime = Tools.addMonth(startTime, serviceKey.expireDay);
+                record.setStartTime(startTime);
+                record.setEndTime(endTime);
+            }
+            return record;
+        }));
+
+        useRecordCallback.put(ProductType.COURSE, (record->{
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+
+            Course course = courseService.get(record.getProductId());
+            Integer expireDay = 0;
+            if (course.getCourseModule().equals(CourseModule.VS.key)){
+                // 根据课时数进行计算
+                expireDay = courseExtendService.computeExpire(record.getNumber(), course);
+            }else{
+                // 根据设置进行计算
+
+            }
+            Date startTime = time;
+            Date endTime = Tools.addDate(startTime, expireDay);
+            UserCourse userCourse = userCourseService.getCourse(record.getUserId(), record.getProductId());
+            if(userCourse == null){
+                userCourse = UserCourse.builder()
+                        .userId(record.getUserId())
+                        .recordId(record.getId())
+                        .startTime(startTime)
+                        .expireTime(endTime)
+                        .build();
+                userCourse = userCourseService.add(userCourse);
+            }else{
+                if (userCourse.getExpireTime().before(time)){
+                    // 已到期 - 续期
+                    userCourse.setStartTime(startTime);
+                    userCourse.setExpireTime(endTime);
+                    userCourse.setRecordId(record.getId());
+                }else{
+                    // 未到期 - 报错
+                    throw new ParameterException("已开通当前课程");
+                }
+                userCourse = userCourseService.edit(userCourse);
+            }
+            record.setUseStartTime(startTime);
+            record.setUseEndTime(endTime);
+            return record;
+        }));
+        useRecordCallback.put(ProductType.COURSE_PACKAGE, (record->{
+            // miss
+            throw new ParameterException("套餐无需开通");
+        }));
+        useRecordCallback.put(ProductType.DATA, (record->{
+            // miss
+            throw new ParameterException("资料无需开通");
+        }));
+        useRecordCallback.put(ProductType.SERVICE, (record->{
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+
+            ServiceKey serviceKey = ServiceKey.ValueOf(record.getService());
+            Integer expireDay = serviceKey.useExpireDay;
+            if(serviceKey == ServiceKey.VIP){
+                ServiceVipKey vipKey = ServiceVipKey.ValueOf(record.getParam());
+                expireDay = vipKey.useExpireDay;
+            }
+
+            Date startTime = time;
+            Date endTime = Tools.addDate(startTime, expireDay);
+            UserService userService = userServiceService.getService(record.getUserId(), serviceKey);
+            if (userService == null){
+                userService = UserService.builder()
+                        .userId(record.getUserId())
+                        .isSubscribe(record.getIsSubscribe())
+                        .startTime(startTime)
+                        .expireTime(endTime)
+                        .build();
+                userService = userServiceService.add(userService);
+            }else{
+                if (userService.getExpireTime().before(time)){
+                    // 已到期 - 续期
+                    userService.setStartTime(startTime);
+                    userService.setExpireTime(endTime);
+                }else{
+                    if (serviceKey == ServiceKey.QX_CAT){
+                        // 未到期 - 报错
+                        throw new ParameterException("已开通当前服务");
+                    }
+                    // 未到期 - 延长有效期
+                    startTime = userService.getExpireTime();
+                    endTime = Tools.addDate(userService.getExpireTime(), expireDay);
+                    userService.setExpireTime(endTime);
+                }
+                userService.setIsSubscribe(record.getIsSubscribe());
+                userService = userServiceService.edit(userService);
+            }
+            record.setUseStartTime(startTime);
+            record.setUseEndTime(endTime);
+            return record;
+        }));
+
+        stopRecordCallback.put(ProductType.COURSE, (record->{
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+
+            Course course = courseService.get(record.getProductId());
+            Integer expireDay = 0;
+            if (course.getCourseModule().equals(CourseModule.VS.key)){
+                // 根据课时数进行计算
+                expireDay = courseExtendService.computeExpire(record.getNumber(), course);
+            }else{
+                // 根据设置进行计算
+
+            }
+            Date startTime = time;
+            Date endTime = Tools.addDate(startTime, expireDay);
+            UserCourse userCourse = userCourseService.getCourse(record.getUserId(), record.getProductId());
+            if(userCourse == null){
+                userCourse = UserCourse.builder()
+                        .userId(record.getUserId())
+                        .recordId(record.getId())
+                        .startTime(startTime)
+                        .expireTime(endTime)
+                        .build();
+                userCourse = userCourseService.add(userCourse);
+            }else{
+                if (userCourse.getExpireTime().before(time)){
+                    // 已到期 - 续期
+                    userCourse.setStartTime(startTime);
+                    userCourse.setExpireTime(endTime);
+                    userCourse.setRecordId(record.getId());
+                }else{
+                    // 未到期 - 报错
+                    throw new ParameterException("已开通当前课程");
+                }
+                userCourse = userCourseService.edit(userCourse);
+            }
+            record.setUseStartTime(startTime);
+            record.setUseEndTime(endTime);
+            return record;
+        }));
+        stopRecordCallback.put(ProductType.COURSE_PACKAGE, (record->{
+            // miss
+            throw new ParameterException("套餐无需停用");
+        }));
+        stopRecordCallback.put(ProductType.DATA, (record->{
+            // 停用后无法查看
+            record.setIsStop(1);
+            return record;
+        }));
+        stopRecordCallback.put(ProductType.SERVICE, (record->{
+            record.setIsUsed(1);
+            Date time = new Date();
+            record.setUseTime(time);
+
+            ServiceKey serviceKey = ServiceKey.ValueOf(record.getService());
+            Integer expireDay = serviceKey.useExpireDay;
+            if(serviceKey == ServiceKey.VIP){
+                ServiceVipKey vipKey = ServiceVipKey.ValueOf(record.getParam());
+                expireDay = vipKey.useExpireDay;
+            }
+
+            Date startTime = time;
+            Date endTime = Tools.addDate(startTime, expireDay);
+            UserService userService = userServiceService.getService(record.getUserId(), serviceKey);
+            if (userService == null){
+                userService = UserService.builder()
+                        .userId(record.getUserId())
+                        .isSubscribe(record.getIsSubscribe())
+                        .startTime(startTime)
+                        .expireTime(endTime)
+                        .build();
+                userService = userServiceService.add(userService);
+            }else{
+                if (userService.getExpireTime().before(time)){
+                    // 已到期 - 续期
+                    userService.setStartTime(startTime);
+                    userService.setExpireTime(endTime);
+                }else{
+                    if (serviceKey == ServiceKey.QX_CAT){
+                        // 未到期 - 报错
+                        throw new ParameterException("已开通当前服务");
+                    }
+                    // 未到期 - 延长有效期
+                    startTime = userService.getExpireTime();
+                    endTime = Tools.addDate(userService.getExpireTime(), expireDay);
+                    userService.setExpireTime(endTime);
+                }
+                userService.setIsSubscribe(record.getIsSubscribe());
+                userService = userServiceService.edit(userService);
+            }
+            record.setUseStartTime(startTime);
+            record.setUseEndTime(endTime);
+            return record;
+        }));
+    }
+
+    /**
+     * 完成订单支付
+     * @param orderId
+     * @param userId
+     * @param payTime
+     * @param payMethod
+     * @return
+     */
+    @Transactional
+    private boolean payed(Integer orderId, Integer userId, Long payId, Date payTime, PayMethod payMethod, String transactionNo){
+        UserOrder userOrder = userOrderService.get(orderId);
+        if (userOrder == null) return false;
+        if (userOrder.getPayStatus() > 0) return false;
+        userOrder = userOrderService.edit(UserOrder.builder()
+                .id(userOrder.getId())
+                .payId(payId)
+                .payMethod(payMethod.key)
+                .payTime(payTime)
+                .transactionNo(transactionNo)
+                .payStatus(1)
+                .build());
+
+        // 转换购物车记录为购买记录
+        // todo 套餐保留关系
+        List<UserOrderCheckout> checkoutList = userOrderCheckoutService.allByUser(userId, orderId);
+        userOrderCheckoutService.deleteByUser(userId, orderId);
+        for(UserOrderCheckout checkout : checkoutList){
+            UserOrderRecord record = Transform.convert(checkout, UserOrderRecord.class);
+            record.setSource(RecordSource.FromPayModule(payMethod).key);
+            InitRecord callback = initRecordCallback.get(ProductType.ValueOf(record.getProductType()));
+            callback.callback(record);
+            userOrderRecordService.add(record);
+        }
+
+        // 累加用户支付信息
+        usersService.accumulation(userOrder);
+        return true;
+    }
+
+    /**
+     * 添加购物车
+     * @param userId
+     * @param checkout
+     * @return
+     */
+    @Transactional
+    public int addCheckout(Integer userId, UserOrderCheckout checkout){
+        List<UserOrderCheckout> list = userOrderCheckoutService.allByUser(userId, 0);
+        InitCheckout callback = initCheckoutCallback.get(ProductType.ValueOf(checkout.getProductType()));
+        checkout = callback.callback(checkout, list);
+        if (checkout == null){
+            // 无需操作
+        }else if(checkout.getId() > 0){
+            userOrderCheckoutService.edit(checkout);
+        }else{
+            userOrderCheckoutService.add(checkout);
+        }
+
+        // 计算购物车数量
+        return userOrderCheckoutService.allByUser(userId, 0).size();
+    }
+
+    /**
+     * 删除购物车
+     * @param checkoutId
+     * @param userId
+     * @return
+     */
+    @Transactional
+    public int removeCheckout(Integer checkoutId, Integer userId){
+        UserOrderCheckout in = userOrderCheckoutService.get(checkoutId);
+        if (in == null){
+            throw new ParameterException("记录不存在");
+        }
+        if (!in.getUserId().equals(userId)){
+            throw new ParameterException("记录不存在");
+        }
+        if (in.getParentId() != null && in.getParentId() > 0){
+            // 如果能删除子项目,要处理删除父项目,转移其他子项目
+            throw new ParameterException("套餐内,不允许单独删除");
+        }
+        userOrderCheckoutService.delete(checkoutId);
+
+        List<UserOrderCheckout> list = userOrderCheckoutService.allByUser(userId, 0);
+        // 删除子项目
+        for(UserOrderCheckout checkout: list){
+            if (checkout.getParentId()==null || checkout.getParentId() == 0) continue;
+            if (checkout.getParentId().equals(in.getId())){
+                userOrderCheckoutService.delete(checkout.getId());
+            }
+        }
+        RemoveCheckout callback = removeCheckoutCallback.get(ProductType.ValueOf(in.getProductType()));
+        callback.callback(in, list);
+
+        // 计算购物车数量
+        return userOrderCheckoutService.allByUser(userId, 0).size();
+    }
+
+    /**
+     * 根据购物车生成对应订单信息
+     * @param list
+     * @return
+     */
+    public UserOrder preOrderWithCheckout(Integer userId, List<UserOrderCheckout> list){
+        UserOrder order = UserOrder.builder()
+                .userId(userId)
+                .originMoney(BigDecimal.valueOf(0))
+                .money(BigDecimal.valueOf(0))
+                .build();
+        // 记录订单优惠
+        JSONObject promote = new JSONObject();
+        order.setPromote(promote);
+        // 记录订单类型
+        JSONArray productTypes = new JSONArray();
+        order.setProductTypes(productTypes);
+
+        InitOrder callback;
+
+        // 课程: 并包含套餐
+        List<UserOrderCheckout> courseCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.COURSE.key) || checkout.getProductType().equals(ProductType.COURSE_PACKAGE.key)).collect(Collectors.toList());
+        callback = initOrderCallback.get(ProductType.COURSE);
+        callback.callback(order, courseCheckout);
+        if(courseCheckout.size() > 0){
+            productTypes.add(ProductType.COURSE.key);
+        }
+        // 资料
+        List<UserOrderCheckout> dataCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.DATA.key)).collect(Collectors.toList());
+        callback = initOrderCallback.get(ProductType.DATA);
+        callback.callback(order, courseCheckout);
+        if (dataCheckout.size() > 0){
+            productTypes.add(ProductType.DATA.key);
+        }
+        // 服务
+        List<UserOrderCheckout> serviceCheckout = list.stream().filter((checkout)-> checkout.getProductType().equals(ProductType.SERVICE.key)).collect(Collectors.toList());
+        callback = initOrderCallback.get(ProductType.SERVICE);
+        callback.callback(order, courseCheckout);
+        if(serviceCheckout.size() > 0){
+            productTypes.add(ProductType.SERVICE.key);
+        }
+
+        return order;
+    }
+
+    /**
+     * 支付购物车
+     * @param userId
+     * @return
+     */
+    @Transactional
+    public UserOrder makeOrder(Integer userId){
+        List<UserOrderCheckout> list = userOrderCheckoutService.allByUser(userId, 0);
+        UserOrder order = preOrderWithCheckout(userId, list);
+        order = userOrderService.add(order);
+
+        // 转移购物车信息
+        userOrderCheckoutService.changeByUser(userId, 0, UserOrderCheckout.builder().orderId(order.getId()).build());
+        return order;
+    }
+
+    /**
+     * 立即购买
+     * @param userId
+     * @param checkout
+     * @return
+     */
+    @Transactional
+    public UserOrder makeOrderWithProduct(Integer userId, UserOrderCheckout checkout){
+        List<UserOrderCheckout> list = new ArrayList<UserOrderCheckout>(){{add(checkout);}};
+        UserOrder order = preOrderWithCheckout(userId, list);
+        order = userOrderService.add(order);
+        checkout.setOrderId(order.getId());
+        userOrderCheckoutService.add(checkout);
+        return order;
+    }
+
+    /**
+     * 后台直接添加记录
+     * @param record
+     * @return
+     */
+    @Transactional
+    public UserOrderRecord addRecord(UserOrderRecord record){
+        InitRecord callback = initRecordCallback.get(ProductType.ValueOf(record.getProductType()));
+        callback.callback(record);
+        return userOrderRecordService.add(record);
+    }
+
+    /**
+     * 开通记录
+     * @param userId
+     * @param recordId
+     * @return
+     */
+    @Transactional
+    public UserOrderRecord useRecord(Integer userId, Integer recordId){
+        UserOrderRecord in = userOrderRecordService.get(recordId);
+        if (in == null){
+            throw new ParameterException("记录不存在");
+        }
+        if (!in.getUserId().equals(userId)){
+            throw new ParameterException("记录不存在");
+        }
+
+        UseRecord callback = useRecordCallback.get(ProductType.ValueOf(in.getProductType()));
+        callback.callback(in);
+        return in;
+    }
+
+    public void stopRecord(Integer recordId){
+        UserOrderRecord in = userOrderRecordService.get(recordId);
+        if (in == null){
+            throw new ParameterException("记录不存在");
+        }
+        if (in.getIsStop()> 0){
+            throw new ParameterException("已停用");
+        }
+
+        UseRecord callback = useRecordCallback.get(ProductType.ValueOf(in.getProductType()));
+        callback.callback(in);
+    }
+
+    /**
+     * 给用户邀请奖励: 7天vip
+     * @param userId
+     */
+    public void giveInvite(Integer userId){
+        UserOrderRecord record = UserOrderRecord.builder()
+                .userId(userId)
+                .service(ServiceKey.VIP.key)
+                .param(ServiceVipKey.DAY7.key)
+                .source(RecordSource.INVITE.key)
+                .build();
+        record = initRecordCallback.get(ProductType.SERVICE).callback(record);
+        userOrderRecordService.add(record);
+    }
+
+    /**
+     * 给用户备考奖励: 7天vip
+     * @param userId
+     */
+    public void givePrepare(Integer userId){
+        UserOrderRecord record = UserOrderRecord.builder()
+                .userId(userId)
+                .service(ServiceKey.VIP.key)
+                .param(ServiceVipKey.DAY7.key)
+                .source(RecordSource.PREPARE.key)
+                .build();
+        record = initRecordCallback.get(ProductType.SERVICE).callback(record);
+        userOrderRecordService.add(record);
+    }
+
+    /**
+     * 给用户实名认证奖励: 180天vip
+     * @param userId
+     */
+    public void giveReal(Integer userId){
+        UserOrderRecord record = UserOrderRecord.builder()
+                .userId(userId)
+                .service(ServiceKey.VIP.key)
+                .param(ServiceVipKey.MONTH6.key)
+                .source(RecordSource.REAL.key)
+                .build();
+        record = initRecordCallback.get(ProductType.SERVICE).callback(record);
+        userOrderRecordService.add(record);
+    }
+
+}

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


Some files were not shown because too many files changed in this diff