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

硅谷课堂-智慧星球 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
 *
 * 

* 课程 前端控制器 *

*/
@Tag(name = "课程接口", description = "课程管理接口") @RestController @RequestMapping("/admin/vod/course") public class CourseController { @Autowired private 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
 *
 * 

* 课程 服务实现类 *

*/
@Service public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService { /** * 查询所有课程 * * @return courseList 所有课程列表 */ @Override public 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、定义调用接口

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第1张图片

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,
    });
  },
};

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第2张图片

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、创建直播页面

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第3张图片

1.4.1、list.vue
<template>
  <div class="app-container">
    <head>
      
      <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>

    
    <el-table
      v-loading="listLoading"
      :data="list"
      stripe
      border
      style="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-tag
            v-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-button
            type="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-form
        ref="flashPromotionForm"
        label-width="150px"
        size="small"
        style="padding-right: 40px"
      >
        
        <el-form-item label="直播教师">
          <el-select v-model="liveCourse.teacherId" placeholder="请选择">
            <el-option
              v-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-picker
            v-model="liveCourse.startTime"
            type="datetime"
            placeholder="选择开始日期"
            value-format="yyyy-MM-dd HH:mm:ss"
          />
        el-form-item>
        <el-form-item label="直播结束时间">
          <el-date-picker
            v-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-dialog
      title="查看账号"
      :visible.sync="accountDialogVisible"
      width="60%"
    >
      <el-form
        ref="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"
                >https://live.zhibodun.com/live/courseLogin.php?course_id={{
                liveCourseAccount.courseId }}&role=adminspan
              >
            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"
                >https://live.zhibodun.com/live/courseLogin.php?course_id={{
                liveCourseAccount.courseId }}&role=adminspan
              >
            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"
                >http://smartplanetmobile.free.idcfengye.com/#/live/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: "https://img-blog.csdnimg.cn/b9f45932f6cf471aa53ee24e5555d9c2.png",
  };

  export default {
    data() {
      return {
        BASE_API: "http://localhost:8333",
        // BASE_API: "http://smartplanet.free.idcfengye.com",

        // 数据是否正在加载
        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(() => {
            // 点击确定,远程调用ajax
            return 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">
      <div
        style="
          background-color: #e0e0e0;
          width: 100%;
          padding: 1px 10px;
          margin: 10px 0;
        "
      >
        <h3>功能设置   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>

      <div
        style="
          background-color: #e0e0e0;
          width: 100%;
          padding: 1px 10px;
          margin: 10px 0;
        "
      >
        <h3>
          商品列表   
          <el-button type="" size="mini" @click="addCourse()"
            >添加 / 更新el-button
          >
        h3>
      div>
      <el-table
        v-loading="listLoading"
        :data="liveCourseConfigVo.liveCourseGoodsList"
        stripe
        border
        style="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-dialog
        title="添加 / 更新课程"
        :visible.sync="dialogVisible"
        width="50%"
      >
        <el-form
          :inline="true"
          label-width="150px"
          size="small"
          style="padding-right: 40px"
        >
          <el-table
            v-loading="listLoading"
            :data="courseList"
            stripe
            border
            style="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:
              "http://smartplanetmobile.free.idcfengye.com/#/course/info/" +
              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、用户观看端集成

接口文档:https://open.talk-fun.com/docs/js/index.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
{
    @Resource
    private 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
 *
 * 

* 直播课程 服务类 *

*/
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
 *
 * 

* 直播课程 服务实现类 *

*/
@Service public class LiveCourseServiceImpl extends ServiceImpl<LiveCourseMapper, LiveCourse> implements LiveCourseService { @Autowired private UserInfoFeignClient userInfoFeignClient; @Autowired private MTCloud mtCloudClient; /** * 获取用户 access_token * * @param id id 直播课程 id * @param userId 用户 id * @return accessToken 访问令牌 */ @SneakyThrows @Override public 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

下载地址:https://open.talk-fun.com/docs/js/download.html

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第4张图片

1.3、使用快捷模板

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第5张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第6张图片

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

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第7张图片

DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
    />
    <title>TalkFun Vod QuickStart - v2title>
    <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>
    
    
    <script
      type="text/javascript"
      src="https://static-1.talk-fun.com/open/TalkFun_SDK_Pack/v7.0/TalkFunWebSDK-7.7.min.js"
    >script>
  head>

  <body>
    
    <div class="wrapper">
      
      <div id="videoPlayer">
        <p id="loadplayer">p>
      div>
    div>

    <script>
      // [第一步] 如何获取 access_token => http://open.talk-fun.com/docs/getstartV2/access_token.html
      // [第二步] 根据Api文档方法 监听 / 调用方法 JS Api => http://open.talk-fun.com/docs/js/sdk.js.getstart.html
      var url = window.location.search;
      var token = url.split("=")[1];

      // 更多配置项 => https://open.talk-fun.com/docs/js/sdk.js.getstart.html?h=%E9%85%8D%E7%BD%AE%E9%A1%B9

      var HT = new MT.SDK.main(
        token,
        {
          config: {
            // Safari 浏览器建议设置为 HLS
            techOrder: "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>
      
      <meta name="referrer" content="no-referrer" />
    head>

    <van-image
      width="100%"
      height="200"
      src="https://img-blog.csdnimg.cn/b9f45932f6cf471aa53ee24e5555d9c2.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
{
    @Resource
    private 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
 *
 * 

* 直播课程 服务实现类 *

*/
@Service public class LiveCourseServiceImpl extends ServiceImpl<LiveCourseMapper, LiveCourse> implements LiveCourseService { @Autowired private CourseFeignClient teacherFeignClient; @Autowired private LiveCourseDescriptionService liveCourseDescriptionService; /** * 根据直播课程 id 查询直播课程信息 * * @param liveCourseId 直播课程 id * @return liveCourseInfo 直播课程信息 */ @Override public 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、微信分享实现方式

参考文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

2.1、绑定域名

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

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第8张图片

2.2、公众号测试号配置

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第9张图片

2.3、引入 JS 文件
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,,chrome=1" />
    <meta
      name="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" />

    <script
      src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"
      type="text/javascript"
    >script>

    
    <link
      href="https://web.sdk.qcloud.com/player/tcplayer/release/v4.7.2/tcplayer.min.css"
      rel="stylesheet"
    />

    
    <script src="https://web.sdk.qcloud.com/player/tcplayer/release/v4.7.2/libs/hls.min.1.1.6.js">script>

    
    <script src="https://web.sdk.qcloud.com/player/tcplayer/release/v4.7.2/libs/flv.min.1.6.3.js">script>

    
    <script src="https://web.sdk.qcloud.com/player/tcplayer/release/v4.7.2/libs/dash.all.min.4.5.2.js">script>

    
    <script src="https://web.sdk.qcloud.com/player/tcplayer/release/v4.7.2/tcplayer.v4.7.2.min.js">script>
  head>

  <body>
    <script
      type="text/javascript"
      src="https://static-1.talk-fun.com/open/TalkFun_SDK_Pack/v7.0/TalkFunWebSDK-7.7.min.js"
    >script>

    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
        properly without JavaScript enabled. Please enable it to
        continue.strong
      >
    noscript>
    <div id="app">div>
    
  body>
html>

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第10张图片

2.4、封装分享 js

参考官方文档封装接口。

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

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

/**
 * 微信 js-sdk
 * 参考文档:https://mp.weixin.qq.com/wiki?t=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,
      // 必填,签名,见附录 1
      signature: data.signature,
      // 必填,需要使用的 JS 接口列表,所有 JS 接口列表见附录 2
      jsApiList: ["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.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.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
{
    @Autowired
    private 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'"
          plain
          type="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"
        >
          <ul
            class="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、原始部署方式

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第11张图片

2、整合 Jenkins

完整 DevOps 示例如下:

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第12张图片

3、整合 CODING

整合 CODING 实现 DevOps:

https://console.cloud.tencent.com/coding/container-devops

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第13张图片

二、腾讯云 CODING DevOps 概述

腾讯云使用文档:

https://help.coding.net/docs/start/new.html

1、产品简介

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第14张图片

1.1、CODING DevOps 是什么

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

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

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

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

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

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

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

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

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

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

1.3、CODING DevOps 功能特性

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

团队级功能:

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

项目级功能:

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

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

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

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

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

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

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

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

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

2、使用流程概述

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第17张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第18张图片

2.1、创建或加入团队

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第19张图片

2.2、新建项目

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第20张图片

2.3、开始项目协同

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第21张图片

2.4、使用代码仓库

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第22张图片

2.5、启动代码扫描

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第23张图片

2.6、编译构建

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第24张图片

2.7、管理制品

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第25张图片

2.8、实施持续部署

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第26张图片

2.9、管理测试用例

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第27张图片

2.10、管理项目文档

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

三、开通 CODING DevOps

1、搜索 CODING DevOps

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第28张图片

(2)进入界面。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第29张图片

2、开通账号

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第30张图片

(2)产品授权。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第31张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第32张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第33张图片

3、创建团队

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第34张图片

(2)开通成功。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第35张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第36张图片

(4)进入工作台。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第37张图片

四、使用 CODING DevOps

1、创建项目

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

1.1、点击创建项目

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第38张图片

1.2、选择项目模板

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第39张图片

1.3、填写项目基本信息

填写项目基本信息

1.4、完成创建

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第40张图片

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

(1)前往初始化。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第41张图片

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

选择经典项目管理。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第42张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第43张图片

2.2、项目协同具体实现

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第44张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第45张图片

(2)填写迭代信息。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第46张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第47张图片

(4)点击查看详情。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第48张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第49张图片

(6)效果。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第50张图片

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

添加仓库

(1)填写仓库信息。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第51张图片


(2)克隆仓库。

点击克隆。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第52张图片

3.2、克隆代码仓库到本地

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

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

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第53张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第54张图片

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

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

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

(2)创建提交文件。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第55张图片

(3)进入 cmd 窗口执行。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第56张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第57张图片

3.4、推送项目到代码仓库

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第58张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第59张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第60张图片

项目路径 cmd 窗口执行

项目路径 cmd 窗口执行

(3)查看效果。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第61张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第62张图片

4.2、选择类型

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第63张图片

4.3、设置构建信息

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第64张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第65张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第66张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第67张图片

4.4、修改流程配置

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第68张图片

4.5、立即构建

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第69张图片

4.6、构建完成

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第70张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第71张图片

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

(1)创建集群。

在容器服务中创建集群。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第72张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第73张图片

(2)开放外网。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第74张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第75张图片

5.3、复制凭证

(1)复制集群凭证。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第76张图片

(2)复制到云账户。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第77张图片

6、持续部署(Kubernetes)

持续部署(Kubernetes)

6.1、创建部署

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第78张图片

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第79张图片

6.2.2、镜像配置

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

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第80张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第81张图片

6.2.3、应用部署

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第82张图片

6.3、等待发布

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第83张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第84张图片

6.4、获取发布地址

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第85张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第86张图片

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第87张图片

6.5、访问测试

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第88张图片

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

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第89张图片

7.2、创建流程

(1)点击创建流程。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第90张图片

(2)选择流程。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第91张图片

(3)修改流程内容。

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第92张图片

7.3、启动执行

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第93张图片

7.4、集群查看

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

硅谷课堂-智慧星球 Day 15~Day 16——尚硅谷项目笔记 2022 年_第94张图片

你可能感兴趣的:(项目笔记,Spring,Cloud,Spring,Boot)