一、说明
本后台代码实现的功能是针对于商户后台,主要实现图片管理,餐桌管理,菜品管理,订单管理,评分管理,员工信息管理等模块;
并且该项目为了高效快捷,所以采用SpringBoot构建项目,MyBatisPlus做持久化框架,MySQL做数据存储;
二、技术点
SpringBoot
MyBatisPlus
MySQL
OSS文件服务器
三、创建SpringBoot项目
名称:quick-dine-sys-store
四、依赖引入
<properties> <java.version>1.8</java.version> <mybatis-plus.version>3.4.0</mybatis-plus.version> <velocity.version>2.0</velocity.version> <swagger.version>2.7.0</swagger.version> <aliyun.oss.version>3.1.0</aliyun.oss.version> <jodatime.version>2.10.1</jodatime.version> <commons-fileupload.version>1.3.1</commons-fileupload.version> <commons-io.version>2.6</commons-io.version> <commons-lang.version>3.9</commons-lang.version> <fastjson.version>1.2.28</fastjson.version> <commons-dbutils.version>1.7</commons-dbutils.version> </properties> <dependencies> <!--web启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--MySQL驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--lombok工具--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--数据访问,mybatisplus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>${velocity.version}</version> </dependency> <!--swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> </dependency> <!--swagger ui--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency> <!--aliyunOSS--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun.oss.version}</version> </dependency> <!--日期时间工具--> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${jodatime.version}</version> </dependency> <!--文件上传--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload.version}</version> </dependency> <!--commons-io--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io.version}</version> </dependency> <!--commons-lang3--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang.version}</version> </dependency> <!--json--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!--测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
五,代码生成器
在test中创建CodeGenerator类
创建BaseEntity
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class BaseEntity implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "ID") @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty(value = "创建时间") @TableField(fill = FieldFill.INSERT) private Date createTime; @ApiModelProperty(value = "更新时间") @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; }
编写代码生成测试类
/** * @Package: cn.liuliang.quickdinesysstore * @Author: liuliang * @CreateTime: 2020/10/24 - 9:59 * @Description: 代码生成器 */ public class CodeGenerator { @Test public void genCode() { String moduleName = "t"; // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("j3_liuliang"); gc.setOpen(false); //生成后是否打开资源管理器 // gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I gc.setIdType(IdType.ASSIGN_ID); //主键策略 gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 gc.setSwagger2(true);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://192.168.159.98:3306/quick_dine_sys?serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); //pc.setModuleName(moduleName); //模块名 pc.setParent("cn.liuliang.quickdinesysstore"); pc.setController("controller"); pc.setEntity("entity"); pc.setService("service"); pc.setMapper("mapper"); mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 strategy.setTablePrefix(moduleName + "_");//设置表前缀不生成 strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名 strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀 //设置BaseEntity strategy.setSuperEntityClass("cn.liuliang.quickdinesysstore.base.BaseEntity"); // 填写BaseEntity中的公共字段 strategy.setSuperEntityColumns("id", "create_time", "update_time"); //自动填充 TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT); TableFill gmtModified = new TableFill("update_time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtModified); strategy.setTableFillList(tableFills); strategy.setRestControllerStyle(true); //restful api风格控制器 strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符 mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } }
效果
六、配置Swagger2
前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
可测性 (直接在接口文档上进行测试,以方便理解业务)
创建Swagger2配置类:Swagger2Config
/** * @Package: cn.liuliang.quickdinesysstore.config * @Author: liuliang * @CreateTime: 2020/10/24 - 10:50 * @Description: */ @Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket adminApiConfig(){ return new Docket(DocumentationType.SWAGGER_2) .groupName("adminApi") .apiInfo(adminApiInfo()) .select() //只显示admin路径下的页面 //.paths(Predicates.and(PathSelectors.regex("/admin/.*"))) .build(); } private ApiInfo adminApiInfo(){ return new ApiInfoBuilder() .title("后台管理系统-API文档") .description("本文档描述了后台管理系统微服务接口定义") .version("1.0") .contact(new Contact("liuliang", "https://blog.csdn.net/qq_40399646", "j3_liuliang@163.com")) .build(); } }
在controller和entity中加入相关注解即可完成swagger的相关功能,如下:
访问:http://localhost:项目端口/swagger-ui.html#/
例如:http://localhost:9110/swagger-ui.html#/
效果
七、统一返回结果
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
列表:
{ "success": true, "code": 20000, "message": "成功", "data": { "items": [ { "id": "1", "name": "刘德华", "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余" } ] } }
分页:
{ "success": true, "code": 20000, "message": "成功", "data": { "total": 17, "rows": [ { "id": "1", "name": "刘德华", "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余" } ] } }
没有数据
{ "success": true, "code": 20000, "message": "成功", "data": {} }
失败
{ "success": false, "code": 20001, "message": "失败", "data": {} }
因此,我们定义统一结果:
{ "success": 布尔, //响应是否成功 "code": 数字, //响应码 "message": 字符串, //返回消息 "data": HashMap //返回数据,放在键值对中 }
创建如下类
ResultDTO ResultCodeEnum
位置:
/** * @Package: cn.liuliang.quickdinesysstore.base.result * @Author: liuliang * @CreateTime: 2020/10/24 - 13:51 * @Description: */ @Data @ApiModel(value = "全局统一返回结果") public class ResultDTO { @ApiModelProperty(value = "是否成功") private Boolean success; @ApiModelProperty(value = "返回码") private Integer code; @ApiModelProperty(value = "返回消息") private String message; @ApiModelProperty(value = "返回数据") private Map<String, Object> data = new HashMap<String, Object>(); public ResultDTO(){} public static ResultDTO success(){ ResultDTO resultDTO = new ResultDTO(); resultDTO.setSuccess(ResultCodeEnum.SUCCESS.getSuccess()); resultDTO.setCode(ResultCodeEnum.SUCCESS.getCode()); resultDTO.setMessage(ResultCodeEnum.SUCCESS.getMessage()); return resultDTO; } public static ResultDTO success(String key, Object value){ ResultDTO resultDTO = new ResultDTO(); resultDTO.setSuccess(ResultCodeEnum.SUCCESS.getSuccess()); resultDTO.setCode(ResultCodeEnum.SUCCESS.getCode()); resultDTO.setMessage(ResultCodeEnum.SUCCESS.getMessage()); resultDTO.data.put(key,value); return resultDTO; } public static ResultDTO error(){ ResultDTO resultDTO = new ResultDTO(); resultDTO.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess()); resultDTO.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode()); resultDTO.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage()); return resultDTO; } public static ResultDTO setResult(ResultCodeEnum resultCodeEnum) { ResultDTO resultDTO = new ResultDTO(); resultDTO.setSuccess(resultCodeEnum.getSuccess()); resultDTO.setCode(resultCodeEnum.getCode()); resultDTO.setMessage(resultCodeEnum.getMessage()); return resultDTO; } public ResultDTO success(Boolean success) { this.setSuccess(success); return this; } public ResultDTO message(String message) { this.setMessage(message); return this; } public ResultDTO code(Integer code) { this.setCode(code); return this; } public ResultDTO data(String key, Object value) { this.data.put(key, value); return this; } public ResultDTO data(Map<String, Object> map) { this.setData(map); return this; } }
/** * @Package: cn.liuliang.quickdinesysstore * @Author: liuliang * @CreateTime: 2020/10/24 - 13:52 * @Description: */ @Getter @ToString public enum ResultCodeEnum { SUCCESS(true, 20000, "成功"), UNKNOWN_REASON(false, 20001, "未知错误"), BAD_SQL_GRAMMAR(false, 21001, "sql语法错误"), JSON_PARSE_ERROR(false, 21002, "json解析异常"), PARAM_ERROR(false, 21003, "参数不正确"), FILE_UPLOAD_ERROR(false, 21004, "文件上传错误"), FILE_DELETE_ERROR(false, 21005, "文件刪除错误"), VIDEO_UPLOAD_ALIYUN_ERROR(false, 22001, "视频上传至阿里云失败"), VIDEO_UPLOAD_TOMCAT_ERROR(false, 22002, "视频上传至业务服务器失败"), VIDEO_DELETE_ALIYUN_ERROR(false, 22003, "阿里云视频文件删除失败"), FETCH_VIDEO_UPLOADAUTH_ERROR(false, 22004, "获取上传地址和凭证失败"), REFRESH_VIDEO_UPLOADAUTH_ERROR(false, 22005, "刷新上传地址和凭证失败"), URL_ENCODE_ERROR(false, 23001, "URL编码失败"), ILLEGAL_CALLBACK_REQUEST_ERROR(false, 23002, "非法回调请求"), FETCH_ACCESSTOKEN_FAILD(false, 23003, "获取accessToken失败"), FETCH_USERINFO_ERROR(false, 23004, "获取用户信息失败"), LOGIN_ERROR(false, 23005, "登录失败"), PAY_RUN(false, 25000, "支付中"), PAY_UNIFIEDORDER_ERROR(false, 25001, "统一下单错误"), PAY_ORDERQUERY_ERROR(false, 25002, "查询支付结果错误"), GATEWAY_ERROR(false, 26000, "服务不能访问"), LOGIN_PHONE_ERROR(false, 28009, "手机号码不正确"), LOGIN_MOBILE_ERROR(false, 28001, "账号不正确"), LOGIN_PASSWORD_ERROR(false, 28008, "密码不正确"), LOGIN_DISABLED_ERROR(false, 28002, "该用户已被禁用"), REGISTER_MOBLE_ERROR(false, 28003, "手机号已被注册"), LOGIN_AUTH(false, 28004, "需要登录"); 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; } }
修改菜品管理类:DishesController
@Api(value = "菜品管理", tags = "菜品管理") @RestController @RequestMapping("/dishes") public class DishesController { @Autowired private DishesService dishesService; @ApiOperation(value = "获取所有菜品信息", notes = "获取所有菜品信息") @GetMapping("/getAll") public ResultDTO getAll() { return ResultDTO.success("items", dishesService.list()); } }
效果:
八、分页查询
新建:MybatisPlusConfig配置类
/** * @Package: cn.liuliang.quickdinesysstore.config * @Author: liuliang * @CreateTime: 2020/10/24 - 10:29 * @Description: */ @EnableTransactionManagement @Configuration @MapperScan("cn.liuliang.quickdinesysstore.mapper") public class MybatisPlusConfig { /** * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
控制层代码使用分页插件
@ApiOperation(value = "获取所有菜品信息", notes = "获取所有菜品信息") @GetMapping("/getAll") public ResultDTO getAll(@ApiParam("当前页码") @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, @ApiParam("每页显示条数") @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize) { // 分页设置 Page<Dishes> pageParam = new Page<>(pageNum, pageSize); // 业务类调用分页查询方法,分页查询 IPage<Dishes> page = dishesService.page(pageParam); // 获取结果 List<Dishes> records = page.getRecords(); // 获取总条数 long total = page.getTotal(); return ResultDTO.success().data("total", total).data("rows", records); }
效果:
{ "success": true, "code": 20000, "message": "成功", "data": { "total": 4, "rows": [ { "id": 1, "createTime": "2020-10-24 10:39:00", "updateTime": "2020-10-24 10:39:02", "dishesName": "麻婆豆腐", "dishesTypeId": 1, "briefIntroduction": "好处不上火,还便宜", "imageUrl": null, "price": 18 }, { "id": 2, "createTime": "2020-10-24 14:32:03", "updateTime": "2020-10-24 14:32:04", "dishesName": "辣椒炒肉", "dishesTypeId": 1, "briefIntroduction": null, "imageUrl": null, "price": 26 } ] } }
九、自动填充
创建类:MyMetaObjectHandler
@Component public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入数据时,填充字段 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { //可以查看官网 自动填充那一快 //第一种:this.setFieldValByName("createTime", new Date(), metaObject); //第二种: Date.class 要与实体类中字段的类型一样,否者就是不填充 strict:严格的 this.strictInsertFill(metaObject, "createTime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用) //this.setFieldValByName("updateTime", new Date(), metaObject); this.strictInsertFill(metaObject, "updateTime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用) } /** * 修改数据时,填充字段 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { //this.setFieldValByName("updateTime", new Date(), metaObject); this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); // 起始版本 3.3.0(推荐) } }
十、统一异常处理
我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理
创建统一异常处理器:GlobalExceptionHandler
自定义异常类:QuickException
/** * @Package: cn.liuliang.quickdinesysstore.handler * @Author: liuliang * @CreateTime: 2020/10/24 - 15:24 * @Description: */ @Slf4j @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody public ResultDTO error(Exception e) { log.error("error", e); return ResultDTO.error(); } @ExceptionHandler(BadSqlGrammarException.class) @ResponseBody public ResultDTO error(BadSqlGrammarException e) { log.error("error", e); return ResultDTO.setResult(ResultCodeEnum.BAD_SQL_GRAMMAR); } @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseBody public ResultDTO error(HttpMessageNotReadableException e) { log.error("error", e); return ResultDTO.setResult(ResultCodeEnum.JSON_PARSE_ERROR); } @ExceptionHandler(QuickException.class) @ResponseBody public ResultDTO error(QuickException e) { log.error("error", e); return ResultDTO.error().message(e.getMessage()).code(e.getCode()); } }
/** * @Package: cn.liuliang.quickdinesysstore.exception * @Author: liuliang * @CreateTime: 2020/10/24 - 15:27 * @Description: */ @Data public class QuickException extends RuntimeException { private Integer code; public QuickException(String message, Integer code) { super(message); this.code = code; } public QuickException(ResultCodeEnum resultCodeEnum) { super(resultCodeEnum.getMessage()); this.code = resultCodeEnum.getCode(); } @Override public String toString() { return "GuLiException{" + "code=" + code + ",message=" + this.getMessage() + '}'; } }
案例:
@Override public ResultDTO add(DishesVO dishesVO) { Dishes dishes = new Dishes(); BeanUtils.copyProperties(dishesVO, dishes); boolean save = this.save(dishes); if (save) { return ResultDTO.success(); } else { // 抛出自定义异常 throw new QuickException("插入失败", 400); } }
效果:
十一、统一日志处理
resources 中创建 logback-spring.xml (默认日志的名字,必须是这个名字)
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="10 seconds"> <contextName>logback</contextName> <property name="log.path" value="E:/java_work_file/quick-dine-sys-store/log" /> <!--控制台日志格式:彩色日志--> <!-- magenta:洋红 --> <!-- boldMagenta:粗红--> <!-- cyan:青色 --> <!-- white:白色 --> <!-- magenta:洋红 --> <property name="CONSOLE_LOG_PATTERN" value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <!--文件日志格式--> <property name="FILE_LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" /> <!--编码--> <property name="ENCODING" value="UTF-8" /> <!--输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <!--日志级别--> <level>DEBUG</level> </filter> <encoder> <!--日志格式--> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!--日志字符集--> <charset>${ENCODING}</charset> </encoder> </appender> <!--输出到文件--> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--日志过滤器:此日志文件只记录INFO级别的--> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_info.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径以及格式 --> <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> </appender> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 日志过滤器:此日志文件只记录WARN级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>WARN</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_warn.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> </appender> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 日志过滤器:此日志文件只记录ERROR级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <!-- 正在记录的日志文件的路径及文件名 --> <file>${log.path}/log_error.log</file> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文件保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> </appender> <!--开发环境--> <springProfile name="dev"> <!--可以灵活设置此处,从而控制日志的输出--> <root level="DEBUG"> <appender-ref ref="CONSOLE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> <!--生产环境--> <springProfile name="pro"> <root level="ERROR"> <appender-ref ref="ERROR_FILE" /> </root> </springProfile> </configuration>
// 输出错误堆栈信息,可以在项目的全局异常处理中使用 log.error("error", e);
到此项目的基本边角就已经是搭建完成了,如果后期有变动的话,会第一时间来补充的
结束语
- 下期开始实现快捷餐饮店家后台项目的功能模块
- 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。
- 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。
- 感谢您的阅读,十分欢迎并感谢您的关注。