此项目地址已在码云上,地址:https://gitee.com/youxiaxiaomage/jfl-platform-parent
此项目主要使用了Druid
,MyBatis-Plus
,redis
,dubbo
,shiro
,SpringMVC
,thymeleaf
,layUI
,Bootstrap
后台管理系统,以及相应的restful
接口。
1.基类实体类(BaseModel)
@Data
public abstract class BaseModel implements Serializable
{
/**
* id
*/
@JsonSerialize(using=ToStringSerializer.class)
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 状态
*/
@TableField("enable")
public Integer enable;
/**
* 备注
*/
@TableField("remark")
private String remark;
/**
* 创建人
*/
@TableField("create_by")
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
@TableField("create_time")
private Date createTime;
/**
* 更新时间
*/
@TableField("update_by")
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
@TableField("update_time")
private Date updateTime;
/** 请求参数 */
@TableField(exist=false)
private Map<String, Object> params;
}
此基类实体类主要是基于Mybatis-Plus
插件,表名定义,字段定义都使用了该方式。其中,主键id
使用长整型,其长度为20位,页面展示时,精度会丢失,因此该字段序列化为字符串返回。
这些字段都是在定义表时,必须包含的字段,其中请求参数params
用于接收前端的参数,非数据库字段。
2.基类Mapper(BaseMapper)
public interface BaseMapper<T extends BaseModel> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T>
{
List<Long> selectIdPage(@Param("cm") T paramT);
List<Long> selectIdPage(@Param("cm") Map<String, Object> paramMap);
List<Long> selectIdPage(RowBounds paramRowBounds, @Param("cm") Map<String, Object> paramMap);
List<Long> selectIdPage(RowBounds paramRowBounds, @Param("cm") T paramT);
List<T> selectPage(RowBounds paramRowBounds, @Param("cm") Map<String, Object> paramMap);
Integer selectCount(@Param("cm") Map<String, Object> paramMap);
}
分页重写了mybatisplus
的BaseMapper
,其中列出的方法需要在***Mapper.xml
中实现,具体参见对应的xml文件。
3.基类Service(BaseService)
public interface BaseService<T extends BaseModel> extends IService<T>
{
/**
* 修改
* @param record
* @param userId
* @return
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
T update(T record) throws BusinessException, ValidateException;
/**
* 逻辑删除
* @param ids 删除的id
* @param userId 用户id
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
void del(List<Long> ids, Long userId) throws BusinessException, ValidateException;
/**
* 逻辑删除单条
* @param id
* @param userId
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
void del(Long id, Long userId) throws BusinessException, ValidateException;
/**
* 物理删除
* @param id
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
void delete(Long id) throws BusinessException, ValidateException;
/**
* 物理删除
* @param entity
* @return
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
Integer deleteByEntity(T entity) throws BusinessException, ValidateException;
/**
* 物理删除
* @param columnMap
* @return
* @throws BusinessException
* @throws ValidateException
* @see [类、类#方法、类#成员]
*/
@Transactional
Integer deleteByMap(Map<String, Object> columnMap) throws BusinessException, ValidateException;
...
}
此类中部分方法未列举。
4.基类Service实现类(BaseServiceImpl)
public class BaseServiceImpl<T extends BaseModel, M extends BaseMapper<T>> extends ServiceImpl<BaseMapper<T>, T> implements BaseService<T>
{
/**
* logger 日志
*/
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* mapper
*/
@Autowired
protected M mapper;
@Transactional
@Override
public T update(T record) throws BusinessException, ValidateException
{
if (record.getId() != null)
{
record.setUpdateTime(new Date());
this.mapper.updateById(record);
}
else
{
record.setCreateTime(new Date());
record.setUpdateTime(new Date());
record.setUpdateBy(record.getCreateBy());
this.mapper.insert(record);
}
return this.mapper.selectById(record.getId());
}
/**
* 逻辑删除
* @param ids
* @param userId
* @throws BusinessException
* @throws ValidateException
* @see com.jfl.base.BaseService#del(java.util.List, java.lang.Long)
*/
@Override
@Transactional
public void del(List<Long> ids, Long userId) throws BusinessException, ValidateException
{
// lamda表达式 JDK1.8才支持
ids.forEach(id -> del(id, userId));
}
/**
* 逻辑删除
* @param id
* @param userId
* @throws BusinessException
* @throws ValidateException
* @see com.jfl.base.BaseService#del(java.lang.Long, java.lang.Long)
*/
@Override
@Transactional
public void del(Long id, Long userId) throws BusinessException, ValidateException
{
try
{
T record = this.queryById(id);
record.setEnable(0);
record.setUpdateBy(userId);
record.setUpdateTime(new Date());
this.mapper.updateById(record);
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* 物理删除
* @param id
* @throws BusinessException
* @throws ValidateException
* @see com.jfl.base.BaseService#delete(java.lang.Long)
*/
@Override
@Transactional
public void delete(Long id) throws BusinessException, ValidateException
{
try
{
this.mapper.deleteById(id);
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* 物理删除
* @param entity
* @return
* @throws BusinessException
* @throws ValidateException
* @see com.jfl.base.BaseService#deleteByEntity(com.jfl.base.BaseModel)
*/
@Override
@Transactional
public Integer deleteByEntity(T entity) throws BusinessException, ValidateException
{
Wrapper<T> wrapper = new UpdateWrapper<T>(entity);
return this.mapper.delete(wrapper);
}
/**
* 物理删除
* @param columnMap
* @return
* @throws BusinessException
* @throws ValidateException
* @see com.jfl.base.BaseService#deleteByMap(java.util.Map)
*/
@Override
@Transactional
public Integer deleteByMap(Map<String, Object> columnMap) throws BusinessException, ValidateException
{
return this.mapper.deleteByMap(columnMap);
}
/**
* 根据Id查询
* @param id
* @return
* @see com.jfl.base.BaseService#queryById(java.lang.Long)
*/
@Override
public T queryById(Long id)
{
return this.mapper.selectById(id);
}
/**
* 分页查询
* @param params 其中params必须为数据库中字段
* @return
* @see com.jfl.base.BaseService#query(java.util.Map)
*/
@Override
public PageInfo<T> query(Map<String, Object> params)
{
// 默认当前页为1
int pageNum = 1;
// 默认页码大小为10
int pageSize = 10;
// 默认计算count
String orderBy = null;
if(params.get("pageNum") != null && StringUtils.isNotBlank(params.get("pageNum") + ""))
{
pageNum = Integer.valueOf(params.get("pageNum").toString());
params.remove("pageNum");
}
if(params.get("pageSize") != null && StringUtils.isNotBlank(params.get("pageSize") + ""))
{
pageSize = Integer.valueOf(params.get("pageSize").toString());
params.remove("pageSize");
}
if(params.get("orderBy")!= null && StringUtils.isNotBlank(params.get("orderBy") + ""))
{
orderBy = params.get("orderBy").toString();
params.remove("orderBy");
}
// 设置分页的参数
PageHelper.startPage(pageNum, pageSize, orderBy);
// 有效数据
params.put("enable", 1);
// 根据条件查询
List<T> list = this.mapper.selectByMap(params);
// 分装成分页对象
return new PageInfo<T>(list);
}
/**
* 根据实体参数分页查询
* @param entity
* @param rowBounds
* @return
* @see com.jfl.base.BaseService#query(com.jfl.base.BaseModel, com.github.pagehelper.PageInfo)
*/
@Override
public PageInfo<T> query(T entity, PageInfo<T> rowBounds)
{
Page<T> page = new Page<T>();
try
{
BeanUtils.copyProperties(page, rowBounds);
}
catch (Exception e)
{
logger.error(ExceptionUtil.getStackTraceAsString(e));
}
// List<Long> ids = this.mapper.selectIdPage(page,entity);
return new PageInfo<T>(null);
}
/**
* 根据参数查询
* @param params
* @return
* @see com.jfl.base.BaseService#queryList(java.util.Map)
*/
@Override
public List<T> queryList(Map<String, Object> params)
{
// 根据参数获取全部数据的Id 从DB中查询
List<Long> ids = this.mapper.selectIdPage(params);
List<T> list = queryList(ids);
return list;
}
/**
* 根据Id查询 如果缓存中有则从缓存中获取,否则从DB中获取
* @param ids
* @return
* @see com.jfl.base.BaseService#queryList(java.util.List)
*/
@Override
public List<T> queryList(List<Long> ids)
{
final List<T> list = Lists.newArrayList();
if (ids != null)
{
// lamda表达式
ids.forEach(id -> list.add(queryById(id)));
}
return list;
}
@Override
public <K> List<K> queryList(List<Long> ids, Class<K> clazz)
{
final List<K> list = Lists.newArrayList();
if(ids != null)
{
for (int i = 0; i < ids.size(); i++)
{
T t = queryById(ids.get(i));
K k = InstanceUtil.to(t, clazz);
list.set(i, k);
}
}
return list;
}
/**
* 根据实体参数查询
* @param entity
* @return
* @see com.jfl.base.BaseService#queryList(com.jfl.base.BaseModel)
*/
@Override
public List<T> queryList(T entity)
{
// 先查出所有有关的id
List<Long> ids = this.mapper.selectIdPage(entity);
// 缓存中有则从缓存中取值,否则从数据库取值
List<T> list = queryList(ids);
return list;
}
/**
* 从数据库中查询
* @param params
* @return
* @see com.jfl.base.BaseService#queryFromDB(java.util.Map)
*/
@Override
public PageInfo<T> queryFromDB(Map<String, Object> params)
{
return null;
}
@Override
public PageInfo<T> queryFromDB(T entity, PageInfo<T> rowBounds)
{
return null;
}
/**
* 从数据库中查询
* @param params 表字段
* @return
* @see com.jfl.base.BaseService#queryListFromDB(java.util.Map)
*/
@Override
public List<T> queryListFromDB(Map<String, Object> params)
{
return this.mapper.selectByMap(params);
}
@Override
public PageInfo<T> selectList(PageRequest request, T record)
{
PageHelper.startPage(request.getPageNum(), request.getPageSize(), request.getOrderBy());
QueryWrapper<T> wrapper = new QueryWrapper<T>();
if (record != null)
{
wrapper = tranform(record);
if(record.getParams() != null)
{
if(StringUtils.isNotBlank(record.getParams().get("beginTime") + "") && StringUtils.isNotBlank(record.getParams().get("endTime") + ""))
{
wrapper.between("create_time", record.getParams().get("beginTime") , record.getParams().get("endTime"));
}
}
}
wrapper.eq("enable", 1);
List<T> list = this.mapper.selectList(wrapper);
return new PageInfo<T>(list);
}
/**
* 转换查询
* @param record
* @return
* @see [类、类#方法、类#成员]
*/
private QueryWrapper<T> tranform(T record)
{
QueryWrapper<T> wrapper = new QueryWrapper<T>();
Field[] fields = record.getClass().getDeclaredFields();
try
{
for (Field field : fields)
{
field.setAccessible(true);
String fieldName = field.getName();
String clazz = field.getType().getTypeName();
Method method = record.getClass().getDeclaredMethod("get"+ fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
Object obj = method.invoke(record);
if(obj != null && StringUtils.isNotBlank(obj + ""))
{
if ("java.lang.String".equals(clazz))
{
if("status".equals(fieldName))
{
wrapper.eq(ConvertUtil.underLine2Camel(fieldName), obj);
}
else
{
wrapper.like(ConvertUtil.underLine2Camel(fieldName), obj);
}
}
else if ("java.lang.Integer".equals(clazz) || "java.lang.Short".equals(clazz) || "java.lang.Long".equals(clazz))
{
wrapper.eq(ConvertUtil.underLine2Camel(fieldName), obj);
}
}
}
}
catch (Exception e)
{
logger.error("转换异常", e);
}
return wrapper;
}
/**
* 唯一性统一返回
* @param record 参数对象
* @param id 数据库中数据Id
* @return "0":存在 "1":唯一
*/
public String result(T record, Long id)
{
if(record != null && record.getId().longValue() != id.longValue())
{
return Constants.DATA_NOT_UNIQUE;
}
return Constants.DATA_UNIQUE;
}
}
主要注意分页动态sql的拼接问题,比如精确查询,模糊查询等,这个也可以转化为动态***Mapper.xml
文件。
5.控制器
@Controller
@RequestMapping("${jfl.module.system}/user")
public class SysUserController extends AbstractController
{
/**
* dubbo接口 @Reference 通过配置文件连接服务
*/
@Reference(version = "${jfl.version}")
private SysUserService sysUserService;
@Reference(version = "${jfl.version}")
private SysDeptService sysDeptService;
@Reference(version = "${jfl.version}")
private SysRoleService sysRoleService;
@Reference(version = "${jfl.version}")
private SysPostService sysPostService;
/**
* 分隔符
*/
private static final String SEG_CHAR = ",";
/**
* 用户管理界面
*
* @param modelMap
* @return
*/
@RequiresPermissions("system:user:view")
@GetMapping
public String user(ModelMap modelMap)
{
modelMap.put("user", ShiroUtils.getCurrentUser());
return Constants.MODULE_SYS_USER_PREFIX + "user";
}
/**
* 跳转用户添加页面
*
* @param modelMap
* @return
*/
@GetMapping("/add")
public String add(ModelMap modelMap)
{
modelMap.put("roles", this.sysRoleService.queryList(Maps.newHashMap()));
modelMap.put("posts", this.sysPostService.queryList(Maps.newHashMap()));
return Constants.MODULE_SYS_USER_PREFIX + "add";
}
/**
* 添加用户
*
* @param user
* @return
*/
@Log(module = Module.ROLE, value = "添加用户", type = LogType.INSERT)
@RequiresPermissions("system:user:add")
@PostMapping("/add")
@ResponseBody
public ResponseEntity<ModelMap> addSave(SysUser user)
{
user.setSalt(EncryptUtils.randomSalt());
user.setPassword(EncryptUtils.encryptPassword(user.getUserName(), user.getPassword(), user.getSalt()));
user.setCreateBy(ShiroUtils.getCurrentUserId());
this.sysUserService.saveUser(user);
return setSuccessModelMap();
}
/**
* 分页查询
*
* @param sysUser
* @return
* @see [类、类#方法、类#成员]
*/
@RequiresPermissions("system:user:list")
@PostMapping("/list")
@ResponseBody
public ResponseEntity<ModelMap> list(SysUser sysUser)
{
PageInfo<SysUser> pageInfo = this.sysUserService.selectList(ConvertRequestUtil.pageRequest(), sysUser);
return setSuccessModelMap(pageInfo);
}
/**
* 跳转用户编辑页面
*
* @param userId
* @param modelMap
* @return
*/
@GetMapping("/edit/{userId}")
public String edit(@PathVariable("userId") Long userId, ModelMap modelMap)
{
modelMap.put("user", this.sysUserService.queryById(userId));
modelMap.put("roles", this.sysRoleService.selectRolesByUserId(userId));
modelMap.put("posts", this.sysPostService.selectPostsByUserId(userId));
return Constants.MODULE_SYS_USER_PREFIX + "edit";
}
/**
* 修改保存用户
*
* @param user
* @return
*/
@Log(module = Module.ROLE, value = "修改用户", type = LogType.UPDATE)
@RequiresPermissions("system:user:edit")
@PostMapping("/edit")
@ResponseBody
public ResponseEntity<ModelMap> editSave(SysUser user)
{
if (user.getId() != null && user.getId().longValue() == 1)
{
throw new BusinessException("管理员用户,不支持修改!");
}
return setSuccessModelMap(this.sysUserService.updateUser(user));
}
/**
* 跳转用户重置密码页面
*
* @param userId
* @param modelMap
* @return
*/
@RequiresPermissions("system:user:resetPwd")
@GetMapping("/resetPwd/{userId}")
public String resetPwd(@PathVariable("userId") Long userId, ModelMap modelMap)
{
modelMap.put("user", this.sysUserService.queryById(userId));
return Constants.MODULE_SYS_USER_PREFIX + "resetPwd";
}
/**
* 保存密码操作
*
* @param user
* @return
*/
@Log(module = Module.ROLE, value = "修改密码", type = LogType.UPDATE)
@RequiresPermissions("system:user:resetPwd")
@PostMapping("/resetPwd")
@ResponseBody
public ResponseEntity<ModelMap> resetPwdSave(SysUser user)
{
// 密码加密
user.setSalt(EncryptUtils.randomSalt());
user.setPassword(EncryptUtils.encryptPassword(user.getUserName(), user.getPassword(), user.getSalt()));
this.sysUserService.update(user);
return setSuccessModelMap();
}
/**
* 删除用户 支持批量删除
*
* @param ids
* @return
*/
@Log(module = Module.ROLE, value = "删除用户", type = LogType.DELETE)
@RequiresPermissions("system:user:remove")
@PostMapping("/remove")
@ResponseBody
public ResponseEntity<ModelMap> remove(String ids)
{
String[] idAttr = ids.split(SEG_CHAR);
List<Long> list = Lists.newArrayList();
for (String id : idAttr)
{
list.add(Long.valueOf(id));
}
// 逻辑删除
this.sysUserService.deleteUsers(list, ShiroUtils.getCurrentUserId());
return setSuccessModelMap();
}
/**
* 校验用户名是否重复
*
* @param user
* @return
*/
@PostMapping("/checkUserNameUnique")
@ResponseBody
public String checkUserNameUnique(SysUser user)
{
return String.valueOf(this.sysUserService.countByUserName(user));
}
/**
* 校验邮箱是否重复
*
* @param user
* @return
*/
@PostMapping("/checkEmailUnique")
@ResponseBody
public String checkEmailUnique(SysUser user)
{
return this.sysUserService.countByEmail(user);
}
/**
* 校验邮箱是否重复
*
* @param user
* @return
*/
@PostMapping("/checkPhoneUnique")
@ResponseBody
public String checkPhoneUnique(SysUser user)
{
return this.sysUserService.countByPhone(user);
}
}
上述以系统用户控制器为例子,其中Service
都是dubbo
的接口。注意shiro
权限的配置以及日志的配置。
6.国际化的配置
主要是一些异常信息的配置message_*.properties
文件
7.页面
主要知道页面模板的配置,以及相应的shiro
权限控制的按钮状态的控制,注意分页查询,页面数据渲染等,都可以按照其他已有模块拷贝修改即可。