天天看點

SpringBoot實作微信短視訊小程式

小程式頁面目錄:

SpringBoot實作微信短視訊小程式

index 首頁

userRegist 使用者注冊頁面

userLogin 使用者登入頁面

chooseBgm 選擇背景音樂頁面

resource 圖檔存放目錄

1.使用者注冊實作

1.1 注冊頁面

regist.wxml

<view>
    <view class="login-icon">
        <image class="login-img" src="../resource/images/dsp.jpg"></image>
    </view>
    <view class="login-from">
        <form bindsubmit='doRegist'>
            <!--賬号-->
            <view class="inputView">
                <image class="nameImage" src="../resource/images/username.png"></image>
                <label class="loginLabel">賬号</label>
                <input name="username" class="inputText" placeholder="請輸入賬号"/>
            </view>
            
            <view class="line"></view>

            <!--密碼-->
            <view class="inputView">
                <image class="keyImage" src="../resource/images/password.png"></image>
                <label class="loginLabel">密碼</label>
                <input name="password" class="inputText" password="true" placeholder="請輸入密碼"/>
            </view>

            <!--按鈕-->
            <view>
                <button class="loginBtn" type="primary" form-type='submit'>注冊</button>
            </view>

            <view>
                <button class="goLoginBtn" type="warn" bindtap="goLoginPage">傳回登入</button>
            </view>
        </form>
    </view>
</view>      

1.2 注冊接口

@ApiOperation(value="使用者注冊", notes="使用者注冊的接口")
  @PostMapping("/regist")
  public IMoocJSONResult regist(@RequestBody Users user) throws Exception {
    
    // 1. 判斷使用者名和密碼必須不為空
    if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())) {
      return IMoocJSONResult.errorMsg("使用者名和密碼不能為空");
    }
    
    // 2. 判斷使用者名是否存在
    boolean usernameIsExist = userService.queryUsernameIsExist(user.getUsername());
    
    // 3. 儲存使用者,注冊資訊
    if (!usernameIsExist) {
      user.setNickname(user.getUsername());
      user.setPassword(MD5Utils.getMD5Str(user.getPassword()));
      user.setFansCounts(0);
      user.setReceiveLikeCounts(0);
      user.setFollowCounts(0);
      userService.saveUser(user);
    } else {
      return IMoocJSONResult.errorMsg("使用者名已經存在,請換一個再試");
    }
    user.setPassword("");
    UsersVO userVO = setUserRedisSessionToken(user);
    return IMoocJSONResult.ok(userVO);
  }
  
  public UsersVO setUserRedisSessionToken(Users userModel) {
    String uniqueToken = UUID.randomUUID().toString();
    redis.set(USER_REDIS_SESSION + ":" + userModel.getId(), uniqueToken, 1000 * 60 * 30);
    
    UsersVO userVO = new UsersVO();
    BeanUtils.copyProperties(userModel, userVO);
    userVO.setUserToken(uniqueToken);
    return userVO;
  }      

1.3.互動

點選注冊按鈕,送出表單,<form bindsubmit='doRegist'>,

js執行doRegist方法,使用e.detail.value擷取表單對象,進而擷取使用者名和密碼,驗證使用者名和密碼的長度是否為0,為0表示為空,驗證通過後,展示一個彈框請等待,使用wx.request請求背景注冊接口,如果背景傳回code為200時,wx.hideLoading();去掉彈框,并彈出使用者注冊成功,app.setGlobalUserInfo(res.data.data);将使用者資訊存儲到全局對象app中,使用 wx.redirectTo跳轉到我的頁面

doRegist: function(e) {
      var me = this;
      var formObject = e.detail.value;
      var username = formObject.username;
      var password = formObject.password;

      // 簡單驗證
      if (username.length == 0 || password.length == 0) {
        wx.showToast({
          title: '使用者名或密碼不能為空',
          icon: 'none',
          duration: 3000
        })
      } else {
        var serverUrl = app.serverUrl;
        wx.showLoading({
          title: '請等待...',
        });
        wx.request({
          url: serverUrl + '/regist',
          method: "POST",
          data: {
            username: username,
            password: password
          },
          header: {
            'content-type': 'application/json' // 預設值
          },
          success: function(res) {
            console.log(res.data);
            wx.hideLoading();
            var status = res.data.status;
            if (status == 200) {
              wx.showToast({
                title: "使用者注冊成功~!!!",
                icon: 'none',
                duration: 3000
              }),
              // app.userInfo = res.data.data;
              // fixme 修改原有的全局對象為本地緩存
              app.setGlobalUserInfo(res.data.data);
              // 頁面跳轉
                wx.redirectTo({
                  url: '../mine/mine',
                })
            } else if (status == 500) {
              wx.showToast({
                title: res.data.msg,
                icon: 'none',
                duration: 3000
              })
            }
          }
        })
      }
    },      

點選傳回登入按鈕,觸發事件bindtap="goLoginPage"

goLoginPage:function() {
      wx.navigateTo({
        url: '../userLogin/login',
      })
    }      

使用wx.navigateTo跳轉到登入頁面

2.使用者登入實作

2.1 登入頁面

login.wxml

<view>
    <view class="login-icon">
        <image class="login-img" src="../resource/images/dsp.jpg"></image>
    </view>
    <view class="login-from">
        <form bindsubmit='doLogin'>
            <!--賬号-->
            <view class="inputView">
                <image class="nameImage" src="../resource/images/username.png"></image>
                <label class="loginLabel">賬号</label>
                <input name="username" value='imooc' class="inputText" placeholder="請輸入賬号" />
            </view>
            <view class="line"></view>

            <!--密碼-->
            <view class="inputView">
                <image class="keyImage" src="../resource/images/password.png"></image>
                <label class="loginLabel">密碼</label>
                <input name="password" value='imooc' class="inputText" password="true" placeholder="請輸入密碼" />
            </view>

            <!--按鈕-->
            <view>
                <button class="loginBtn" type="primary" form-type='submit'>登入</button>
            </view>

            <view>
                <button class="goRegistBtn" type="warn" bindtap="goRegistPage">沒有賬号?點選注冊</button>
            </view>
        </form>
    </view>
</view>      

2.2 登入接口

@ApiOperation(value="使用者登入", notes="使用者登入的接口")
  @PostMapping("/login")
  public IMoocJSONResult login(@RequestBody Users user) throws Exception {
    String username = user.getUsername();
    String password = user.getPassword();
    
//    Thread.sleep(3000);
    
    // 1. 判斷使用者名和密碼必須不為空
    if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
      return IMoocJSONResult.ok("使用者名或密碼不能為空...");
    }
    
    // 2. 判斷使用者是否存在
    Users userResult = userService.queryUserForLogin(username, 
        MD5Utils.getMD5Str(user.getPassword()));
    
    // 3. 傳回
    if (userResult != null) {
      userResult.setPassword("");
      UsersVO userVO = setUserRedisSessionToken(userResult);
      return IMoocJSONResult.ok(userVO);
    } else {
      return IMoocJSONResult.errorMsg("使用者名或密碼不正确, 請重試...");
    }
  }      

2.3 互動

進入登入頁面,加載onLoad()方法

onLoad: function (params) {
    var me = this;
    var redirectUrl = params.redirectUrl;
    // debugger;
    if (redirectUrl != null && redirectUrl != undefined && redirectUrl != '') {
      redirectUrl = redirectUrl.replace(/#/g, "?");
      redirectUrl = redirectUrl.replace(/@/g, "=");

      me.redirectUrl = redirectUrl;
    }
  },      

點選登入按鈕,送出表單<form bindsubmit='doLogin'>

先使用e.detail.value擷取表單對象,進行擷取使用者名和密碼,對其驗證是否為空,彈出請等待框,使用wx.request請求背景登入接口,在success回調方法中去掉彈框,如果傳回的code為200則彈出登入成功,将使用者對象設定到全局對象app中,跳轉到我的頁面

// 登入  
  doLogin: function (e) {
    var me = this;
    var formObject = e.detail.value;
    var username = formObject.username;
    var password = formObject.password;
    // 簡單驗證
    if (username.length == 0 || password.length == 0) {
      wx.showToast({
        title: '使用者名或密碼不能為空',
        icon: 'none',
        duration: 3000
      })
    } else {
      var serverUrl = app.serverUrl;
      wx.showLoading({
        title: '請等待...',
      });
      // 調用後端
      wx.request({
        url: serverUrl + '/login',
        method: "POST",
        data: {
          username: username,
          password: password
        },
        header: {
          'content-type': 'application/json' // 預設值
        },
        success: function (res) {
          console.log(res.data);
          wx.hideLoading();
          if (res.data.status == 200) {
            // 登入成功跳轉 
            wx.showToast({
              title: '登入成功',
              icon: 'success',
              duration: 2000
            });
            // app.userInfo = res.data.data;
            // fixme 修改原有的全局對象為本地緩存
            app.setGlobalUserInfo(res.data.data);
            // 頁面跳轉

            var redirectUrl = me.redirectUrl;
            if (redirectUrl != null && redirectUrl != undefined && redirectUrl != '') {
              wx.redirectTo({
                url: redirectUrl,
              })
            } else {
              wx.redirectTo({
                url: '../mine/mine',
              })
            }
            
          } else if (res.data.status == 500) {
            // 失敗彈出框
            wx.showToast({
              title: res.data.msg,
              icon: 'none',
              duration: 3000
            })
          }
        }
      })
    }
  },      

點選 沒有賬号?點選注冊 觸發事件

bindtap="goRegistPage"

goRegistPage:function() {
    wx.redirectTo({
      url: '../userRegist/regist',
    })
  }      

跳轉到注冊頁面

3.上傳頭像實作

3.1 上傳代碼

mine.wxml

<block wx:if="{{isMe}}">
      <image src="{{faceUrl}}" class="face" bindtap='changeFace'></image>
    </block>      

isMe字段定義在js裡面,辨別是否是目前登入使用者,這裡分為2中情況:

1)登入的使用者

2)從視訊詳情裡面的釋出者點選進去,跳轉到我的頁面,該頁面隻能檢視自己的頭像

頁面加載函數的邏輯:

根據params.publisherId判斷是否是目前登入使用者,為空就是目前登入使用者,不為空就是視訊釋出者使用者,設定使用者id,調用查詢使用者資訊接口,擷取使用者資訊

onLoad: function (params) {
    var me = this;

    // var user = app.userInfo;
    // fixme 修改原有的全局對象為本地緩存
    var user = app.getGlobalUserInfo();
    var userId = user.id;

    var publisherId = params.publisherId;
    if (publisherId != null && publisherId != '' && publisherId != undefined) {
      userId = publisherId;
      me.setData({
        isMe: false,
        publisherId: publisherId,
        serverUrl: app.serverUrl
      })
    }
    me.setData({
      userId: userId
    })


    wx.showLoading({
      title: '請等待...',
    });
    var serverUrl = app.serverUrl;
    // 調用後端
    wx.request({
      url: serverUrl + '/user/query?userId=' + userId + "&fanId=" + user.id,
      method: "POST",
      header: {
        'content-type': 'application/json', // 預設值
        'headerUserId': user.id,
        'headerUserToken': user.userToken
      },
      success: function (res) {
        console.log(res.data);
        wx.hideLoading();
        if (res.data.status == 200) {
          var userInfo = res.data.data;
          var faceUrl = "../resource/images/noneface.png";
          if (userInfo.faceImage != null && userInfo.faceImage != '' && userInfo.faceImage != undefined) {
            faceUrl = serverUrl + userInfo.faceImage;
          }


          me.setData({
            faceUrl: faceUrl,
            fansCounts: userInfo.fansCounts,
            followCounts: userInfo.followCounts,
            receiveLikeCounts: userInfo.receiveLikeCounts,
            nickname: userInfo.nickname,
            isFollow: userInfo.follow
          });
        } else if (res.data.status == 502) {
          wx.showToast({
            title: res.data.msg,
            duration: 3000,
            icon: "none",
            success: function () {
              wx.redirectTo({
                url: '../userLogin/login',
              })
            }
          })
        }
      }
    })

    me.getMyVideoList(1);
  },      

changeFace()事件,使用wx.chooseImage傳回上傳後的檔案路徑,wx.uploadFile調用上傳頭像接口

changeFace: function () {
    var me = this;
    wx.chooseImage({
      count: 1,
      sizeType: ['compressed'],
      sourceType: ['album'],
      success: function (res) {
        var tempFilePaths = res.tempFilePaths;
        console.log(tempFilePaths);

        wx.showLoading({
          title: '上傳中...',
        })
        var serverUrl = app.serverUrl;
        // fixme 修改原有的全局對象為本地緩存
        var userInfo = app.getGlobalUserInfo();

        wx.uploadFile({
          url: serverUrl + '/user/uploadFace?userId=' + userInfo.id,  //app.userInfo.id,
          filePath: tempFilePaths[0],
          name: 'file',
          header: {
            'content-type': 'application/json', // 預設值
            'headerUserId': userInfo.id,
            'headerUserToken': userInfo.userToken
          },
          success: function (res) {
            var data = JSON.parse(res.data);
            console.log(data);
            wx.hideLoading();
            if (data.status == 200) {
              wx.showToast({
                title: '上傳成功!~~',
                icon: "success"
              });

              var imageUrl = data.data;
              me.setData({
                faceUrl: serverUrl + imageUrl
              });

            } else if (data.status == 500) {
              wx.showToast({
                title: data.msg
              });
            } else if (res.data.status == 502) {
              wx.showToast({
                title: res.data.msg,
                duration: 2000,
                icon: "none",
                success: function () {
                  wx.redirectTo({
                    url: '../userLogin/login',
                  })
                }
              });

            }

          }
        })


      }
    })
  },      

3.2 上傳頭像接口

@ApiOperation(value="使用者上傳頭像", notes="使用者上傳頭像的接口")
  @ApiImplicitParam(name="userId", value="使用者id", required=true, 
            dataType="String", paramType="query")
  @PostMapping("/uploadFace")
  public IMoocJSONResult uploadFace(String userId, 
        @RequestParam("file") MultipartFile[] files) throws Exception {
    
    if (StringUtils.isBlank(userId)) {
      return IMoocJSONResult.errorMsg("使用者id不能為空...");
    }
    
    // 檔案儲存的命名空間
    String fileSpace = "C:/imooc_videos_dev";
    // 儲存到資料庫中的相對路徑
    String uploadPathDB = "/" + userId + "/face";
    
    FileOutputStream fileOutputStream = null;
    InputStream inputStream = null;
    try {
      if (files != null && files.length > 0) {
        
        String fileName = files[0].getOriginalFilename();
        if (StringUtils.isNotBlank(fileName)) {
          // 檔案上傳的最終儲存路徑
          String finalFacePath = fileSpace + uploadPathDB + "/" + fileName;
          // 設定資料庫儲存的路徑
          uploadPathDB += ("/" + fileName);
          
          File outFile = new File(finalFacePath);
          if (outFile.getParentFile() != null || !outFile.getParentFile().isDirectory()) {
            // 建立父檔案夾
            outFile.getParentFile().mkdirs();
          }
          
          fileOutputStream = new FileOutputStream(outFile);
          inputStream = files[0].getInputStream();
          IOUtils.copy(inputStream, fileOutputStream);
        }
        
      } else {
        return IMoocJSONResult.errorMsg("上傳出錯...");
      }
    } catch (Exception e) {
      e.printStackTrace();
      return IMoocJSONResult.errorMsg("上傳出錯...");
    } finally {
      if (fileOutputStream != null) {
        fileOutputStream.flush();
        fileOutputStream.close();
      }
    }
    
    Users user = new Users();
    user.setId(userId);
    user.setFaceImage(uploadPathDB);
    userService.updateUserInfo(user);
    
    return IMoocJSONResult.ok(uploadPathDB);
  }      

4.上傳作品功能

4.1 上傳作品代碼

<button size='mini' class='primary' bindtap='uploadVideo'> 上傳作品</button>      

uploadVideo事件,wx.chooseVideo打開選擇視訊控件,選擇好視訊後,打開選擇背景音頻頁面并攜帶相關參數

uploadVideo: function () {
    // fixme 視訊上傳複用
    // videoUtil.uploadVideo();
    // 以下是原來的代碼,不删除,便于參照
    var me = this;

    wx.chooseVideo({
      sourceType: ['album'],
      success: function (res) {
        console.log(res);

        var duration = res.duration;
        var tmpHeight = res.height;
        var tmpWidth = res.width;
        var tmpVideoUrl = res.tempFilePath;
        var tmpCoverUrl = res.thumbTempFilePath;//圖檔臨時目錄

        if (duration > 11) {
          wx.showToast({
            title: '視訊長度不能超過10秒...',
            icon: "none",
            duration: 2500
          })
        } else if (duration < 1) {
          wx.showToast({
            title: '視訊長度太短,請上傳超過1秒的視訊...',
            icon: "none",
            duration: 2500
          })
        } else {
          // 打開選擇bgm的頁面
          wx.navigateTo({
            url: '../chooseBgm/chooseBgm?duration=' + duration
            + "&tmpHeight=" + tmpHeight
            + "&tmpWidth=" + tmpWidth
            + "&tmpVideoUrl=" + tmpVideoUrl
            + "&tmpCoverUrl=" + tmpCoverUrl
            ,
          })
        }

      }
    })

  },      

4.2 bgm頁面

<view>
    <form bindsubmit='upload'>

        <radio-group name="bgmId">


          <block wx:for="{{bgmList}}">
            <view class='container'>
          <audio name="{{item.name}}" author="{{item.author}}" src="{{serverUrl}}{{item.path}}" style='width:300px' id="myAudio" controls loop></audio>
          <radio style='margin-top:20px;' value='{{item.id}}'></radio>
        </view>
          </block>


        </radio-group>

        <view class="inputView">
            <label class="loginLabel">視訊描述:</label>
            <input name="desc" class="inputText" placeholder="說點什麼吧" />
        </view>

        <!-- 送出 -->
        <button class="submitBtn" type="primary" form-type='submit'>上傳視訊</button>
        
        <button class="gobackBtn" type="warn" form-type='reset'>重置</button>
    </form>
</view>      

頁面加載函數,onLoad(),使用wx.request請求背景背景音頻清單接口展示在頁面

onLoad: function (params) {

var me = this;

console.log(params);

me.setData({

videoParams: params

});

wx.showLoading({

title: '請等待...',

});

var serverUrl = app.serverUrl;

var user = app.getGlobalUserInfo();

debugger;

// 調用後端

wx.request({

url: serverUrl + '/bgm/list',

method: "POST",

header: {

'content-type': 'application/json', // 預設值

'headerUserId': user.id,

'headerUserToken': user.userToken

},

success: function (res) {

console.log(res.data);

wx.hideLoading();

if (res.data.status == 200) {

var bgmList = res.data.data;

me.setData({

bgmList: bgmList,

serverUrl: serverUrl

});

} else if (res.data.status == 502) {

wx.showToast({

title: res.data.msg,

duration: 2000,

icon: "none",

success: function () {

wx.redirectTo({

url: '../userLogin/login',

})

}

});

}

}

})

},

4.3 互動

<form bindsubmit='upload'>,使用 wx.uploadFile調用背景上傳視訊接口

upload: function(e) {
      var me = this;

      var bgmId = e.detail.value.bgmId;
      var desc = e.detail.value.desc;

      console.log("bgmId:" + bgmId);
      console.log("desc:" + desc);

      var duration = me.data.videoParams.duration;
      var tmpHeight = me.data.videoParams.tmpHeight;
      var tmpWidth = me.data.videoParams.tmpWidth;
      var tmpVideoUrl = me.data.videoParams.tmpVideoUrl;
      var tmpCoverUrl = me.data.videoParams.tmpCoverUrl;

      // 上傳短視訊
      wx.showLoading({
        title: '上傳中...',
      })
      var serverUrl = app.serverUrl;
      // fixme 修改原有的全局對象為本地緩存
      var userInfo = app.getGlobalUserInfo();

      wx.uploadFile({
        url: serverUrl + '/video/upload',
        formData: {
          userId: userInfo.id,    // fixme 原來的 app.userInfo.id
          bgmId: bgmId,
          desc: desc,
          videoSeconds: duration,
          videoHeight: tmpHeight,
          videoWidth: tmpWidth
        },
        filePath: tmpVideoUrl,
        name: 'file',
        header: {
          'content-type': 'application/json', // 預設值
          'headerUserId': userInfo.id,
          'headerUserToken': userInfo.userToken
        },
        success: function (res) {
          var data = JSON.parse(res.data);
          wx.hideLoading();
          if (data.status == 200) {
            wx.showToast({
              title: '上傳成功!~~',
              icon: "success"
            });     
            // 上傳成功後跳回之前的頁面
            wx.navigateBack({
              delta: 1
            })

          } else if (res.data.status == 502) {
            wx.showToast({
              title: res.data.msg,
              duration: 2000,
              icon: "none"
            });
            wx.redirectTo({
              url: '../userLogin/login',
            })
          } else {
            wx.showToast({
              title: '上傳失敗!~~',
              icon: "success"
            });
          }

        }
      })
    }      

4.4 上傳視訊接口

@ApiOperation(value="上傳視訊", notes="上傳視訊的接口")
  @ApiImplicitParams({
    @ApiImplicitParam(name="userId", value="使用者id", required=true, 
        dataType="String", paramType="form"),
    @ApiImplicitParam(name="bgmId", value="背景音樂id", required=false, 
        dataType="String", paramType="form"),
    @ApiImplicitParam(name="videoSeconds", value="背景音樂播放長度", required=true, 
        dataType="String", paramType="form"),
    @ApiImplicitParam(name="videoWidth", value="視訊寬度", required=true, 
        dataType="String", paramType="form"),
    @ApiImplicitParam(name="videoHeight", value="視訊高度", required=true, 
        dataType="String", paramType="form"),
    @ApiImplicitParam(name="desc", value="視訊描述", required=false, 
        dataType="String", paramType="form")
  })
  @PostMapping(value="/upload", headers="content-type=multipart/form-data")
  public IMoocJSONResult upload(String userId, 
        String bgmId, double videoSeconds, 
        int videoWidth, int videoHeight,
        String desc,
        @ApiParam(value="短視訊", required=true)
        MultipartFile file) throws Exception {
    
    if (StringUtils.isBlank(userId)) {
      return IMoocJSONResult.errorMsg("使用者id不能為空...");
    }
    
    // 檔案儲存的命名空間
//    String fileSpace = "C:/imooc_videos_dev";
    // 儲存到資料庫中的相對路徑
    String uploadPathDB = "/" + userId + "/video";
    String coverPathDB = "/" + userId + "/video";
    
    FileOutputStream fileOutputStream = null;
    InputStream inputStream = null;
    // 檔案上傳的最終儲存路徑
    String finalVideoPath = "";
    try {
      if (file != null) {
        
        String fileName = file.getOriginalFilename();
        // abc.mp4
        String arrayFilenameItem[] =  fileName.split("\\.");
        String fileNamePrefix = "";
        for (int i = 0 ; i < arrayFilenameItem.length-1 ; i ++) {
          fileNamePrefix += arrayFilenameItem[i];
        }
        // fix bug: 解決小程式端OK,PC端不OK的bug,原因:PC端和小程式端對臨時視訊的命名不同
//        String fileNamePrefix = fileName.split("\\.")[0];
        
        if (StringUtils.isNotBlank(fileName)) {
          
          finalVideoPath = FILE_SPACE + uploadPathDB + "/" + fileName;
          // 設定資料庫儲存的路徑
          uploadPathDB += ("/" + fileName);
          coverPathDB = coverPathDB + "/" + fileNamePrefix + ".jpg";
          
          File outFile = new File(finalVideoPath);
          if (outFile.getParentFile() != null || !outFile.getParentFile().isDirectory()) {
            // 建立父檔案夾
            outFile.getParentFile().mkdirs();
          }
          
          fileOutputStream = new FileOutputStream(outFile);
          inputStream = file.getInputStream();
          IOUtils.copy(inputStream, fileOutputStream);
        }
        
      } else {
        return IMoocJSONResult.errorMsg("上傳出錯...");
      }
    } catch (Exception e) {
      e.printStackTrace();
      return IMoocJSONResult.errorMsg("上傳出錯...");
    } finally {
      if (fileOutputStream != null) {
        fileOutputStream.flush();
        fileOutputStream.close();
      }
    }
    
    // 判斷bgmId是否為空,如果不為空,
    // 那就查詢bgm的資訊,并且合并視訊,生産新的視訊
    if (StringUtils.isNotBlank(bgmId)) {
      Bgm bgm = bgmService.queryBgmById(bgmId);
      String mp3InputPath = FILE_SPACE + bgm.getPath();
      
      MergeVideoMp3 tool = new MergeVideoMp3(FFMPEG_EXE);
      String videoInputPath = finalVideoPath;
      
      String videoOutputName = UUID.randomUUID().toString() + ".mp4";
      uploadPathDB = "/" + userId + "/video" + "/" + videoOutputName;
      finalVideoPath = FILE_SPACE + uploadPathDB;
      tool.convertor(videoInputPath, mp3InputPath, videoSeconds, finalVideoPath);
    }
    System.out.println("uploadPathDB=" + uploadPathDB);
    System.out.println("finalVideoPath=" + finalVideoPath);
    
    // 對視訊進行截圖
    FetchVideoCover videoInfo = new FetchVideoCover(FFMPEG_EXE);
    videoInfo.getCover(finalVideoPath, FILE_SPACE + coverPathDB);
    
    // 儲存視訊資訊到資料庫
    Videos video = new Videos();
    video.setAudioId(bgmId);
    video.setUserId(userId);
    video.setVideoSeconds((float)videoSeconds);
    video.setVideoHeight(videoHeight);
    video.setVideoWidth(videoWidth);
    video.setVideoDesc(desc);
    video.setVideoPath(uploadPathDB);
    video.setCoverPath(coverPathDB);
    video.setStatus(VideoStatusEnum.SUCCESS.value);
    video.setCreateTime(new Date());
    
    String videoId = videoService.saveVideo(video);
    
    return IMoocJSONResult.ok(videoId);
  }      

5.關注作品功能

5.1 關注代碼

<block wx:if="{{!isFollow}}">
        <button size='mini' type='primary' class='follow' data-followType='1' bindtap='followMe'>關注我</button>
      </block>
 
followMe()事件,e.currentTarget.dataset.followtype擷取data-followType的值,根據類型判斷是關注或取消關注,調用對應的接口
 
followMe: function (e) {
    var me = this;

    var user = app.getGlobalUserInfo();
    var userId = user.id;
    var publisherId = me.data.publisherId;

    var followType = e.currentTarget.dataset.followtype;

    // 1:關注 0:取消關注
    var url = '';
    if (followType == '1') {
      url = '/user/beyourfans?userId=' + publisherId + '&fanId=' + userId;
    } else {
      url = '/user/dontbeyourfans?userId=' + publisherId + '&fanId=' + userId;
    }

    wx.showLoading();
    wx.request({
      url: app.serverUrl + url,
      method: 'POST',
      header: {
        'content-type': 'application/json', // 預設值
        'headerUserId': user.id,
        'headerUserToken': user.userToken
      },
      success: function () {
        wx.hideLoading();
        if (followType == '1') {
          me.setData({
            isFollow: true,
            fansCounts: ++me.data.fansCounts
          })
        } else {
          me.setData({
            isFollow: false,
            fansCounts: --me.data.fansCounts
          })
        }
      }
    })
  },      

5.2 關注接口

@PostMapping("/beyourfans")
  public IMoocJSONResult beyourfans(String userId, String fanId) throws Exception {
    
    if (StringUtils.isBlank(userId) || StringUtils.isBlank(fanId)) {
      return IMoocJSONResult.errorMsg("");
    }
    
    userService.saveUserFanRelation(userId, fanId);
    
    return IMoocJSONResult.ok("關注成功...");
  }
  
  @PostMapping("/dontbeyourfans")
  public IMoocJSONResult dontbeyourfans(String userId, String fanId) throws Exception {
    
    if (StringUtils.isBlank(userId) || StringUtils.isBlank(fanId)) {
      return IMoocJSONResult.errorMsg("");
    }
    
    userService.deleteUserFanRelation(userId, fanId);
    
    return IMoocJSONResult.ok("取消關注成功...");
  }      

6.首頁視訊展示功能

6.1 首頁頁面

<view wx:for="{{videoList}}" class="item-container">  


     //封面圖
     <view style='width:{{screenWidth}}px;height:210px;' class='back-img'> 
        <image src="{{serverUrl}}{{item.coverPath}}" style='width:{{screenWidth}}px;height:210px;' mode="aspectFit" bindtap='showVideoInfo' data-arrindex='{{index}}'></image>
     </view> 


    <view class="desc">
        <view class="faceName">
            <image class='myface' src="{{serverUrl}}{{item.faceImage}}"></image>
            <view class="nickname">{{item.nickname}}</view>
        </view>
    </view>


  </view>      

6.2 擷取視訊清單接口

/**
   * 
   * @Description: 分頁和搜尋查詢視訊清單
   * isSaveRecord:1 - 需要儲存
   *          0 - 不需要儲存 ,或者為空的時候
   */
  @PostMapping(value="/showAll")
  public IMoocJSONResult showAll(@RequestBody Videos video, Integer isSaveRecord,
      Integer page, Integer pageSize) throws Exception {
    
    if (page == null) {
      page = 1;
    }
    
    if (pageSize == null) {
      pageSize = PAGE_SIZE;
    }
    
    PagedResult result = videoService.getAllVideos(video, isSaveRecord, page, pageSize);
    return IMoocJSONResult.ok(result);
  }      

6.3 互動

頁面加載的時候調用背景擷取視訊清單接口,onPullDownRefresh為下拉重新整理觸底監聽事件,onReachBottom是頁面上拉觸底事件的函數

onLoad: function (params) {
    var me = this;
    var screenWidth = wx.getSystemInfoSync().screenWidth;
    me.setData({
      screenWidth: screenWidth,
    });

    var searchContent = params.search;
    var isSaveRecord = params.isSaveRecord;
    if (isSaveRecord == null || isSaveRecord == '' || isSaveRecord == undefined) {
      isSaveRecord = 0;
    }

    me.setData({
      searchContent: searchContent
    });

    // 擷取目前的分頁數
    var page = me.data.page;
    me.getAllVideoList(page, isSaveRecord);
  },

  getAllVideoList: function (page, isSaveRecord) {
    var me = this;
    var serverUrl = app.serverUrl;
    wx.showLoading({
      title: '請等待,加載中...',
    });

    var searchContent = me.data.searchContent;

    wx.request({
      url: serverUrl + '/video/showAll?page=' + page + "&isSaveRecord=" + isSaveRecord,
      method: "POST",
      data: {
        videoDesc: searchContent
      },
      success: function (res) {
        wx.hideLoading();
        wx.hideNavigationBarLoading();
        wx.stopPullDownRefresh();

        console.log(res.data);

        // 判斷目前頁page是否是第一頁,如果是第一頁,那麼設定videoList為空
        if (page === 1) {
          me.setData({
            videoList: []
          });
        }

        var videoList = res.data.data.rows;
        var newVideoList = me.data.videoList;

        me.setData({
          videoList: newVideoList.concat(videoList),
          page: page,
          totalPage: res.data.data.total,
          serverUrl: serverUrl
        });

      }
    })
  },

  onPullDownRefresh: function() {
      //在目前頁面顯示導覽列加載動畫
    wx.showNavigationBarLoading();
    this.getAllVideoList(1, 0);
  },

  //上拉重新整理
  onReachBottom:function() {
    var me = this;
    var currentPage = me.data.page;
    var totalPage = me.data.totalPage;

    // 判斷目前頁數和總頁數是否相等,如果想的則無需查詢
    if (currentPage === totalPage) {
      wx.showToast({
        title: '已經沒有視訊啦~~',
        icon: "none"
      })
      return;
    }

    var page = currentPage + 1;

    me.getAllVideoList(page, 0);
  },

  showVideoInfo: function(e) {
    var me = this;
    var videoList = me.data.videoList;
    var arrindex = e.target.dataset.arrindex;
    var videoInfo = JSON.stringify(videoList[arrindex]);

    wx.redirectTo({
      url: '../videoinfo/videoinfo?videoInfo=' + videoInfo
    })
  }      

7.視訊搜尋功能

7.1 搜尋頁面

wxSearchView.wxml

<view class="weui-search-bar">
   <view class="weui-search-bar__form">
    <view class="weui-search-bar__box">
      <icon class="weui-icon-search_in-box" type="search" size="14"></icon>
      <input type="text" class="weui-search-bar__input" placeholder="請輸入查詢内容" value="{{wxSearchData.value}}" bindinput="wxSearchInput" bindconfirm="wxSearchConfirm" />
      <view class="weui-icon-clear" wx:if="{{wxSearchData.value.length > 0}}" bindtap="wxSearchClear">
        <icon type="clear" size="14"></icon>
      </view>
    </view>
  </view>
    <view class="weui-search-bar__cancel-btn" bindtap="wxSearchConfirm">
         <text wx:if="{{wxSearchData.value.length>0}}" data-key='search'>搜尋</text>
         <text wx:else data-key='back'>首頁</text>
     </view>
</view>

<view class="wxSearch" style="'block';height:{{wxSearchData.view.seachHeight}}px;top:{{wxSearchData.view.barHeight}}px;">

  <view class="wxSearchInner">
    <!-- 搜尋提示部分 -->
    <view class="wxSearchMindKey">
      <view class="wxSearchMindKeyList">
        <block wx:for="{{wxSearchData.tipKeys}}">
          <view class="wxSearchMindKeyItem" bindtap="wxSearchKeyTap" data-key="{{item}}">{{item}}</view>
        </block>
      </view>
    </view>

    <view wx:if="{{wxSearchData.his[0]}}" class="wxSearchHistory" style="display:{{wxSearchData.value.length>0 ? 'none':'block'}}">
      <view class="wxSearchHistoryItem">
        <text class="wxSearchHistoryItemTitle">搜尋記錄</text>
        <!--text class="wxSearchHistoryItemDel" bindtap="wxSearchDeleteAll">删除</text-->
        <icon type="clear" bindtap="wxSearchDeleteAll" size="18" />
      </view>
      <view class="wxSearchKeyList">
        <block wx:for="{{wxSearchData.his}}">
          <view class="wxSearchKeyItem" bindtap="wxSearchKeyTap" data-key="{{item}}">{{item}}</view>
        </block>
      </view>
    </view>

    <view class="wxSearchKey" style="display:{{wxSearchData.value.length>0 ? 'none':'block'}}">
      <text wx:if="{{wxSearchData.hotKeys[0]}}" class="wxSearchTitle">搜尋熱點</text>
      <view class="wxSearchKeyList">
        <block wx:for="{{wxSearchData.hotKeys}}">
          <view class="wxSearchKeyItem" bindtap="wxSearchKeyTap" data-key="{{item}}">{{item}}</view>
        </block>
      </view>
    </view>
  </view>
</view>      

7.2 互動

// 1 導入js檔案
var WxSearch = require('../../wxSearchView/wxSearchView.js');

const app = getApp()

Page({
  data: {
  },

  onLoad: function () {

    // 2 搜尋欄初始化
    var that = this;

    // 查詢熱搜詞
    var serverUrl = app.serverUrl;
    wx.request({
      url: serverUrl + '/video/hot',
      method: "POST",
      success: function(res) {
        console.log(res);
        var hotList = res.data.data;

        WxSearch.init(
          that,  // 本頁面一個引用
          hotList,
          // ['熱門視訊', '推薦視訊', "java", "小程式", 'zookeeper', 'springboot'], // 熱點搜尋推薦,[]表示不使用
          hotList,// 搜尋比對,[]表示不使用
          that.mySearchFunction, // 提供一個搜尋回調函數
          that.myGobackFunction //提供一個傳回回調函數
        );



      }
    })

  },

  // 3 轉發函數,固定部分,直接拷貝即可
  wxSearchInput: WxSearch.wxSearchInput,  // 輸入變化時的操作
  wxSearchKeyTap: WxSearch.wxSearchKeyTap,  // 點選提示或者關鍵字、曆史記錄時的操作
  wxSearchDeleteAll: WxSearch.wxSearchDeleteAll, // 删除所有的曆史記錄
  wxSearchConfirm: WxSearch.wxSearchConfirm,  // 搜尋函數
  wxSearchClear: WxSearch.wxSearchClear,  // 清空函數

  // 4 搜尋回調函數  
  mySearchFunction: function (value) {
    // do your job here
    // 示例:跳轉
    wx.redirectTo({
      url: '../index/index?isSaveRecord=1&search=' + value
    })
  },

  // 5 傳回回調函數
  myGobackFunction: function () {
    // do your job here
    // 示例:傳回
    wx.redirectTo({
      url: '../index/index'
    })
  }


})      

8.收藏功能

1.頁面代碼

videoinfo.wxml

<!-- 喜歡收藏按鈕 -->
            <block wx:if="{{userLikeVideo}}">
                <cover-image class="size-me" src='../resource/images/like.png' style='margin-top:30rpx;' bindtap='likeVideoOrNot'></cover-image>
            </block>
            <block wx:else>
                <cover-image class="size-me" src='../resource/images/unlike.png' style='margin-top:30rpx;' bindtap='likeVideoOrNot'></cover-image>
            </block>      

2.互動

觸發likeVideoOrNot事件,通過me.data.userLikeVideo來判斷是收藏還是取消收藏

likeVideoOrNot: function () {
    var me = this;
    var videoInfo = me.data.videoInfo;
    var user = app.getGlobalUserInfo();

    if (user == null || user == undefined || user == '') {
      wx.navigateTo({
        url: '../userLogin/login',
      })
    } else {
      
      var userLikeVideo = me.data.userLikeVideo;
      var url = '/video/userLike?userId=' + user.id + '&videoId=' + videoInfo.id + '&videoCreaterId=' + videoInfo.userId;
      if (userLikeVideo) {
        url = '/video/userUnLike?userId=' + user.id + '&videoId=' + videoInfo.id + '&videoCreaterId=' + videoInfo.userId;
      }

      var serverUrl = app.serverUrl;
      wx.showLoading({
        title: '...',
      })
      wx.request({
        url: serverUrl + url,
        method: 'POST',
        header: {
          'content-type': 'application/json', // 預設值
          'headerUserId': user.id,
          'headerUserToken': user.userToken
        },
        success:function(res) {
          wx.hideLoading();
          me.setData({
            userLikeVideo: !userLikeVideo
          });
        }
      })


    }
  },      

3.收藏和取消收藏接口

@PostMapping(value="/userLike")
  public IMoocJSONResult userLike(String userId, String videoId, String videoCreaterId) 
      throws Exception {
    videoService.userLikeVideo(userId, videoId, videoCreaterId);
    return IMoocJSONResult.ok();
  }
  
  @PostMapping(value="/userUnLike")
  public IMoocJSONResult userUnLike(String userId, String videoId, String videoCreaterId) throws Exception {
    videoService.userUnLikeVideo(userId, videoId, videoCreaterId);
    return IMoocJSONResult.ok();
  }      

8.評論功能

1.頁面代碼

videoinfo.wxml

<!-- 評論按鈕 -->
 <cover-image class="size-me" src='../resource/images/comments.png' style='margin-top:30rpx;' bindtap='leaveComment'></cover-image>
 
     
     <view>

<view class="saySthView">
<input name="commentContent" class="saySth" placeholder="{{placeholder}}" confirm-type="send" bindconfirm="saveComment" focus='{{commentFocus}}' value='{{contentValue}}'
data-replyFatherCommentId='{{replyFatherCommentId}}'
data-replyToUserId='{{replyToUserId}}'
/>
</view>

<block wx:for="{{commentsList}}">
  <view class='comments-all' bindtap='replyFocus' 
  data-fatherCommentId='{{item.id}}'  
  data-toUserId='{{item.fromUserId}}' 
  data-toNickname='{{item.nickname}}' 
  >
      <view class='container-comments'>
          <image class="face-comments" src='{{serverUrl}}{{item.faceImage}}' ></image>
          <view class='nickname-comments'>
              <label class='nickname-lbl'>@{{item.nickname}}</label>
              于 
              <label class='date-lbl'>{{item.timeAgoStr}}</label>
              <!-- 留言: -->
              <block wx:if="{{item.toNickname != null}}">
                回複
                <label class='nickname-lbl'>@{{item.toNickname}}</label>
              </block>
              <block wx:else>
                留言:
              </block>
          </view>
      </view>
      <view class='comments-content'>{{item.comment}}</view>
  </view>
</block> 

</view      

2.互動

點選評論按鈕,觸發leaveComment事件,将commentFocus值設定為true

leaveComment: function() {
    this.setData({
      commentFocus: true
    });
  },

  replyFocus: function(e) {
    var fatherCommentId = e.currentTarget.dataset.fathercommentid;
    var toUserId = e.currentTarget.dataset.touserid;
    var toNickname = e.currentTarget.dataset.tonickname;
 
    this.setData({
      placeholder: "回複  " + toNickname,
      replyFatherCommentId: fatherCommentId,
      replyToUserId: toUserId,
      commentFocus: true
    });
  },

  saveComment:function(e) {
    var me = this;
    var content = e.detail.value;

    // 擷取評論回複的fatherCommentId和toUserId
    var fatherCommentId = e.currentTarget.dataset.replyfathercommentid;
    var toUserId = e.currentTarget.dataset.replytouserid;

    var user = app.getGlobalUserInfo();
    var videoInfo = JSON.stringify(me.data.videoInfo);
    var realUrl = '../videoinfo/videoinfo#videoInfo@' + videoInfo;

    if (user == null || user == undefined || user == '') {
      wx.navigateTo({
        url: '../userLogin/login?redirectUrl=' + realUrl,
      })
    } else {
      wx.showLoading({
        title: '請稍後...',
      })
      wx.request({
        url: app.serverUrl + '/video/saveComment?fatherCommentId=' + fatherCommentId + "&toUserId=" + toUserId,
        method: 'POST',
        header: {
          'content-type': 'application/json', // 預設值
          'headerUserId': user.id,
          'headerUserToken': user.userToken
        },
        data: {
          fromUserId: user.id,
          videoId: me.data.videoInfo.id,
          comment: content
        },
        success: function(res) {
          console.log(res.data)
          wx.hideLoading();

          me.setData({
            contentValue: "",
            commentsList: []
          });

          me.getCommentsList(1);
        }
      })
    }
  },

// commentsPage: 1,
//   commentsTotalPage: 1,
//   commentsList: []

    getCommentsList: function(page) {
      var me = this;

      var videoId = me.data.videoInfo.id;

      wx.request({
        url: app.serverUrl + '/video/getVideoComments?videoId=' + videoId + "&page=" + page + "&pageSize=5",
        method: "POST",
        success: function(res) {
          console.log(res.data);

          var commentsList = res.data.data.rows;
          var newCommentsList = me.data.commentsList;

          me.setData({
            commentsList: newCommentsList.concat(commentsList),
            commentsPage: page,
            commentsTotalPage: res.data.data.total
          });
        }
      })
    },      

繼續閱讀