2022-11-28

一、教务中心业务

1、Nacos

1.1、common配置

pom文件

<dependencies>
<!--nacos-->
        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
        <!--服务注册/发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--配置中心-->
        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
</dependencies>    

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

1.2、注册中心

renren-fast项目配置

pom文件中添加common依赖

<dependency>
	<groupId>com.sloth.school</groupId>
	<artifactId>school-common</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>

application.yml添加配置

spring:
    application:
        name: renren-fast
    cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848

重启renren-fast.

报错:

跟踪进去发现该类使用了lombok的相关注解@Date

使用lombok是为了省略类的getter/setter/构造器/toString等常用方法,使类看起来更整洁。使用这个注解配套的在idea上要下载lombok插件来支持这种功能。

但是idea上下载的插件一般都是最新版本的plugin,可能与旧版本的lombok jar包有冲突。

解决:更改pom文件lombok依赖版本即可

我是升级到了1.18.24版本解决问题

其他项目同上。

1.2、配置中心

1、导入依赖

2、在src/resources 下创建 bootstrap.properties 文件,添加配置中心相关配置

spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=[命名空间ID]

 

2、跨域处理

1.1、跨域问题:

 

1.2、跨域处理

1.2.1、配置网关路由以及路径重写

找到前端项目中 static/config/index.js 文件,修改api请求地址,统一路径请求发送给网关

window.SITE_CONFIG['baseUrl'] = 'http://localhost:88';

项目会将请求统一发送给网关,再由网关来路由到指定的服务

1.2.2、网关服务搭建

 1、创建school-gateway模块

2、pom文件添加common模块依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sloth.school</groupId>
    <artifactId>school-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>school-gateway</name>
    <description>API网关</description>
    <properties>
        <java.version>18</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sloth.school</groupId>
            <artifactId>school-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

 application.properties 配置

spring.application.name=school-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
server.port=88

bootstrap.properties 配置

spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=[gateway-命名空间ID]

 3、启动网关服务

报错1:

原因:test api版本跟springboot版本不对应

解决:改成 import org.junit.Test;

 

报错2:

原因:项目不需要连接数据库,启动报错

解决:启动类配置添加

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})

 

2022-12-1

4、application.yml配置

spring:
  cloud:
    gateway:
      routes:
#        - id: test_route
#          uri: https://www.baidu.com
#          predicates:
#            - Query=url,baidu
#
#        - id: qq_route
#          uri: https://www.qq.com
#          predicates:
#            - Query=url,qq

        - id: deu_route
          uri: lb://school-edu
          predicates:
            - Path=/api/edu/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api(?<segment>/?.*),/renren-fast/$\{segment}


## 前端项目,/api

重启gateway服务

验证码请求路径由 http://localhost:8080/renren-fast/captcha.jpg 转为 http://localhost:88/api/captcha.jpg

再由gateway服务路径重写。

2022-12-3

1.2.3、CROS配置

在gateway模块下新建 com.sloth.school.gateway.conf.SchoolCorsConfiguration.java

package com.sloth.school.gateway.conf;

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 SchoolCorsConfiguration {
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1.配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

 重启服务。

成功登录。

2、班级管理

1.2、学生管理

2.1.1、前端项目布置

复制逆向生成代码中,将src/views/modules/edu/ 下的文件全部复制粘贴到以下位置

2.1.2、CRUD测试

1. 展示学生信息

补充table和分班按钮

<el-button type="text" size="small" @click="changeClazzHandle(scope.row.stuId)">分班</el-button>

照片样式设置

<el-table-column
        prop="stuHeader"
        header-align="center"
        align="center"
        label="照片">
        <template slot-scope="scope">
          <img :src="scope.row.stuHeader" style="width: 100px; height: 80px" />
        </template>
</el-table-column>

日期统一格式

在applicat.yml中添加

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss

 2022-12-

2.1.3、OSS存储

点击立即开通,开通服务

使用步骤参考官方API:https://help.aliyun.com/document_detail/31947.html?spm=5176.8465980.entries.dapi.4e701450539QSz

创建Bucket(一般一个项目对应以一个Bucket)

其他配置保持默认即可。

上传文件

参考文档:https://help.aliyun.com/document_detail/84778.html

1.  导入依赖

在pom文件中导入依赖

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

2. 文件上传测试代码


    @Test
    public void testOSS(){
        // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
        String endpoint = "oss-cn-shenzhen.aliyuncs.com";//oss-cn-shenzhen.aliyuncs.com
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = "yourAccessKeyId";
        String accessKeySecret = "yourAccessKeySecret";
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = "exampledir/exampleobject.txt";
        // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
        // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
        String filePath= "D:\\localpath\\examplefile.txt";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            InputStream inputStream = new FileInputStream(filePath);
            // 创建PutObject请求。
            ossClient.putObject(bucketName, objectName, inputStream);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

3. ROM密钥

点击

选择

还没开通的直接点击立即开通即可。

新建用户

添加权限

选择 FullAccess 权限,点击确认完成

4. 修改测试代码

endpoint accessKeySecret bucketName objectName filePath

 换成自己对应的信息

2022-12-7


项目突然启动失败

报错信息

E:\Project\slothschool_web\.idea\workspace.xml
Failed to load project configuration: cannot parse file E:\Project\slothschool_web\.idea\workspace.xml: java.io.IOException: 数据错误(循环冗余检查)。

2022-12-11

许多事揉在一起,终于挤出时间解决了上面的bug。

解决:删除workspace.xml文件,再到文件资源管理器中删除 .idea 文件夹。

重新打开idea导入项目,maven insatll。

maven报错。

报错信息:

报错原因:idea上次异常关闭,有些设置和文件出现异常。

解决:maven仓库位置被初始化为系统默认位置,需要修改成自己安装的maven位置。

重新clear-install,尝试运行test,成功运行。


继续测试OSS功能

文件上传成功。

2022-12-13

5. 整合项目-创建第三方服务支持服务模块

模块名:school-third-party

pom文件

查看代码
 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sloth.school</groupId>
    <artifactId>school-third-party</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>school-third-party</name>
    <description>第三方服务</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sloth.school</groupId>
            <artifactId>school-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--Oss-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            <version>2.2.0.RELEASE</version>
        </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>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>



</project>

 application.yml

server:
  port: 30000

spring:
  application:
    name: school-third-party
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: []
      secret-key: []
      oss:
        endpoint: oss-cn-shenzhen.aliyuncs.com
        bucket: sloth-school
logging:
  level:
    com.yxj.gulimall.product: debug

bootstrap.properties

spring.application.name=gulimail-third-party

spring.cloud.nacos.config.server-addr=127.0.0.1:8848

spring.cloud.nacos.config.namespace=4c2760e1-8ef1-4327-8d26-ed8d4a5d8b23

#spring.cloud.nacos.config.group=dev

spring.cloud.nacos.config.extension-configs[0].data-id=oss.yml
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

其他服务如需使用OSS,只需在对应的pom文件中添加school-third-party依赖

启动服务:

报错:

报错原因:springboot版本和springcloud版本不兼容

查看pom文件,发现springboot是3.0.0,springcloud是Greenwich.SR3

解决:将springboot版本改为

2.1.8.RELEASE

test类报错:

原因:版本修改,test支持改动

解决:修改test支持类

启动成功

重新测试OSS上传文件功能

package com.sloth.school.thirdparty;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;


@SpringBootTest
public class SchoolThirdPartyApplicationTests {

    @Test
    public void contextLoads() {
    }

    @Resource
    OSSClient ossClient;


    @Test
    public void testUpdata() throws FileNotFoundException 

        // 填写Bucket名称,例如examplebucket。
        String bucketName = "sloth-school";
        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = "exampledir/zhang2.jpg";
        // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
        // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。
        String filePath= "E:\\Project\\slothschool\\pic\\student\\zhang.jpg";
        try {
            InputStream inputStream = new FileInputStream(filePath);
            // 创建PutObject请求。
            //ossClient.putObject(bucketName, objectName, inputStream);
            ossClient.putObject(bucketName,objectName,inputStream);
            System.out.println("success!!!!!");
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

}

报错:

解决:添加spring运行注解

@RunWith(SpringRunner.class)

上传成功

2022-12-14

获取服务端直传服务签名

编写controller.OssController 类

package com.sloth.school.thirdparty.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 com.sloth.school.common.utils.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

@RestController
public class OssController {
    @Resource
    OSS ossClient;

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

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

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

    @Value("${spring.cloud.alicloud.secret-key}")
    String accessKey ;


    @RequestMapping("/oss/policymap")
    public Map<String, String> policyMap(){

        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint

        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format+"/"; // 用户上传文件时指定的前缀。

        Map<String, String> respMap=null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            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);

            respMap= new LinkedHashMap<String, String>();
            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;
    }

    @RequestMapping("/oss/policyr")
    public R policyR(){

        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint

        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format+"/"; // 用户上传文件时指定的前缀。

        Map<String, String> respMap=null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            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);

            respMap= new LinkedHashMap<String, String>();
            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 R.ok().put("data",respMap);
    }
}

重启项目,在浏览器输入localhost:30000/oss/policymap 

但是我们的服务都是通过网关指定的,所以我们实际的请求地址是:http://localhost:88/api/thirdparty/oss/policymap

gateway网关配置

2022-12-15

今天打开idea又又出现异常,maven设定又回到系统默认,每天就只能挤出一点时间写写项目,时间全花在debug上。。。

重新设置maven仓库后重新maven install

依然报错

到maven仓库删除改包,重新install

仍有其他jar包报错,依次处理了重新

edu项目启动报错

Oss endpoint can't be empty.

原因:项目关于Oss的相关依赖没有清除

处理:到项目依赖管理中,删除依赖。

重新启动成功。

2022-12-19

6. 整合项目-前端上传文件

6.1、创建组件

在src/components下创建upload文件夹

依次创建

multiUpload.vue

multiUpload.vue
<template>
  <div>
    <!--gulimail-sloth.oss-cn-guangzhou.aliyuncs.com-->
    <el-upload
      action="http://sloth-school.oss-cn-shenzhen.aliyuncs.com"
      :data="dataObj"
      list-type="picture-card"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview"
      :limit="maxCount"
      :on-exceed="handleExceed"
    >
      <i class="el-icon-plus"></i>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="dialogImageUrl" alt />
    </el-dialog>
  </div>
</template>
<script>
import { policy } from "./policy";
import { getUUID } from '@/utils'
export default {
  name: "multiUpload",
  props: {
    //图片属性数组
    value: Array,
    //最大上传图片数量
    maxCount: {
      type: Number,
      default: 30
    }
  },
  data() {
    return {
      dataObj: {
        policy: "",
        signature: "",
        key: "",
        ossaccessKeyId: "",
        dir: "",
        host: "",
        uuid: ""
      },
      dialogVisible: false,
      dialogImageUrl: null
    };
  },
  computed: {
    fileList() {
      let fileList = [];
      for (let i = 0; i < this.value.length; i++) {
        fileList.push({ url: this.value[i] });
      }

      return fileList;
    }
  },
  mounted() {},
  methods: {
    emitInput(fileList) {
      let value = [];
      for (let i = 0; i < fileList.length; i++) {
        value.push(fileList[i].url);
      }
      this.$emit("input", value);
    },
    handleRemove(file, fileList) {
      this.emitInput(fileList);
    },
    handlePreview(file) {
      this.dialogVisible = true;
      this.dialogImageUrl = file.url;
    },
    beforeUpload(file) {
      let _self = this;
      return new Promise((resolve, reject) => {
        policy()
          .then(response => {
            console.log("这是什么${filename}");
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            _self.dataObj.key = response.data.dir + "/"+getUUID()+"_${filename}";
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            resolve(true);
          })
          .catch(err => {
            console.log("出错了...",err)
            reject(false);
          });
      });
    },
    handleUploadSuccess(res, file) {
      this.fileList.push({
        name: file.name,
        // url: this.dataObj.host + "/" + this.dataObj.dir + "/" + file.name; 替换${filename}为真正的文件名
        url: this.dataObj.host + "/" + this.dataObj.key.replace("${filename}",file.name)
      });
      this.emitInput(this.fileList);
    },
    handleExceed(files, fileList) {
      this.$message({
        message: "最多只能上传" + this.maxCount + "张图片",
        type: "warning",
        duration: 1000
      });
    }
  }
};
</script>
<style>
</style>

policy.js

policy.js
import http from '@/utils/httpRequest.js'
export function policy() {
   return  new Promise((resolve,reject)=>{
        http({
            url: http.adornUrl("/thirdparty/oss/policymap"),
            method: "get",
            params: http.adornParams({})
        }).then(({ data }) => {
            resolve(data);
        })
    });
}
singleUpload.vue
 <template> 
  <div>
    <el-upload
      action="http://gulimail-sloth.oss-cn-guangzhou.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :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 './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("data",response)
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            _self.dataObj.key = response.data.dir + '/'+getUUID()+'_${filename}';
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            resolve(true)
          }).catch(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>

6.2、导入组件

在 src\views\modules\edu\studentinfo-add-or-update.vue 中导入并使用文件上传组件

1.

在<script>标签中添加代码

import SingleUpload from "@/components/upload/singleUpload";
在export default {}中添加代码
  components: { SingleUpload },
引用文件上传样式
    <el-form-item label="照片" prop="stuHeader">
      <!-- <el-input v-model="dataForm.stuHeader" placeholder=""></el-input> -->
      <single-upload v-model="dataForm.stuHeader"></single-upload>
    </el-form-item>
6.3、效果
点击上传后,oss报跨域异常,需要到阿里云控制台进行跨域配置
点击创建规则,按例图填写规则。
 
重新测试,文件上传成功。
 
2022-12-20
2.1.3、前端页面调整和优化
1. 新增和批量删除按钮不显示
新增和批量删除按钮权限开启
由于后期会加入角色权限功能,由isAuth方法实现。
<el-button v-if="isAuth('edu:studentinfo:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('edu:studentinfo:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
/**
 * 是否有权限
 * @param {*} key
 */
export function isAuth (key) {
  return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
}
为了方便开发,打开isAuth()权限。找到src\utils\index.js下的isAuth,将返回结果都返回true
2. 前端项目eslint报错
这并不是代码有错误,语法上并没有错误,是eslint检查机制太严格产生的警告
将build\webpack.base.conf.js中 createLintingRule 中的属性全部注释掉
const createLintingRule = () => ({
  // test: /\.(js|vue)$/,
  // loader: 'eslint-loader',
  // enforce: 'pre',
  // include: [resolve('src'), resolve('test')],
  // options: {
  //   formatter: require('eslint-friendly-formatter'),
  //   emitWarning: !config.dev.showEslintErrorsInOverlay
  // }
})
下次重启项目时就不会再报错
3. 状态信息显示
学生状态在数据库中以01形式存储,实际信息为在读/离校。
      <el-table-column
        prop="stuStatus"
        header-align="center"
        align="center"
        label="状态">
        <template slot-scope="scope">
          <el-tag v-if="scope.row.stuStatus == 0">离校</el-tag>
          <el-tag type="info" v-if="scope.row.stuStatus == 1">在读</el-tag>
        </template>
      </el-table-column>

4. 新增页面修改组件中也需要修改

状态

<el-form-item label="状态" prop="stuStatus">
      <!-- <el-input v-model="dataForm.stuStatus" placeholder=""></el-input> -->
      <el-select style="width:120px;" v-model="dataForm.stuStatus" placeholder="请选择状态" clearable>
          <el-option label="离校" :value="0"></el-option>
          <el-option label="在读" :value="1"></el-option>
        </el-select>
    </el-form-item>

性别

<el-form-item label="性别" prop="stuSex">
      <!-- <el-input v-model="dataForm.stuSex" placeholder="性别"></el-input> -->
      <el-select style="width:120px;" v-model="dataForm.stuStatus" placeholder="请选择" clearable>
          <el-option label="男" :value="男"></el-option>
          <el-option label="女" :value="女"></el-option>
        </el-select>
    </el-form-item>

生日

<el-form-item label="出生日期" prop="stuDate">
      <!-- <el-input v-model="dataForm.stuDate" placeholder="出生日期"></el-input> -->
      <el-date-picker
      v-model="dataForm.stuDate"
      type="date"
      placeholder="选择日期">
    </el-date-picker>
    </el-form-item>
    

年龄自动计算

????

2022-12-23

入校时间

<el-form-item label="入校时间" prop="stuCreateTime">
      <!-- <el-input v-model="dataForm.stuCreateTime" placeholder="入校时间"></el-input> -->
      <el-date-picker
      v-model="dataForm.stuCreateTime"
      align="right"
      type="date"
      placeholder="选择日期"
      :picker-options="pickerOptions">
    </el-date-picker>
</el-form-item>

pickerOptions: {
          disabledDate(time) {
            return time.getTime() > Date.now();
          },
          shortcuts: [{
            text: '今天',
            onClick(picker) {
              picker.$emit('pick', new Date());
            }
          }, {
            text: '昨天',
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() - 3600 * 1000 * 24);
              picker.$emit('pick', date);
            }
          }, {
            text: '一周前',
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit('pick', date);
            }
          }]
        }

2.1.3、新增功能测试

确认提交,报错信息

{
    "timestamp": "2022-12-23 03:30:07",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize value of type `java.util.Date` from String \"2012-03-07T16:00:00.000Z\": not a valid representation (error: Failed to parse Date value '2012-03-07T16:00:00.000Z': Unparseable date: \"2012-03-07T16:00:00.000Z\"); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String \"2012-03-07T16:00:00.000Z\": not a valid representation (error: Failed to parse Date value '2012-03-07T16:00:00.000Z': Unparseable date: \"2012-03-07T16:00:00.000Z\")\n at [Source: (PushbackInputStream); line: 6, column: 14] (through reference chain: com.sloth.school.edu.entity.StudentInfoEntity[\"stuDate\"])",
    "path": "/edu/studentinfo/save"
}

原因:前端date格式和后台date格式不符

解决:需要到后端自定义接收数据的Date格式

common项目pom中添加依赖

<dependency>
	<groupId>joda-time</groupId>
	<artifactId>joda-time</artifactId>
	<version>2.3</version>
</dependency>

修改StudentInfoEntity

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8")
    private Date stuDate;
    
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8")
    private Date stuCreateTime;

postman测试成功

 

页面测试成功

但是,新增窗口不会自己关闭

问题待解决

2022-12-25

idea重启后又出现maven设置变回默认值,项目启动失败的bug。

重新设置仓库,到项目管理中删除多出的项目,重新install。

前端项目优化

选择生日自动生成年龄

组件    
<el-form-item label="出生日期" prop="stuDate">
      <!-- <el-input v-model="dataForm.stuDate" placeholder="出生日期"></el-input> -->
      <el-date-picker
      v-model="dataForm.stuDate"
      type="date"
      placeholder="选择日期"
      value-format="yyyy-MM-dd"
      @change="getAge()">
    </el-date-picker>
    </el-form-item>
逻辑代码
getAge(){
        console.log("stuDate11:"+this.dataForm.stuDate);
        //出生时间 毫秒
        var birthDayTime = new Date(this.dataForm.stuDate).getTime(); 
        //当前时间 毫秒
        var nowTime = new Date().getTime(); 
        //一年毫秒数(365 * 86400000 = 31536000000)
        //return Math.ceil((nowTime-birthDayTime)/31536000000)
        var age = Math.ceil((nowTime-birthDayTime)/31536000000);
        this.$set(this.dataForm,"stuAge",age);
      }

2022-12-27

解决新增按钮异常问题

新增按钮点击无响应

//1
//找到:
this.$refs['dataForm'].resetFields()
//修改代码
if (this.$refs['dataForm']!==undefined) {
    this.$refs['dataForm'].resetFields()
}
//2
//在data中添加
visible: false,

 分页功能以及模糊查询

在com.sloth.school.edu下创建config.MybatisConfig

@Configuration
@EnableTransactionManagement
@MapperScan("com.sloth.gulimail.product.dao")
public class MyBatisConfig {
    //引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setOverflow(true);
        paginationInterceptor.setLimit(1000);
        return paginationInterceptor;
    }
}

修改queryPage方法

@Override
    public PageUtils queryPage(Map<String, Object> params) {
        //1. 获取key
        String key = (String) params.get("key");
        QueryWrapper<StudentInfoEntity> queryWrapper = new QueryWrapper<>();
        if(!StringUtils.isEmpty(key)){
            //key不为空,需要进行模糊检索
            queryWrapper.eq("stu_id",key)
                    .or().like("stu_name",key)
                    .or().like("stu_address",key);
        }

        IPage<StudentInfoEntity> page = this.page(
                new Query<StudentInfoEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

2022-12-31

学生选班功能

前端模块

在studentinfo页面添加

<el-dialog
      title="关联班级"
      :visible.sync="cateRelationDialogVisible"
      width="30%"
    >
      <el-popover placement="right-end" v-model="popCatelogSelectVisible">
        <!-- <category-cascader :catelogPath.sync="catelogPath"></category-cascader> -->
        <el-select v-model="clazzId" clearable filterable placeholder="请选择">
          <el-option
            v-for="item in options"
            :key="item.clazzId"
            :label="item.clazzName"
            :value="item.clazzId"
          >
          </el-option>
        </el-select>
        <div style="text-align: right; margin: 0">
          <el-button
            size="mini"
            type="text"
            @click="popCatelogSelectVisible = false"
            >取消</el-button
          >
          <el-button type="primary" size="mini" @click="addCatelogSelect"
            >确定</el-button
          >
        </div>
        <el-button slot="reference">新增关联</el-button>
      </el-popover>
      <el-table :data="cateRelationTableData" style="width: 100%">
        <el-table-column prop="clazzId" label="班级编号"></el-table-column>
        <el-table-column prop="clazzName" label="班级名"></el-table-column>
        <el-table-column fixed="right" header-align="center" align="center" label="操作">
          <template slot-scope="scope">
            <el-button
              type="text"
              size="small"
              @click="deleteCateRelationHandle(scope.row.stuId,scope.row.clazzId)"
            >移除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <span slot="footer" class="dialog-footer">
        <el-button @click="cateRelationDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="cateRelationDialogVisible = false"
          >确 定</el-button
        >
      </span>
    </el-dialog>
    
data:{
      stuId:"",
      clazzId:'',
      dataListLoading: false,
      dataListSelections: [],
      options: [],
      cateRelationTableData:[],
      cateRelationDialogVisible: false,
      popCatelogSelectVisible: false,
}

效果:

打开关联窗口和获取关联信息逻辑

前端逻辑
/**打开处理选班功能 */
    changeClazzHandle(stuId) {
      console.log("开始选班:" + stuId);
      this.cateRelationDialogVisible = true;
      this.stuId = stuId;
      this.getCateRelation();
    },
    /** 获取关联信息*/
    getCateRelation(){
      this.$http({
        url: this.$http.adornUrl("/edu/clazzstudentplacement/stu/list"),
        method: "get",
        params: this.$http.adornParams({
          stuId: this.stuId
        })
      }).then(({ data }) => {
        console.log("data:"+data.data)
        this.cateRelationTableData = data.data;
      });
    },

后端逻辑
新建StuClazzRelationVo类

package com.sloth.school.edu.vo;


import lombok.Data;

@Data
public class StuClazzRelationVo {
    Long id;

    Long ClazzId;

    Long StuId;

    String ClazzName;

    String StuName;
}

controller层

/**
     * 获取指定id学生的班级关联信息
     */
    @RequestMapping("/stu/list")
    public R infoByStuid(@RequestParam Map<String, Object> params){
        //System.out.println("stuId:"+params.get("stuId"));
        String stuId = (String) params.get("stuId");
        List<StuClazzRelationVo> voList = clazzStudentPlacementService.infoByStuid(stuId);


        return R.ok().put("data",voList);
    }

service层

//ClazzStudentPlacementService
List<StuClazzRelationVo> infoByStuid(String stuId);
//ClazzStudentPlacementServiceImpl
@Override
    public List<StuClazzRelationVo> infoByStuid(String stuId) {

        List<ClazzStudentPlacementEntity> list = this.list(new QueryWrapper<ClazzStudentPlacementEntity>().eq("stu_Id", stuId));

        List<StuClazzRelationVo> collect = list.stream().map((entity) -> {
            StuClazzRelationVo vo = new StuClazzRelationVo();
            ClazzInfoEntity clazzInfoEntity = clazzInfoService.getById(entity.getClazzId());
            vo.setClazzId(entity.getClazzId());
            vo.setClazzName(clazzInfoEntity.getClazzName());
            StudentInfoEntity studentInfoEntity = studentInfoService.getById(entity.getStuId());
            vo.setStuId(entity.getStuId());
            vo.setStuName(studentInfoEntity.getStuName());
            return vo;
        }).collect(Collectors.toList());


        return collect;
    }

新增关联逻辑

前端逻辑: 
/**添加关联信息 */
    addCatelogSelect(){
      console.log("添加关联信息:"+this.stuId+","+this.clazzId);
      this.popCatelogSelectVisible =false;
      this.$http({
        url: this.$http.adornUrl("/edu/clazzstudentplacement/save"),
        method: "post",
        data: this.$http.adornData({clazzId:this.clazzId,stuId:this.stuId}, false)
      }).then(({ data }) => {
        this.getCateRelation();
      });
    },

 删除关联逻辑

前端
/**删除关联 */
    deleteCateRelationHandle(stuid, clazzid){
      //console.log("删除关联")
        this.$confirm('此操作将永久删除该关联, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.$http({
            url: this.$http.adornUrl("/edu/clazzstudentplacement/delbyeq"),
            method:"post",
            data: this.$http.adornData({stuId:stuid, clazzId:clazzid}, false)
          }).then(({data}) => {
            this.getCateRelation();
          })
          this.$message({
            type: 'success',
            message: '删除成功!'
          });
        });
后端controller层
/**
     * 根据stuid和clazzid删除
     */
    @RequestMapping("/delbyeq")
    //@RequiresPermissions("edu:clazzstudentplacement:delete")
    public R delbyeq(@RequestBody StuClazzRelationVo params) {
        //clazzStudentPlacementService.removeByIds(Arrays.asList(ids));
        //System.out.println("stuid:"+params.getStuId()+" "+"clazzid:"+params.getClazzId());

        QueryWrapper<ClazzStudentPlacementEntity> queryWrapper = new QueryWrapper<ClazzStudentPlacementEntity>().eq("stu_Id", params.getStuId());
        queryWrapper.and(wrapper->
                    wrapper.eq("clazz_Id", params.getClazzId())
                );
        clazzStudentPlacementService.remove(queryWrapper);
        return R.ok();
    }

 

 posted on 2022-11-27 22:58  Slothhh  阅读(47)  评论(0编辑  收藏  举报