Browse Source

Merge branch 'master' of www.gitinn.com:zaixianjiaoyu/sourcecode

KaysonCui 5 years ago
parent
commit
7662a2af31
33 changed files with 306 additions and 177 deletions
  1. 2 2
      front/config/local.json
  2. 1 1
      front/project/admin/routes/course/detail/page.js
  3. 12 2
      front/project/admin/routes/setting/rank/page.js
  4. 1 1
      front/project/h5/components/Button/index.js
  5. 3 2
      front/project/h5/components/Input/index.js
  6. 1 1
      front/project/h5/index.js
  7. 1 0
      front/project/h5/routes/page/bind/page.js
  8. 2 1
      front/project/h5/routes/page/index.js
  9. 24 15
      front/project/h5/routes/page/login/page.js
  10. 4 4
      front/project/h5/routes/product/dataDetail/page.js
  11. 8 5
      front/project/h5/stores/user.js
  12. 9 7
      front/project/www/components/Login/index.js
  13. 2 1
      front/project/www/index.js
  14. 17 10
      front/project/www/stores/user.js
  15. 3 3
      server/data/src/main/resources/db/migration/V1__init_table.sql
  16. 1 0
      server/gateway-api/build.gradle
  17. 51 16
      server/gateway-api/src/main/java/com/qxgmat/controller/admin/SettingController.java
  18. 25 14
      server/gateway-api/src/main/java/com/qxgmat/controller/api/AuthController.java
  19. 1 1
      server/gateway-api/src/main/java/com/qxgmat/controller/gateway/OauthController.java
  20. 22 18
      server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseDetailDto.java
  21. 18 18
      server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseListDto.java
  22. 3 0
      server/gateway-api/src/main/java/com/qxgmat/help/ShiroHelp.java
  23. 31 11
      server/gateway-api/src/main/java/com/qxgmat/help/SmsHelp.java
  24. 5 0
      server/gateway-api/src/main/java/com/qxgmat/help/VideoHelp.java
  25. 4 4
      server/gateway-api/src/main/java/com/qxgmat/help/WechatHelp.java
  26. 24 25
      server/gateway-api/src/main/java/com/qxgmat/service/UsersService.java
  27. 1 0
      server/gateway-api/src/main/java/com/qxgmat/service/extend/MessageExtendService.java
  28. 2 1
      server/gateway-api/src/main/java/com/qxgmat/util/shiro/OauthRealm.java
  29. 3 3
      server/gateway-api/src/main/profile/dev/application-runtime.yml
  30. 1 1
      server/gateway-api/src/main/profile/test/application-runtime.yml
  31. 9 0
      server/gateway-api/src/main/resources/application.yml
  32. 5 3
      server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudSms.java
  33. 10 7
      server/tools/src/main/java/com/nuliji/tools/third/wechat/WechatClient.java

+ 2 - 2
front/config/local.json

@@ -15,9 +15,9 @@
         "to": "/"
       }
     ],
-    "PcUrl": "http://127.0.0.1:3000",
+    "PcUrl": "http://test.duoshaojiaoyu.com",
     "WechatPcAppId": "wxa6a1620243459e6a",
-    "H5Url": "http://127.0.0.1:3000",
+    "H5Url": "http://mobiletest.duoshaojiaoyu.com",
     "WechatH5AppId": "wx65c7d378b4184bcc"
   },
   "test": {

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

@@ -33,7 +33,7 @@ export default class extends Page {
         return <Upload
           showUploadList={false}
           beforeUpload={(file) => System.uploadVideo(file).then((result) => {
-            return Course.addNo({ courseId: this.params.id, resource: result.url, time: result.time });
+            return Course.addNo({ courseId: this.params.id, resource: result.url, time: result.time, title: file.name.split('.')[0] });
           }).then(() => {
             this.refreshNo();
           })}

+ 12 - 2
front/project/admin/routes/setting/rank/page.js

@@ -61,6 +61,12 @@ export default class extends Page {
       title: 'Q排名',
       dataIndex: 'quantRank',
     }, {
+      title: 'V分',
+      dataIndex: 'verbalScore',
+    }, {
+      title: 'V排名',
+      dataIndex: 'verbalRank',
+    }, {
       title: 'IR分',
       dataIndex: 'irScore',
     }, {
@@ -74,8 +80,12 @@ export default class extends Page {
       render: (item) => {
         return <Upload
           showUploadList={false}
-          beforeUpload={(file) => System.importRank(file).then((result) => {
-            asyncSMessage(result);
+          beforeUpload={(file) => System.importRank({ file }).then((result) => {
+            asyncSMessage(`导入成功: ${result}条数据`);
+            this.refresh();
+            return Promise.reject();
+          }).catch(e => {
+            asyncSMessage(e.message, 'error');
             return Promise.reject();
           })}
         >

+ 1 - 1
front/project/h5/components/Button/index.js

@@ -21,11 +21,11 @@ export default class Button extends Component {
         className={`g-button-wrapper ${className} ${size} ${theme} ${radius ? 'radius' : ''} ${block ? 'block' : ''} ${
           disabled ? 'disabled' : ''
         }`}
+        onClick={() => onClick && onClick()}
       >
         <Touch
           style={{ width: width || 'auto', margin: margin ? `0 ${margin}px` : '0 auto' }}
           className="g-button"
-          onClick={() => onClick && onClick()}
         >
           {children}
         </Touch>

+ 3 - 2
front/project/h5/components/Input/index.js

@@ -69,7 +69,7 @@ export class VerificationInput extends Component {
     if (onSend) {
       onSend()
         .then(() => {
-          console.log(time);
+          if (this.timeKey) clearTimeout(this.timeKey);
           this.setTime(time);
         });
     }
@@ -84,7 +84,7 @@ export class VerificationInput extends Component {
 
   render() {
     const { loading } = this.state;
-    const { className = '', onChange, placeholder, value } = this.props;
+    const { className = '', onChange, placeholder, value, error } = this.props;
     return (
       <Input
         className={className}
@@ -98,6 +98,7 @@ export class VerificationInput extends Component {
           )
         }
         value={value}
+        error={error}
         placeholder={placeholder}
         onChange={data => onChange && onChange(data)}
       />

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

@@ -2,7 +2,7 @@ export default {
   mode: () => import('./app'),
   apiToken: 'token',
   loginAuth(route, { user }) {
-    if (route.needLogin && !user.login) return false;
+    if (route.needLogin && !user.login) return true;
     return true;
   },
 };

+ 1 - 0
front/project/h5/routes/page/bind/page.js

@@ -84,6 +84,7 @@ export default class extends Page {
         }
       })
       .catch(err => {
+        console.log(err.message);
         if (err.message.indexOf('验证码') >= 0) {
           this.setState({ validError: err.message });
         } else {

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

@@ -8,5 +8,6 @@ import study from './study';
 import message from './message';
 import open from './open';
 import pay from './pay';
+import bought from './bought';
 
-export default [home, login, bind, identity, invitation, study, id, message, open, pay];
+export default [home, login, bind, identity, invitation, study, id, message, open, pay, bought];

+ 24 - 15
front/project/h5/routes/page/login/page.js

@@ -1,32 +1,41 @@
 import React from 'react';
 import './index.less';
 import Page from '@src/containers/Page';
-import { WechatH5AppId } from '../../../../Constant';
+import { asyncSMessage } from '@src/services/AsyncTools';
+import { WechatH5AppId, H5Url } from '../../../../Constant';
 import { User } from '../../../stores/user';
 
 export default class extends Page {
   init() {
-    const { code, url } = this.props.core.query;
+    const { code, state = '' } = this.props.core.query;
+    console.log(this.state);
+    const [scope, jump] = state.split('|');
+
     if (code) {
-      User.loginWechat(code).then(info => {
-        if (info.bindMobile) {
-          if (url) {
-            toLink(url);
-          } else {
-            replaceLink('/');
-          }
+      User.loginWechat(code, !!scope).then((info) => {
+        if (!info.id && !scope) {
+          this.redirect('snsapi_userinfo', `1|${jump || '/'}`);
+        } else if (info.bindMobile) {
+          toLink(jump || '/');
         } else {
-          replaceLink(`/bind?url=${url}`);
+          replaceLink(`/bind?url=${encodeURIComponent(jump)}`);
         }
-      });
+      })
+        .catch(e => {
+          asyncSMessage(e.message, 'error');
+        });
     } else {
-      const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${WechatH5AppId}&redirect_uri=${encodeURIComponent(
-        `${window.location.href}`,
-      )}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
-      window.location.href = authUrl;
+      this.redirect('snsapi_base', `|${this.state.search.url || '/'}`);
     }
   }
 
+  redirect(scope, state) {
+    const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${WechatH5AppId}&redirect_uri=${encodeURIComponent(
+      `${H5Url}/login`,
+    )}&response_type=code&scope=${scope}&state=${encodeURIComponent(state)}#wechat_redirect`;
+    window.location.href = url;
+  }
+
   renderView() {
     return <div />;
   }

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

@@ -20,7 +20,7 @@ export default class extends Page {
   }
 
   initData() {
-    const { id } = this.state;
+    const { id } = this.params;
     Course.getData(id).then(data => {
       this.setState({ data });
     });
@@ -75,10 +75,10 @@ export default class extends Page {
           <div className="info">
             <div className="title">{data.title}</div>
             <div className="tags">
-              {data.isOriginal && <Tag size="small">原创</Tag>}
-              {data.isNovice && <Tag size="small">适合新手</Tag>}
+              {data.isOriginal > 0 && <Tag size="small">原创</Tag>}
+              {data.isNovice > 0 && <Tag size="small">适合新手</Tag>}
             </div>
-            <div className="data">页数: {data.page}页</div>
+            <div className="data">页数: {data.pages}页</div>
             <div className="data">格式: {DataTypeMap[data.dataType]}</div>
             <div className="data">{formatDate(data.updateTime, 'YYYY-MM-DD HH:mm')} 更新</div>
             <Money value={data.price} size="lager" theme="sell" />

+ 8 - 5
front/project/h5/stores/user.js

@@ -52,8 +52,8 @@ export default class UserStore extends BaseStore {
     });
   }
 
-  loginWechat(code) {
-    return this.apiGet('/auth/wechat', { code }).then((result) => {
+  loginWechat(code, userInfo) {
+    return this.apiGet('/auth/wechat', { code, userInfo }).then((result) => {
       this.infoHandle(result);
       return result;
     });
@@ -71,14 +71,17 @@ export default class UserStore extends BaseStore {
    * @param {*} area 区域码
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
-   * @param {*} inviteCode 邀请人手机/邀请码
    * @param {*} email 绑定邮箱
+   * @param {*} inviteCode 邀请人手机/邀请码
    */
-  bind(area, mobile, mobileVerifyCode, inviteCode, email) {
+  bind(area, mobile, mobileVerifyCode, email, inviteCode) {
     if (!inviteCode) {
       ({ inviteCode } = this.state);
     }
-    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email });
+    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email }).then(result => {
+      this.infoHandle(result);
+      return result;
+    });
   }
 
   /**

+ 9 - 7
front/project/www/components/Login/index.js

@@ -7,7 +7,7 @@ import { Icon as GIcon } from '../Icon';
 import { Button as GButton } from '../Button';
 import { User } from '../../stores/user';
 import { Common } from '../../stores/common';
-import { MobileArea, WechatPcAppId } from '../../../Constant';
+import { MobileArea, WechatPcAppId, PcUrl } from '../../../Constant';
 
 const LOGIN_PHONE = 'LOGIN_PHONE';
 const LOGIN_WX = 'LOGIN_WX';
@@ -46,7 +46,7 @@ export default class Login extends Component {
     if (mobileError || validError) return;
     if (!area || !mobile || !mobileVerifyCode) return;
     if (needEmail && !email) return;
-    User.login(area, mobile, mobileVerifyCode, email)
+    User.login(area, mobile, mobileVerifyCode, email, null, false)
       .then(result => {
         if (result.bindWechat) {
           this.close();
@@ -83,7 +83,7 @@ export default class Login extends Component {
   }
 
   scanLogin(code) {
-    User.loginWechat(code).then(result => {
+    User.loginWechat(code, false).then(result => {
       if (result.bindMobile) {
         this.close();
       } else {
@@ -93,7 +93,7 @@ export default class Login extends Component {
   }
 
   scanBind(code) {
-    User.loginWechat(code)
+    User.loginWechat(code, false)
       .then(() => {
         this.close();
       })
@@ -267,7 +267,7 @@ export default class Login extends Component {
         <div className="qr-code">
           <iframe
             frameBorder="0"
-            src={`/login.html?appid=${WechatPcAppId}&redirectUri=${encodeURIComponent('http://www.duoshaojiaoyu.com')}`}
+            src={`/login.html?appid=${WechatPcAppId}&redirectUri=${encodeURIComponent(PcUrl)}`}
             width="300"
             height="300"
           />
@@ -356,7 +356,7 @@ export default class Login extends Component {
         <div className="qr-code">
           <iframe
             frameBorder="0"
-            src={`/login.html?appid=${WechatPcAppId}&redirectUri=${encodeURIComponent('http://www.duoshaojiaoyu.com')}`}
+            src={`/login.html?appid=${WechatPcAppId}&redirectUri=${encodeURIComponent(PcUrl)}`}
             width="300"
             height="300"
           />
@@ -475,6 +475,7 @@ export class VerificationInput extends Component {
     const { onSend, time = 60 } = this.props;
     if (onSend) {
       onSend().then(() => {
+        if (this.timeKey) clearTimeout(this.timeKey);
         this.setTime(time);
       });
     }
@@ -489,7 +490,7 @@ export class VerificationInput extends Component {
 
   render() {
     const { loading } = this.state;
-    const { className = '', onChange, placeholder, value } = this.props;
+    const { className = '', onChange, placeholder, value, error } = this.props;
     return (
       <Input
         className={className}
@@ -503,6 +504,7 @@ export class VerificationInput extends Component {
           )
         }
         value={value}
+        error={error}
         placeholder={placeholder}
         onChange={data => onChange && onChange(data)}
       />

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

@@ -1,8 +1,9 @@
 export default {
   mode: () => import('./app'),
   apiToken: 'token',
+  loginPath: '/',
   loginAuth(route, { user }) {
-    if (route.needLogin && !user.login) return true;
+    if (route.needLogin && !user.login) return false;
     return true;
   },
   tabs: [

+ 17 - 10
front/project/www/stores/user.js

@@ -59,9 +59,9 @@ export default class UserStore extends BaseStore {
       });
   }
 
-  infoHandle(result) {
+  infoHandle(result, auto = true) {
     if (result.token) this.setToken(result.token);
-    this.setState({ login: true, needLogin: false, info: result, username: result.username });
+    this.setState({ login: true, needLogin: !auto, info: result, username: result.username });
   }
 
   originInviteCode(inviteCode) {
@@ -88,22 +88,22 @@ export default class UserStore extends BaseStore {
    * 登陆
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
-   * @param {*} inviteCode 邀请人手机/邀请码
    * @param {*} email 绑定邮箱
+   * @param {*} inviteCode 邀请人手机/邀请码
    */
-  login(area, mobile, mobileVerifyCode, inviteCode, email) {
+  login(area, mobile, mobileVerifyCode, email, inviteCode, auto) {
     if (!inviteCode) {
       ({ inviteCode } = this.state);
     }
     return this.apiPost('/auth/login', { area, mobile, mobileVerifyCode, inviteCode, email }).then(result => {
-      this.infoHandle(result);
+      this.infoHandle(result, auto);
       return result;
     });
   }
 
-  loginWechat(code) {
+  loginWechat(code, auto) {
     return this.apiGet('/auth/wechat_pc', { code }).then(result => {
-      this.infoHandle(result);
+      this.infoHandle(result, auto);
       return result;
     });
   }
@@ -129,13 +129,20 @@ export default class UserStore extends BaseStore {
 
   /**
    * 绑定手机
+   * @param {*} area 区域码
    * @param {*} mobile 手机号
    * @param {*} mobileVerifyCode 手机验证码
-   * @param {*} inviteCode 邀请人手机/邀请码
    * @param {*} email 绑定邮箱
+   * @param {*} inviteCode 邀请人手机/邀请码
    */
-  bind(area, mobile, mobileVerifyCode, inviteCode, email) {
-    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email });
+  bind(area, mobile, mobileVerifyCode, email, inviteCode) {
+    if (!inviteCode) {
+      ({ inviteCode } = this.state);
+    }
+    return this.apiPost('/auth/bind', { area, mobile, mobileVerifyCode, inviteCode, email }).then(result => {
+      this.infoHandle(result);
+      return result;
+    });
   }
 
   /**

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

@@ -66,7 +66,7 @@ CREATE TABLE course (
   expire_pre_days int(11) unsigned NOT NULL DEFAULT '0' COMMENT '1v1课时有效天数',
   expire_days int(11) unsigned NOT NULL DEFAULT '0' COMMENT '视频课程开通有效时长',
   ask_extend_days int(11) unsigned NOT NULL DEFAULT '0' COMMENT '提问扩展天数',
-  use_expire_time int(11) unsigned NOT NULL DEFAULT '0' COMMENT '使用有效时长',
+  use_expire_days int(11) unsigned NOT NULL DEFAULT '0' COMMENT '使用有效时长',
   wechat_avatar varchar(255) DEFAULT NULL COMMENT '微信头像',
   teacher_content text COMMENT '老师资质',
   base_content text COMMENT '基本参数',
@@ -906,8 +906,8 @@ CREATE TABLE user (
   wechat_openid_pc varchar(64) NOT NULL DEFAULT '' COMMENT '微信openid:pc端',
   wechat_openid_wechat varchar(64) NOT NULL DEFAULT '' COMMENT '微信openid: 公众号',
   wechat_unionid varchar(64) NOT NULL DEFAULT '' COMMENT '微信关联id',
-  wechat_access_token varchar(64) NOT NULL DEFAULT '' COMMENT '微信公众号accessToken',
-  wechat_refresh_token varchar(64) NOT NULL DEFAULT '' COMMENT '微信公众号refresh_token',
+  wechat_access_token varchar(255) NOT NULL DEFAULT '' COMMENT '微信公众号accessToken',
+  wechat_refresh_token varchar(255) NOT NULL DEFAULT '' COMMENT '微信公众号refresh_token',
   wechat_expire_time datetime DEFAULT NULL COMMENT '微信公众号expireTime',
   real_time datetime DEFAULT NULL COMMENT '实名通过时间',
   real_name varchar(50) DEFAULT NULL COMMENT '实名:姓名',

+ 1 - 0
server/gateway-api/build.gradle

@@ -24,6 +24,7 @@ dependencies {
 
     // https://mvnrepository.com/artifact/org.apache.poi/poi
     compileClasspath group: 'org.apache.poi', name: 'poi', version: '3.17'
+    compileClasspath group: 'org.apache.poi', name: 'poi-ooxml', version: '3.17'
 
 
 //    compile group: 'commons-lang', name: 'commons-lang', version:'2.6'

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

@@ -28,6 +28,11 @@ import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -756,29 +761,34 @@ public class SettingController {
         return ResponseHelp.success(rankList);
     }
 
-    @RequestMapping(value = "/rank/import", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, method = RequestMethod.POST)
+    @RequestMapping(value = "/rank/import", method = RequestMethod.POST)
     @ApiOperation(value = "导入排行数据", httpMethod = "POST")
-    private Response<Boolean> importRank(@RequestParam("file") MultipartFile multipartFile) throws IOException {
+    private Response<Integer> importRank(@RequestParam("file") MultipartFile multipartFile) throws IOException {
         // 删除所有排行数据, 并导入excel数据
         // 读取文件
-        InputStream is = new FileInputStream("002.xls");
+//        InputStream is = new FileInputStream();
         // 将文件流解析成 POI 文档
-        POIFSFileSystem fs = new POIFSFileSystem(is);
-        // 再将 POI 文档解析成 Excel 工作簿
-        HSSFWorkbook wb = new HSSFWorkbook(fs);
-        HSSFRow row = null;
-        HSSFCell cell = null;
+        Workbook wb =null;
+        Row row = null;
+        Cell cell = null;
+
+        if (multipartFile.getOriginalFilename().contains(".xlsx")){
+            // 再将 POI 文档解析成 Excel 工作簿
+            wb = new XSSFWorkbook(multipartFile.getInputStream());
+        }else{
+            // 再将 POI 文档解析成 Excel 工作簿
+            wb = new HSSFWorkbook(multipartFile.getInputStream());
+        }
         // 得到第 1 个工作簿
-        HSSFSheet sheet = wb.getSheetAt(0);
-        // 得到这一行一共有多少列
-        int totalColumns = sheet.getRow(0).getPhysicalNumberOfCells();
+        Sheet sheet = wb.getSheetAt(0);
+        int totalColumns = 8; // total_score, total_rank, quant_score, quant_rank, verbal_score, verbal_rank
         // 得到最后一行的坐标
         Integer lastRowNum = sheet.getLastRowNum();
         System.out.println("lastRowNum => " + lastRowNum);
 
+
         List<Rank> rankList = new ArrayList<>();
         Rank rank = null;
-        String cellValue = null;
 
         // 从第 2 行开始读
         for(int i=1;i<=lastRowNum;i++){
@@ -787,14 +797,39 @@ public class SettingController {
             for(int j=0;j<totalColumns;j++){
                 cell = row.getCell(j);
                 if(cell!=null){
-                    cellValue = cell.getStringCellValue();
-                }else {
-                    cellValue = "【没有数据】";
+                    switch(j){
+                        case 0:
+                            rank.setTotalScore((int) cell.getNumericCellValue());
+                            break;
+                        case 1:
+                            rank.setTotalRank((int) cell.getNumericCellValue());
+                            break;
+                        case 2:
+                            rank.setQuantScore((int) cell.getNumericCellValue());
+                            break;
+                        case 3:
+                            rank.setQuantRank((int) cell.getNumericCellValue());
+                            break;
+                        case 4:
+                            rank.setVerbalScore((int) cell.getNumericCellValue());
+                            break;
+                        case 5:
+                            rank.setVerbalRank((int) cell.getNumericCellValue());
+                            break;
+                        case 6:
+                            rank.setIrScore((int) cell.getNumericCellValue());
+                            break;
+                        case 7:
+                            rank.setIrRank((int) cell.getNumericCellValue());
+                            break;
+                    }
+                }else{
+                    throw new ParameterException(String.format("%d行%d列数据为空", i, j));
                 }
             }
             rankList.add(rank);
         }
         rankService.replaceAll(rankList);
-        return ResponseHelp.success(true);
+        return ResponseHelp.success(rankList.size());
     }
 }

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

@@ -63,7 +63,7 @@ public class AuthController {
 
     @RequestMapping(value = "/token", method = RequestMethod.POST)
     @ApiOperation(value = "验证token", httpMethod = "POST")
-    public Response<MyDto> token(@RequestHeader("token") String token, HttpSession session, HttpServletRequest request) {
+    public Response<MyDto> token(@RequestHeader(value = "token", required = false) String token, HttpSession session, HttpServletRequest request) {
         User user;
         if (token == null || token.isEmpty()){
             user = shiroHelp.getLoginUser();
@@ -109,29 +109,33 @@ public class AuthController {
         User user = (User) shiroHelp.getLoginUser();
         if (user!=null){
             // 已登录用户,绑定
-            usersService.Oauth(user, code, "wechat_pc");
+            user = usersService.Oauth(user, code, "wechat_pc", true);
         }else{
-            shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_pc"));
+            shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_pc", true));
+            user = shiroHelp.getLoginUser();
         }
-        User entity = shiroHelp.getLoginUser();
-        MyDto dto = processUser(entity, request);
+        MyDto dto = processUser(user, request);
         return ResponseHelp.success(dto);
     }
 
+    // 公众号登录注册:wechat(false) -> wechat(true) -> bind
+    // pc登录注册:wechat_pc(true) -> bind
+    //           login -> wechat_pc(true)
     @RequestMapping(value = "/wechat", method = RequestMethod.GET)
-    @ApiOperation(value = "直接微信二维码登录", httpMethod = "GET")
+    @ApiOperation(value = "直接微信公众号登录", httpMethod = "GET")
     public Response<MyDto> directWechat(
             @RequestParam(required = false, defaultValue = "") String code,
+            @RequestParam(required = false, defaultValue = "") boolean userInfo,
             HttpSession session, HttpServletRequest request) {
         User user = (User) shiroHelp.getLoginUser();
         if (user!=null){
-            // 已登录用户,绑定
-            usersService.Oauth(user, code, "wechat_native");
+            // 第二次获取userInfo的,重新登录
+            shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_native", userInfo));
         }else{
-            shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_native"));
+            shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_native", userInfo));
+            user = shiroHelp.getLoginUser();
         }
-        User entity = shiroHelp.getLoginUser();
-        MyDto dto = processUser(entity, request);
+        MyDto dto = processUser(user, request);
         return ResponseHelp.success(dto);
     }
 
@@ -144,14 +148,14 @@ public class AuthController {
 
     @RequestMapping(value = "/bind", method = RequestMethod.POST)
     @ApiOperation(value = "绑定手机号", notes="第三方登录后可执行", httpMethod = "POST")
-    public Response<Boolean> bind(@RequestBody @Validated UserValidMobileDto userValidMobileDto, HttpSession session, HttpServletRequest request) {
+    public Response<MyDto> bind(@RequestBody @Validated UserValidMobileDto userValidMobileDto, HttpSession session, HttpServletRequest request) {
         if (!smsHelp.verifyCode(userValidMobileDto.getArea(), userValidMobileDto.getMobile(), userValidMobileDto.getMobileVerifyCode(), session)) {
             throw new ParameterException("验证码有误,请重新获取!");
         }
         User openUser = (User) shiroHelp.getLoginUser();
         if(openUser == null)
             throw new SystemException("第三方登录错误");
-        if(openUser.getMobile().length() > 0)
+        if(openUser.getMobile() != null && openUser.getMobile().length() > 0)
             throw new SystemException("手机号已绑定");
 
         try{
@@ -161,7 +165,11 @@ public class AuthController {
         }catch (ParameterException e){
             throw new ParameterException("该手机号绑定其他账号,请更换手机号码!");
         }
-        return ResponseHelp.success(true);
+        shiroHelp.getSession().login(shiroHelp.user(userValidMobileDto.getArea()+":"+userValidMobileDto.getMobile(), ""));
+
+        User entity = shiroHelp.getLoginUser();
+        MyDto dto = processUser(entity, request);
+        return ResponseHelp.success(dto);
     }
 
     @RequestMapping(value = "/valid/invite_code", method = RequestMethod.GET)
@@ -204,6 +212,9 @@ public class AuthController {
     }
 
     private MyDto processUser(User user, HttpServletRequest request){
+        if (user.getId() != null){
+            user = usersService.get(user.getId());
+        }
         MyDto dto = Transform.convert(user, MyDto.class);
         if (user.getId() == null || user.getId() == 0) return dto;
         String ip = Tools.getClientIp(request);

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

@@ -50,7 +50,7 @@ public class OauthController {
         User user = (User) shiroHelp.getLoginUser();
         if (user!=null){
             // 已登录用户,绑定
-            usersService.Oauth(user, code, "wechat_pc");
+            usersService.Oauth(user, code, "wechat_pc", true);
         }
         shiroHelp.getSession().login(shiroHelp.oauth(code, "wechat_pc"));
         Object jump = session.getAttribute("jump");

+ 22 - 18
server/gateway-api/src/main/java/com/qxgmat/dto/response/CourseDetailDto.java

@@ -39,9 +39,9 @@ public class CourseDetailDto extends Course {
 
     private Integer expireDays;
 
-    private Integer expireTime;
+    private Integer expirePreDays;
 
-    private Integer useExpireTime;
+    private Integer useExpireDays;
 
     private Collection<CourseNoExtendDto> courseNos;
 
@@ -145,22 +145,6 @@ public class CourseDetailDto extends Course {
         this.expireDays = expireDays;
     }
 
-    public Integer getExpireTime() {
-        return expireTime;
-    }
-
-    public void setExpireTime(Integer expireTime) {
-        this.expireTime = expireTime;
-    }
-
-    public Integer getUseExpireTime() {
-        return useExpireTime;
-    }
-
-    public void setUseExpireTime(Integer useExpireTime) {
-        this.useExpireTime = useExpireTime;
-    }
-
     public String getComment() {
         return comment;
     }
@@ -200,4 +184,24 @@ public class CourseDetailDto extends Course {
     public void setCourseNos(Collection<CourseNoExtendDto> courseNos) {
         this.courseNos = courseNos;
     }
+
+    @Override
+    public Integer getUseExpireDays() {
+        return useExpireDays;
+    }
+
+    @Override
+    public void setUseExpireDays(Integer useExpireDays) {
+        this.useExpireDays = useExpireDays;
+    }
+
+    @Override
+    public Integer getExpirePreDays() {
+        return expirePreDays;
+    }
+
+    @Override
+    public void setExpirePreDays(Integer expirePreDays) {
+        this.expirePreDays = expirePreDays;
+    }
 }

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

@@ -35,9 +35,9 @@ public class CourseListDto {
 
     private Integer expireDays;
 
-    private Integer expireTime;
+    private Integer expirePreDays;
 
-    private Integer useExpireTime;
+    private Integer useExpireDays;
 
     private Collection<CommentExtendDto> comments;
 
@@ -137,22 +137,6 @@ public class CourseListDto {
         this.expireDays = expireDays;
     }
 
-    public Integer getExpireTime() {
-        return expireTime;
-    }
-
-    public void setExpireTime(Integer expireTime) {
-        this.expireTime = expireTime;
-    }
-
-    public Integer getUseExpireTime() {
-        return useExpireTime;
-    }
-
-    public void setUseExpireTime(Integer useExpireTime) {
-        this.useExpireTime = useExpireTime;
-    }
-
     public String getCover() {
         return cover;
     }
@@ -168,4 +152,20 @@ public class CourseListDto {
     public void setComments(Collection<CommentExtendDto> comments) {
         this.comments = comments;
     }
+
+    public Integer getUseExpireDays() {
+        return useExpireDays;
+    }
+
+    public void setUseExpireDays(Integer useExpireDays) {
+        this.useExpireDays = useExpireDays;
+    }
+
+    public Integer getExpirePreDays() {
+        return expirePreDays;
+    }
+
+    public void setExpirePreDays(Integer expirePreDays) {
+        this.expirePreDays = expirePreDays;
+    }
 }

+ 3 - 0
server/gateway-api/src/main/java/com/qxgmat/help/ShiroHelp.java

@@ -46,6 +46,9 @@ public class ShiroHelp{
     public RealmUsernamePasswordToken manager(String username, String password, Boolean remember){
         return new RealmUsernamePasswordToken(username, password, remember == null ? false : remember, ManagerRealm.class);
     }
+    public RealmUsernamePasswordToken oauth(String code, String platform, Boolean userInfo){
+        return new RealmUsernamePasswordToken(code, platform, userInfo == null ? false : userInfo, OauthRealm.class);
+    }
 
     public <T> T getLogin(Class<T> clazz){
         PrincipalCollection pc = SecurityUtils.getSubject().getPrincipals();

+ 31 - 11
server/gateway-api/src/main/java/com/qxgmat/help/SmsHelp.java

@@ -15,6 +15,8 @@ import org.springframework.stereotype.Service;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpSession;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -23,10 +25,14 @@ import java.util.Objects;
 @Service
 public class SmsHelp {
     private static final Logger logger = LoggerFactory.getLogger(SmsHelp.class);
-    final public String REGISTER_TEMPLATE = "SMS_105270065";
-    final public String FORGET_TEMPLATE = "SMS_105305060";
 
-    final public String VALID_TEMPLATE = "";
+    final public String[] VALID_TEMPLATE = new String[]{"34888",""}; // yzm: 验证码
+
+    final public String[] ABNORMAL_TEMPLATE = new String[]{"34889", ""}; //time: yyyy年MM月dd日hh:mm:ss
+    final public String[] COURSE_EXPIRE_TEMPLATE = new String[]{"34891",""}; // yhnc: 用户昵称 %date%: 格式为:yyyy年MM月dd日 wktfw: 未开通的服务名称
+    final public String[] COURSE_USE_EXPIRE_TEMPLATE = new String[]{"34893", ""}; // yhnc: 用户昵称 %kcmc%: 课程名称 %date%: 格式为:yyyy年MM月dd日
+    final public String[] LIBRARY_TEMPLATE = new String[]{}; // 最新换库时间是%zx_date%日,上次换库时间是%sc_date%日,间隔%jgts%天,库长%kcts%天。获取机经: %jjdz% ,也可搜索微信订阅号“%dyh%”查阅机经。
+
     final private Integer EXPIRE_MINUTES = 15;
 
     private SendCloudSms sms;
@@ -49,24 +55,38 @@ public class SmsHelp {
         date.setTime(date.getTime() + EXPIRE_MINUTES*60*1000);
         dto.setExpireTime(date);
         try {
-            JSONObject vars = new JSONObject();
-            vars.put("code", code);
-            Object response = sms.sendSms(area, mobile, this.VALID_TEMPLATE, vars);
-            dto.setResponse(response);
+            Map<String,String> vars = new HashMap<>();
+            vars.put("yzm", code);
+            Object response = sms.sendSms(area, mobile, this.VALID_TEMPLATE[0], this.VALID_TEMPLATE[1], vars);
+            dto.setResponse(null);
+        } catch (Exception e) {
+            logger.error("发送短信失败", e);
+            return false;
+        }
+        logger.debug("sms code: {}({})", dto.getCode(), dto.getMobile());
+        session.setAttribute(SessionKey.SMS_KEY, JSONObject.toJSONString(dto));
+        return true;
+    }
+
+    public boolean send(String area, String mobile, String[] template, Map<String, String> params){
+        try {
+            Object response = sms.sendSms(area, mobile, template[0], template[1], params);
         } catch (Exception e) {
             logger.error("发送短信失败", e);
             return false;
         }
-        session.setAttribute(SessionKey.SMS_KEY, dto);
         return true;
     }
 
     public boolean verifyCode(String area, String mobile, String code, HttpSession session) {
-        SmsSessionDto dto = (SmsSessionDto) session.getAttribute(SessionKey.SMS_KEY);
-        if(dto == null){
-            throw new ParameterException("手机验证码错误!");
+//        return true;
+        String dtoString = (String) session.getAttribute(SessionKey.SMS_KEY);
+        if(dtoString == null){
+            throw new ParameterException("验证码错误!");
         }
+        JSONObject json = JSONObject.parseObject(dtoString);
         session.removeAttribute(SessionKey.SMS_KEY);
+        SmsSessionDto dto = Transform.convert(json, SmsSessionDto.class);
         String originCode = dto.getCode();
         String originMobile = dto.getMobile();
         String originArea = dto.getArea();

+ 5 - 0
server/gateway-api/src/main/java/com/qxgmat/help/VideoHelp.java

@@ -1,5 +1,7 @@
 package com.qxgmat.help;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -11,6 +13,7 @@ import java.util.regex.Pattern;
 
 @Service
 public class VideoHelp {
+    private static final Logger logger = LoggerFactory.getLogger(VideoHelp.class);
 
     @Value("${video.ffmpeg}")
     private String ffmpegPath;
@@ -33,6 +36,8 @@ public class VideoHelp {
                 sb.append(line);
             }
             br.close();
+            logger.debug("videoPath: {}", videoPath);
+            logger.debug("ffmpeg: {}", sb.toString());
 
             //从视频信息中解析时长
             String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";

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

@@ -38,12 +38,12 @@ public class WechatHelp {
     @Value("${third.wechat.courseTemplate}")
     private String courseTemplate;
 
-    public OauthData oauthPc(String code) {
-        return wechatPc.webAuthorize(code);
+    public OauthData oauthPc(String code, boolean userInfo) {
+        return wechatPc.webAuthorize(code,userInfo);
     }
 
-    public OauthData oauthNative(String code){
-        return wechat.webAuthorize(code);
+    public OauthData oauthNative(String code, boolean userInfo){
+        return wechat.webAuthorize(code, userInfo);
     }
 
     public OauthData refreshNative(String refreshToken) {

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

@@ -39,9 +39,9 @@ import java.util.*;
  */
 @Service
 public class UsersService extends AbstractService {
-    final public String PLATORM_WECHAT_NATIVE = "wechat_openid_wechat";
-    final public String PLATORM_WECHAT_PC = "wechat_openid_pc";
-    final public String PLATORM_WECHAT = "wechat_unionid";
+    final public String PLATORM_WECHAT_NATIVE = "wechatOpenidWechat";
+    final public String PLATORM_WECHAT_PC = "wechatOpenidPc";
+    final public String PLATORM_WECHAT = "wechatUnionid";
 
     @Value("${self.secret}")
     private String secret;
@@ -105,24 +105,22 @@ public class UsersService extends AbstractService {
      * @return
      */
     @Transactional
-    public User Oauth(User user, String code, String platform){
+    public User Oauth(User user, String code, String platform, boolean userInfo){
         OauthData data;
         switch(platform){
             case "wechat_pc":
-                data = wechatHelp.oauthPc(code);
+                data = wechatHelp.oauthPc(code, userInfo);
                 break;
             case "wechat_native":
-                data = wechatHelp.oauthNative(code);
+                data = wechatHelp.oauthNative(code, userInfo);
                 break;
             default:
                 throw new ParameterException("第三方平台"+platform+"不支持");
         }
+        User openUser = getByOpen(data.getOpenId(), data.getUnionId(), platform);
         // 获取已关联的账号
-        if (user == null){
-            user = getByOpen(data.getOpenId(), data.getUnionId(), platform);
-        } else {
+        if (user != null && user.getId() != null){
             // 检验是否已经绑定
-            User openUser = getByOpen(data.getOpenId(), data.getUnionId(), platform);
             if(openUser != null && !openUser.getId().equals(user.getId())){
 //                // 自动合并账号
 //                // 更新消息
@@ -149,30 +147,30 @@ public class UsersService extends AbstractService {
                 throw new ParameterException("该微信账户已绑定其他手机号,您可直接使用微信登录");
             }
         }
-        if (user == null){
-            user = new User();
-            user.setAvatar(data.getAvatar());
-            user.setNickname(data.getNickName());
-        }
+        openUser = User.builder()
+                .id(openUser != null ? openUser.getId() : user != null ? user.getId() : null)
+                .build();
+        if (user ==null || user.getAvatar() == null || user.getAvatar().isEmpty()) openUser.setAvatar(data.getAvatar());
+        if (user == null || user.getNickname() == null|| user.getNickname().isEmpty() )openUser.setNickname(data.getNickName());
         switch(platform){
             case "wechat_pc":
-                user.setWechatOpenidPc(data.getOpenId());
-                user.setWechatUnionid(data.getUnionId());
+                openUser.setWechatOpenidPc(data.getOpenId());
+                openUser.setWechatUnionid(data.getUnionId());
                 break;
             case "wechat_native":
-                user.setWechatOpenidWechat(data.getOpenId());
-                user.setWechatUnionid(data.getUnionId());
-                user.setWechatAccessToken(data.getAccessToken());
-                user.setWechatRefreshToken(data.getRefreshToken());
-                user.setWechatExpireTime(data.getExpiresTime());
+                openUser.setWechatOpenidWechat(data.getOpenId());
+                openUser.setWechatUnionid(data.getUnionId());
+                openUser.setWechatAccessToken(data.getAccessToken());
+                openUser.setWechatRefreshToken(data.getRefreshToken());
+                openUser.setWechatExpireTime(data.getExpiresTime());
                 break;
         }
-        if (!user.getMobile().isEmpty()){
+        if (openUser.getId() != null){
             // 直接更新数据
-            edit(user);
+            edit(openUser);
         }
 
-        return user;
+        return openUser;
     }
 
     /**
@@ -194,6 +192,7 @@ public class UsersService extends AbstractService {
                 // openUser不为空,则用于绑定微信
                 throw new ParameterException("该手机已绑定其他账号,请更换手机号码");
             }
+            user = User.builder().id(user.getId()).build();
             if (user.getRegisterIp() == null || user.getRegisterIp().isEmpty()){
                 user.setRegisterIp(registerIp);
                 user.setRegisterCity(registerInfo != null ? String.join(",",registerInfo) : "");

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

@@ -321,6 +321,7 @@ public class MessageExtendService {
     }
 
     private String replaceBody(String body, Map<String, String> params){
+        if (body == null) return body;
         for(String key :params.keySet()){
             body = body.replaceAll("\\{"+key+"}", params.getOrDefault(key, ""));
         }

+ 2 - 1
server/gateway-api/src/main/java/com/qxgmat/util/shiro/OauthRealm.java

@@ -26,9 +26,10 @@ public class OauthRealm extends AuthorizingRealm {
     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
         String code = token.getUsername();
+        boolean userInfo = token.isRememberMe();
         String platform = new String(token.getPassword());
 
-        User user = usersService.Oauth(null, code, platform);
+        User user = usersService.Oauth(null, code, platform, userInfo);
         return new SimpleAuthenticationInfo(user, platform, getName());
     }
 }

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

@@ -91,11 +91,11 @@ third:
   wechat:
     pc:
       appId: wxa6a1620243459e6a
-      appSecret: 51b8bf5029502d8eccb7a658529b1372
+      appSecret: 2fa03342c000253b9e3b06f631c885df
 
     native:
-      appId: wxbee75af2ece94ed7
-      appSecret: efdef63acfae765b0b890072e12a0198
+      appId: wx65c7d378b4184bcc
+      appSecret: 1c14eb97a156863770e115463c44a83f
 
     questionTemplate: 123123
     courseTemplate: 1312

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

@@ -91,7 +91,7 @@ third:
   wechat:
     pc:
       appId: wxa6a1620243459e6a
-      appSecret: 51b8bf5029502d8eccb7a658529b1372
+      appSecret: 2fa03342c000253b9e3b06f631c885df
 
     native:
       appId: wx65c7d378b4184bcc

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

@@ -2,6 +2,11 @@ spring:
   profiles:
     active: runtime, tools, data
 
+  servlet:
+    multipart:
+      max-file-size: -1
+      max-request-size: -1
+
   jpa:
     database: mysql
     show-sql: true
@@ -41,6 +46,8 @@ third:
     from: 123@123,com
     fromName: 千行GMAT
 
+
+
   baiduai:
     appKey: LD0Si697zIdSut8iv5GoPpE8
     appSecret: DhpsxWkTbI379Q2tUsnD5RG8jiGVGggo
@@ -66,6 +73,8 @@ ip:
 self:
   secret: qianxing-duoshaojiaoyu
 
+
+
 paper:
   sentenceLength: 20
   textbookLength: 31

+ 5 - 3
server/tools/src/main/java/com/nuliji/tools/third/sendcloud/SendCloudSms.java

@@ -49,15 +49,17 @@ public class SendCloudSms {
         }
     }
 
-    public Response sendSms(String area, String mobile, String templateId, JSONObject vars) {
+    public Response sendSms(String area, String mobile, String localId, String globalId, Map<String, String> vars) {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
         if (area != null && !area.equals("86") && !area.equals("+86")){
             // https://sendcloud.kf5.com/posts/view/1074678/
             // 国际号码格式
             mobile = "00"+area.replace("+", "")+mobile;
+            params.add("templateId", globalId);
+        }else{
+            params.add("templateId", localId);
         }
-        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
         params.add("smsUser", smsUser);
-        params.add("templateId", templateId);
         params.add("msgType", "0");
         params.add("phone", mobile);
         params.add("vars", JSONObject.toJSONString(vars));

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

@@ -114,7 +114,7 @@ public class WechatClient {
     }
 
 
-    public OauthData webAuthorize(String code) {
+    public OauthData webAuthorize(String code, boolean userInfo) {
         JSONObject result = getWebAccessToken(code);
 
         // 从result中解析出openid和session
@@ -122,16 +122,19 @@ public class WechatClient {
             logger.warn(String.format("jscodeToSession failed: result = %s", result));
             throw new RuntimeException(String.format("从微信服务器换取用户登录态信息: 错误码: %d, 错误信息: %s", result.getIntValue("errcode"), result.getString("errmsg")));
         }
-        JSONObject info = getWebUserInfo(result.getString("access_token"), result.getString("openid"));
         OauthData data = new OauthData();
         data.setRefreshToken(result.getString("refresh_token"));
         data.setAccessToken(result.getString("access_token"));
         data.setExpiresTime(new Date(new Date().getTime() + result.getInteger("expires_in")));
-        data.setAvatar(info.getString("avatar"));
-        data.setNickName(info.getString("nickname"));
-        data.setUnionId(info.getString("unionid"));
-        data.setOpenId(info.getString("openid"));
-        data.setGender(info.getIntValue("sex") == 1 ? "mela":"femela");
+        data.setOpenId(result.getString("openid"));
+        data.setUnionId(result.getString("unionid"));
+        if (userInfo){
+            JSONObject info = getWebUserInfo(result.getString("access_token"), result.getString("openid"));
+            data.setAvatar(info.getString("headimgurl"));
+            data.setNickName(info.getString("nickname"));
+            data.setUnionId(info.getString("unionid"));
+            data.setGender(info.getIntValue("sex") == 1 ? "mela":"femela");
+        }
         return data;
     }