硅谷课堂

编程入门 行业动态 更新时间:2024-10-19 02:18:27

<a href=https://www.elefans.com/category/jswz/34/1770121.html style=硅谷课堂"/>

硅谷课堂

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年

文章目录

  • 硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年
    • Day 15-直播管理模块和微信分享
      • 一、后台系统-直播管理模块
        • 1、直播课程管理前端整合
          • 1.1、service-vod 添加方法
          • 1.2、router/index.js 路由
          • 1.3、定义调用接口
          • 1.4、创建直播页面
            • 1.4.1、list.vue
            • 1.4.2、config.vue
      • 二、公众号直播对接
        • 1、用户观看端集成
          • 1.1、获取用户 access_token
          • 1.2、下载前端 SDK
          • 1.3、使用快捷模板
          • 1.4、与前端项目结合
          • 1.5、补充接口
      • 三、微信分享
        • 1、实现目标
        • 2、微信分享实现方式
          • 2.1、绑定域名
          • 2.2、公众号测试号配置
          • 2.3、引入 JS 文件
          • 2.4、封装分享 js
          • 2.5、服务器端接口
          • 2.6、点播课程详情分享
          • 2.7、测试
    • Day 16-腾讯云部署
      • 一、项目部署方案
        • 1、原始部署方式
        • 2、整合 Jenkins
        • 3、整合 CODING
      • 二、腾讯云 CODING DevOps 概述
        • 1、产品简介
          • 1.1、CODING DevOps 是什么
          • 1.2、CODING DevOps 优势
          • 1.3、CODING DevOps 功能特性
        • 2、使用流程概述
          • 2.1、创建或加入团队
          • 2.2、新建项目
          • 2.3、开始项目协同
          • 2.4、使用代码仓库
          • 2.5、启动代码扫描
          • 2.6、编译构建
          • 2.7、管理制品
          • 2.8、实施持续部署
          • 2.9、管理测试用例
          • 2.10、管理项目文档
      • 三、开通 CODING DevOps
        • 1、搜索 CODING DevOps
        • 2、开通账号
        • 3、创建团队
      • 四、使用 CODING DevOps
        • 1、创建项目
          • 1.1、点击创建项目
          • 1.2、选择项目模板
          • 1.3、填写项目基本信息
          • 1.4、完成创建
        • 2、项目协同
          • 2.1、项目协同初始化
          • 2.2、项目协同具体实现
        • 3、代码仓库
          • 3.1、添加仓库
          • 3.2、克隆代码仓库到本地
          • 3.3、推送本地代码至 CODING 仓库
          • 3.4、推送项目到代码仓库
        • 4、持续集成
          • 4.1、创建构建计划
          • 4.2、选择类型
          • 4.3、设置构建信息
          • 4.4、修改流程配置
          • 4.5、立即构建
          • 4.6、构建完成
        • 5、持续部署(绑定云账号)
          • 5.1、输入账号名称
          • 5.2、创建集群,开放外网 ip
          • 5.3、复制凭证
        • 6、持续部署(Kubernetes)
          • 6.1、创建部署
          • 6.2、集群配置
            • 6.2.1、集群配置
            • 6.2.2、镜像配置
            • 6.2.3、应用部署
          • 6.3、等待发布
          • 6.4、获取发布地址
          • 6.5、访问测试
        • 7、持续部署(腾讯云弹性伸缩)
          • 7.1、创建应用
          • 7.2、创建流程
          • 7.3、启动执行
          • 7.4、集群查看

Day 15-直播管理模块和微信分享

一、后台系统-直播管理模块

1、直播课程管理前端整合
1.1、service-vod 添加方法

(1)CourseController 添加方法。

package com.myxh.smart.planet.vod.controller;import com.myxh.smart.planet.model.vod.Course;
import com.myxh.smart.planet.result.Result;
import com.myxh.smart.planet.vod.service.CourseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author MYXH* @date 2023/10/8** <p>* 课程 前端控制器* </p>*/
@Tag(name = "课程接口", description = "课程管理接口")
@RestController
@RequestMapping("/admin/vod/course")
public class CourseController
{@Autowiredprivate CourseService courseService;/*** 查询所有课程** @return Result 全局统一返回结果*/@Operation(summary = "查询所有课程", description = "查询所有课程")@GetMapping("find/all")public Result<List<Course>> findAll(){List<Course> courseList = courseService.findlist();return Result.ok(courseList);}
}

(2)CourseService 实现方法。

package com.myxh.smart.planet.vod.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myxh.smart.planet.model.vod.Course;
import com.myxh.smart.planet.vod.mapper.CourseMapper;
import com.myxh.smart.planet.vod.service.CourseService;
import org.springframework.stereotype.Service;import java.util.List;/*** @author MYXH* @date 2023/10/8** <p>* 课程 服务实现类* </p>*/
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService
{/*** 查询所有课程** @return courseList 所有课程列表*/@Overridepublic List<Course> findlist(){List<Course> courseList = baseMapper.selectList(null);courseList.stream().forEach(this::getTeacherAndSubjectName);return courseList;}
}
1.2、router/index.js 路由
  // 直播管理{path: "/live",component: Layout,redirect: "/live/liveCourse/list",name: "Live",meta: {title: "直播管理",icon: "el-icon-bangzhu",},alwaysShow: true,children: [{path: "liveCourse/list",name: "liveCourseList",component: () => import("@/views/live/liveCourse/list"),meta: { title: "直播列表" },},{path: "liveCourse/config/:id",name: "liveCourseConfig",component: () => import("@/views/live/liveCourse/config"),meta: { title: "直播配置" },hidden: true,},{path: "liveVisitor/list/:id",name: "liveVisitor",component: () => import("@/views/live/liveVisitor/list"),meta: { title: "观看记录" },hidden: true,},],},
1.3、定义调用接口

import request from "@/utils/request";const LIVE_COURSE_API = "/admin/live/live/course";export default {/*** 直播课程分页列表** @param {number} current 当前页码* @param {number} limit 每页记录数* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/getPageList(page, limit) {return request({url: `${LIVE_COURSE_API}/find/query/page/${page}/${limit}`,method: "get",});},/*** 添加直播课程** @param {Object} liveCourse 直播课程* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/save(liveCourse) {return request({url: `${LIVE_COURSE_API}/save`,method: "post",data: liveCourse,});},/*** 删除直播课程** @param {number} id id* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/removeById(id) {return request({url: `${LIVE_COURSE_API}/remove/${id}`,method: "delete",});},/*** 根据 id 查询直播课程基本信息和描述信息** @param {number} id id* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/getById(id) {return request({url: `${LIVE_COURSE_API}/get/info/${id}`,method: "get",});},/*** 根据 id 修改直播课程** @param {Object} liveCourse 直播课程* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/updateById(liveCourse) {return request({url: `${LIVE_COURSE_API}/update`,method: "put",data: liveCourse,});},/*** 获取直播账号信息** @param {number} id id* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/getLiveCourseAccount(id) {return request({url: `${LIVE_COURSE_API}/get/live/course/account/${id}`,method: "get",});},/*** 获取直播配置信息** @param {number} id id* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/getCourseConfig(id) {return request({url: `${LIVE_COURSE_API}/get/course/config/${id}`,method: "get",});},/*** 修改直播配置信息** @param {Object} liveCourseConfigVo 直播配置信息* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/updateConfig(liveCourseConfigVo) {return request({url: `${LIVE_COURSE_API}/update/config`,method: "put",data: liveCourseConfigVo,});},/*** 获取最近的直播** @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/findLatelyList() {return request({url: `${LIVE_COURSE_API}/find/lately/list`,method: "get",});},/*** 批量删除直播课程** @param {Array}idList id 数组,Json 数组 [1,2,3,...]* @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/removeRows(idList) {return request({url: `${LIVE_COURSE_API}/remove/batch`,method: "delete",data: idList,});},
};

import request from "@/utils/request";const COURSE_API = "/admin/vod/course";export default {/*** 查询所有课程** @returns {Promise} 返回一个 Promise 对象,表示操作的异步结果*/findAll() {return request({url: `${COURSE_API}/find/all`,method: "get",});},
};
1.4、创建直播页面

1.4.1、list.vue
<template><div class="app-container"><head><!-- 设置 referrer 为 no-referrer,用于绕过防盗链限制,从而正常显示图片 --><meta name="referrer" content="no-referrer" /></head><!-- 工具条 --><el-card class="operate-container" shadow="never"><i class="el-icon-tickets" style="margin-top: 5px"></i><span style="margin-top: 5px">数据列表</span><el-button class="btn-add" size="mini" @click="add">添 加</el-button></el-card><!-- banner 列表 --><el-tablev-loading="listLoading":data="list"stripeborderstyle="width: 100%; margin-top: 10px"><el-table-column label="序号" width="50" align="center"><template slot-scope="scope">{{ (page - 1) * limit + scope.$index + 1 }}</template></el-table-column><el-table-column label="封面" width="200" align="center"><template slot-scope="scope"><img :src="scope.row.cover" width="100%" /></template></el-table-column><el-table-column prop="courseName" label="直播名称" /><el-table-column prop="startTime" label="直播时间"><template slot-scope="scope">{{ scope.row.startTime }} 至 {{ scope.row.endTime }}</template></el-table-column><el-table-column prop="endTime" label="直播结束时间" /><el-table-column prop="param.teacherName" label="直播教师" /><el-table-column label="头衔" width="90"><template slot-scope="scope"><el-tagv-if="scope.row.param.teacherLevel === 1"type="success"size="mini">高级教师</el-tag><el-tag v-if="scope.row.param.teacherLevel === 0" size="mini">首席教师</el-tag></template></el-table-column><el-table-column prop="createTime" label="创建时间" /><el-table-column label="操作" width="200" align="center"><template slot-scope="scope"><el-button type="text" size="mini" @click="edit(scope.row.id)">修改</el-button><el-buttontype="text"size="mini"@click="removeDataById(scope.row.id)">删除</el-button><el-button type="text" size="mini" @click="showAccount(scope.row)">查看账号</el-button><router-link :to="'/live/liveCourse/config/' + scope.row.id"><el-button type="text" size="mini" style="margin-left: 10px">配置</el-button></router-link><router-link :to="'/live/liveVisitor/list/' + scope.row.id"><el-button type="text" size="mini">观看记录</el-button></router-link></template></el-table-column></el-table><!-- 分页组件 --><el-pagination:current-page="page":total="total":page-size="limit":page-sizes="[5, 10, 20, 30, 40, 50, 100]"style="padding: 30px 0; text-align: center"layout="sizes, prev, pager, next, jumper, ->, total, slot"@current-change="fetchData"@size-change="changeSize"/><el-dialog title="添加/修改" :visible.sync="dialogVisible" width="60%"><el-formref="flashPromotionForm"label-width="150px"size="small"style="padding-right: 40px"><!-- 课程教师 --><el-form-item label="直播教师"><el-select v-model="liveCourse.teacherId" placeholder="请选择"><el-optionv-for="teacher in teacherList":key="teacher.id":label="teacher.name":value="teacher.id"/></el-select></el-form-item><el-form-item label="直播教师登录密码" v-if="liveCourse.id === ''"><el-input v-model="liveCourse.password" /></el-form-item><el-form-item label="直播名称"><el-input v-model="liveCourse.courseName" /></el-form-item><el-form-item label="直播开始时间"><el-date-pickerv-model="liveCourse.startTime"type="datetime"placeholder="选择开始日期"value-format="yyyy-MM-dd HH:mm:ss"/></el-form-item><el-form-item label="直播结束时间"><el-date-pickerv-model="liveCourse.endTime"type="datetime"placeholder="选择结束日期"value-format="yyyy-MM-dd HH:mm:ss"/></el-form-item><el-form-item label="直播封面"><el-upload:show-file-list="false":on-success="handleCoverSuccess":before-upload="beforeCoverUpload":on-error="handleCoverError":action="BASE_API + '/admin/vod/file/upload?module=cover'"class="cover-uploader"><img v-if="liveCourse.cover" :src="liveCourse.cover" width="60%" /><i v-else class="el-icon-plus avatar-uploader-icon" /></el-upload></el-form-item><el-form-item label="直播详情"><el-input v-model="liveCourse.description" type="textarea" rows="5" /></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false" size="small">取 消</el-button><el-button type="primary" @click="saveOrUpdate()" size="small">确 定</el-button></span></el-dialog><el-dialogtitle="查看账号":visible.sync="accountDialogVisible"width="60%"><el-formref="accountForm"label-width="150px"size="small"style="padding-right: 40px"><div style="margin-left: 40px"><h4>主播帮助信息</h4><el-row style="height: 35px"><el-co><span class="spd-info">主播登录链接:</span><span class="spd-info">.php?course_id={{liveCourseAccount.courseId }}&role=admin</span></el-co></el-row><el-row style="height: 35px"><el-col><span class="spd-info">主播登录密码:{{ liveCourseAccount.anchorKey }}</span></el-col></el-row></div><div style="margin-left: 40px"><h4>主播客户端账号信息</h4><el-row style="height: 35px"><el-col><span class="spd-info">主播登录账户:{{ liveCourseAccount.anchorAccount }}</span></el-col></el-row><el-row style="height: 35px"><el-col><span class="spd-info">主播登录密码:{{ liveCourseAccount.anchorPassword }}</span></el-col></el-row></div><div style="margin-left: 40px"><h4>助教信息</h4><el-row style="height: 35px"><el-co><span class="spd-info">助教登录连接:</span><span class="spd-info">.php?course_id={{liveCourseAccount.courseId }}&role=admin</span></el-co></el-row><el-row style="height: 35px"><el-col><span class="spd-info">主播登录密码:{{ liveCourseAccount.adminKey }}</span></el-col></el-row></div><div style="margin-left: 40px"><h4>学生观看信息</h4><el-row style="height: 35px"><el-co><span class="spd-info">观看连接:</span><span class="spd-info">/{{liveCourseAccount.courseId }}</span></el-co></el-row><el-row style="height: 35px"><el-col><span class="spd-info">观看二维码:<img src="@/assets/SmartPlanet.png" width="80px"/></span></el-col></el-row></div></el-form><span slot="footer" class="dialog-footer"><el-button @click="accountDialogVisible = false" size="small">关 闭</el-button></span></el-dialog></div>
</template><script>import liveCourseAPI from "@/api/live/liveCourse";import teacherAPI from "@/api/vod/teacher";const defaultForm = {id: "",courseName: "",startTime: "",endTime: "",teacherId: "",password: "",description: "",cover: ".png",};export default {data() {return {BASE_API: "http://localhost:8333",// BASE_API: "",// 数据是否正在加载listLoading: true,// banner 列表list: null,// 数据库中的总记录数total: 0,// 默认页码page: 1,// 每页记录数limit: 10,// 查询表单对象searchObj: {},// 教师列表teacherList: [],dialogVisible: false,liveCourse: defaultForm,saveBtnDisabled: false,accountDialogVisible: false,liveCourseAccount: {courseId: "",},};},// 生命周期函数:内存准备完毕,页面尚未渲染created() {console.log("list created...");this.fetchData();// 获取教师列表this.initTeacherList();},// 生命周期函数:内存准备完毕,页面渲染成功mounted() {console.log("list mounted...");},methods: {// 当页码发生改变的时候changeSize(size) {console.log(size);this.limit = size;this.fetchData(1);},// 加载 banner 列表数据fetchData(page = 1) {console.log("翻页..." + page);// 异步获取远程数据(ajax)this.page = page;liveCourseAPI.getPageList(this.page, this.limit).then((response) => {this.list = response.data.records;this.total = response.data.total;// 数据加载并绑定成功this.listLoading = false;});},// 获取教师列表initTeacherList() {teacherAPI.list().then((response) => {this.teacherList = response.data;});},// 重置查询表单resetData() {console.log("重置查询表单");this.searchObj = {};this.fetchData();},// 根据 id 删除数据removeDataById(id) {this.$confirm("此操作将永久删除该记录, 是否继续?", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {// 点击确定,远程调用ajaxreturn liveCourseAPI.removeById(id);}).then((response) => {this.fetchData(this.page);if (response.code) {this.$message({type: "success",message: "删除成功!",});}}).catch(() => {this.$message({type: "info",message: "已取消删除",});});},add() {this.dialogVisible = true;this.liveCourse = Object.assign({}, defaultForm);},edit(id) {this.dialogVisible = true;this.fetchDataById(id);},fetchDataById(id) {liveCourseAPI.getById(id).then((response) => {this.liveCourse = response.data;});},saveOrUpdate() {// 防止表单重复提交this.saveBtnDisabled = true;if (!this.liveCourse.teacherId) {this.$message.error("请选择直播教师");this.saveBtnDisabled = false;return;}if (!this.liveCourse.id && !this.liveCourse.password) {this.$message.error("请输入直播教师登录密码");this.saveBtnDisabled = false;return;}if (!this.liveCourse.courseName) {this.$message.error("请输入直播名称");this.saveBtnDisabled = false;return;}if (!this.liveCourse.courseName) {this.$message.error("请输入直播名称");this.saveBtnDisabled = false;return;}if (!this.liveCourse.startTime) {this.$message.error("请选择直播开始时间");this.saveBtnDisabled = false;return;}if (!this.liveCourse.endTime) {this.$message.error("请选择直播结束时间");this.saveBtnDisabled = false;return;}// 验证开始时间和结束时间的合法性if (!this.validateDateRange()) {return;}if (!this.liveCourse.description) {this.$message.error("请输入直播详情");this.saveBtnDisabled = false;return;}if (!this.liveCourse.id) {this.saveData();} else {this.updateData();}},// 验证开始时间和结束时间的合法性validateDateRange() {if (this.liveCourse.startTime &&this.liveCourse.endTime &&this.liveCourse.startTime > this.liveCourse.endTime) {this.$message.error("开始时间不能晚于结束时间");return false;}const startDateTime = new Date(this.liveCourse.startTime);const endDateTime = new Date(this.liveCourse.endTime);const durationInMilliseconds = endDateTime - startDateTime;const durationInHours = durationInMilliseconds / (1000 * 60 * 60);if (durationInHours > 24) {this.$message.error("直播时间不能超过24小时");return false;}return true;},// 新增saveData() {liveCourseAPI.save(this.liveCourse).then((response) => {if (response.code) {this.$message({type: "success",message: response.message,});this.dialogVisible = false;this.fetchData(this.page);}});},// 根据 id 更新记录updateData() {liveCourseAPI.updateById(this.liveCourse).then((response) => {if (response.code) {this.$message({type: "success",message: response.message,});this.dialogVisible = false;this.fetchData(this.page);}});},// 根据 id 查询记录fetchDataById(id) {liveCourseAPI.getById(id).then((response) => {this.liveCourse = response.data;});},showAccount(row) {this.accountDialogVisible = true;liveCourseAPI.getLiveCourseAccount(row.id).then((response) => {this.liveCourseAccount = response.data;this.liveCourseAccount.courseId = row.courseId;});},// 上传成功回调handleCoverSuccess(res, file) {this.liveCourse.cover = res.data;},// 上传校验beforeCoverUpload(file) {const isJPG = file.type === "image/jpeg";const isPNG = file.type === "image/png";const isLt2M = file.size / 1024 / 1024 < 2;if (!isJPG && !isPNG) {this.$message.error("上传直播封面只能是 JPG 或 PNG 格式!");}if (!isLt2M) {this.$message.error("上传直播封面大小不能超过 2MB!");}return (isJPG || isPNG) && isLt2M;},// 错误处理handleCoverError() {console.log("error");this.$message.error("上传失败");},},};
</script>
1.4.2、config.vue
<template><div class="app-container"><el-form label-width="120px" size="small"><divstyle="background-color: #e0e0e0;width: 100%;padding: 1px 10px;margin: 10px 0;"><h3>功能设置&nbsp;&nbsp;&nbsp;</h3></div><el-form-item label="界面模式"><el-radio-group v-model="liveCourseConfigVo.pageViewMode"><el-radio :label="1">全屏模式</el-radio><el-radio :label="0">二分屏</el-radio><el-radio :label="2">课件模式</el-radio></el-radio-group></el-form-item><el-form-item label="观看人数开关"><el-radio-group v-model="liveCourseConfigVo.numberEnable"><el-radio :label="1">是</el-radio><el-radio :label="0">否</el-radio></el-radio-group></el-form-item><el-form-item label="商城开关:"><el-radio-group v-model="liveCourseConfigVo.storeEnable"><el-radio :label="1">是</el-radio><el-radio :label="0">否</el-radio></el-radio-group></el-form-item><divstyle="background-color: #e0e0e0;width: 100%;padding: 1px 10px;margin: 10px 0;"><h3>商品列表&nbsp;&nbsp;&nbsp;<el-button type="" size="mini" @click="addCourse()">添加 / 更新</el-button></h3></div><el-tablev-loading="listLoading":data="liveCourseConfigVo.liveCourseGoodsList"stripeborderstyle="width: 100%; margin-top: 10px"><el-table-column label="序号" width="70" align="center"><template slot-scope="scope"> {{ scope.$index + 1 }} </template></el-table-column><el-table-column label="商品图片" width="120" align="center"><template slot-scope="scope"><img :src="scope.row.img" width="80px" /></template></el-table-column><el-table-column prop="name" label="名称" width="100" /><el-table-column prop="price" label="价格" width="100" /><el-table-column prop="originalPrice" label="原价" /></el-table><el-dialogtitle="添加 / 更新课程":visible.sync="dialogVisible"width="50%"><el-form:inline="true"label-width="150px"size="small"style="padding-right: 40px"><el-tablev-loading="listLoading":data="courseList"stripeborderstyle="width: 100%; margin-top: 10px"@selection-change="handleSelectionChange"><el-table-column type="selection" width="55" /><el-table-column label="序号" width="70" align="center"><template slot-scope="scope"> {{ scope.$index + 1 }} </template></el-table-column><el-table-column label="分类"><template slot-scope="scope">{{ scope.row.param.subjectParentTitle }} > {{scope.row.param.subjectTitle }}</template></el-table-column><el-table-column prop="title" label="课程名称" width="150" /><el-table-column prop="lessonNum" label="课时" width="100" /><el-table-column prop="param.teacherName" label="教师" /></el-table><el-form-item style="margin-top: 10px"><el-button type="" @click="dialogVisible = false">取消</el-button><el-button type="" @click="selectCourse()">保存</el-button></el-form-item></el-form></el-dialog><br /><br /><el-form-item><el-button type="primary" @click="saveOrUpdate">保存</el-button><el-button @click="back">返回</el-button></el-form-item></el-form></div>
</template><script>import liveCourseAPI from "@/api/live/liveCourse";import courseAPI from "@/api/vod/course";const defaultForm = {id: "",liveCourseId: "",pageViewMode: 1,numberEnable: 1,storeEnable: 1,storeType: 1,liveCourseGoodsList: [],};export default {data() {return {// 数据是否正在加载listLoading: true,liveCourseConfigVo: defaultForm,saveBtnDisabled: false,dialogVisible: false,courseList: [],// 批量选择中选择的记录列表multipleSelection: [],};},// 监听器watch: {$route(to, from) {console.log("路由变化...");console.log(to);console.log(from);this.init();},},// 生命周期方法(在路由切换,组件不变的情况下不会被调用)created() {console.log("form created...");this.init();},methods: {// 表单初始化init() {this.liveCourseConfigVo.liveCourseId = this.$route.params.id;this.fetchDataById(this.liveCourseConfigVo.liveCourseId);this.fetchCourseList();},back() {this.$router.push({ path: "/live/liveCourse/list" });},// 根据 id 查询记录fetchDataById(id) {liveCourseAPI.getCourseConfig(id).then((response) => {if (null !== response.data.id) {this.liveCourseConfigVo = response.data;}this.listLoading = false;});},fetchCourseList() {courseAPI.findAll().then((response) => {this.courseList = response.data;});},handleSelectionChange(selection) {console.log(selection);this.multipleSelection = selection;},addCourse() {this.dialogVisible = true;},selectCourse() {if (this.multipleSelection.length === 0) {this.$message({type: "warning",message: "请选择对应课程!",});return;}let list = [];this.multipleSelection.forEach((item) => {var obj = {liveCourseId: this.liveCourseConfigVo.liveCourseId,goodsId: item.id,name: item.title,img: item.cover,price: item.price,originalPrice: item.price,tab: "1",url:"/" +item.id,putaway: "1",pay: "1",qrcode: "",};list.push(obj);});this.liveCourseConfigVo.liveCourseGoodsList = list;this.dialogVisible = false;},saveOrUpdate() {liveCourseAPI.updateConfig(this.liveCourseConfigVo).then((response) => {this.$message({type: "success",message: response.message,});this.$router.push({ path: "/live/liveCourse/list" });});},},};
</script>

二、公众号直播对接

1、用户观看端集成

接口文档:.html

1.1、获取用户 access_token

用户要观看直播,必须获取对应的用户 access_token,通过 access_token 获取观看的直播课程;

接口参数:直播 id,用户 id。

(1)创建 LiveCourseApiController。

package com.myxh.smart.planet.live.api;import com.alibaba.fastjson2.JSONObject;
import com.myxh.smart.planet.live.service.LiveCourseService;
import com.myxh.smart.planet.order.AuthContextHolder;
import com.myxh.smart.planet.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author MYXH* @date 2023/10/28*/
@Tag(name = "直播课程 API", description = "直播课程 API 接口")
@RestController
@RequestMapping("api/live/live/course")
public class LiveCourseApiController
{@Resourceprivate LiveCourseService liveCourseService;/*** 获取用户 access_token** @param id 直播课程 id* @return Result 全局统一返回结果*/@Operation(summary = "获取用户 access_token", description = "获取用户 access_token")@GetMapping("get/play/auth/{id}")public Result<JSONObject> getPlayAuth(@PathVariable("id") Long id){JSONObject accessToken = liveCourseService.getAccessToken(id, AuthContextHolder.getUserId());return Result.ok(accessToken);}
}

(2)LiveCourseService 添加方法。

package com.myxh.smart.planet.live.service;import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import com.myxh.smart.planet.model.live.LiveCourse;/*** @author MYXH* @date 2023/10/26** <p>* 直播课程 服务类* </p>*/
public interface LiveCourseService extends IService<LiveCourse>
{/*** 获取用户 access_token** @param id     id 直播课程 id* @param userId 用户 id* @return accessToken 访问令牌*/JSONObject getAccessToken(Long id, Long userId);
}

(3)LiveCourseServiceImpl 实现方法。

package com.myxh.smart.planet.live.service.impl;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myxh.smart.planet.client.user.UserInfoFeignClient;
import com.myxh.smart.planet.exception.SmartPlanetException;
import com.myxh.smart.planet.live.mapper.LiveCourseMapper;
import com.myxh.smart.planet.live.mtcloud.CommonResult;
import com.myxh.smart.planet.live.mtcloud.MTCloud;
import com.myxh.smart.planet.live.service.LiveCourseService;
import com.myxh.smart.planet.model.live.LiveCourse;
import com.myxh.smart.planet.model.user.UserInfo;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;/*** @author MYXH* @date 2023/10/26** <p>* 直播课程 服务实现类* </p>*/
@Service
public class LiveCourseServiceImpl extends ServiceImpl<LiveCourseMapper, LiveCourse> implements LiveCourseService
{@Autowiredprivate UserInfoFeignClient userInfoFeignClient;@Autowiredprivate MTCloud mtCloudClient;/*** 获取用户 access_token** @param id     id 直播课程 id* @param userId 用户 id* @return accessToken 访问令牌*/@SneakyThrows@Overridepublic JSONObject getAccessToken(Long id, Long userId){// 根据课程 id 获取直播课程信息LiveCourse liveCourse = baseMapper.selectById(id);// 根据用户 id 获取用户信息UserInfo userInfo = userInfoFeignClient.getUserInfoById(userId);// 封装需要的参数HashMap<Object, Object> options = new HashMap<>();String res = mtCloudClient.courseAccess(liveCourse.getCourseId().toString(),userId.toString(),userInfo.getNickName(),MTCloud.ROLE_USER,80 * 80 * 80,options);System.out.println("res = " + res);CommonResult<JSONObject> commonResult = JSON.parseObject(res, CommonResult.class);if (Integer.parseInt(commonResult.getCode()) == MTCloud.CODE_SUCCESS){JSONObject accessToken = commonResult.getData();System.out.println("access_token = " + accessToken.getString("access_token"));return accessToken;}else{throw new SmartPlanetException(20001, "获取失败");}}
}
1.2、下载前端 SDK

下载地址:.html

1.3、使用快捷模板

下载模板,修改 token 获取方式。

var url = window.location.search;
var token = url.split("=")[1];
1.4、与前端项目结合

(1)创建直播播放页面 live.html。

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><metaname="viewport"content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/><title>TalkFun Vod QuickStart - v2</title><style type="text/css">* {margin: 0;padding: 0;list-style-type: none;font-family: "Microsoft YaHei", "STHeiti";}.flash-wran {display: none;position: absolute;top: 0;width: 100%;padding: 5px 0;text-align: center;background: #fff6a2;border: 1px solid #ffd913;}.flash-wran a {color: red;}.wrapper {display: flex;padding: 10px;}#cameraPlayer,#pptPlayer {height: auto;flex: 1;text-align: center;font-size: 12px;overflow: hidden;}#pptPlayer {height: 300px;width: 100%;}#modPptPlayer,#modCameraPlayer {margin-top: 10px;border: 1px solid #c7c7c7;}.chat-wrap {padding: 5px;margin: 10px;border: 2px solid #cccccc;background: #f1f1f1;}.mod-chat-list {margin: 20px 0;border: 1px solid #cccccc;min-height: 100px;font-size: 12px;background: #dedede;padding: 5px;max-height: 200px;overflow-y: scroll;}.mod-chat-list li {padding: 2px 0;margin-bottom: 5px;border-bottom: 1px dotted #cccccc;}input {display: inline-block;width: 200px;padding: 5px;}button {display: inline-block;padding: 5px;margin-left: 5px;}#toast {padding: 20px;position: fixed;z-index: 100;display: none;background: rgba(212, 28, 28, 0.8);left: 50%;top: 30%;border-radius: 50em;font-size: 14px;color: #ffffff;box-shadow: 0 0 6px 0px #bb2d2d;}#talkfun-video-wrap,#talkfun-camera-wrap {position: relative;background: #000;}</style><!-- SDK 版本 --><!-- 获取最新版本 ==> .html --><scripttype="text/javascript"src=".0/TalkFunWebSDK-7.7.min.js"></script></head><body><!-- wrap --><div class="wrapper"><!-- 桌面分享|视频插播模式 --><div id="videoPlayer"><p id="loadplayer"></p></div></div><script>// [第一步] 如何获取 access_token => .html// [第二步] 根据Api文档方法 监听 / 调用方法 JS Api => .js.getstart.htmlvar url = window.location.search;var token = url.split("=")[1];// 更多配置项 => .js.getstart.html?h=%E9%85%8D%E7%BD%AE%E9%A1%B9var HT = new MT.SDK.main(token,{config: {// Safari 浏览器建议设置为 HLStechOrder: "FLV",},},function (data) {console.warn("sdk 加载完成", data);});// 连接状态HT.on("connect", function () {console.log("TalkFun 通信 => 连接成功...");});// 课件播放器HT.whiteboardPlayer("pptPlayer", "docPlayer", function (player) {console.log("课件播放器 => 初始化成功");document.querySelector("#loadplayer").innerHTML = "画板模块加载完成";});// 视频插播 | 桌面分享HT.videoPlayer("videoPlayer", "modVideoplayer", function (player) {console.log("视频播放器 => 初始化成功");document.querySelector("#loadplayer").innerHTML = "视频插播加载完成";});// 摄像头播放器HT.camera("cameraPlayer", "modCameraPlayer", function () {console.log("摄像头播放器 => 初始化成功");document.querySelector("#loadcam").innerHTML = "摄像头模块加载完成";});// 接收聊天var receivedChat = function (chat) {var tpl = chat.nickname + ": " + chat.msg;var chatItem = document.createElement("li");chatItem.innerHTML = tpl;chatItem.className = "chat-" + chat.xid;document.querySelector("#chat-list").appendChild(chatItem);};// 接收聊天信息HT.on("chat:send", function (chat) {receivedChat(chat);});// 发送聊天信息document.querySelector("#chatSubmit").addEventListener("click",function () {var chatIpt = document.querySelector("#chatVal");var chatValue = chatIpt.value;HT.emit("chat:send", { msg: chatValue }, function (res) {// 发送成功if (Number(res.code) === 0) {receivedChat(res.data);chatIpt.value = "";}// 发送失败else {console.warn(res.msg);}});},false);// Flash 插件异常HT.on("flash:load:error", function (obj) {if (!obj.flash) {document.querySelector("#flashTip").style.display = "block";}});// 课程错误信息HT.on("live:course:access:error", function (res) {console.error("错误信息 ==>", res);});// 课程错误信息HT.on("system:room:error", function (res) {var toast = document.querySelector("#toast");if (typeof res === "string") {toast.innerHTML = res.msg;} else if (res.msg) {toast.innerHTML = res.msg;}toast.style.display = "block";var _left = toast.clientWidth / 2;toast.style.marginLeft = -_left + "px";});</script></body>
</html>

观众在直播详情页面点击观看,获取通过接口获取 access_token,然后带上 access_token 参数跳转到直播观看页面即可,关键代码:

liveInfo.vue

<template><div><head><!-- 设置 referrer 为 no-referrer,用于绕过防盗链限制,从而正常显示图片 --><meta name="referrer" content="no-referrer" /></head><van-imagewidth="100%"height="200"src=".png"/><h1 class="van-ellipsis course_title">{{ liveCourse.courseName }}</h1><div class="course_teacher_price_box"><div class="course_teacher_price"><div class="course_price">直播时间:</div><div class="course_price_number">{{ liveCourse.param.startTimeString }}至{{liveCourse.param.endTimeString }}</div></div></div><div class="course_teacher_price_box"><div class="course_teacher_box"><div class="course_teacher">教师: {{ teacher.name }}</div><van-image :src="teacher.avatar" round width="50px" height="50px" /></div></div><div class="course_contents"><div class="course_title_font">课程详情</div><van-divider :style="{ margin: '5px 0 ' }" /><div class="course_content" v-html="description"></div></div><van-goods-action><van-goods-action-button type="danger" text="直播中" @click="play" /></van-goods-action><van-loading vertical="true" v-show="loading">加载中...</van-loading></div>
</template><script>import liveAPI from "@/api/live";import shareAPI from "@/api/share";import wxShare from "@/utils/wxShare";export default {data() {return {loading: false,liveCourseId: null,liveCourse: { param: {} },description: "",teacher: {},liveStatus: 0,activeNames: ["1"],};},created() {this.liveCourseId = this.$route.params.liveCourseId;this.fetchData();},methods: {fetchData() {this.loading = true;liveAPI.getInfo(this.liveCourseId).then((response) => {console.log(response.data);this.liveCourse = response.data.liveCourse;this.description = response.data.description;this.teacher = response.data.teacher;this.liveStatus = response.data.liveStatus;this.loading = false;// 分享注册this.wxRegister();});},play() {liveAPI.getPlayAuth(this.liveCourseId).then((response) => {console.log(response.data);// this.$router.push({ path: '/live/online?token='+response.data.access_token })window.location = "./live.html?token=" + response.data.access_token;this.finished = true;});},wxRegister() {// 说明:后台加密 url 必须与当前页面 url 一致let url = window.location.href.replace("#", "smartplanet");shareAPI.getSignature(url).then((response) => {console.log(response.data);// 记录分享用户let link = "";if (window.location.href.indexOf("?") != -1) {link =window.location.href + "&recommend=" + response.data.userEedId;} else {link =window.location.href + "?recommend=" + response.data.userEedId;}let option = {title: this.liveCourse.courseName,desc: this.description,link: link,imgUrl: this.liveCourse.cover,};wxShare.wxRegister(response.data, option);});},},};
</script><style lang="scss" scoped>.gap {height: 10px;}::v-deep.van-image {display: block;}.course_count {background-color: #82848a;color: white;padding: 5px;text-align: center;border-right: 1px solid #939393;h1 {font-size: 14px;margin: 0;}p {margin: 0;font-size: 16px;}}.course_title {font-size: 20px;margin: 10px;}.course_teacher_price_box {margin: 10px;display: flex;justify-content: space-between;align-items: center;.course_teacher_price {display: flex;font-size: 14px;align-items: center;.course_price_number {color: red;font-size: 18px;font-weight: bold;}.course_teacher {margin-left: 20px;}}.course_teacher_box {display: flex;justify-content: center;align-items: center;.course_teacher {margin-right: 20px;}}}.course_contents {margin: 10px;.course_title_font {color: #68cb9b;font-weight: bold;}.course_content {margin-bottom: 20px;}}.course_chapter_list {display: flex;justify-content: space-between;align-items: center;h2 {font-size: 14px;}p {margin: 0;}}
</style>

http://localhost:8080/live.html 为直播观看访问方式。

1.5、补充接口

(1)LiveCourseApiController 类。

package com.myxh.smart.planet.live.api;import com.myxh.smart.planet.live.service.LiveCourseService;
import com.myxh.smart.planet.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;/*** @author MYXH* @date 2023/10/28*/
@Tag(name = "直播课程 API", description = "直播课程 API 接口")
@RestController
@RequestMapping("api/live/live/course")
public class LiveCourseApiController
{@Resourceprivate LiveCourseService liveCourseService;/*** 根据直播课程 id 查询直播课程信息** @param liveCourseId 直播课程 id* @return Result 全局统一返回结果*/@Operation(summary = "根据直播课程 id 查询直播课程信息", description = "根据直播课程 id 查询直播课程信息")@GetMapping("get/info/{liveCourseId}")public Result<Map<String, Object>> getInfo(@Parameter(name = "liveCourseId", description = "直播课程 ID", required = true)@PathVariable("liveCourseId") Long liveCourseId){Map<String, Object> liveCourseInfo = liveCourseService.getInfoById(liveCourseId);return Result.ok(liveCourseInfo);}
}

(2)LiveCourseServiceImpl 实现。

package com.myxh.smart.planet.live.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.myxh.smart.planet.client.course.CourseFeignClient;
import com.myxh.smart.planet.live.mapper.LiveCourseMapper;
import com.myxh.smart.planet.live.service.LiveCourseDescriptionService;
import com.myxh.smart.planet.live.service.LiveCourseService;
import com.myxh.smart.planet.model.live.LiveCourse;
import com.myxh.smart.planet.model.live.LiveCourseDescription;
import com.myxh.smart.planet.model.vod.Teacher;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;/*** @author MYXH* @date 2023/10/26** <p>* 直播课程 服务实现类* </p>*/
@Service
public class LiveCourseServiceImpl extends ServiceImpl<LiveCourseMapper, LiveCourse> implements LiveCourseService
{@Autowiredprivate CourseFeignClient teacherFeignClient;@Autowiredprivate LiveCourseDescriptionService liveCourseDescriptionService;/*** 根据直播课程 id 查询直播课程信息** @param liveCourseId 直播课程 id* @return liveCourseInfo 直播课程信息*/@Overridepublic Map<String, Object> getInfoById(Long liveCourseId){LiveCourse liveCourse = this.getById(liveCourseId);liveCourse.getParam().put("startTimeString", new DateTime(liveCourse.getStartTime()).toString("yyyy年MM月dd HH:mm"));liveCourse.getParam().put("endTimeString", new DateTime(liveCourse.getEndTime()).toString("yyyy年MM月dd HH:mm"));Teacher teacher = teacherFeignClient.getTeacherLive(liveCourse.getTeacherId());LiveCourseDescription liveCourseDescription = liveCourseDescriptionService.getLiveCourseDescriptionByLiveCourseId(liveCourseId);Map<String, Object> liveCourseInfo = new HashMap<>();liveCourseInfo.put("liveCourse", liveCourse);liveCourseInfo.put("liveStatus", this.getLiveStatus(liveCourse));liveCourseInfo.put("teacher", teacher);if (liveCourseDescription != null){liveCourseInfo.put("description", liveCourseDescription.getDescription());}else{liveCourseInfo.put("description", "");}return liveCourseInfo;}
}

三、微信分享

1、实现目标

1、点播课程详情页面分享。

2、微信分享实现方式

参考文档:.html

2.1、绑定域名

先登录微信公众平台进入“设置与开发”,“公众号设置”的“功能设置”里填写“JS 接口安全域名”。

说明:本地测试设置内网穿透地址。

2.2、公众号测试号配置

2.3、引入 JS 文件
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge,,chrome=1" /><metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no"/><title>智慧星球</title><link rel="icon" href="<%= BASE_URL %>favicon.ico" /><scriptsrc=".6.0.js"type="text/javascript"></script><!-- 引入播放器 css 文件 --><linkhref=".7.2/tcplayer.min.css"rel="stylesheet"/><!-- 如果需要在 Chrome 和 Firefox 等现代浏览器中通过 H5 播放 HLS 协议的视频,需要在 tcplayer.vx.x.x.min.js 之前引入 hls.min.x.xx.xm.js。 --><script src=".7.2/libs/hls.min.1.1.6.js"></script><!-- 如果需要在 Chrome 和 Firefox 等现代浏览器中通过 H5 播放 FLV 格式的视频,需要在 tcplayer.vx.x.x.min.js 之前引入 flv.min.x.x.x.js。 --><script src=".7.2/libs/flv.min.1.6.3.js"></script><!-- 如果需要在 Chrome 和 Firefox 等现代浏览器中通过 H5 播放 DASH 视频,需要在 tcplayer.vx.x.x.min.js 之前引入 dash.min.x.x.x.js。 --><script src=".7.2/libs/dash.all.min.4.5.2.js"></script><!-- 引入播放器 js 文件 --><script src=".7.2/tcplayer.v4.7.2.min.js"></script></head><body><scripttype="text/javascript"src=".0/TalkFunWebSDK-7.7.min.js"></script><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't workproperly without JavaScript enabled. Please enable it tocontinue.</strong></noscript><div id="app"></div><!-- built files will be auto injected --></body>
</html>

引入前端项目/public/index.html 文件。

2.4、封装分享 js

参考官方文档封装接口。

需要分享的页面有直播详情页、点播课程详情页等,因此把分享代码封装后,在对应的页面直接引入与调用即可。

新建 src/util/wxShare.js 文件。

/*** 微信 js-sdk* 参考文档:=resource/res_main&id=mp1421141115*/
const wxShare = {/*** [wxRegister 微信 Api 初始化]* @param  {Function} callback [ready 回调函数]*/wxRegister(data, option) {// data 是微信配置信息,option 是分享的配置内容wx.config({// 开启调试模式debug: false,// 必填,公众号的唯一标识appId: data.appId,// 必填,生成签名的时间戳timestamp: data.timestamp,// 必填,生成签名的随机串nonceStr: data.nonceStr,// 必填,签名,见附录 1signature: data.signature,// 必填,需要使用的 JS 接口列表,所有 JS 接口列表见附录 2jsApiList: ["onMenuShareAppMessage"],});wx.ready(function () {wx.onMenuShareAppMessage({// 分享标题title: option.title,// 分享描述desc: option.desc,// 分享链接link: option.link,// 分享图标imgUrl: option.imgUrl,success() {// 用户成功分享后执行的回调函数//  option.success()console.log("ok");},cancel() {// 用户取消分享后执行的回调函数// option.error()console.log("cancel");},});});wx.error(function (res) {// config 信息验证失败会执行 error 函数,如签名过期导致验证失败,具体错误信息可以打开 config 的 debug 模式查看,也可以在返回的 res 参数中查看,对于 SPA 可以在这里更新签名。alert("error:" + JSON.stringify(res));});},
};export default wxShare;
2.5、服务器端接口

新增 ShareController 类。

说明:微信分享要对当前 url 加密处理,由于 url 路由都是带“#”符号,服务器端接收不到,因此通过“smartplanet”单词代替了“#”。

package com.myxh.smart.planet.wechat.controller;import com.myxh.smart.planet.order.AuthContextHolder;
import com.myxh.smart.planet.order.Base64Util;
import com.myxh.smart.planet.result.Result;
import com.myxh.smart.planet.vo.wechat.WeChatJsapiSignatureVo;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixinmon.bean.WxJsapiSignature;
import me.chanjar.weixinmon.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author MYXH* @date 2023/10/28*/
@RestController
@RequestMapping("/api/wechat/share")
@Slf4j
public class ShareController
{@Autowiredprivate WxMpService wxMpService;/***  获取签名** @param url url* @return Result 全局统一返回结果* @throws WxErrorException 微信错误异常*/@GetMapping("/get/signature")public Result<WeChatJsapiSignatureVo> getSignature(@RequestParam("url") String url) throws WxErrorException{String currentUrl = url.replace("smartplanet", "#");WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(currentUrl);WeChatJsapiSignatureVo wxJsapiSignatureVo = new WeChatJsapiSignatureVo();BeanUtils.copyProperties(jsapiSignature, wxJsapiSignatureVo);wxJsapiSignatureVo.setUserEedId(Base64Util.base64Encode(AuthContextHolder.getUserId() + ""));return Result.ok(wxJsapiSignatureVo);}
}
2.6、点播课程详情分享

页面:courseInfo.vue。

(1)引入分享。

import shareAPI from "@/api/share";
import wxShare from "@/utils/wxShare";

(2)代码实现。

关键代码。

<template><div><van-image width="100%" height="200" :src="courseVo.cover" /><van-row><van-col span="8"><div class="course_count"><h1>购买数</h1><p>{{ courseVo.buyCount }}</p></div></van-col><van-col span="8"><div class="course_count"><h1>课时数</h1><p>{{ courseVo.lessonNum }}</p></div></van-col><van-col span="8"><div class="course_count"><h1>浏览数</h1><p>{{ courseVo.viewCount }}</p></div></van-col></van-row><h1 class="van-ellipsis course_title">{{ courseVo.title }}</h1><div class="course_teacher_price_box"><div class="course_teacher_price"><div class="course_price">价格:</div><div class="course_price_number">¥{{ courseVo.price }}</div></div><div><van-button@click="see()"v-if="isBuy || courseVo.price == '0.00'"plaintype="warning"size="mini">立即观看</van-button><van-button @click="buy" v-else plain type="warning" size="mini">立即购买</van-button></div></div><div class="course_teacher_price_box"><div class="course_teacher_box"><div class="course_teacher">教师: {{ teacher.name }}</div><van-image :src="teacher.avatar" round width="50px" height="50px" /></div></div><div class="course_contents"><div class="course_title_font">课程详情</div><van-divider :style="{ margin: '5px 0 ' }" /><div class="course_content" v-html="description"></div><div class="course_title_font">课程大纲</div><div class="gap"></div><van-collapse v-model="activeNames"><van-collapse-item:title="item.title":name="item.id"v-for="item in chapterVoList":key="item.id"><ulclass="course_chapter_list"v-for="child in item.children":key="child.id"><h2>{{ child.title }}</h2><p v-if="child.isFree == 1"><van-button @click="play(child)" type="warning" size="mini" plain>免费观看</van-button></p><p v-else><van-button @click="play(child)" type="warning" size="mini" plain>观看</van-button></p></ul></van-collapse-item></van-collapse></div><van-loading vertical="true" v-show="loading">加载中...</van-loading></div>
</template><script>import courseAPI from "@/api/course";import shareAPI from "@/api/share";import wxShare from "@/utils/wxShare";export default {data() {return {loading: false,courseId: null,courseVo: {},description: "",teacher: {},chapterVoList: [],isBuy: false,activeNames: ["1"],};},created() {this.courseId = this.$route.params.courseId;this.fetchData();},methods: {fetchData() {this.loading = true;courseAPI.getInfo(this.courseId).then((response) => {console.log(response.data);this.courseVo = response.data.courseVo;this.description = response.data.description;this.isBuy = response.data.isBuy;this.chapterVoList = response.data.chapterVoList;this.teacher = response.data.teacher;this.loading = false;// 分享注册this.wxRegister();});},buy() {this.$router.push({ path: "/trade/" + this.courseId });},play(video) {let videoId = video.id;let isFree = video.isFree;if (isFree === 1 || this.isBuy || this.courseVo.price == "0.00") {this.$router.push({ path: "/play/" + this.courseId + "/" + videoId });} else {// this.$router.push({ path: "/play/" + this.courseId + "/" + videoId });if (window.confirm("购买了才可以观看, 是否继续?")) {this.buy();}}},see() {this.$router.push({ path: "/play/" + this.courseId + "/0" });},wxRegister() {// 说明:后台加密 url 必须与当前页面 url 一致let url = window.location.href.replace("#", "smartplanet");shareAPI.getSignature(url).then((response) => {console.log(response.data);// 记录分享用户let link = "";if (window.location.href.indexOf("?") != -1) {link =window.location.href + "&recommend=" + response.data.userEedId;} else {link =window.location.href + "?recommend=" + response.data.userEedId;}let option = {title: this.courseVo.title,desc: this.description,link: link,imgUrl: this.courseVo.cover,};wxShare.wxRegister(response.data, option);});},},};
</script><style lang="scss" scoped>.gap {height: 10px;}::v-deep.van-image {display: block;}.course_count {background-color: #82848a;color: white;padding: 5px;text-align: center;border-right: 1px solid #939393;h1 {font-size: 14px;margin: 0;}p {margin: 0;font-size: 16px;}}.course_title {font-size: 20px;margin: 10px;}.course_teacher_price_box {margin: 10px;display: flex;justify-content: space-between;align-items: center;.course_teacher_price {display: flex;font-size: 14px;align-items: center;.course_price_number {color: red;font-size: 18px;font-weight: bold;}.course_teacher {margin-left: 20px;}}.course_teacher_box {display: flex;justify-content: center;align-items: center;.course_teacher {margin-right: 20px;}}}.course_contents {margin: 10px;.course_title_font {color: #68cb9b;font-weight: bold;}.course_content {margin-bottom: 20px;}}.course_chapter_list {display: flex;justify-content: space-between;align-items: center;h2 {font-size: 14px;}p {margin: 0;}}
</style>
2.7、测试

(1)使用手机测试,其他端测试可能会出现错误问题。

Day 16-腾讯云部署

一、项目部署方案

1、原始部署方式

2、整合 Jenkins

完整 DevOps 示例如下:

3、整合 CODING

整合 CODING 实现 DevOps:

二、腾讯云 CODING DevOps 概述

腾讯云使用文档:

.html

1、产品简介

DevOps 是 Development 和 Operations 的组合词,代表着重视「软件开发人员(Dev)」和「IT 运维技术人员(Ops)」之间沟通合作的文化;旨在透过自动化「软件交付」和「架构变更」的流程,使得构建、 测试、发布软件的过程能够更加地快捷、频繁和可靠。Gartner 咨询公司认为 DevOps 代表了 IT 文化的变化趋势。

1.1、CODING DevOps 是什么

CODING DevOps 是面向软件研发团队的一站式研发协作管理平台,提供从需求到设计、开发、构建、测试、发布到部署的全流程协同及研发工具支撑。CODING 解决方案可助力企业实现代码的统一安全管控,并快速实践敏捷开发与 DevOps,提升软件交付质量与速度,降低企业研发成本,实现研发效能升级。

1.2、CODING DevOps 优势
  • 一站式协作平台及研发工具链,提升研发效能。

    CODING 与云端优势相结合,依托业界敏捷项目管理与 DevOps 体系方法融入到产品中,打通研发过程中的工具链孤岛及协作壁垒,覆盖敏捷开发全生命周期,帮助团队实现需求、迭代、开发、测试、持续集成、持续部署全方位研发管理,提升软件研发效能。

  • 支持双态研发体系建设,满足多样化业务需求。

    CODING 适用于不同规模的开发团队以及不同类型的软件开发模式(如瀑布模型、敏捷模型),满足多业务场景的协作需求。

  • 项目工作流和度量数据可视化,项目管理更轻松。

    CODING 提供可视化看板,支持对代码、项目进度、人员工作量等不同维度输出详尽的数据报告,为团队管理者提供决策依据,调整项目计划和合理安排研发人力。

  • 丰富的扩展能力,无缝集成第三方平台。

    CODING 支持无缝集成 GitHub、GitLab 等第三方代码库及各类常见的运维系统和云原生环境,让用户实现跨平台的无缝迁移。

1.3、CODING DevOps 功能特性

CODING DevOps 平台主要提供以下功能特性:

团队级功能:

  • 团队管理:团队管理员通过可视化的仪表盘可以快速掌握团队成员工作数据、监控项目运行状态;通过团队目标助力团队成员聚焦组织目标,全方位协同执行,凝聚团队战斗力,让战略坚实落地;利用工作负载统一查看对比成员的工作量和工作安排;利用研发度量统计并分析团队成员在一段时间内的事项分布、事项概览、代码分布等数据,度量团队成员在周期内完成工作量与工作动态。

项目级功能:

  • 项目协同:软件开发团队可自由选择适合的研发管理模式,支持多项目管理、敏捷迭代管理、需求管理、缺陷跟踪、多维度报表数据等功能。

  • 代码仓库:提供企业级的 Git/SVN 代码管理服务,支持精细化权限管控、多分支并行开发、多版本管理等功能。

  • 代码扫描:提供针对不同编程语言的代码扫描方案,支持对扫描规则、度量规则等进行自定义配置。根据代码扫描测试结果,开发人员可及时发现代码缺陷并作出修正,有效管控代码质量。

  • 持续集成:提供基于云端的自动化代码构建、测试、分析和部署工作流服务,支持通过模板快速创建构建任务并进行可视化编排,极大提高软件开发团队的构建效率。

  • 持续部署:提供全自动化软件部署,可持续、可控地把软件制品在线发布到服务集群中,支持蓝绿分发布、灰度发布(金丝雀发布)等多种发布策略。

  • 制品管理:提供云端构建产物管理服务,支持云端构建和本地构建推送,可快速索引存档构建物、进行版本控制。

  • 测试管理:提供面向敏捷团队的测试一站式云端测试平台,支持可视化的测试规划和多维度的测试报告,满足敏捷团队对测试过程的多样化需求。

  • 文档管理:提供灵活易用的文档管理服务,可用于记录整个项目的来龙去脉,展示当前项目状态,也可让项目成员更好地进行文档书写及协作。

2、使用流程概述

以下流程图展示了 CODING DevOps 软件开发平台的基本操作流程,您可以按照实际需求有选择性阅读。

2.1、创建或加入团队

如要开始使用 CODING DevOps,您需要先注册创建或接受邀请后加入一个团队。

2.2、新建项目

加入团队之后,您可以在团队内创建项目或受他人邀请加入别的项目。“项目”是核心单元,几乎大部分工作都需要在项目中展开。

2.3、开始项目协同

项目创建之后,项目经理、开发、测试等不同的项目角色可通过项目协同实现简单高效的项目协作,包含迭代管理、需求管理、任务管理等。

2.4、使用代码仓库

完成项目规划之后,可利用代码仓库管理项目代码。该功能提供企业级的基于 Git 的云端代码管理服务,支持精细化权限管控、多分支并行开发、多版本管理等功能。

2.5、启动代码扫描

对于使用 CODING 代码仓库管理的代码,开发者可使用代码扫描功能进行代码检查,以便及时发现代码缺陷并作出修正,有效管控代码质量。

2.6、编译构建

项目代码开发完成之后,可通过持续集成功能快速创建构建任务,将项目代码编译打包成软件包。

2.7、管理制品

在您将项目代码构建好之后,可以使用制品管理功能管理构建产物。CODING 支持多种制品库类型,包括 Docker、Maven、Helm 和 npm。

2.8、实施持续部署

当您的项目代码已经完成构建,可使用持续部署把控构建之后的项目发布与部署到生产环境中去。

2.9、管理测试用例

当您在 CODING 平台创建项目之后,您可以使用面向敏捷团队的测试管理功能来管理项目内的测试活动,确保产品的高质量交付。

2.10、管理项目文档

在项目进行中,必然会产生大量的信息,并且需要对这些信息进行记录、传递、分享。文档管理功能提供灵活易用的文档管理服务,可用于记录整个项目的来龙去脉。

三、开通 CODING DevOps

1、搜索 CODING DevOps

(1)腾讯云搜索 CODING-DevOps。

(2)进入界面。

2、开通账号

(1)第一次进入没有账号,去开通。

(2)产品授权。

(3)输入信息,邮箱验证。

3、创建团队

(1)输入团队名称提交。

(2)开通成功。

(3)点击立即使用,进入。

(4)进入工作台。

四、使用 CODING DevOps

1、创建项目

在 CODING DevOps 平台建立团队之后,团队内成员可按需创建项目。只有项目创建之后,项目成员才能按需使用项目协同代码仓库持续集成持续部署等功能。

1.1、点击创建项目

1.2、选择项目模板

1.3、填写项目基本信息

1.4、完成创建

2、项目协同
2.1、项目协同初始化

(1)前往初始化。

(2)配置并开启项目协同。

选择经典项目管理。

2.2、项目协同具体实现

(1)点击右上角创建迭代。

(2)填写迭代信息。

(3)点击创建并规划,创建需求。

(4)点击查看详情。

(5)设置迭代详情信息。

(6)效果。

3、代码仓库
3.1、添加仓库

(1)填写仓库信息。


(2)克隆仓库。

点击克隆。

3.2、克隆代码仓库到本地

(1)在 Git 客户端中输入克隆命令。

git clone <您克隆的代码仓库地址>

首次拉取后会提示填写凭据,此处填写在注册 CODING 时所使用的邮箱与密码即可。

命令操作提示成功之后,你可以在本地代码仓库中进行代码修改。

3.3、推送本地代码至 CODING 仓库

(1)在 Git 客户端,运行以下三条命令将代码推送到 CODING 平台上的代码仓库。

git add .
git commit -m "<您对本次提交备注的信息>"
git push git仓库地址

(2)创建提交文件。

(3)进入 cmd 窗口执行。

(4)查看所有的提交记录。

3.4、推送项目到代码仓库

(1)复制两个文件到项目目录。

(2)项目路径 cmd 窗口执行。

(3)查看效果。

4、持续集成
4.1、创建构建计划

4.2、选择类型

4.3、设置构建信息

4.4、修改流程配置

根据具体需要,自定义执行流程。

4.5、立即构建

4.6、构建完成

5、持续部署(绑定云账号)
5.1、输入账号名称

5.2、创建集群,开放外网 ip

(1)创建集群。

在容器服务中创建集群。

(2)开放外网。

5.3、复制凭证

(1)复制集群凭证。

(2)复制到云账户。

6、持续部署(Kubernetes)

6.1、创建部署

6.2、集群配置
6.2.1、集群配置

6.2.2、镜像配置

1、选择示例镜像用于测试使用。

2、可以选择 CODING Docker 仓库里面自己创建的镜像。

6.2.3、应用部署

6.3、等待发布

6.4、获取发布地址

6.5、访问测试

7、持续部署(腾讯云弹性伸缩)
7.1、创建应用

(1)在部署控制台创建应用,选择腾讯云弹性伸缩。

7.2、创建流程

(1)点击创建流程。

(2)选择流程。

(3)修改流程内容。

7.3、启动执行

7.4、集群查看

(1)执行成功后,集群中查看。

更多推荐

硅谷课堂

本文发布于:2024-02-07 02:10:47,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1752303.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:硅谷   课堂

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!