Spring Boot + mybatisplus + vue 搭建前后端框架

项目流程及问题

数据库设计

数据库字符集问题
  1. 字符集:utf8mb4兼容utf8,Unicode编码区1~126是utf8区,126以下是utf8mb4兼容区。(一般选utf8:不浪费空间)
    Unicode区表
数据库排序规则问题utf8_bin与utf8_general_ci(一般分这两种)?(这里我存在疑问)
  1. bin是二进制,a与A会区别对待(例:SELECT * FROM table WHERE txt = ‘a’)
    utf8_general_ci(case insensitive:大小写不敏感):有注册用户名和邮箱使用?
    utf8_general_cs(case sensitive:大小写敏感):有用户名与邮箱用它有不良后果?
数据库设计规则

58sql军规

  1. 数据库,表,字段用英文单词并是单数
  2. Id为主键,一般为bigint自增(分库分表集群用varchar)
  3. 表必备字段id,创建时间create_time,更新时间update_time(考虑逻辑删除deleted,乐观锁version)
  4. Deleted(最好不要is_deleted:有些框架pojo中isDeleted=getDeleted)
  5. 小数一定要用decimal
  6. 数据库字段中固定长度(id)时(char固定长度(不够填空),效率高,varchar可变,省存储空间)
  7. 两个字符一个中文varchar(20)(手机号必须用这个)
  8. Default默认:empty string空字符串/空/null值(插入null报错,原因选了不为null)
  9. 不设置外键,最好在业务逻辑层去处理
mysql字段-数据类型(数字/时间/字符)
 tinyint(1byte) smallint(2byte:65536) mediumint(3byte) int  bigint(8byte)
Decimal==numeric
Date日期  datetime日期+时间

Spring Boot+mybatisPlus的后台架构搭建

Spring Boot项目创建
Spring配置繁琐,太多xml,Annotation配置,让人眼花缭乱;
Spring Boot采用Java config方式对Spring配置;其二,不用配置tomcat,包括静态资源处理,,视图解析器,注解扫描等

Spring boot小结

  1. spring Initializr(默认从官网拉取创建)(idea要下载Lombok插件)
  2. 添加依赖(可不选,用什么直接在依赖里添加依赖)
  3. 项目依赖(pom.xml springboot有的)spring-boot-starter(+test) junit-vintage-engine 三个依赖
Spring Boot 添加(mybatisPlus等)重要依赖

父pom里定义的一些版本



		<dependency>    
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
		<dependency>	
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        <dependency>	
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>    
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>2.9.2version>
        dependency>
         <dependency>     
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.3.0version>
        dependency>    
        <dependency>   
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
            <version>3.3.0version>
        dependency>
        <dependency>    
            <groupId>org.apache.velocitygroupId>
            <artifactId>velocity-engine-coreartifactId>
            <version>2.1version>
        dependency>	
Spring Boot的文件结构(创建包)
	创建common包放自己写的类(下有config/exception/util三个包)
mabatis plus自动生成主要文件(MP)
	在utils下创建mysqlGenerator类(自动生成)(要修改父包名等等)
public class mysqlGenerator {
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
    public static void main(String[] args) {
        AutoGenerator mpg = new AutoGenerator();	// 代码生成器
        DataSourceConfig dsc = new DataSourceConfig();	 // 数据源配置
        dsc.setDbType(DbType.MYSQL);	//数据库类型
        //dsc.setSchemaName("public");数据库schemaName
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/graduation?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);
       
        PackageConfig pc = new PackageConfig();	 // 包配置
        pc.setParent("com.xxw.graduation");	 //父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名
        pc.setModuleName(scanner("模块名"));	//父包模块名
        mpg.setPackageInfo(pc);

        GlobalConfig gc = new GlobalConfig();	// 全局配置
        String projectPath = System.getProperty("user.dir");
        String pre = pc.getModuleName().substring(0,1).toUpperCase() + pc.getModuleName().substring(1);	//类前缀
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setFileOverride(true);	 //第一次吧第二次覆盖掉
        gc.setOpen(false);	//是否打开输出目录
        gc.setEnableCache(false);	//是否在xml中加二级缓存配置
        gc.setAuthor("xxw");	//作者名字 
        gc.setActiveRecord(true);	//开启AR,与分页有关
        gc.setBaseResultMap(true);	//开启baseResultMap,xmlde resultMap是否生成
        gc.setBaseColumnList(true);	//开启生成基础列,在xml中
        gc.setDateType(DateType.ONLY_DATE);	//datatype指定(only_date只使用Java.util.date代替/sql_pack使用java.sql包下的/time_pack使用java.time下的)
        gc.setEntityName("%s");	//实体命名方式 
        gc.setMapperName("%sMapper");	//mapper命名方式gc.setMapperName(pre+"%sMapper");这个会加上输入的模块名
        gc.setXmlName("%sMapper"); //mapper xml命名方式gc.setXmlName(pre+"%sMapper");
        gc.setServiceName("%sService"); //service命名方式gc.setServiceName(pre+"%sService");
        gc.setServiceImplName("%sServiceImpl");//serviceImpl命名方式gc.setServiceImplName(pre+"%sServiceImpl");
        gc.setControllerName("%sController");//controller命名方式gc.setControllerName(pre+"%sController");
        gc.setIdType(IdType.AUTO);	//主键策略:自动增加
        gc.setSwagger2(true);	//实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);
        
        InjectionConfig cfg = new InjectionConfig() {			// 自定义配置
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        				// 如果模板引擎是 velocity
        String templatePath = "/templates/mapper.xml.vm";
        				// 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        				// 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/"+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //下划线转驼峰s
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //数据表为下划线命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父类
        strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
//        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        // 写于父类中的公共字段
//        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
//        表前面字段(生成时弃掉)
        strategy.setTablePrefix("xxw_");
        strategy.setEntityTableFieldAnnotationEnable(true);
        mpg.setStrategy(strategy);
//        mpg.setTemplateEngine(new VelocityTemplateEngine());
        mpg.execute();
// 输入   xxw_user,xxw_sort,xxw_label,xxw_friend,xxw_comment,xxw_collect,xxw_article_sort,xxw_article_label,xxw_article
    }
}
应用入口添加扫描mapper包 的注解
@MapperScan("com.xxw.test.personWeb.mapper")
yaml多个配置文件 参考博客设置
	配置文件内容(第一个是application.yml)
#设置配置文件的加载(dev开发/prod生产/test测试环境)
spring:
  profiles:
    active: dev
  jackson:
    # 时区设置(中国北京为8时区,以及格式设置)
    date-format: yyyy-MM-dd HH-mm-ss
    time-zone: GMT+8
#mybatis-plus总体配置
mybatis-plus:
  global-config:
    db-config:
      #默认数据库表下滑线命名
      table-underline: true
      #逻辑删除配置(1为已删除,0为未删除)---------实体类字段前加入 @TableLogic!!!!
      logic-not-delete-value: 0
      logic-delete-value: 1
      # 全局主键策略(auto主键自增/uuid全局唯一(所有机器同一时空唯一)/id_work数值类型/id_work_str字符串类型/input自定义;与数据库一致)
      id-type: auto
  configuration:
    #驼峰命名法(默认开启,容易出错:字段nickName,实体类getNickName()“报错:找不到nick-name字段”)最好关闭,这里数据库设计还行,就不关了
    map-underscore-to-camel-case: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
	这个是dev开发环境配置
server:
  port: ????
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/graduation?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver
日志处理
在resources下创建logback-spring.xml文件配置日志(lockback日志)
要先在idea下载Grep Console插件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
    <!-- 日志输出规则  根据当前ROOT 级别,日志输出时,级别高于root默认的级别时  会输出 -->
    <!-- 以下  每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
    <contextName>logback</contextName>
    <!-- 定义日志文件 输入位置 -->
    <!--  巨坑!!!windows复制目录是\而这里要/  -->
    <property name="logPath" value="C:/Users/Administrator/Desktop/GraduationProject/log"/>
    <!-- 日志最大的历史 30天,这个参数要看你基于哪一种滚动策略【基于天,单位为天,月同理】本文件设置的多事基于天滚动策略 -->
    <property name="maxHistory" value="30"/>
    <!-- 日志输出格式【控制台】 -->
    <!-- 我这里不要线程的消息   | %boldGreen(%thread) -->
    <property name="logPatternConsoleLog" value="%date{yyyy-MM-dd HH:mm:ss} | %highlight(%-5level) | %boldYellow(%logger) | %msg%n"/>
    <!-- 日志输出格式【日志文件】   | %thread  -->
    <property name="logPattern" value="%date{yyyy-MM-dd HH:mm:ss} | %-5level | %logger | %msg%n"/>
    <!-- 日志输出编码格式 -->
    <property name="logCharset" value="UTF-8"/>


    <!--配置控制台的输出-->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${logPatternConsoleLog}</pattern>
            <charset>${logCharset}</charset>
        </encoder>
    </appender>


    <!--配置文件输出:error-->
    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}/log_error.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>           <!-- 这里被坑过一次(deny与accept写反了) -->
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>${logPattern}</pattern>
            <charset>${logCharset}</charset>
        </encoder>
        <!--滚动策略:每天一个日志文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>${logPath}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>30,
            则只保存最近30天的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>

    <!--配置文件输出:warn-->
    <appender name="fileWarnLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}/log_warn.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>${logPattern}</pattern>
            <charset>${logCharset}</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logPath}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>

    <!--配置文件输出:info-->
    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}/log_info.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>${logPattern}</pattern>
            <charset>${logCharset}</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logPath}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxHistory>${maxHistory}</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>

    <springProfile name="dev">
        <!--可以输出项目中debug日志,包括mybatis是sql日志-->
        <logger name="com.xxw" level="INFO"/>
        <root level="INFO">
            <appender-ref ref="consoleLog"/>        <!--控制台-->
<!--            <appender-ref ref="fileErrorLog"/>-->
<!--            <appender-ref ref="fileWarnLog"/>-->
<!--            <appender-ref ref="fileInfoLog"/>-->
        </root>
    </springProfile>
    <springProfile name="prod">
        <logger name="com.xxw" level="INFO"/>
        <root level="INFO">
            <appender-ref ref="fileErrorLog"/>      <!--error文件-->
            <appender-ref ref="fileWarnLog"/>       <!--warn文件-->
            <appender-ref ref="fileInfoLog"/>       <!--info文件 -->
        </root>
    </springProfile>
</configuration>
统一返回结果处理
	一:创建enum枚举(先创建类,改class为enum)
@Getter
public enum  ResultCodeEnum {
    SUCCESS(true,200, "成功!"),
    FAIL(false,400,"失败!"),
    PARAM_ERROR(false,4001,"参数出错"),
    FILE_UPLOAD_ERROR(false,4002,"文件上传失败"),
    EXCEL_DATA_IMPORT_ERROR(false,4003,"excel表上传失败"),
    BAD_SQL_GRAMMAR(false,4004,"sql语法出现错误"),
    JSON_PARSE_ERROR(false,4005,"json数据读取(解析)出错"),
    UNKNOWN_ERROR(false,100000, "未知错误!");
    private Boolean success;//响应是否成功
    private Integer code;//返回码
    private String message;//返回信息
    ResultCodeEnum(Boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }
}
	二:全局统一返回结果集类
@Data
public class Result {
    private Boolean success;//是否成功
    private Integer code;//返回码
    private String message;//返回信息
    //返回的数据
    private Map<String,Object> data = new HashMap<String, Object>();
    private Result(){}
    //注意静态方法可以直接使用类名去调用(省略了new对象)
    public static Result ok(){
        Result result =new Result();
        result.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        result.setCode(ResultCodeEnum.SUCCESS.getCode());
        result.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return result;
    }
    public static Result error(){
        Result result =new Result();
        result.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
        result.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
        result.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());
        return result;
    }
    public Result data(Map<String,Object> map){
        this.setData(map);
        return this;
    }
    public Result data(String key,Object value){
        this.data.put(key,value);
        return this;
    }
    // 自定义--> 例如:return Result.ok().message("获取数据成功").data(list)
    public Result message(String message){
        this.setMessage(message);
        return this;
    }
    public Result code(Integer code){
        this.setCode(code);
        return this;
    }
    public Result success(Boolean success){
        this.setSuccess(success);
        return this;
    }
    //    异常处理使用枚举数据的方法
    public static Result setResult(ResultCodeEnum resultCodeEnum){
        Result result=new Result();
        result.setSuccess(resultCodeEnum.getSuccess());
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }
}
异常处理
	一: 自定义异常类
@Data
public class XxwException extends RuntimeException{
    private Integer code;
    @Override
    public String toString() {
        return "XxwException{" +"code=" + code +", message=" + this.getMessage() +'}';
    }
	二:全局异常处理类
@ControllerAdvice       // 全局异常处理/全局数据绑定/全局数据预处理(三个主要作用)
@Slf4j              //log.error(e.getMessage());日志记录器(记录异常message)
public class GlobalExceptionHandler {
    //全局的对应方法
    @ExceptionHandler(Exception.class)      //出现exception异常,自动调用error方法
    @ResponseBody       //异常返回给前端也是json形式
    public Result error(Exception e){
//        e.printStackTrace();                //后端输出异常堆栈信息
        log.error(ExceptionUtil.getMessage(e));
        return Result.error();
    }  
    @ExceptionHandler(BadSqlGrammarException.class)	//sql语法出现错误
    @ResponseBody
    public Result error(BadSqlGrammarException e){
        log.error(ExceptionUtil.getMessage(e));     //出错:log.error(e.getMessage())不输出详细堆栈错误信息
        //return Result.error().code(4001).message("sql语法出现错误");
        return Result.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR);
    }
    @ExceptionHandler(HttpMessageNotReadableException.class)	//json数据读取(解析)出错
    @ResponseBody
    public Result error(HttpMessageNotReadableException e){
        log.error(ExceptionUtil.getMessage(e));
        return  Result.setResult(ResultCodeEnum.JSON_PARSE_ERROR);
    }
    //自定义异常(运行时)(例如上传参数为出现错误时,抛这个)
    @ExceptionHandler(XxwException.class)
    @ResponseBody
    public Result error(XxwException e){
        log.error(ExceptionUtil.getMessage(e));
        return  Result.error().message(e.getMessage()).code(e.getCode());
    }
}
token环境搭建 参考详细资料
	后端搭建基本完成

vue前端搭建

	Vue.js是前端一个比较火的MVVM框架, 是一套构建用户界面的渐进式框架
	Vue 只关注视图层, 采用自底向上增量开发的设计
	Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件
vue及脚手架(vue cli)的安装
首先安装好node.js,它包括了npm。然后,再利用npm安装淘宝镜像的cnpm
  1. 下载安装node.js 官网
    node -v 与 npm -v 查看是否安装与版本
  2. 更换为淘宝镜像
    npm install -g cnpm --registry=https://registry.npm.taobao.org
  3. 升级一下npm(安装Vue需要npm的版本大于3)
    cnpm install cnpm -g
  4. 安装vue
    cnpm install vue
  5. 安装vue脚手架(要全局安装)
    cnpm install -g vue-cli (vue -V检查版本)
    全局安装是将安装包放在 /usr/local 下或者你 node 的安装目录,以后可以直接在命令行里使用(命令新建项目不用再次安装)
vue cli脚手架创建项目
vue ui   终端打开vue脚手架
  1. 选择项目路径,以及项目的名称,选预设设置(下面是手动版)
  2. 功能勾选
    (默认选)Babel-----Linter/Formatter(加上) //Router-----使用配置文件(插件将有各自的配置文件)
  3. Pick a linter/formatter config(选ESLint+Standard config)配置文件的格式
  4. 添加插件
    vue-cli-plugin-elment 改默认配置(import on demand按需导入)
  5. 添加运行依赖 axios
    可以运行写代码了,差不多了

你可能感兴趣的:(vue,web)