1.背景
用于记录公司的客户的租车还车记录。维护公司的客户信息,若客户想要租车,根据条件查询相关车辆信息,车辆信息,如果符合条件,出租信息。也需要记录,客户的还车信息。还车信息。
但是,为了适用于不同的职位,不同的用户登录到系统中,展示的菜单也不相同。不同的用户,可以有不同的角色,不同的角色可以有不同的权限,而用户的权限,可以在系统中进行配置。
2.相关表
用户表
角色表
用户角色表
权限表
角色权限表
客户表
车辆表
租车单表
还车记录表
3.包的划分
可能项目的表结构比较复杂,数据库比较庞大。开发人员相对较多。如果每个表都对应一个实体类,那么这个类放在一个包中,这个包比较臃肿。不便于开发和维护。基于这种考虑,将相关业务模块的表,使用前缀,这样相关业务的表会放在一起,同样的,相关业务的类,使用同一个包,例如:每个业务都有pojo,mapper,service,controler等等包,而使用一个包表示相关业务包裹这些包。sys.pojo,sys.mapper等等,使用包进行模块的划分。
4.搭建框架
|----com.zw
|---bussi-->存放汽车租赁业务相关的类
|---common--->系统公共类的包
|---sys--->系统设置相关的包
由于在开发中每个表,都要进行基本对CRUD操作,于是可以对整个系统的CRUD进行封装
--->BaseMapper:对表的基本的增删查改操作.同理:可以对service进行封装
--->BaseService:对基本的业务增删查改操作.
将封装的BaseMapper和BaseService放在common包中,
4.1 common包
1️⃣base包
4.1.1 BaseMapp接口
表的基础操作:增删查改 我们对来自不同的表进行CRUD的操作,添加操作还好说,没有返回值,但是查询返回的结果都不一样,所以需要指定两个泛型 F-->添加数据时,指定的类型 V-->查询返回的类型
package com.zw.common.base.mapper;
import com.zw.common.base.Query;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 表的基础操作:增删查改
* 我们对来自不同的表进行CRUD的操作,添加操作还好说,没有返回值,但是查询返回的结果都不一样,所以需要指定两个泛型
* @param <F> 添加数据时,指定的类型
* @param <V> 查询返回的类型
*/
public interface BaseMapper<F,V> {
/**
* 新增数据
* @param f 添加数据时,指定的类型
* @return
*/
int insert(F f);
/**
* 根据id删除数据
* @param id
* @return
*/
int delete(@Param("id")Integer id);
/**
* 根据id查询数据
* @param id
* @return
*/
V selectOne(@Param("id")Integer id);
/**
* 因为列表查询需要传条件,但是在这里,我们并不知道传什么,所以我们在common包里指定
* 一个类Query作为所有查询参数的基类,就是所有查询参数的父类,
* @param query
* @return
*/
List<V> selectList(Query query);
/**
* 更新数据
* @param f
* @return
*/
int update(F f);
}
以后所有的mapper类都继承这个BaseMapper类即可.
4.1.2 IBaseService接口
BaseService的增删查改相较于BaseMapper的增删查改比较特殊,对于添加操作,新增的时候,一样需要传入一个F,一样要在BaseService类上指定泛型,V不用指定,执行完某个具体的方法,我们将Result类型作为一个返回结果,Result作为所有业务操作的返回结果.
package com.zw.common.base.service;
import com.zw.common.base.Query;
import com.zw.common.Result;
/**
* BaseService的增删查改相较于BaseMapper的增删查改比较特殊,
* 对于添加操作,新增的时候,一样需要传入一个F,一样要在BaseService类上指定泛型,V不用指定,执行完某个具体的方法,
* 我们将Result类型作为一个返回结果,Result作为所有业务操作的返回结果
* @ClassName:BaseService
* @Description: 所有业务的基类
* @Author: KevinZeng
* @Date 2020/1/8 下午 7:52
*/
public interface IBaseService<F> {
/**
* 新增数据
* @param f
* @return
*/
Result add(F f);
/**
* 删除数据
* @param id
* @return
*/
Result delete(Integer id);
/**
* 根据查询数据
* @param id
* @return
*/
Result query(Integer id);
/**
* 分页查询数据
* @param query
* @return
*/
Result queryPage(Query query);
/**
* 修改数据
* @param f
* @return
*/
Result update(F f);
}
4.1.3 BaseServiceImpl类
实现IBaseService接口
package com.zw.common.base.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zw.common.Query;
import com.zw.common.Result;
import com.zw.common.base.PageInfo;
import com.zw.common.base.mapper.BaseMapper;
import com.zw.common.base.service.IBaseService;
public class BaseServiceImpl<F> implements IBaseService<F> {
/**
* BaseServiceImpl实现IBaseService接口后,service层必须有一个mapper
*/
private BaseMapper baseMapper;
/**
* 通过构造方法传一个baseMapper进来
* 即子类注入一个BaseMapper
* @param baseMapper 传入的baseMapper
*/
public BaseServiceImpl(BaseMapper baseMapper) {
this.baseMapper = baseMapper;
}
@Override
public Result add(F f) {
baseMapper.insert(f);
return new Result();
}
@Override
public Result delete(Integer id) {
baseMapper.delete(id);
return new Result();
}
@Override
public Result query(Integer id) {
Object o = baseMapper.selectOne(id);
Result rs = new Result(o);
return rs;
}
@Override
public Result queryPage(Query query) {
Page<Object> data = PageHelper.startPage(query.getPage(), query.getLimit());
/**
* Result rs = new Result(data);
* 此时的rs不能直接返回,因为前端框架layui需要四个参数进行分页操作
* 1.code
* 2.msg
* 3.data
* 4.count
* 我们需要封装出来一个Page准备这些参数
*/
/**
* com.github.pagehelper.Page类中的属性
* data.getPageNum() -->返回当前页码
* data.getPageSize()-->当前每页数据条数
* data.getTotal() -->总条数
data.getResult()-->需要进行分页的数据
*/
PageInfo page = new PageInfo(data.getPageNum(),data.getPageSize(),data.getTotal(),data.getResult());
Result rs = new Result(page);
return rs;
}
@Override
public Result update(F f) {
baseMapper.update(f);
return new Result();
}
}
4.1.4 PageInfo
为了兼容layui封装page类
package com.zw.common.base;
import java.util.List;
/**
* @ClassName:PageInfo
* @Description: TODO
* @Author: KevinZeng
* @Date 2020/1/8 下午 8:57
*/
public class PageInfo {
/**
* 当前页码
*/
private Integer page;
/**
* 当前每页数据条数
*/
private Integer limit;
/**
* 总数据条数
*/
private long count;
/**
* 符合条件的数据(需要进行分页的数据)
*/
private Object data;
public PageInfo() {
}
public PageInfo(Integer page, Integer limit, long count, Object data) {
this.page = page;
this.limit = limit;
this.count = count;
this.data = data;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
4.1.5 Query
所有查询参数的基类
因为列表查询需要传条件,但是首先在BaseMapper里,我们并不知道传什么,所以我们在common包里指定 一个类Query作为所有查询参数的基类,就是所有查询参数的父类
package com.zw.common;
/**
* @ClassName:Query
* @Description: 所有查询参数的基类
* @Author: KevinZeng
* @Date 2020/1/8 下午 7:48
*/
public class Query {
/**
* 页码默认第一页
*/
private Integer page = 1;
/**
* 每页的数据条数默认10
*/
private Integer limit = 10;
public Query(Integer page, Integer limit) {
this.page = page;
this.limit = limit;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
}
2️⃣Result类
业务操作CRUD方法将Result类型作为一个返回结果,Result作为所有业务操作的返回结果
package com.zw.common;
/**
* @ClassName:Result
* @Description: 所有业务操作的(执行)返回结果
* @Author: KevinZeng
* @Date 2020/1/8 下午 7:57
*/
public class Result {
/**
* 业务编码
*/
private Integer code;
/**
* 业务信息
*/
private String msg;
/**
* 业务数据
*/
private Object data;
/**
* 正常没有数据的Result
*/
public Result(){
this.code = CodeMsg.SUCCESS.CODE;
this.msg = CodeMsg.SUCCESS.MSG;
}
/**
* 不正常无数据的Result
* @param codeMsg 业务信息(包括业务编码和业务消息)
*/
public Result(CodeMsg codeMsg) {
this.code = codeMsg.SUCCESS.CODE;
this.msg = codeMsg.SUCCESS.MSG;
}
/**
* 正常带数据的Result
*/
public Result(Object data) {
//调用无参构造
this();
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
3️⃣CodeMsg类
package com.zw.common;
/**
* 所有信息枚举
*/
public enum CodeMsg {
SUCCESS(200,"成功"),
ERROR(110,"程序员进ICU了...");
/**
* 业务码
*/
Integer CODE;
/**
* 业务消息
*/
String MSG;
CodeMsg(Integer code, String msg) {
this.CODE = code;
this.MSG = msg;
}
}
4️⃣exception包
①BussiException
当程序出现数据异常时就抛出异常信息 有一下两种情况: 1.当数据校验不通过时,抛出异常信息 2.当数据操作出现异常时,抛出异常信息,也是为了数据回滚
package com.zw.common.exception;
/**
* 当程序出现数据异常时就抛出异常信息
* 两种情况:
* 1.当数据校验不通过时,抛出异常信息
* 2.当数据操作出现异常时,抛出异常信息,也是为了数据回滚
*/
/**
* @ClassName:BussiException
* @Description: 自定义业务异常类:
* @Author: KevinZeng
* @Date 2020/1/8 下午 9:39
*/
public class BussiException extends RuntimeException{
/**
* 异常编码
*/
private Integer code;
/**
* 异常信息
*/
private String msg;
public BussiException() {
}
public BussiException(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
5️⃣interceptor包
①LoginInterceptor
登录拦截器类,后续更