Browse Source

feat(server): 服务设置

Go 5 years ago
parent
commit
0d5d5922e2

+ 406 - 1
front/project/admin/routes/setting/service/page.js

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

+ 1 - 1
front/project/admin/routes/user/ask/page.js

@@ -146,7 +146,7 @@ export default class extends Page {
   }
 
   initData() {
-    User.listAsk(this.state.search).then(result => {
+    Question.listAsk(this.state.search).then(result => {
       this.setTableData(result.list, result.total);
     });
   }

+ 4 - 3
front/project/admin/routes/user/askDetail/page.js

@@ -9,7 +9,8 @@ import DragList from '@src/components/DragList';
 import { formatFormError, formatDate, getMap } from '@src/services/Tools';
 import { asyncSMessage } from '@src/services/AsyncTools';
 import { AskTarget, QuestionType } from '../../../../Constant';
-import { User } from '../../../stores/user';
+// import { User } from '../../../stores/user';
+import { Question } from '../../../stores/question';
 
 const QuestionTypeMap = getMap(QuestionType, 'value', 'label');
 const AskTargetMap = getMap(AskTarget, 'value', 'label');
@@ -25,7 +26,7 @@ export default class extends Page {
     const { id } = this.params;
     let handler;
     if (id) {
-      handler = User.getAsk({ id });
+      handler = Question.getAsk({ id });
     } else {
       handler = Promise.resolve({ others: [{ content: 123123123, answer: 123123 }, {}] });
     }
@@ -69,7 +70,7 @@ export default class extends Page {
       if (!err) {
         const data = form.getFieldsValue();
         data.other = this.state.data.others.map(row => row.id);
-        User.editAsk(data).then(() => {
+        Question.editAsk(data).then(() => {
           asyncSMessage('保存成功');
         }).catch((e) => {
           if (e.result) form.setFields(formatFormError(data, e.result));

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

@@ -93,6 +93,30 @@ export default class SystemStore extends BaseStore {
     return this.apiPut('/setting/score_switch', params);
   }
 
+  getServiceVip() {
+    return this.apiGet('/setting/service_vip');
+  }
+
+  setServiceVip(params) {
+    return this.apiPut('/setting/service_vip', params);
+  }
+
+  getServiceTextbook() {
+    return this.apiGet('/setting/service_textbook');
+  }
+
+  setServiceTextbook(params) {
+    return this.apiPut('/setting/service_textbook', params);
+  }
+
+  getServiceQxCat() {
+    return this.apiGet('/setting/service_qx_cat');
+  }
+
+  setServiceQxCat(params) {
+    return this.apiPut('/setting/service_qx_cat', params);
+  }
+
   getTips() {
     return this.apiGet('/setting/tips');
   }

+ 0 - 12
front/project/admin/stores/user.js

@@ -29,18 +29,6 @@ export default class UserStore extends BaseStore {
     return this.apiDel('/user/delete', params);
   }
 
-  listAsk(params) {
-    return this.apiGet('/user/ask/list', params);
-  }
-
-  editAsk(params) {
-    return this.apiPut('/user/ask/edit', params);
-  }
-
-  getAsk(params) {
-    return this.apiGet('/user/ask/detail', params);
-  }
-
   listFeedbackError(params) {
     return this.apiGet('/user/feedback_error/list', params);
   }

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

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

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

@@ -20,6 +20,10 @@ public enum SettingKey {
     SENTENCE_TIME("sentence_time"), // 长难句题目时间
     TEXTBOOK_TIME("textbook_time"), // 机经题目时间
     SCORE_SWITCH("score_switch"), // 分数开关
+    SERVICE_QX_CAT("service_qx_cat"), // 千行CAT服务信息
+    SERVICE_TEXTBOOK("service_textbook"), // 数学机经服务信息
+    SERVICE_VIP("service_vip"), // vip服务信息
+
 
     TIPS("tips"); // 页面提示信息
 
@@ -29,4 +33,21 @@ public enum SettingKey {
     private SettingKey(String key){
         this.key = key;
     }
+
+    /**
+     * 查找服务对应配置信息
+     * @param key
+     * @return
+     */
+    public static SettingKey WithService(ServiceKey key){
+        switch(key){
+            case VIP:
+                return SERVICE_VIP;
+            case TEXTBOOK:
+                return SERVICE_TEXTBOOK;
+            case QX_CAT:
+                return SERVICE_QX_CAT;
+        }
+        return null;
+    }
 }

+ 4 - 1
server/data/src/main/java/com/qxgmat/data/constants/enums/module/PayModule.java

@@ -1,7 +1,10 @@
 package com.qxgmat.data.constants.enums.module;
 
 public enum PayModule {
-    SERVICE("service"), CLASS("class"), DATA("data");
+    SERVICE("service"),
+    CLASS("class"),
+    DATA("data"),
+    ;
     public String key;
     private PayModule(String key){
         this.key = key;

+ 71 - 17
server/data/src/main/java/com/qxgmat/data/dao/entity/UserPay.java

@@ -1,5 +1,6 @@
 package com.qxgmat.data.dao.entity;
 
+import com.alibaba.fastjson.JSONObject;
 import java.io.Serializable;
 import java.util.Date;
 import javax.persistence.*;
@@ -24,10 +25,10 @@ public class UserPay implements Serializable {
     private String module;
 
     /**
-     * 模块扩展信息
+     * 模块扩展信息: json
      */
     @Column(name = "`module_extend`")
-    private String moduleExtend;
+    private JSONObject moduleExtend;
 
     /**
      * 使用状态:0未使用,1已使用
@@ -36,14 +37,26 @@ public class UserPay implements Serializable {
     private Integer isUse;
 
     /**
+     * 最晚使用时间
+     */
+    @Column(name = "`end_time`")
+    private Date endTime;
+
+    /**
      * 使用时间
      */
     @Column(name = "`use_time`")
     private Date useTime;
 
+    /**
+     * 有效期:开始
+     */
     @Column(name = "`start_time`")
     private Date startTime;
 
+    /**
+     * 有效期:结束
+     */
     @Column(name = "`expire_time`")
     private Date expireTime;
 
@@ -103,20 +116,20 @@ public class UserPay implements Serializable {
     }
 
     /**
-     * 获取模块扩展信息
+     * 获取模块扩展信息: json
      *
-     * @return module_extend - 模块扩展信息
+     * @return module_extend - 模块扩展信息: json
      */
-    public String getModuleExtend() {
+    public JSONObject getModuleExtend() {
         return moduleExtend;
     }
 
     /**
-     * 设置模块扩展信息
+     * 设置模块扩展信息: json
      *
-     * @param moduleExtend 模块扩展信息
+     * @param moduleExtend 模块扩展信息: json
      */
-    public void setModuleExtend(String moduleExtend) {
+    public void setModuleExtend(JSONObject moduleExtend) {
         this.moduleExtend = moduleExtend;
     }
 
@@ -139,6 +152,24 @@ public class UserPay implements Serializable {
     }
 
     /**
+     * 获取最晚使用时间
+     *
+     * @return end_time - 最晚使用时间
+     */
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * 设置最晚使用时间
+     *
+     * @param endTime 最晚使用时间
+     */
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
      * 获取使用时间
      *
      * @return use_time - 使用时间
@@ -157,28 +188,36 @@ public class UserPay implements Serializable {
     }
 
     /**
-     * @return start_time
+     * 获取有效期:开始
+     *
+     * @return start_time - 有效期:开始
      */
     public Date getStartTime() {
         return startTime;
     }
 
     /**
-     * @param startTime
+     * 设置有效期:开始
+     *
+     * @param startTime 有效期:开始
      */
     public void setStartTime(Date startTime) {
         this.startTime = startTime;
     }
 
     /**
-     * @return expire_time
+     * 获取有效期:结束
+     *
+     * @return expire_time - 有效期:结束
      */
     public Date getExpireTime() {
         return expireTime;
     }
 
     /**
-     * @param expireTime
+     * 设置有效期:结束
+     *
+     * @param expireTime 有效期:结束
      */
     public void setExpireTime(Date expireTime) {
         this.expireTime = expireTime;
@@ -209,6 +248,7 @@ public class UserPay implements Serializable {
         sb.append(", module=").append(module);
         sb.append(", moduleExtend=").append(moduleExtend);
         sb.append(", isUse=").append(isUse);
+        sb.append(", endTime=").append(endTime);
         sb.append(", useTime=").append(useTime);
         sb.append(", startTime=").append(startTime);
         sb.append(", expireTime=").append(expireTime);
@@ -257,11 +297,11 @@ public class UserPay implements Serializable {
         }
 
         /**
-         * 设置模块扩展信息
+         * 设置模块扩展信息: json
          *
-         * @param moduleExtend 模块扩展信息
+         * @param moduleExtend 模块扩展信息: json
          */
-        public Builder moduleExtend(String moduleExtend) {
+        public Builder moduleExtend(JSONObject moduleExtend) {
             obj.setModuleExtend(moduleExtend);
             return this;
         }
@@ -277,6 +317,16 @@ public class UserPay implements Serializable {
         }
 
         /**
+         * 设置最晚使用时间
+         *
+         * @param endTime 最晚使用时间
+         */
+        public Builder endTime(Date endTime) {
+            obj.setEndTime(endTime);
+            return this;
+        }
+
+        /**
          * 设置使用时间
          *
          * @param useTime 使用时间
@@ -287,7 +337,9 @@ public class UserPay implements Serializable {
         }
 
         /**
-         * @param startTime
+         * 设置有效期:开始
+         *
+         * @param startTime 有效期:开始
          */
         public Builder startTime(Date startTime) {
             obj.setStartTime(startTime);
@@ -295,7 +347,9 @@ public class UserPay implements Serializable {
         }
 
         /**
-         * @param expireTime
+         * 设置有效期:结束
+         *
+         * @param expireTime 有效期:结束
          */
         public Builder expireTime(Date expireTime) {
             obj.setExpireTime(expireTime);

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

@@ -8,8 +8,9 @@
     <id column="id" jdbcType="INTEGER" property="id" />
     <result column="user_id" jdbcType="INTEGER" property="userId" />
     <result column="module" jdbcType="VARCHAR" property="module" />
-    <result column="module_extend" jdbcType="VARCHAR" property="moduleExtend" />
+    <result column="module_extend" jdbcType="VARCHAR" property="moduleExtend" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler" />
     <result column="is_use" jdbcType="INTEGER" property="isUse" />
+    <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
     <result column="use_time" jdbcType="TIMESTAMP" property="useTime" />
     <result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
     <result column="expire_time" jdbcType="TIMESTAMP" property="expireTime" />
@@ -19,7 +20,7 @@
     <!--
       WARNING - @mbg.generated
     -->
-    `id`, `user_id`, `module`, `module_extend`, `is_use`, `use_time`, `start_time`, `expire_time`, 
-    `create_time`
+    `id`, `user_id`, `module`, `module_extend`, `is_use`, `end_time`, `use_time`, `start_time`, 
+    `expire_time`, `create_time`
   </sql>
 </mapper>

+ 43 - 0
server/data/src/main/java/com/qxgmat/data/inline/Product.java

@@ -0,0 +1,43 @@
+package com.qxgmat.data.inline;
+
+public class Product {
+    private Integer price;
+
+    private String module;
+
+    private String title;
+
+    private String key;
+
+    public Integer getPrice() {
+        return price;
+    }
+
+    public void setPrice(Integer price) {
+        this.price = price;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public void setModule(String module) {
+        this.module = module;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+}

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

@@ -154,5 +154,9 @@
             <columnOverride column="setting" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
             <columnOverride column="detail" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
         </table>
+        <table schema="qianxing" tableName="user_pay" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false" delimitAllColumns="true">
+            <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
+            <columnOverride column="module_extend" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR" typeHandler="com.nuliji.tools.mybatis.handler.JsonObjectHandler"/>
+        </table>
     </context>
 </generatorConfiguration>

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

@@ -199,6 +199,57 @@ public class SettingController {
         return ResponseHelp.success(entity.getValue());
     }
 
+    @RequestMapping(value = "/service_vip", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改Vip服务", httpMethod = "PUT")
+    private Response<Boolean> editServiceVip(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.SERVICE_VIP);
+        entity.setValue(dto);
+        settingService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/service_vip", method = RequestMethod.GET)
+    @ApiOperation(value = "获取Vip服务", httpMethod = "GET")
+    private Response<JSONObject> getServiceVip(){
+        Setting entity = settingService.getByKey(SettingKey.SERVICE_VIP);
+
+        return ResponseHelp.success(entity.getValue());
+    }
+
+    @RequestMapping(value = "/service_textbook", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改千行CAT服务", httpMethod = "PUT")
+    private Response<Boolean> editServiceTextbook(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.SERVICE_TEXTBOOK);
+        entity.setValue(dto);
+        settingService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/service_textbook", method = RequestMethod.GET)
+    @ApiOperation(value = "获取千行CAT服务", httpMethod = "GET")
+    private Response<JSONObject> getServiceTextbook(){
+        Setting entity = settingService.getByKey(SettingKey.SERVICE_TEXTBOOK);
+
+        return ResponseHelp.success(entity.getValue());
+    }
+
+    @RequestMapping(value = "/service_qx_cat", method = RequestMethod.PUT)
+    @ApiOperation(value = "修改千行CAT服务", httpMethod = "PUT")
+    private Response<Boolean> editServiceQXCat(@RequestBody @Validated JSONObject dto){
+        Setting entity = settingService.getByKey(SettingKey.SCORE_SWITCH);
+        entity.setValue(dto);
+        settingService.edit(entity);
+        return ResponseHelp.success(true);
+    }
+
+    @RequestMapping(value = "/service_qx_cat", method = RequestMethod.GET)
+    @ApiOperation(value = "获取千行CAT服务", httpMethod = "GET")
+    private Response<JSONObject> getServiceQXCat(){
+        Setting entity = settingService.getByKey(SettingKey.SCORE_SWITCH);
+
+        return ResponseHelp.success(entity.getValue());
+    }
+
     @RequestMapping(value = "/tips", method = RequestMethod.PUT)
     @ApiOperation(value = "修改结构说明", httpMethod = "PUT")
     private Response<Boolean> editTips(@RequestBody @Validated JSONObject dto){

+ 5 - 3
server/gateway-api/src/main/java/com/qxgmat/dto/request/PayProductDto.java

@@ -1,10 +1,12 @@
 package com.qxgmat.dto.request;
 
+import com.alibaba.fastjson.JSONObject;
+
 public class PayProductDto {
 
     private String module;
 
-    private String moduleExtend;
+    private JSONObject moduleExtend;
 
     public String getModule() {
         return module;
@@ -14,11 +16,11 @@ public class PayProductDto {
         this.module = module;
     }
 
-    public String getModuleExtend() {
+    public JSONObject getModuleExtend() {
         return moduleExtend;
     }
 
-    public void setModuleExtend(String moduleExtend) {
+    public void setModuleExtend(JSONObject moduleExtend) {
         this.moduleExtend = moduleExtend;
     }
 }

+ 9 - 5
server/gateway-api/src/main/java/com/qxgmat/service/extend/TradeService.java

@@ -1,5 +1,6 @@
 package com.qxgmat.service.extend;
 
+import com.alibaba.fastjson.JSONObject;
 import com.nuliji.tools.AbstractService;
 import com.nuliji.tools.Tools;
 import com.nuliji.tools.exception.ParameterException;
@@ -72,10 +73,11 @@ public class TradeService extends AbstractService {
         payCallback.put(PayModule.SERVICE, (obj)->{
             Pay pay = (Pay) obj;
             Integer userId = pay.getUserId();
+            JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
             // 写入用户购买
             userPayService.add(UserPay.builder()
                     .module(pay.getModule())
-                    .moduleExtend(pay.getModuleExtend())
+                    .moduleExtend(extend)
                     .isUse(0)
                     .userId(userId).build());
             return true;
@@ -83,10 +85,11 @@ public class TradeService extends AbstractService {
         payCallback.put(PayModule.CLASS, (obj)->{
             Pay pay = (Pay) obj;
             Integer userId = pay.getUserId();
+            JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
             // 写入用户购买
             userPayService.add(UserPay.builder()
                     .module(pay.getModule())
-                    .moduleExtend(pay.getModuleExtend())
+                    .moduleExtend(extend)
                     .isUse(0)
                     .userId(userId).build());
             return true;
@@ -94,10 +97,11 @@ public class TradeService extends AbstractService {
         payCallback.put(PayModule.DATA, (obj)->{
             Pay pay = (Pay) obj;
             Integer userId = pay.getUserId();
+            JSONObject extend = JSONObject.parseObject(pay.getModuleExtend());
             // 写入用户购买
             userPayService.add(UserPay.builder()
                     .module(pay.getModule())
-                    .moduleExtend(pay.getModuleExtend())
+                    .moduleExtend(extend)
                     .isUse(0)
                     .userId(userId).build());
             // 直接绑定数据关系
@@ -117,7 +121,7 @@ public class TradeService extends AbstractService {
      * @param request
      * @return
      */
-    public PayResponseData pay(Integer userId, String subject, String body, String module, String moduleExtend, BigDecimal money, PayChannel channel, HttpServletRequest request) throws Exception {
+    public PayResponseData pay(Integer userId, String subject, String body, String module, JSONObject moduleExtend, BigDecimal money, PayChannel channel, HttpServletRequest request) throws Exception {
         if(!payCallback.containsKey(module))
             throw new ParameterException("不支持的支付模块");
         PayRequestData data = new PayRequestData();
@@ -128,7 +132,7 @@ public class TradeService extends AbstractService {
         String pid = preprocess(data);
         String clientIp = Tools.getClientIp(request);
         PayType payType = PayType.FromChannel(channel);
-        Pay pay = payService.make(userId, payType == null ? "":payType.key, channel.key, money, module, moduleExtend, pid, subject, body, clientIp);
+        Pay pay = payService.make(userId, payType == null ? "":payType.key, channel.key, money, module, moduleExtend.toJSONString(), pid, subject, body, clientIp);
 
         PayInfo info = process(pay, channel);
         PayResponseData payResponseData = new PayResponseData();

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

@@ -24,12 +24,6 @@ public class UserServiceService extends AbstractService {
     @Resource
     private UserServiceMapper userServiceMapper;
 
-    // 开通服务
-    public boolean used(UserPay pay){
-        add(UserService.builder().userId(pay.getUserId()).service(pay.getModuleExtend()).build());
-        return true;
-    }
-
     /**
      * 判断是否有权限
      * @param userId