浏览代码

feat(front): 心经

Go 4 年之前
父节点
当前提交
337105db5d
共有 19 个文件被更改,包括 457 次插入119 次删除
  1. 1 1
      front/project/admin/routes/course/experience/page.js
  2. 15 1
      front/project/admin/routes/course/experienceDetail/page.js
  3. 2 1
      front/project/www/routes/course/data/page.js
  4. 2 2
      front/project/www/routes/course/dataDetail/page.js
  5. 152 61
      front/project/www/routes/course/experience/page.js
  6. 46 33
      front/project/www/routes/course/experienceDetail/page.js
  7. 11 3
      front/project/www/routes/course/online/page.js
  8. 27 2
      front/project/www/routes/course/packageDetail/page.js
  9. 3 3
      front/project/www/routes/my/tools/page.js
  10. 61 0
      server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java
  11. 5 3
      server/data/src/main/java/com/qxgmat/data/dao/mapping/CourseExperienceMapper.xml
  12. 1 0
      server/data/src/main/resources/db/migration/V1__init_table.sql
  13. 29 5
      server/gateway-api/src/main/java/com/qxgmat/controller/api/CourseController.java
  14. 21 3
      server/gateway-api/src/main/java/com/qxgmat/controller/api/MyController.java
  15. 21 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/request/CourseExperienceDto.java
  16. 10 0
      server/gateway-api/src/main/java/com/qxgmat/dto/admin/response/CourseExperienceListDto.java
  17. 19 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseExperienceDetailDto.java
  18. 30 0
      server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseExperienceListDto.java
  19. 1 1
      server/gateway-api/src/main/java/com/qxgmat/service/UserCollectExperienceService.java

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

@@ -14,7 +14,7 @@ import { System } from '../../../stores/system';
 import { Course } from '../../../stores/course';
 import { User } from '../../../stores/user';
 
-const PrepareStatusMap = getMap(PrepareStatus, 'value', 'label');
+const PrepareStatusMap = getMap(PrepareStatus, 'value', 'short');
 const ExperiencePercentMap = getMap(ExperiencePercent, 'value', 'label');
 
 export default class extends Page {

+ 15 - 1
front/project/admin/routes/course/experienceDetail/page.js

@@ -1,5 +1,6 @@
 import React from 'react';
-import { Form, Input, Button, Row, Col, InputNumber } from 'antd';
+import moment from 'moment';
+import { Form, Input, Button, Row, Col, InputNumber, DatePicker } from 'antd';
 import './index.less';
 import Editor from '@src/components/Editor';
 import Page from '@src/containers/Page';
@@ -40,6 +41,7 @@ export default class extends Page {
         if (result.userId) {
           this.setState({ showNickname: false });
         }
+        result.experienceTime = moment(result.experienceTime);
         generateSearch('userId', {
           allowClear: true,
         }, this, (search) => {
@@ -60,6 +62,7 @@ export default class extends Page {
       if (!err) {
         const data = form.getFieldsValue();
         let handler;
+        data.description = (data.content || '').replace(/<[^>]+>/g, '').substr(0, 200);
         if (data.id) {
           handler = Course.editExperience(data);
         } else {
@@ -154,6 +157,17 @@ export default class extends Page {
                 )}
               </Form.Item>
             </Col>
+            <Col span={12}>
+              <Form.Item labelCol={{ span: 8 }} wrapperCol={{ span: 14 }} label='考试时间'>
+                {getFieldDecorator('experienceTime', {
+                  rules: [{
+                    required: true, message: '请选择',
+                  }],
+                })(
+                  <DatePicker placeholder='考试时间' />,
+                )}
+              </Form.Item>
+            </Col>
           </Row>
         </Form.Item>
         <Form.Item labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} label='问卷链接'>

+ 2 - 1
front/project/www/routes/course/data/page.js

@@ -145,7 +145,8 @@ export default class extends Page {
   onFilter(key, value) {
     const { filterMap } = this.state;
     filterMap[key] = value;
-    this.search(filterMap);
+    this.search(filterMap, false);
+    this.initData();
   }
 
   onChangePage(page) {

+ 2 - 2
front/project/www/routes/course/dataDetail/page.js

@@ -96,7 +96,7 @@ export default class extends Page {
   }
 
   renderDetail() {
-    const { tab, data = {} } = this.state;
+    const { tab, data = {}, add } = this.state;
     return [
       <div className="center">
         <div className="content">
@@ -139,7 +139,7 @@ export default class extends Page {
                 }}>
                   立即购买
                 </Button>}
-                {!data.have && <Button className="m-r-1" theme="default" radius size="lager" onClick={() => this.add()}>
+                {!data.have && <Button className="m-r-1" theme="default" disabled={data.add || add} radius size="lager" onClick={() => this.add()}>
                   <Assets name="add" />
                 </Button>}
                 {!data.have && <Button theme="default" radius size="lager" onClick={() => openLink(data.resource)}>

+ 152 - 61
front/project/www/routes/course/experience/page.js

@@ -1,37 +1,122 @@
 import React, { Component } from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
-import { formatDate, formatMonth, getMap } from '@src/services/Tools';
+import { formatDate, formatMonth, getMap, formatPercent } from '@src/services/Tools';
 import Ratio from '../../../components/Ratio';
 import UserAction from '../../../components/UserAction';
-import { PrepareStatus, ExperiencePercent } from '../../../../Constant';
+import UserPagination from '../../../components/UserPagination';
+import { ExperienceScore, ExperienceDay, PrepareStatus, ExperiencePercent } from '../../../../Constant';
+import { Main } from '../../../stores/main';
+import { My } from '../../../stores/my';
+import { Course } from '../../../stores/course';
 
 const PrepareStatusMap = getMap(PrepareStatus, 'value', 'label');
 const ExperiencePercentMap = getMap(ExperiencePercent, 'value', 'label');
-
+const colorList = ['#2754E0', '#3F86EA', '#41A6F3', '#9BD4FF'];
 export default class extends Page {
   initState() {
+    const prepareStatusSelect = PrepareStatus.map(row => {
+      return {
+        title: row.label,
+        key: row.value,
+      };
+    });
+    prepareStatusSelect.unshift({
+      title: '身份',
+      key: '',
+    });
+    const experienceDaySelect = ExperienceDay.map(row => {
+      return {
+        title: row.label,
+        key: row.value,
+      };
+    });
+    experienceDaySelect.unshift({
+      title: '备考周期',
+      key: '',
+    });
+    const experienceScoreSelect = ExperienceScore.map(row => {
+      return {
+        title: row.label,
+        key: row.value,
+      };
+    });
+    experienceScoreSelect.unshift({
+      title: '分手成绩',
+      key: '',
+    });
+    const experiencePercentSelect = ExperiencePercent.map(row => {
+      return {
+        title: row.label,
+        key: row.value,
+      };
+    });
+    experiencePercentSelect.unshift({
+      title: '提分幅度',
+      key: '',
+    });
     return {
-      tab: '1',
-      key: '1',
-      filterMap: {},
-      list: [{}, {}],
+      prepareStatusSelect,
+      experienceDaySelect,
+      experienceScoreSelect,
+      experiencePercentSelect,
     };
   }
 
-  onChangeTab(tab) {
-    this.setState({ tab });
+  init() {
+    Main.getExperience()
+      .then(result => {
+        const totalScoreNumber = result.score.reduce((x, y) => x + y, 0);
+        const totalPercentNumber = result.percent.reduce((x, y) => x + y, 0);
+        const scoreList = ExperienceScore.map((row, index) => {
+          const percent = formatPercent(result.score[index] || 0, totalScoreNumber);
+          return {
+            color: colorList[index],
+            label: `${row.label} ${percent} ${result.score[index] || 0}人`,
+            value: percent,
+          };
+        });
+        const percentList = ExperiencePercent.map((row, index) => {
+          const percent = formatPercent(result.percent[index] || 0, totalPercentNumber);
+          return {
+            color: colorList[index],
+            label: `${row.label} ${percent} ${result.percent[index] || 0}人`,
+            value: percent,
+          };
+        });
+        this.setState({ data: result, scoreList, percentList });
+      });
+  }
+
+  initData() {
+    const data = Object.assign(this.state, this.state.search);
+    if (data.order) {
+      data.sortMap = { [data.order]: data.direction };
+    }
+    data.filterMap = this.state.search;
+    this.setState(data);
+
+    Course.listExperience(Object.assign({}, this.state.search))
+      .then(result => {
+        this.setState({ list: result.list, total: result.total });
+      });
+  }
+
+  onFilter(value) {
+    this.search(value, false);
+    this.initData();
   }
 
-  onChangeItem(key) {
-    this.setState({ key });
+  onChangePage(page) {
+    this.search({ page }, false);
+    this.initData();
   }
 
   renderView() {
     return (
       <div>
         <div className="top content t-8">
-          千行课堂 > 全部课程 > OG20综合刷题 > 课时3 > <span className="t-1">心经首页</span>
+          千行课堂 > <span className="t-1">学员表现</span>
         </div>
         {this.renderDetail()}
       </div>
@@ -39,82 +124,63 @@ export default class extends Page {
   }
 
   renderDetail() {
-    const { filterMap, list } = this.state;
+    const { filterMap, list, data = {}, scoreList, percentList, total, page } = this.state;
+    const { prepareStatusSelect, experienceDaySelect, experienceScoreSelect, experiencePercentSelect } = this.state;
     return [
       <div className="center">
         <div className="content">
           <div className="total-list">
             <div className="total-item">
               <div className="title">学员人数</div>
-              <div className="label">1231431</div>
+              <div className="label">{data.number || 0}</div>
             </div>
             <div className="total-item">
               <div className="title">考分分布</div>
               <Ratio
                 size="small"
-                values={[
-                  { color: '#2754E0', label: '750+  36%  3600人', value: 10 },
-                  { color: '#41A6F3', label: '750+  36%  3600人', value: 10 },
-                  { color: '#9BD4FF', label: '750+  36%  3600人', value: 10 },
-                ]}
+                values={scoreList || []}
               />
             </div>
             <div className="total-item">
               <div className="title">出分周期</div>
-              <div className="label">35天</div>
+              <div className="label">{data.period || 0}天</div>
             </div>
             <div className="total-item">
-              <div className="title">考分分布</div>
+              <div className="title">提分比例</div>
               <Ratio
                 size="small"
-                values={[
-                  { color: '#2754E0', label: '750+  36%  3600人', value: 10 },
-                  { color: '#41A6F3', label: '750+  36%  3600人', value: 10 },
-                  { color: '#9BD4FF', label: '750+  36%  3600人', value: 10 },
-                ]}
+                values={percentList || []}
               />
             </div>
           </div>
           <UserAction
             selectList={[
               {
-                children: [
-                  {
-                    key: 'subject',
-                    placeholder: '学科',
-                    select: [],
-                  },
-                  {
-                    placeholder: '题型',
-                    key: 'questionType',
-                    be: 'subject',
-                    selectMap: [],
-                  },
-                ],
+                key: 'prepareStatus',
+                select: prepareStatusSelect,
               },
               {
-                label: '范围',
-                children: [
-                  {
-                    key: 'one',
-                    placeholder: '全部',
-                    select: [],
-                  },
-                  {
-                    key: 'two',
-                    be: 'one',
-                    placeholder: '全部',
-                    selectMap: [],
-                  },
-                ],
+                key: 'experienceDay',
+                select: experienceDaySelect,
+              },
+              {
+                key: 'experienceScore',
+                select: experienceScoreSelect,
+              },
+              {
+                key: 'experiencePercent',
+                select: experiencePercentSelect,
               },
             ]}
             filterMap={filterMap}
             onFilter={value => this.onFilter(value)}
           />
           {list.map(item => {
-            return <Article data={item} onClick={() => {}} onUnCollect={() => this.collectArticle(item, false)} />;
+            return <Article data={item} />;
           })}
+          {total > 0 && list.length > 0 && (
+            <UserPagination total={total} current={page} pageSize={this.state.search.size} onChange={p => this.onChangePage(p)} />
+          )}
         </div>
       </div>,
     ];
@@ -122,29 +188,54 @@ export default class extends Page {
 }
 
 class Article extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {};
+  }
+
+  onCollect() {
+    const { data } = this.props;
+    const { collect } = this.state;
+    if (collect === false || !data.collect) {
+      My.addExperienceCollect(data.id)
+        .then(() => {
+          this.setState({ collect: true });
+        });
+    } else {
+      My.delExperienceCollect(data.id)
+        .then(() => {
+          this.setState({ collect: false });
+        });
+    }
+  }
+
   render() {
-    const { data, onClick, onCollect } = this.props;
+    const { data } = this.props;
+    const { collect } = this.state;
     return (
-      <div className="article-item p-t-2 b-b" onClick={() => onClick && onClick()}>
+      <div className="article-item p-t-2 b-b" onClick={() => linkTo(`/course/experience/detail/${data.id}`)}>
         <div className="t-1 t-s-14 f-w-b">
           {data.title}
           <div className="f-r t-3 t-s-12 f-w-d">
-            <span>{formatDate(data.updateTime, 'YYYY-MM-DD HH:mm:ss')}</span>
+            <span>{data.updateTime && formatDate(data.updateTime, 'YYYY-MM-DD HH:mm:ss')}</span>
             <span className="m-l-2">阅读 {data.viewNumber}</span>
-            <span className="m-l-2" onClick={() => onCollect()}>
-              收藏
+            <span className="m-l-2" onClick={(e) => {
+              e.stopPropagation();
+              this.onCollect();
+            }}>
+              {(collect === false || !data.collect) ? '收藏' : '取消收藏'}
             </span>
           </div>
         </div>
         <div className="t-1 t-s-12 m-b-2">
           <span className="m-r-2">{data.user ? data.user.nickname : data.nickname}</span>
           <span className="m-r-2">{PrepareStatusMap[data.prepareStatus]}</span>
-          <span className="m-r-2">备考:{formatMonth(data.experienceDay, false)}</span>
+          <span className="m-r-2">备考:{!!data.experienceDay && formatMonth(data.experienceDay, false)}</span>
           <span className="m-r-2">
             {data.experienceScore}分 /提分 {ExperiencePercentMap[data.experiencePercent]}
           </span>
         </div>
-        <div className="t-2 m-b-2 detail" dangerouslySetInnerHTML={{ __html: data.content }} />
+        <div className="t-2 m-b-2 detail">{data.description}</div>
       </div>
     );
   }

+ 46 - 33
front/project/www/routes/course/experienceDetail/page.js

@@ -1,6 +1,13 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
+import { formatDate, formatMonth, getMap } from '@src/services/Tools';
+import { Course } from '../../../stores/course';
+import { My } from '../../../stores/my';
+import { PrepareStatus, ExperiencePercent } from '../../../../Constant';
+
+const PrepareStatusMap = getMap(PrepareStatus, 'value', 'short');
+const ExperiencePercentMap = getMap(ExperiencePercent, 'value', 'label');
 
 export default class extends Page {
   initState() {
@@ -9,48 +16,54 @@ export default class extends Page {
     };
   }
 
+  initData() {
+    const { id } = this.params;
+    Course.getExperience(id)
+      .then(result => {
+        this.setState({ data: result });
+      });
+    Course.experienceView(id);
+  }
+
+  onCollect() {
+    const { data } = this.state;
+    const { collect } = this.state;
+    if (collect === false || !data.collect) {
+      My.addExperienceCollect(data.id)
+        .then(() => {
+          this.setState({ collect: true });
+        });
+    } else {
+      My.delExperienceCollect(data.id)
+        .then(() => {
+          this.setState({ collect: false });
+        });
+    }
+  }
+
   renderView() {
+    const { data = {}, collect } = this.state;
     return (
       <div>
         <div className="center">
           <div className="content">
-            <div className="t-1 t-s-28">GMAT:掉进坑里多少次才能真正懂得“学习” </div>
+            <div className="t-1 t-s-28">{data.title} </div>
             <div className="t-6 m-b-2">
-              <span className="m-r-2">2019-04-27 15:13:31</span>
-              <span className="m-r-2 m-l-1">阅读 8990</span>
-              <span className="m-l-1">收藏</span>
+              <span className="m-r-2">{data.updateTime && formatDate(data.updateTime, 'YYYY-MM-DD HH:mm:ss')}</span>
+              <span className="m-r-2 m-l-1">阅读 {data.viewNumber || 0}</span>
+              <span className="m-l-1" onClick={() => this.onCollect()}>
+                {(collect === false || !data.collect) ? '收藏' : '取消收藏'}
+              </span>
             </div>
             <div className="t-1">
-              <span className="m-r-2">翻翻le</span>
-              <span className="m-r-2 m-l-1">学生-D</span>
-              <span className="m-r-2 m-l-1">备考:2个月</span>
-              <span className="m-r-2 m-l-1">考试时间:2019-06-27</span>
-              <span className="m-r-2 m-l-1">750分 /提分 150+</span>
-              <span className="m-l-1 t-4">更多信息</span>
-            </div>
-            <div className="detail t-1">
-              楼主双非财经大学会计专业 2015 年毕业,毕业之后在一家跨国民营企业战略部工作。雅思两次就刷到了 7.5,Gmat
-              考了5次才勉强出坑,考完就写了一篇总结,过了一个月,思考了更多,及时填坑。 匆匆准备 2 个月的一战:2018 年 3
-              月 10 号,580 分 临时抱佛脚准备半个月的二战:2018 年 5 月,560 分 端正态度准备 2 个月的三站:2018 年 10
-              月,580 分 没有留后路准备 1 个月的四站:2018 年 11 月,560 分 劫后逢生准备 3 个月的五站:2019 年 3 月 23
-              号,710 分 这篇文章主要分享给屡战屡败,屡败屡战的考友们;一边上班一遍申请一遍考 Gmat 的在职党;
-              失败多次,但不愿意面试现实,将失败归因于运气 、心态的考友们。 这篇文章想说的话很多,有些是脱离了 Gmat
-              的内容,但是主题都是关于一点 ——“什么才是真正的读书,什么才是真正的学习”?
-              1.真正的学习,一定是有效的,无效的学习就是意淫。
-              在学习的时候,我们往往会陷入“无效学习的陷阱”,肉体的疲惫和精神的麻痹,让我们“误以为”自己将读到的每一个知识点,做的每一套题目,学习某一个方法都吃透了。
-              但是,真的是这样吗?真的学会了吗?你确定吗?
-              这一点也是我五站中体会比较深刻的一点,看书不要机械式看书,譬如看《曼哈顿语法》,没看完一句话都要
-              countable,联想之前遇到哪些题目考察这一点的?或者如果你是出题人的话,你会从这个考点发散出来什么题目?简单的题目会是什么样子?难的题目会是什么样子?
-              这样子的看书和学习才是有效的,因为你是从目的/终点(也就是考试、做题)出发来进行学习,以终为始,始终带着清晰的目的性,而不要陷入盲目忙碌的状态。
-              2.真正的学习,不是某一个动作,而是一连串的环节和动作。而学习是否有效,不是取决于某个动作的质量,而是这一套连贯动作的质量。
-              这一节会具体说一下我在 5
-              次考试中的学习方式的改变,从只聚焦在一个环节(刷题、看书)到全环节打通(明确考点、现状认知、学习、联系、巩固、复习)
-              3.真正的学习,绝对不是痛苦的,而应该是轻松愉悦的。改变你对Gmat的认知态度,会改变你的学习状态和效率。
-              这一节会分享一下心态的变化,以及心态变化所带来的情绪、状态、学习效率的变化。
-              4.真正的学习,绝对不是忙碌的眉毛鼻子一把抓,而是有侧重。将二八原则应用到 Gmat
-              学习中,抓住自己的核心问题,集中攻破。
-              (但是如果你像我一样,V的每一部分都很差的话,可能需要比其他人努力更多。) 第一次备考(580):
+              <span className="m-r-2">{data.user ? data.user.nickname : data.nickname}</span>
+              <span className="m-r-2 m-l-1">{PrepareStatusMap[data.prepareStatus]}</span>
+              <span className="m-r-2 m-l-1">备考:{!!data.experienceDay && formatMonth(data.experienceDay, false)}</span>
+              <span className="m-r-2 m-l-1">考试时间:{data.experienceTime && formatDate(data.experienceTime, 'YYYY-MM-DD HH:mm:ss')}</span>
+              <span className="m-r-2 m-l-1">{data.experienceScore}分 /提分 {ExperiencePercentMap[data.experiencePercent]}</span>
+              {data.link && <a className="m-l-1 t-4" href={data.link} target="_blank">更多信息</a>}
             </div>
+            <div className="detail t-1" dangerouslySetInnerHTML={{ __html: data.content }} />
           </div>
         </div>
       </div>

+ 11 - 3
front/project/www/routes/course/online/page.js

@@ -9,6 +9,7 @@ import { CommentFalls, AnswerCarousel, Consultation, Contact } from '../../../co
 import Tabs from '../../../components/Tabs';
 import Filter from '../../../components/Filter';
 import { SingleItem, PackageItem } from '../../../components/Item';
+import UserPagination from '../../../components/UserPagination';
 import { Main } from '../../../stores/main';
 import { Course } from '../../../stores/course';
 
@@ -112,7 +113,8 @@ export default class extends Page {
   onFilter(key, value) {
     const { filterMap } = this.state;
     filterMap[key] = value;
-    this.search(filterMap);
+    this.search(filterMap, false);
+    this.initData();
   }
 
   onChangePage(page) {
@@ -160,7 +162,7 @@ export default class extends Page {
   }
 
   renderTabsingle() {
-    const { filterMap, list = [], courseStructSelect, courseStructTreeMap = {} } = this.state;
+    const { filterMap, list = [], courseStructSelect, courseStructTreeMap = {}, total, page } = this.state;
     const child = (courseStructTreeMap[filterMap.parentStructId] || []).map(row => {
       return {
         title: row.title,
@@ -191,11 +193,14 @@ export default class extends Page {
           return <SingleItem data={data} />;
         })}
       </div>,
+      total > 0 && list.length > 0 && (
+        <UserPagination total={total} current={page} pageSize={this.state.search.size} onChange={p => this.onChangePage(p)} />
+      ),
     ];
   }
 
   renderTabpackage() {
-    const { filterMap, list = [], packageStructSelect } = this.state;
+    const { filterMap, list = [], packageStructSelect, total, page } = this.state;
     return [
       <Filter
         filter={filterMap}
@@ -212,6 +217,9 @@ export default class extends Page {
           return <PackageItem data={data} />;
         })}
       </div>,
+      total > 0 && list.length > 0 && (
+        <UserPagination total={total} current={page} pageSize={this.state.search.size} onChange={p => this.onChangePage(p)} />
+      ),
     ];
   }
 }

+ 27 - 2
front/project/www/routes/course/packageDetail/page.js

@@ -9,6 +9,8 @@ import Footer from '../../../components/Footer';
 import { Contact } from '../../../components/Other';
 import { Main } from '../../../stores/main';
 import { Course } from '../../../stores/course';
+import { User } from '../../../stores/user';
+import { Order } from '../../../stores/order';
 import { ServiceKey, ServiceParamMap } from '../../../../Constant';
 
 const serviceIconMap = {
@@ -38,8 +40,31 @@ export default class extends Page {
       });
   }
 
+  buy() {
+    const { data } = this.props;
+    User.needLogin().then(() => {
+      Order.speedPay({ productType: 'course_package', productId: data.id })
+        .then(result => {
+          User.needPay(result)
+            .then(() => {
+              linkTo('/my/course');
+            });
+        });
+    });
+  }
+
+  add() {
+    const { data } = this.props;
+    User.needLogin().then(() => {
+      Order.addCheckout({ productType: 'course_package', productId: data.id })
+        .then(() => {
+          this.setState({ add: true });
+        });
+    });
+  }
+
   renderView() {
-    const { data = {}, base = {} } = this.state;
+    const { data = {}, base = {}, add } = this.state;
     return (
       <div>
         <div className="top content t-8">
@@ -52,7 +77,7 @@ export default class extends Page {
               <Button className="m-r-1" radius size="lager" onClick={() => this.buy()}>
                 立即购买
               </Button>
-              <Button theme="default" radius size="lager" onClick={() => this.add()}>
+              <Button theme="default" radius size="lager" disabled={data.add || add} onClick={() => this.add()}>
                 <Assets name="add" />
               </Button>
             </div>

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

@@ -486,15 +486,15 @@ export default class extends Page {
                 <Assets name="sun_blue" src={item.cover} />
                 <div className="fixed">
                   <div className="btns">
-                    <Button
+                    {<Button
                       size="small"
                       radius
                       onClick={() => {
                         openLink(item.resource);
                       }}
                     >
-                      阅读
-                    </Button>
+                      {item.have ? '阅读' : '预览'}
+                    </Button>}
                     <div
                       hidden={item.resource === item.trailResource}
                       className="white"

+ 61 - 0
server/data/src/main/java/com/qxgmat/data/dao/entity/CourseExperience.java

@@ -59,6 +59,9 @@ public class CourseExperience implements Serializable {
     @Column(name = "`experience_percent`")
     private String experiencePercent;
 
+    @Column(name = "`experience_time`")
+    private Date experienceTime;
+
     /**
      * 阅读量
      */
@@ -83,6 +86,12 @@ public class CourseExperience implements Serializable {
     @Column(name = "`content`")
     private String content;
 
+    /**
+     * 简介
+     */
+    @Column(name = "`description`")
+    private String description;
+
     private static final long serialVersionUID = 1L;
 
     /**
@@ -244,6 +253,20 @@ public class CourseExperience implements Serializable {
     }
 
     /**
+     * @return experience_time
+     */
+    public Date getExperienceTime() {
+        return experienceTime;
+    }
+
+    /**
+     * @param experienceTime
+     */
+    public void setExperienceTime(Date experienceTime) {
+        this.experienceTime = experienceTime;
+    }
+
+    /**
      * 获取阅读量
      *
      * @return view_number - 阅读量
@@ -325,6 +348,24 @@ public class CourseExperience implements Serializable {
         this.content = content;
     }
 
+    /**
+     * 获取简介
+     *
+     * @return description - 简介
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * 设置简介
+     *
+     * @param description 简介
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -340,11 +381,13 @@ public class CourseExperience implements Serializable {
         sb.append(", experienceDay=").append(experienceDay);
         sb.append(", experienceScore=").append(experienceScore);
         sb.append(", experiencePercent=").append(experiencePercent);
+        sb.append(", experienceTime=").append(experienceTime);
         sb.append(", viewNumber=").append(viewNumber);
         sb.append(", collectNumber=").append(collectNumber);
         sb.append(", createTime=").append(createTime);
         sb.append(", updateTime=").append(updateTime);
         sb.append(", content=").append(content);
+        sb.append(", description=").append(description);
         sb.append("]");
         return sb.toString();
     }
@@ -449,6 +492,14 @@ public class CourseExperience implements Serializable {
         }
 
         /**
+         * @param experienceTime
+         */
+        public Builder experienceTime(Date experienceTime) {
+            obj.setExperienceTime(experienceTime);
+            return this;
+        }
+
+        /**
          * 设置阅读量
          *
          * @param viewNumber 阅读量
@@ -494,6 +545,16 @@ public class CourseExperience implements Serializable {
             return this;
         }
 
+        /**
+         * 设置简介
+         *
+         * @param description 简介
+         */
+        public Builder description(String description) {
+            obj.setDescription(description);
+            return this;
+        }
+
         public CourseExperience build() {
             return this.obj;
         }

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

@@ -14,6 +14,7 @@
     <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="experience_time" jdbcType="TIMESTAMP" property="experienceTime" />
     <result column="view_number" jdbcType="INTEGER" property="viewNumber" />
     <result column="collect_number" jdbcType="INTEGER" property="collectNumber" />
     <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
@@ -24,19 +25,20 @@
       WARNING - @mbg.generated
     -->
     <result column="content" jdbcType="LONGVARCHAR" property="content" />
+    <result column="description" jdbcType="LONGVARCHAR" property="description" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
     `id`, `user_id`, `nickname`, `title`, `link`, `prepare_status`, `experience_day`, 
-    `experience_score`, `experience_percent`, `view_number`, `collect_number`, `create_time`, 
-    `update_time`
+    `experience_score`, `experience_percent`, `experience_time`, `view_number`, `collect_number`, 
+    `create_time`, `update_time`
   </sql>
   <sql id="Blob_Column_List">
     <!--
       WARNING - @mbg.generated
     -->
-    `content`
+    `content`, `description`
   </sql>
 </mapper>

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

@@ -146,6 +146,7 @@ CREATE TABLE course_experience (
   experience_day int(11) unsigned NOT NULL DEFAULT '0' COMMENT '备考周期: 天',
   experience_score int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分手成绩: 具体分数',
   experience_percent varchar(255) NOT NULL DEFAULT '' COMMENT '提分幅度',
+  experience_time datetime DEFAULT NULL COMMENT '考试时间',
   view_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '阅读量',
   collect_number int(11) unsigned NOT NULL DEFAULT '0' COMMENT '收藏量',
   create_time datetime DEFAULT NULL,

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

@@ -19,6 +19,7 @@ import com.qxgmat.dto.extend.*;
 import com.qxgmat.dto.request.*;
 import com.qxgmat.dto.response.*;
 import com.qxgmat.help.ShiroHelp;
+import com.qxgmat.service.UserCollectExperienceService;
 import com.qxgmat.service.UsersService;
 import com.qxgmat.service.extend.CourseExtendService;
 import com.qxgmat.service.extend.PreviewService;
@@ -105,6 +106,9 @@ public class CourseController {
     private UserCourseDataSubscribeService userCourseDataSubscribeService;
 
     @Autowired
+    private UserCollectExperienceService userCollectExperienceService;
+
+    @Autowired
     private UsersService usersService;
 
 
@@ -491,28 +495,48 @@ public class CourseController {
             @RequestParam(required = false, defaultValue = "desc") String direction,
             HttpSession session) {
 
+        User user = (User) shiroHelp.getLoginUser();
         Page<CourseExperience> p = courseExperienceService.list(page, size, prepareStatus, ExperienceScoreRange.ValueOf(experienceScore), ExperienceDayRange.ValueOf(experienceDay), experiencePercent, order, DirectionStatus.ValueOf(direction));
+        Collection ids = Transform.getIds(p, CourseExperience.class, "id");
 
         // 绑定用户
-        Collection userIds = Transform.getIds(p, CourseExperienceListDto.class, "userId");
+        Collection userIds = Transform.getIds(p, CourseExperience.class, "userId");
         List<User> userList = usersService.select(userIds);
         courseExperienceService.replaceUser(p, userList);
 
         List<CourseExperienceListDto> pr = Transform.convert(p, CourseExperienceListDto.class);
 
+        if(user != null){
+            List<UserCollectExperience> userCollectExperienceList = userCollectExperienceService.listByUserAndExperiences(user.getId(), ids);
+            Map userCollectExperienceMap = Transform.getMap(userCollectExperienceList, UserCollectExperience.class, "experienceId");
+
+            for(CourseExperienceListDto dto : pr){
+                dto.setCollect(userCollectExperienceMap.containsKey(dto.getId()));
+            }
+        }
+
         return ResponseHelp.success(pr, page, size, p.getTotal());
     }
 
     @RequestMapping(value = "/experience/detail", method = RequestMethod.GET)
     @ApiOperation(value = "心经查看", httpMethod = "GET")
-    public Response<CourseExperience> detailExperience(int experienceId, HttpSession session) {
+    public Response<CourseExperienceDetailDto> detailExperience(int experienceId, HttpSession session) {
+
+        User user = (User) shiroHelp.getLoginUser();
 
         CourseExperience entity = courseExperienceService.get(experienceId);
         if(entity.getUserId() > 0){
-            User user = usersService.get(entity.getUserId());
-            entity.setNickname(user.getNickname());
+            User target = usersService.get(entity.getUserId());
+            entity.setNickname(target.getNickname());
         }
-        return ResponseHelp.success(entity);
+
+        CourseExperienceDetailDto dto = Transform.convert(entity, CourseExperienceDetailDto.class);
+
+        if (user != null){
+            UserCollectExperience userCollectExperience = userCollectExperienceService.getByUserAndExperience(user.getId(), experienceId);
+            dto.setCollect(userCollectExperience != null);
+        }
+        return ResponseHelp.success(dto);
     }
 
     @RequestMapping(value = "/experience/view", method = RequestMethod.POST)

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

@@ -204,6 +204,9 @@ public class MyController {
     private UserOrderRecordService userOrderRecordService;
 
     @Autowired
+    private UserOrderCheckoutService userOrderCheckoutService;
+
+    @Autowired
     private QuestionFlowService questionFlowService;
 
     @Autowired
@@ -1594,7 +1597,7 @@ public class MyController {
 
     @RequestMapping(value = "/data/list", method = RequestMethod.GET)
     @ApiOperation(value = "订阅资料记录", httpMethod = "GET")
-    public Response<PageMessage<CourseData>> listData(
+    public Response<PageMessage<CourseDataListDto>> listData(
             @RequestParam(required = false, defaultValue = "1") int page,
             @RequestParam(required = false, defaultValue = "100") int size,
             @RequestParam(required = false) Integer structId,
@@ -1605,10 +1608,25 @@ public class MyController {
         User user = (User) shiroHelp.getLoginUser();
 
         Page<CourseData> p = courseDataService.listByUser(page, size, user.getId(), structId, DataType.ValueOf(dataType),order, DirectionStatus.ValueOf(direction));
-
         courseExtendService.refreshDataResource(user, p);
+        List<CourseDataListDto> pr = Transform.convert(p, CourseDataListDto.class);
 
-        return ResponseHelp.success(p, page, size, p.getTotal());
+        Collection ids = Transform.getIds(p, CourseData.class, "id");
+
+        // 已购买: 查看当前服务
+        List<UserOrderRecord> userOrderRecordList = userOrderRecordService.listWithUserData(user.getId(), ids);
+        Map userOrderRecordMap = Transform.getMap(userOrderRecordList, UserOrderRecord.class, "productId");
+
+        // 添加购物车
+        List<UserOrderCheckout> userOrderCheckoutList = userOrderCheckoutService.listWithProduct(user.getId(), ProductType.DATA, ids);
+        Map userOrderCheckoutMap = Transform.getMap(userOrderCheckoutList, UserOrderCheckout.class, "productId");
+
+        for(CourseDataListDto dto : pr){
+            dto.setHave(userOrderRecordMap.containsKey(dto.getId()));
+            dto.setAdd(userOrderCheckoutMap.containsKey(dto.getId()));
+        }
+
+        return ResponseHelp.success(pr, page, size, p.getTotal());
     }
 
     @RequestMapping(value = "/course/list", method = RequestMethod.GET)

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

@@ -4,6 +4,7 @@ import com.nuliji.tools.annotation.Dto;
 import com.qxgmat.data.dao.entity.CourseExperience;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 @Dto(entity = CourseExperience.class)
 public class CourseExperienceDto {
@@ -15,6 +16,8 @@ public class CourseExperienceDto {
 
     private String title;
 
+    private String description;
+
     private String link;
 
     private String prepareStatus;
@@ -25,6 +28,8 @@ public class CourseExperienceDto {
 
     private String experiencePercent;
 
+    private Date experienceTime;
+
     private String content;
 
     public Integer getId() {
@@ -106,4 +111,20 @@ public class CourseExperienceDto {
     public void setExperiencePercent(String experiencePercent) {
         this.experiencePercent = experiencePercent;
     }
+
+    public Date getExperienceTime() {
+        return experienceTime;
+    }
+
+    public void setExperienceTime(Date experienceTime) {
+        this.experienceTime = experienceTime;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
 }

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

@@ -26,6 +26,8 @@ public class CourseExperienceListDto {
 
     private String experiencePercent;
 
+    private Date experienceTime;
+
     private Integer viewNumber;
 
     private Integer collectNumber;
@@ -137,4 +139,12 @@ public class CourseExperienceListDto {
     public void setExperiencePercent(String experiencePercent) {
         this.experiencePercent = experiencePercent;
     }
+
+    public Date getExperienceTime() {
+        return experienceTime;
+    }
+
+    public void setExperienceTime(Date experienceTime) {
+        this.experienceTime = experienceTime;
+    }
 }

+ 19 - 0
server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseExperienceDetailDto.java

@@ -0,0 +1,19 @@
+package com.qxgmat.dto.response;
+
+import com.nuliji.tools.annotation.Dto;
+import com.qxgmat.data.dao.entity.CourseExperience;
+
+import java.util.Date;
+
+@Dto(entity = CourseExperience.class)
+public class CourseExperienceDetailDto extends CourseExperience {
+    private Boolean collect;
+
+    public Boolean getCollect() {
+        return collect;
+    }
+
+    public void setCollect(Boolean collect) {
+        this.collect = collect;
+    }
+}

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

@@ -18,6 +18,8 @@ public class CourseExperienceListDto {
 
     private String link;
 
+    private String description;
+
     private String prepareStatus;
 
     private Integer experienceDay;
@@ -26,6 +28,8 @@ public class CourseExperienceListDto {
 
     private String experiencePercent;
 
+    private Date experienceTime;
+
     private Integer viewNumber;
 
     private Integer collectNumber;
@@ -34,6 +38,8 @@ public class CourseExperienceListDto {
 
     private Date updateTime;
 
+    private Boolean collect;
+
     public Integer getId() {
         return id;
     }
@@ -137,4 +143,28 @@ public class CourseExperienceListDto {
     public void setUpdateTime(Date updateTime) {
         this.updateTime = updateTime;
     }
+
+    public Boolean getCollect() {
+        return collect;
+    }
+
+    public void setCollect(Boolean collect) {
+        this.collect = collect;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Date getExperienceTime() {
+        return experienceTime;
+    }
+
+    public void setExperienceTime(Date experienceTime) {
+        this.experienceTime = experienceTime;
+    }
 }

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

@@ -100,7 +100,7 @@ public class UserCollectExperienceService extends AbstractService {
      */
     @Transactional
     public boolean deleteExperience(Integer userId, Integer experienceId){
-        Example example = new Example(UserCollectQuestion.class);
+        Example example = new Example(UserCollectExperience.class);
         example.and(
                 example.createCriteria()
                         .andEqualTo("userId", userId)