2022-05-28  2022-05-28    11693 字   24 分钟

第五章

优学题库项目开发3

优就业.JAVA教研室

学习目标

  • 掌握OSS云存储、账号开通、上传测试
  • 掌握图片上传到OSS云存储
  • 掌握开发广告内容前端维护功能
  • 掌握开发用户管理前端维护功能
  • 掌握微信小程序登录及验证调用接口
  • 掌握开发微信小程序分类、题库、广告轮播图调用接口
  • 了解接口文档生成方式

1、OSS云存储介绍,开通、上传测试

文件上传在系统中用的很频繁,所以我们需要将上传的文件进行存储,传统的将文件上传到本机已不适用分布式系统。自己搭建文件服务器有复杂性和维护成本。所以我们可以采用市面上成熟的文件存储服务,如阿里云的OSS对象存储服务。

1.1 登录阿里云开通OSS存储

产品->精选->对象存储OSS 立即开通 需要实名认证后才能开通

1.2、创建Bucket

点击创建Bucket按钮,弹出创建Bucket界面

特别强调这里的读写权限一定设置为“公共的”。

新建文件夹[pic,video],点击目录可以上传图片,并可以访问

1.3、accesskey申请

管理控制台,右上角,点击用户头像,弹出菜单:

选择:AccessKey管理,弹出提示,选择: 开始使用子用户 AccessKey

点击: 开始使用子用户AccessKey

出现创建用户界面:输入登录用户名、显示用户名,勾选 编程访问,点击 确定即可创建用户。

l 选择左边菜单的用户连接,进入用户列表

l 选择用户,点击左下角的 添加权限按钮,即可进入设置权限页面

点击确定,出现授权成功提示

l 选择用户,设置用户AccessKey

点击用户列表,中的用户名,进入用户设置界面

l 点击创建AccessKey,弹出Accesskey创建成功页面,复制并保存好相关秘钥

1.4、引入sdk

https://help.aliyun.com/document_detail/32009.html

SDK依赖:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.8.0</version>
</dependency>

1.5、java编程方式上传文件到oss

package com.offcn.utils;

public class OSSTest {

    /**
     * OSS 使用步骤 阿里云
     * 1)、引入SDK
     * 2)、配置好相应的属性
     */
    public static void main(String[] args)throws IOException {
        // Endpoint以北京为例,其它Region请按实际情况填写。
        String endpoint = "http://oss-cn-beijing.aliyuncs.com";
        // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
        String accessKeyId = "LTAI4F***33p318BifWc";
        String accessKeySecret = "epNR5QdCC44***bwW4FW8P3UEW";
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        // 上传文件流。
        InputStream inputStream = new FileInputStream(new File("D:\\007.jpg"));
//要存放的存储桶名称,存储文件名 ,文件输入流
        ossClient.putObject("offcn20200330", "pic/008.jpg", inputStream);
        // 关闭OSSClient。
        ossClient.shutdown();

        System.out.println("测试完成");
    }
}

执行上传程序成功,查看oss服务器端

访问图片地址:

Bucket+域名 + 图片路径 + 文件名

https://offcn20200330.oss-cn-beijing.aliyuncs.com/pic/008.jpg

**

2、实现图片上传到oss云存储

2.1、上传原理

2.1.1、通过应用服务器中转上传

每个 OSS 的用户都会用到上传服务。Web 端常见的上传方法是用户在浏览器或 APP 端上传文件到应用服务器,应用服务器再把文件上传到 OSS。具体流程如下图所示。

和数据直传到 OSS 相比,以上方法有三个缺点:

上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,可以节省应用服务器

2.1.2、直接javaScript客户端上传到oss

采用JavaScript客户端直接签名(参见JavaScript客户端签名直传)时,AccessKeyID和AcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OSS提供了服务端签名后直传的方案。

服务器端获取签名,客户端直接上传到oss原理

服务端签名后直传的原理如下:

  1. 用户发送上传Policy请求到应用服务器。

  2. 应用服务器返回上传Policy和签名给用户。

  3. 用户直接上传数据到OSS。

    参考官方文档:https://help.aliyun.com/document_detail/31926.htm

2.2、搭建获取oss签名微服务

2.2.1、创建新模块u-oss(标准springboot工程)

最终创建好模块

2.2.2、pom.xml引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>u-oss</artifactId>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.0.RELEASE</spring-cloud-alibaba.version>
    </properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
</dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

2.2.3、配置文件application.yml

server:
  port: 14000
spring:
  application:
    name: u-oss
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    alicloud:
      access-key: LTAI4G7***xizdRu
      secret-key: aSHoxLMB&&&&GFSeb5nsLmJIYTwf
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: java0817-001

logging:
  level:
    com.offcn.oss: debug

14.2.4、编写获取oss服务器签名OssController

package com.offcn.oss.controller;

import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

@RestController
@RequestMapping("/thirdparty/v1/admin/oss")
public class OssController {

    @Autowired
    OSS ossClient;

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;

    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;

    @RequestMapping("/getPolicy")
    public Map<String, String> getPolicy() {
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        String formatDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = formatDate + "/"; // 用户上传文件时指定的前缀。
        Map<String, String> respMap = new LinkedHashMap<String, String>();
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
//PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            //获取授权策略
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            //获取授权策略签名
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            //封装accessid、签名等信息返回
            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));


        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        } finally {
            ossClient.shutdown();
        }

        return respMap;
    }
}

2.2.4、编写主启动类

@SpringBootApplication
@EnableDiscoveryClient
public class OssStartApplication {
    public static void main(String[] args) {
        SpringApplication.run(OssStartApplication.class,args);
    }
}

2.2.5、启动服务,访问controller获取签名

访问地址:http://localhost:14000/thirdparty/v1/admin/oss/getPolicy

2.3、修改网关配置获取签名路由

打开java服务cloud-gateway,修改配置文件application.yml

增加到获取签名微服务的路由,注意配置顺序要在renren-fast 之前

        - id: oss-route # oss服务路由
          uri: lb://u-oss
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

http://localhost:8888/api/thirdparty/v1/admin/oss/getPolicy

2.4、开发前端工程,配置上传

2.4.1、新建获取签名的js

在前端工程renren-fast-vue目录src/utils/ 新建获取签名js文件 policy.js

import http from '@/utils/httpRequest.js'
export function policy () {
  return new Promise((resolve) => {
    http({
      url: http.adornUrl('/thirdparty/v1/admin/oss/getPolicy'),
      method: 'get',
      params: http.adornParams({})
    }).then(({ data }) => {
      resolve(data)
    })
  })
}

2.4.2、新建文件上传控件页面

在前端工程renren-fast-vue目录src/views/modules/common目录下 创建上传页面 singleUpload.vue

注意:**修改下面 action属性 ** 指向oss存储桶地址:http://java0817-001.oss-cn-guangzhou.aliyuncs.com!

<template> 
  <div>
    <el-upload
      action="http://java0817-001.oss-cn-guangzhou.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :auto-upload="true"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件且不超过10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
  </div>
</template>
<script>
   import {policy} from '@/utils/policy'
   import { getUUID } from '@/utils'

  export default {
    name: 'singleUpload',
    props: {
      value: String
    },
    computed: {
      imageUrl() {
        return this.value;
      },
      imageName() {
        if (this.value != null && this.value !== '') {
          return this.value.substr(this.value.lastIndexOf("/") + 1);
        } else {
          return null;
        }
      },
      fileList() {
        return [{
          name: this.imageName,
          url: this.imageUrl
        }]
      },
      showFileList: {
        get: function () {
          return this.value !== null && this.value !== ''&& this.value!==undefined;
        },
        set: function (newValue) {
        }
      }
    },
    data() {
      return {
        dataObj: {
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: '',
          // callback:'',
        },
        dialogVisible: false
      };
    },
    methods: {
      emitInput(val) {
        this.$emit('input', val)
      },
      handleRemove(file, fileList) {
        this.emitInput('');
      },
      handlePreview(file) {
        this.dialogVisible = true;
      },
      beforeUpload(file) {
        let _self = this;
        return new Promise((resolve, reject) => {
          policy().then(response => {
            // console.log(response)
            _self.dataObj.policy = response.policy;
            _self.dataObj.signature = response.signature;
            _self.dataObj.ossaccessKeyId = response.accessid;
            _self.dataObj.key = response.dir + getUUID()+'_${filename}';
            _self.dataObj.dir = response.dir;
            _self.dataObj.host = response.host;           
            resolve(true)
          }).catch(err => {
            console.log(err)
            reject(false)
          })
        })
      },
      handleUploadSuccess(res, file) {
        console.log("上传成功...")
        this.showFileList = true;
        this.fileList.pop();
        this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
        this.emitInput(this.fileList[0].url);
      }
    }
  }
</script>
<style>

</style>

2.4.3、修改题目分类新增页面,增加上传控件

在前端工程renren-fast-vue目录src/views/modules/question目录下 修改页面 type-add-or-update.vue

引入上传控件

import SingleUpload from "../common/singleUpload" // 引入单文件上传组件

注册上传组件:

export default {
    components:{ SingleUpload }
},

修改分类logo输入框,改成上传按钮

<el-form-item label="类型logo路径" prop="logoUrl">
     <single-upload v-model="dataForm.logoUrl"></single-upload>
</el-form-item>

2.4.4、测试上传

点击上传 按钮,选择要上传的图片

上传图片成功:

注意:oss服务器存储桶一定要设置允许跨域

配置跨域访问

  1. 登录OSS管理控制台
  2. 单击Bucket列表,之后单击目标Bucket名称。
  3. 单击权限管理 > 跨域设置,在跨域设置区域单击设置
  4. 单击创建规则,配置如下图所示。

3、开发广告内容前端维护管理

3.1、添加广告内容管理目录(一级菜单)

选择管理界面–系统管理–菜单管理,点击 新增按钮

3.2、添加广告轮播图管理菜单(二级菜单)

3.3、添加广告资讯管理菜单(二级菜单)

3.4、生成广告前端页面

用renren-generator自动生成前端代码

注意配置数据库连接文件application.yml,指向数据库 uxue_cms

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/uxue_cms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root

修改模板生成属性配置文件generator.properties

mainPath=com.offcn
#\u5305\u540D
package=com.offcn
moduleName=context
#\u4F5C\u8005
author=sunny
#Email
email=hk109@126.com
#\u8868\u524D\u7F00(\u7C7B\u540D\u4E0D\u4F1A\u5305\u542B\u8868\u524D\u7F00)
tablePrefix=cms_

启动renren-generator

3.5、拷贝前端代码到 前端工程renren-fast-vue

拷贝\main\resources\src\views\modules\context目录到前端目录 renren-fast-vue\src\views\modules

3.6、配置网关转发

找到cloud-gateway,修改配置文件application.yml

- id: cms-route # 广告微服务路由
  uri: lb://u-context
  predicates:
       - Path=/api/context/**
  filters:
       - RewritePath=/api/(?<segment>.*),/$\{segment}        

3.7、测试广告轮播图、广告资讯基本管理

刷新页面,点击菜单 广告内容管理—》广告轮播图

点击菜单 广告内容管理—》广告资讯

分别测试增删改

注意测试新增前,先把日期时间字段改成不是必须输入

修改banner-add-or-update.vue、news-add-or-update.vue

createTime: [
            { required: false, message: '创建时间不能为空', trigger: 'blur' }
          ],
          updateTime: [
            { required: false, message: '更新时间不能为空', trigger: 'blur' }
          ]

3.8、修改广告轮播图广告配图上传

在前端工程renren-fast-vue目录src/views/modules/context目录下 修改页面 banner-add-or-update.vue

引入上传控件

import SingleUpload from "../common/singleUpload" // 引入单文件上传组件

注册上传组件:

export default {
    components:{ SingleUpload }
}

修改图片路径输入框,改成上传按钮

<el-form-item label="图片路径" prop="imgUrl">      
      <single-upload v-model="dataForm.imgUrl"></single-upload>
</el-form-item>

实际运行效果:

3.9、修改广告资讯广告配图上传

在前端工程renren-fast-vue目录src/views/modules/context目录下 修改页面 news-add-or-update.vue

具体修改方式和广告轮播相同

4、开发用户管理前端维护管理

4.1、添加用户管理目录(一级菜单)

选择管理界面–系统管理–菜单管理,点击 新增按钮

4.2、添加用户列表管理菜单(二级菜单)

4.3、添加用户充值记录管理菜单(二级菜单)

4.4、生成用户服务相关前端页面

用renren-generator自动生成前端代码

注意配置数据库连接文件application.yml,指向数据库 uxue_cms

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    #MySQL配置
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/uxue_ums?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root

修改模板生成属性配置文件generator.properties

mainPath=com.offcn
#\u5305\u540D
package=com.offcn
moduleName=member
#\u4F5C\u8005
author=sunny
#Email
email=hk109@126.com
#\u8868\u524D\u7F00(\u7C7B\u540D\u4E0D\u4F1A\u5305\u542B\u8868\u524D\u7F00)
tablePrefix=ums_

启动renren-generator

4.5、拷贝前端代码到 前端工程renren-fast-vue

拷贝\main\resources\src\views\modules\member目录到前端目录 renren-fast-vue\src\views\modules

4.6、配置网关转发

找到cloud-gateway,修改配置文件application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: http://localhost:8848
    gateway:
      routes:        
        - id: ums-route # 用户微服务路由
          uri: lb://u-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}       

4.7、测试用户列表、充值记录基本管理

刷新页面,点击菜单用户管理—》用户列表

点击菜单用户管理—》用户充值记录

分别测试增删改

注意测试新增前,先把日期时间字段改成不是必须输入

修改member-add-or-update.vue、growthchangehistory-add-or-update.vue

createTime: [
            { required: false, message: '创建时间不能为空', trigger: 'blur' }
          ],
          updateTime: [
            { required: false, message: '更新时间不能为空', trigger: 'blur' }
          ]

4.8.用户注册数量统计分析

4.8.1 编写java后端代码

打开java模块u-member ,编辑接口MemberService新增统计注册数量方法

public interface MemberService extends IService<MemberEntity> {
  

    public List<Map<String, Object>>  countByDateTime(String beginTime, String endTime);
}

编辑接口实现类MemberServiceImpl,编写统计注册数量实现方法

@Autowired
    private MemberDao memberDao;
     @Override
    public List<Map<String, Object>> countByDateTime(String beginTime, String endTime) {
        QueryWrapper<MemberEntity> queryWrapper = new QueryWrapper<MemberEntity>().select("DATE_FORMAT(create_time,'%Y-%m-%d') AS NAME ,COUNT(id) AS VALUE").between("create_time", beginTime+" 00:00:00", endTime+" 23:59:59").groupBy("NAME");

        List<Map<String, Object>> mapList = memberDao.selectMaps(queryWrapper);

        return mapList;
    }

编辑MemberController,新增统计注册数量方法

 //根据开始时间,结束时间,统计每日注册账号
    @RequestMapping("countAccountCreate")
    public R countAccountCreate(String beginTime,String endTime){
        List<Map<String, Object>> mapList = memberService.countByDateTime(beginTime, endTime);
        return R.ok().put("mapList",mapList);
    }

4.8.2 编写前端代码

前端项目renren-fast-vue/src/views/modules/member目录 新增统计注册用户数量页面 echarts.vue

<template> 
  <div class="mod-demo-echarts">   
    <!--表单-->
    <el-form :inline="true" class="demo-form-inline">
      <el-form-item label="开始日期">
        <el-date-picker
          v-model="beginTime"
          type="date"
          placeholder="选择要统计的开始日期"
          value-format="yyyy-MM-dd" />
      </el-form-item>
        <el-form-item label="结束日期">
        <el-date-picker
          v-model="endTime"
          type="date"
          placeholder="选择要统计的结束日期"
          value-format="yyyy-MM-dd" />
      </el-form-item>
      <el-button
        :disabled="btnDisabled"
        type="primary"
        @click="create()">生成</el-button>
    </el-form>
    <el-row :gutter="20">
      
      <el-col :span="24">
        <el-card>
          <div id="J_chartBarBox" class="chart-box"></div>
        </el-card>
      </el-col>      
    </el-row>
  </div>
</template>

<script>
  import echarts from 'echarts'
  export default {
    data () {
      return {       
        chartBar: null,
         beginTime: '',
         endTime: '',
         mapList: [],
         xData: [],
         yData: [],
         btnDisabled: false
      }
    },
    mounted () {     
         
    },
    activated () {
      // 由于给echart添加了resize事件, 在组件激活时需要重新resize绘画一次, 否则出现空白bug     
      if (this.chartBar) {
        this.chartBar.resize()
      }     
    },
    methods: {      
      create () {
         this. getDataList ()
         this.initChartBar()  
      },
       // 获取数据列表
      getDataList () {
        this.dataListLoading = true
        this.$http({
          url: this.$http.adornUrl('/member/member/countAccountCreate'),
          method: 'get',
          params: this.$http.adornParams({
            'beginTime': this.beginTime,
            'endTime': this.endTime
          })
        }).then(({data}) => {
          if (data && data.code === 0) {            
            this.mapList = data.mapList   
            this.xData = []
            this.yData = []         
            for(let j in this.mapList){
            this.xData.push(this.mapList[j].NAME)
            this.yData.push(this.mapList[j].VALUE)
            }
             console.log(this.xData)
            console.log(this.yData)
            this.chartBar.resize()
          // this.xData= this.mapList.map(obj => n.NAME);       
          // this.yData= this.mapList.map(obj => n.VALUE);  
          this.chartBar.setOption({
             xAxis: [
            {
              type: 'category',
              data: this.xData
            }
          ],
          yAxis: [
            {
              type: 'value'
            }
          ],
          series: [
            {
              name: '每日注册用户数',
              type: 'line',
              data: this.yData
            }
          ]
          })
          } else {
            this.mapList = []           
          }        
        })
      },
      // 柱状图
      initChartBar () {

        var option = {
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'shadow'
            }
          },
          legend: {
            data: ['每日注册用户数']
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true
          },
          xAxis: [
            {
              type: 'category',
              data: this.xData
            }
          ],
          yAxis: [
            {
              type: 'value'
            }
          ],
          series: [
            {
              name: '每日注册用户数',
              type: 'bar',
              data: this.yData
            }
          ]
        }
        this.chartBar = echarts.init(document.getElementById('J_chartBarBox'))
       // this.chartBar.setOption(option)
        window.addEventListener('resize', () => {
          this.chartBar.resize()
        })
      }     
    }
  }
</script>

<style lang="scss">
  .mod-demo-echarts {
    > .el-alert {
      margin-bottom: 10px;
    }
    > .el-row {
      margin-top: -10px;
      margin-bottom: -10px;
      .el-col {
        padding-top: 10px;
        padding-bottom: 10px;
      }
    }
    .chart-box {
      min-height: 400px;
    }
  }
</style>

4.8.3 配置用户注册统计菜单

新增菜单后,刷新,即可查看菜单

选中开始日期,结束日期,点击 生成按钮 出现统计折线图

5、开发微信小程序登录及验证调用接口

5.1.编写JWT工具类

5.1.1 打开模块u-common,编辑pom.xml

引入jwt依赖库

<dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
</dependency>

5.1.2 JWT工具类 JWTUtil

package com.offcn.common.utils;

import io.jsonwebtoken.*;

import java.util.Date;

public class JWTUtil {
    private static final String SECRET_KEY = "u-member-offcn123"; //秘钥
    public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token过期时间
    public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken过期时间
    private static final String ISSUER = "offcn"; //签发人

    //生成签名
    public static String generateToken(String username){
        Date now = new Date();
       return   Jwts.builder()
             .setIssuer(ISSUER) //签发人
             .setIssuedAt(now)//签发时间
             .setExpiration(new Date(now.getTime()+TOKEN_EXPIRE_TIME))//过期时间
             .claim("username",username)//用户名
             .signWith(SignatureAlgorithm.HS256,SECRET_KEY)//签名算法、秘钥
             .compact();
    }

    //解析token
    private static Claims parseToken(String token){
     return    Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token).getBody();
    }

    //验证签名
    public static boolean verify(String token){
       //尝试解析token
        try {
            Claims claims = parseToken(token);
           return true;

        } catch (Exception e) {
           // e.printStackTrace();
            System.out.println("签名验证失败");
            return false;
        }
    }

    //从token获取用户名
    public static String getUsername(String token){
        try {
            Claims claims = parseToken(token);
            String username = (String) claims.get("username");
            return username;
        } catch (Exception e) {
           // e.printStackTrace();
            System.out.println("解析令牌失败");
            return "";
        }
    }
}

5.2.编写用户登录接口

打开模块u-member,MemberService增加登录接口

public interface MemberService extends IService<MemberEntity> {
   

    public MemberEntity login(String username,String password);
}

打开模块u-member,MemberServiceImpl增加登录实现方法

@Override
public MemberEntity login(String username, String password) {
        MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>().eq("user_name", username).eq("PASSWORD", password));
        return memberEntity;
}

5.3.编写用户登录、刷新token控制器

u-member添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在memberController

@Autowired
    private StringRedisTemplate stringRedisTemplate;

//登录
    @PostMapping("/login")
    public R login(String username,String password){
        MemberEntity memberEntity = memberService.login(username, password);

        if(memberEntity!=null) {
            String token = JWTUtil.generateToken(memberEntity.getUserName());
            //生成refreshToken
            String refreshToken = UUID.randomUUID().toString().replace("-", "");
            stringRedisTemplate.opsForHash().put(refreshToken, "token", token);
            stringRedisTemplate.opsForHash().put(refreshToken, "username", username);
            //设置token的过期时间
            stringRedisTemplate.expire(refreshToken, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);

            return R.ok().put("token",token).put("refreshToken",refreshToken);

        }else {
            return R.error("账号密码错误");

        }


    }

//刷新token
    @PostMapping("/refreshtoken")
    public R refreshToken(String refreshToken){
        //根据refreshToken从redis读取用户名
       String username= (String) stringRedisTemplate.boundHashOps(refreshToken).get("username");

       if(StringUtils.isEmpty(username)){
           return R.error("刷新token失败");
       }
       //根据用户名生成新的token
        String token = JWTUtil.generateToken(username);
        //更新token到redis
        stringRedisTemplate.boundHashOps(refreshToken).put("token",token);
          //设置token的过期时间
            stringRedisTemplate.expire(refreshToken, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        return R.ok().put("token",token).put("refreshToken",refreshToken);
    }
redis:
    host: localhost
    port: 6379

5.4.测试用户登录、刷新token

登录地址:http://localhost:10002/member/member/login

传递参数: username 用户账号 password 密码

刷新token地址:http://localhost:10002/member/member/refreshtoken

传递参数:refreshToken

返回结果:

5.5、配置网关路由转发

修改cloud-gateway模块,配置文件application.yml增加登录路由转发

        - id: weixin-login-ums-route # 提供微信客户端调用的,用户微服务路由
          uri: lb://u-member
          predicates:
            - Path=/member/member/login/**          
        - id: weixin-register-ums-route # 提供微信客户端调用的,用户微服务路由
          uri: lb://u-member
          predicates:
            - Path=/member/member/save/**          

测试网关访问地址:

http://localhost:8888/member/member/login?username=test&password=123

响应:

5.6、用户注册接口

http://localhost:8888/member/member/save

请求参数:

{
  "miniOpenid": "1001",
  "mpOpenid": "8877111",
  "unionid": "test001",
  "levelId": 1,
  "userName": "test003",
  "password": "456",
  "nickname": "NickName",
  "phone": "17562128765",
  "email": "hk109@139.com",
  "avatar": "1.jpg",
  "gender": 0,
  "city": "北京",
  "birth": "1978-09-11",
  "sourceType": 1,
  "integration": 1
}

5.7、配置网关过滤器,拦截微信客户端请求验证token

5.7.1、修改pom.xml引入common依赖,排除不需要的mybatis-plus-boot-starter依赖

 <dependency>
            <groupId>com.offcn</groupId>
            <artifactId>u-common</artifactId>
            <version>1.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                    <groupId>com.baomidou</groupId>
                </exclusion>
            </exclusions>
</dependency>

5.7.2、编写网关过滤器类

编写自定义过滤器实现类:InnerFilter

package com.offcn.filter;

import com.offcn.common.utils.JWTUtil;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class InnerFilter implements GatewayFilter, Ordered {
    private JwtCheckGatewayFilterFactory.Config config;
    InnerFilter(JwtCheckGatewayFilterFactory.Config config){
        this.config=config;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //尝试从请求头获取令牌
        String token = request.getHeaders().getFirst("Authorization");
        //判断令牌如果不为空
        if(!StringUtils.isEmpty(token)){
            //调用令牌工具类,校验令牌
            boolean verify = JWTUtil.verify(token);
            //判断令牌校验失败
            if(!verify){
                //令牌校验失败,结束转发
                Mono mono = returnFail(response);
                return    response.writeWith(mono);
            }else {
                //令牌校验成功,放行
                return     chain.filter(exchange);
            }
        }else {
            //令牌不存在,结束转发
            Mono mono = returnFail(response);
            return   response.writeWith(mono);
        }
    }

    //返回失败方法
    private Mono returnFail(ServerHttpResponse response){
        //获取响应对象头
        HttpHeaders headers = response.getHeaders();
        //设置响应头内容Content-Type 响应内容类型
        headers.add("Content-Type","application/json; charset=UTF-8");
        //禁止缓存
        headers.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
        //设置响应内容
        String body="token验证失败";
        DataBuffer dataBuffer = response.bufferFactory().wrap(body.getBytes());
        //把内容写入到响应对象
        return Mono.just(dataBuffer);
    }
    @Override
    public int getOrder() {
        return 1;
    }
}

编写自定义网关过滤器

package com.offcn.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;


public class JwtCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<JwtCheckGatewayFilterFactory.Config> {
   //要向父类传递配置类,要不然就会报类型转换错误
    public JwtCheckGatewayFilterFactory() {
        super(Config.class);
    }
    @Override
    public GatewayFilter apply(Config config) {
        return new InnerFilter(config);
    }


    //定义一个内部类,配置过滤器属性,必须是静态的
    public static class Config{

    }
}

5.7.3、配置网关过滤器

package com.offcn.config;

import com.offcn.filter.JwtCheckGatewayFilterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class GatewayCorsConfiguration {   

    @Bean
    public JwtCheckGatewayFilterFactory jwtCheckGatewayFilterFactory(){
        return new JwtCheckGatewayFilterFactory();
    }
}

5.7.4、修改网关配置

spring:
  cloud:
    nacos:
      discovery:
        server-addr: http://localhost:8848
    gateway:
      routes:
        - id: weixin-login-ums-route # 提供微信客户端调用的,用户微服务路由
          uri: lb://u-member
          predicates:
            - Path=/member/member/login/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
        - id: weixin-test-ums-route # 提供微信客户端调用的,用户微服务路由
          uri: lb://u-member
          predicates:
            - Path=/member/member/refreshtoken/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
            - JwtCheck

刷新token接口,启用token校验过滤器,填上过滤器前缀就可以 JwtCheck

5.7.5、测试

访问登录接口地址:http://localhost:8888/member/member/login?username=test&password=123

不需要token验证。

访问刷新token接口地址:http://localhost:8888/member/member/refreshtoken?refreshToken=4257e35399db461ca0bfac3b6b3a61f9

在请求头不带参数,提示toekn验证失败

在请求头带上参数 Authorization

6、开发微信小程序分类、及题库、广告轮播图读取调用接口

6.1.获取题库分类接口

6.1.1、修改网关路由配置添加获取题库分类接口路由

修改application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: http://localhost:8848
    gateway:
      routes:
        - id: weixin-question-route # 提供微信客户端调用的,题库微服务路由
          uri: lb://u-question
          predicates:
            - Path=/question/**
          filters:
            - RewritePath=/(?<segment>.*),/$\{segment}
            - JwtCheck

访问路径:http://localhost:8888/question/type/findall

6.2.获取题库数据接口

6.2.1.获取根据分类获取题库列表数据接口

访问路径:http://localhost:8888/question/question/list

可以传递参数:key 等于题库分类编号

http://localhost:8888/question/question/list?key=6

6.3.获取根据实体编号获取题目详情数据接口

http://localhost:8888/question/question/info/50

6.4.获取广告轮播图数据接口

6.4.1.获取广告轮播图列表数据接口

 - id: weixin-context-route # 提供微信客户端调用的,用户微服务路由
   uri: lb://u-context
   predicates:
     - Path=/context/**
   filters:
     - RewritePath=/(?<segment>.*),/$\{segment}
     - JwtCheck

访问路径:http://localhost:8888/context/banner/list?page=1&limit=5

7、接口文档

7.1、用户注册

<a id=用户注册> </a>

基本信息

Path: /member/member/save

Method: POST

接口描述:


Request

Headers

参数名称 参数值 是否必须 示例 备注
Content-Type application/json

Body

{
  "miniOpenid": "1001", //小程序openid
  "mpOpenid": "8877111", //服务号openid
  "unionid": "test001", //微信unionid
  "levelId": 1, //会员等级id 整数
  "userName": "test003", //用户登录账号
  "password": "456", //用户密码
  "nickname": "NickName",//用户昵称
  "phone": "17562128765", //用户手机号
  "email": "hk109@139.com",//用户邮箱
  "avatar": "1.jpg",//用户头像
  "gender": 0, //性别 0 女 1男
  "city": "北京", //用户地址
  "birth": "1978-09-11",//用户生日
  "sourceType": 1,//来源 1 微信 2 app
  "integration": 1 //积分,初始化注册积分为1
}

Reponse

{
  "msg": "success", //操作结果
  "code": 0  //状态码 0 成功
}

7.2、用户登录

<a id=用户登录> </a>

基本信息

Path: /member/member/login

Method: GET

接口描述:


Request

Query

参数名称 是否必须 示例 备注
username test001 用户账号
password 123 密码

Reponse

{
    "msg": "success",//响应成功消息
    "code": 0,//响应状态码 0 成功
    //token ,注意后续访问 获取分类、题库数据接口 需要鉴权 请求头里面带 token
    //请求头参数 Authorization  token值
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJvZmZjbiIsImV4cCI6MTYwODY1MDMyMywiaWF0IjoxNjA4NjM4MzIzLCJ1c2VybmFtZSI6InRlc3QifQ.1JV-O3o7IXOuTnjdzZjHKm5FiIHrtviqy2CXEY4DZB4",
    //token失效,刷新token使用
    "refreshToken": "2bd89f56c6ba4befae70fc7399edda44"
}

7.3、刷新token

<a id=刷新token> </a>

基本信息

Path:/member/member/refreshtoken

Method: POST

接口描述:


Request

Headers

参数名称 参数值 是否必须 示例 备注
Content-Type application/x-www-form-urlencoded

Query

参数名称 是否必须 示例 备注
refreshToken refreshToken 登录成功后有返回

Body

参数名称 参数类型 是否必须 示例 备注
refreshToken text refreshToken登录成功后有返回

Reponse

{
    "msg": "success",//消息
    "code": 0,//状态码 0 成功
    //最新的token
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJvZmZjbiIsImV4cCI6MTYwODY1MDY4MywiaWF0IjoxNjA4NjM4NjgzLCJ1c2VybmFtZSI6InRlc3QifQ.G_0WcKRnWAZcL4sI-9KO3H-Sj1wJGzNUAmxQeGzmTdQ",
   //刷新token的值
    "refreshToken": "9332b3bfd3ce46e590ae0720579b8bc1"
}

7.4、获取广告轮播图

<a id=获取广告轮播图> </a>

基本信息

Path: /context/banner/list

Method: GET

接口描述:


Request

Headers

参数名称 参数值 是否必须 示例 备注
Authorization 必须 登录获取到token

Query

参数名称 是否必须 示例 备注
page 1 页码
limit 5 轮播图数量

Reponse

{
   "msg": "success",
   "code": 0,
   "page": {
      "totalCount": 0,
      "pageSize": 5,
      "totalPage": 0,
      "currPage": 1,
      "list": [
         {
            "id": 1,
            "imgUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2021-01-06/1533d9e1-d221-4c48-8804-118f32fdd173_6.png",
            "title": "github",
            "displayOrder": 1,
            "enable": 1,
            "renderType": 1,
            "renderUrl": "http://www.ujiuye.com",
            "delFlag": 0,
            "createTime": "2021-01-06T04:30:29.000+00:00",
            "updateTime": "2021-01-06T04:30:29.000+00:00"
         },
         {
            "id": 2,
            "imgUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2021-01-06/cb1fa277-1fce-4a1e-882d-a3c3cce551f2_7.png",
            "title": "9",
            "displayOrder": 2,
            "enable": 1,
            "renderType": 1,
            "renderUrl": "http://www.baidu.com",
            "delFlag": 0,
            "createTime": "2021-01-06T04:38:04.000+00:00",
            "updateTime": "2021-01-06T04:38:04.000+00:00"
         },
         {
            "id": 3,
            "imgUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2021-01-06/21cf3022-14f1-4a65-8889-acc196d84239_2.png",
            "title": "222",
            "displayOrder": 3,
            "enable": 1,
            "renderType": 1,
            "renderUrl": "http://www.offcn.com",
            "delFlag": 0,
            "createTime": "2021-01-06T04:38:37.000+00:00",
            "updateTime": "2021-01-06T04:38:37.000+00:00"
         }
      ]
   }
}

7.5、获取试题分类

<a id=获取试题分类> </a>

基本信息

Path: /api/question/type/findall

Method: GET

接口描述:


Request

Headers

参数名称 参数值 是否必须 示例 备注
Authorization 必须 登录成功获取到的token

Reponse

{
   "msg": "success",
   "code": 0,
   "data": [
      {
         "id": 6,
         "type": "JAVA基础",
         "comments": "JAVA基础",
         "logoUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2020-12-18/0cdabddd-3f3b-4b49-8cd5-e008b3e6ad08_1.jpg",
         "delFlag": 0,
         "createTime": "2020-12-18T11:44:18.000+00:00",
         "updateTime": "2020-12-18T11:44:18.000+00:00"
      },
      {
         "id": 7,
         "type": "多线程",
         "comments": "多线程",
         "logoUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2020-12-18/b4d68e14-7abe-4680-8645-e9d8584b6159_thread.png",
         "delFlag": 0,
         "createTime": "2020-12-18T11:48:11.000+00:00",
         "updateTime": "2020-12-18T11:48:11.000+00:00"
      },
      {
         "id": 8,
         "type": "分布式架构",
         "comments": "分布式架构",
         "logoUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2020-12-18/ccc99163-69a9-44f4-8ee4-75fbdced3291_jichu.png",
         "delFlag": 0,
         "createTime": "2020-12-18T11:50:18.000+00:00",
         "updateTime": "2020-12-18T11:50:18.000+00:00"
      },
      {
         "id": 9,
         "type": "业务逻辑",
         "comments": "业务逻辑",
         "logoUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2020-12-18/3478c4fe-80b6-412a-88f1-8788a09ff3c9_yewu.png",
         "delFlag": 0,
         "createTime": "2020-12-18T11:51:09.000+00:00",
         "updateTime": "2020-12-18T11:51:09.000+00:00"
      },
      {
         "id": 10,
         "type": "智能运维",
         "comments": "运维",
         "logoUrl": "https://java0817-001.oss-cn-guangzhou.aliyuncs.com/2020-12-18/8ab19994-c80f-4dce-8f85-7d4be876f073_运维.png",
         "delFlag": 0,
         "createTime": "2020-12-18T11:51:45.000+00:00",
         "updateTime": "2020-12-18T11:51:45.000+00:00"
      }
   ]
}

7.6、读取指定分类对应的试题列表

<a id=读取指定分类对应的试题> </a>

基本信息

Path: /api/question/question/list

Method: GET

接口描述:


Request

Headers

参数名称 参数值 是否必须 示例 备注
Authorization 必须 登录成功获取到token

Query

参数名称 是否必须 示例 备注
page 1 页码
limit 10 每页显示记录数
key 1 分类编号

Reponse

{
   "msg": "success",
   "code": 0,
   "page": {
      "totalCount": 8,
      "pageSize": 10,
      "totalPage": 1,
      "currPage": 1,
      "list": [
         {
            "id": 50,
            "title": "请介绍一下SpringSecurity是做什么的?",
            "answer": "SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。",
            "level": 3,
            "displayOrder": 1,
            "subTitle": "SpringSecurity",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:54:36.000+00:00",
            "updateTime": "2020-12-18T11:54:36.000+00:00"
         },
         {
            "id": 51,
            "title": "介绍一下商品图片的上传流程?",
            "answer": "客户端发送请求到trackerServer 由trackerServer去查询可用StorageServer.返回给客户端.客户端找到StorageServer进行文件上传.StorageServer保存数据并且生产file_id.并将file_id(路径信息和文件名)返回给客户端.",
            "level": 4,
            "displayOrder": 2,
            "subTitle": "图片的上传流程",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:55:33.000+00:00",
            "updateTime": "2020-12-18T11:55:33.000+00:00"
         },
         {
            "id": 52,
            "title": "什么是富文本编辑器?",
            "answer": "富文本编辑器,Rich Text Editor, 简称 RTE, 它提供类似于 Microsoft Word 的编辑功能。常用的富文本编辑器:\nKindEditor    http://kindeditor.net/\nUEditor         http://ueditor.baidu.com/website/\nCKEditor      http://ckeditor.com/",
            "level": 2,
            "displayOrder": 3,
            "subTitle": "富文本编辑器",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:56:10.000+00:00",
            "updateTime": "2020-12-18T11:56:10.000+00:00"
         },
         {
            "id": 53,
            "title": "什么是逻辑删除?",
            "answer": "逻辑删除:顾名思义,就是“假删除”。在企业开发中,一般做真实的物理删除情况较少,我们大多数都是通过一些标记字段,来表示该数据是否被删除,这样后期的统计、分析、研究数据都比较方便,便于积累大量的用户数据。",
            "level": 4,
            "displayOrder": 4,
            "subTitle": "逻辑删除",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:56:42.000+00:00",
            "updateTime": "2020-12-18T11:56:42.000+00:00"
         },
         {
            "id": 54,
            "title": "简单说明redis存储的流程?",
            "answer": "1.直接从reids中获取数据。\n2.判断获取的数据是否为空\n3.如果为空,则从数据库中获取数据,并将从数据库中获取的数据,存储到redis中,再返回。\n4.如果不为空,则直接将redis中获取的数据返回",
            "level": 4,
            "displayOrder": 5,
            "subTitle": "redis存储",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:57:15.000+00:00",
            "updateTime": "2020-12-18T11:57:15.000+00:00"
         },
         {
            "id": 55,
            "title": "solr是什么?有什么优点?",
            "answer": "基于Lucene的全文搜索服务器。同时对其进行了扩展,提供了比\nLucene更为丰富的查询语言,同时实现了可配置、可扩展并对查询性能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎",
            "level": 2,
            "displayOrder": 6,
            "subTitle": "solr",
            "type": 6,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:57:44.000+00:00",
            "updateTime": "2020-12-18T11:57:44.000+00:00"
         },
         {
            "id": 56,
            "title": "activemq的作用以及原理",
            "answer": "Activemq 的作用就是系统之间进行通信。 当然可以使用其他方式进行系统间通信, 如果使用 Activemq 的话可以对系统之间的调用进行解耦, 实现系统间的异步通信。 原理就是生产者生产消息, 把消息发送给activemq。 Activemq 接收到消息, 然后查看有多少个消费者, 然后把消息转发给消费者, 此过程中生产者无需参与。 消费者接收到消息后做相应的处理和生产者没有任何关系",
            "level": 4,
            "displayOrder": 7,
            "subTitle": "activemq",
            "type": 7,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:58:30.000+00:00",
            "updateTime": "2020-12-18T11:58:30.000+00:00"
         },
         {
            "id": 57,
            "title": "什么是 Freemarker",
            "answer": "FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。\nFreeMarker与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。\n它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。",
            "level": 3,
            "displayOrder": 7,
            "subTitle": " Freemarker",
            "type": 7,
            "enable": 1,
            "delFlag": 0,
            "createTime": "2020-12-18T11:59:01.000+00:00",
            "updateTime": "2020-12-18T11:59:01.000+00:00"
         }
      ]
   }
}

7.7、获取试题详情

<a id=获取试题详情> </a>

基本信息

Path: /question/question/info/{id}

Method: GET

接口描述:


Request

路径参数

参数名称 示例 备注
id 1 题目id

Reponse

{
   "msg": "success",
   "code": 0,
   "question": {
      "id": 50,
      "title": "请介绍一下SpringSecurity是做什么的?",
      "answer": "SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。",
      "level": 3,
      "displayOrder": 1,
      "subTitle": "SpringSecurity",
      "type": 6,
      "enable": 1,
      "delFlag": 0,
      "createTime": "2020-12-18 11:54:36",
      "updateTime": "2020-12-18T11:54:36.000+00:00"
   }
}

具体参见线上接口文档

https://www.25xt.com/html5css3/16782.html

https://mock.yonyoucloud.com/ 账号:hk109@126.com 密码:offcn123


avatar
青山
悟已往之不谏 知来者之可追
一言
今日诗词
站点信息
本站访客数 :
本站总访问量 :