框架主要使用spring boot + mybatis ,可参考spring mvc结构解读代码。
项目结构
-java 核心java 代码
-priv.dl.base 主要包名
-system 系统功能 例如登录、注册等系统常用功能
-util 工具类
-exam 模块相关-resources 资源
config 配置
sql 数据库
static 静态文件,例如js、html、css等文件
template thymeleaf 模板
application.yml spring配置文件
logback.xml 系统日志配置
控制器(Controller)
浏览器请求地址,例如/loign/admin_login,接受到请求后,找到注解是
@RequestMapping(“login”)的loginController控制器,调用它的admin_login方法。
列表(list)
- 参数 item 查询条件,如UserController下list方法的第一个参数User item,作为列表查询条件,比如User.role = 1则是查询角色=1的所有用户
- 参数 key,关键字模糊查询,例如搜索用户名中含有101的用户
- 参数 p,分页,当前第几页
- 参数 model,用来向html传参数,渲染模板
@RequestMapping("list")
public String list(Users item, String key, Integer p,Model model){
//默认分页大小
int pageSize = 10;
if(item == null){
item = new Users();
}
//查询用户列表
List<Users> list = usersService.page(item,key,p,pageSize);
//统计共计多少条
Integer count = Math.toIntExact(usersService.count(item, key));
//分页字符串生成
PageUtil pageUtil = new PageUtil(pageSize, count,(p==null?1:p));
//把参数传向页面
model.addAttribute("page", pageUtil.page());
model.addAttribute("list", list);
model.addAttribute("count", count);
model.addAttribute("role_list", roleService.page(new Role(),null, 1, 1000));
model.addAttribute("condition", item);
//使用模板 templates/system/sys_user/list 渲染
return template_path + "list";
}
新增 (add)
@RequestMapping(“add”)注释的方法,跳转到新增页面
新增(do_add)
@RequestMapping(“do_add”),将add页面提交的表单数据处理后,保存到数据库中。例如userService.insert(item)就是调用用户服务层,把数据插入到数据库中。
编辑 (edit)
@RequestMapping(“edit”) 跳转到编辑页面,具体路径参考list方法
编辑保存(update)
@RequestMapping(“update”) 保存提交的信息,例如 userService.update(item)就是调用用户服务层,把数据保存到数据库
删除(del)
@RequestMapping(“del”) 删除指定ID的信息。
服务层(service)
主要包名是service,例如UserService 就是用户相关的服务,比如保存,新增,删除,查询等操作。调用数据库服务层(dao)来向控制层提供服务。
新增 (insert)
把数据插入到数据库中
public int insert(Users item) {
return mapper.insertSelective(item);
}
根据ID查找
findByKey(Long key)根据ID查找信息
public Users findByKey(Long key) {
return mapper.selectByPrimaryKey(key);
}
删除
del(long key) 根据ID删除信息
public void del(Long key) {
mapper.deleteByPrimaryKey(key);
}
更新
update(Users item) 根据ID编辑信息
public void update(Users item) {
mapper.updateByPrimaryKeySelective(item);
}
分页查询
page(Users item, String key, Integer p, Integer size);分页查询信息列表,具体可参考controller/list 介绍
统计共计多少条
count(Users item,String key, Integer p, Integer size)
数据库操作层
指dao包下文件,主要是mybatis的mapper文件,使用mybatis 语法操作数据库。例如UserMapper是指对用户表的数据库操作。其中代码使用mybatis生成(具体可百度mybatis 生成).
由于代码是生成的,所以这里不做介绍,可根据单词对应翻译即可,比如insert插入,update更新,select 查询,count计数,del删除。
模型 Model
主要指model包下文件,是由mybatis 代码生成工具生成。主要有俩个部分组成,实体类Entity和 Mybatis 的Example帮助类。Entity对应数据库字段,例如用户表(sys_users)对应Users类,Users类的userId字段 对应数据库用户表(sys_users)的用户ID(user_id)字段。
例如**UsersExample** 类主要负责用户(Users类,sys_users表)数据库查询SQL构建,为每个字段都创建Criterion,如果数据库表字段很多,该文件会比较庞大。理论上只要该类足够完善,可以生成任何条件查询。一般由Mybatis-Generator生成,详细参见官方文档。
工具类
工具类是指一些常用工具类,提供一些基础操作,比如文件压缩,分页处理,文件上传,文件下载等工作。
CustomerShiro
是shiro集成类,主要负责权限验证,请参考Apache Shiro框架,中文文档。
FileUtil
文件操作
FileDownload
文件下载
JsonResults
Ajax返回数据格式
PageUtil
分页工具类
配置文件 Config
config 包下文件,主要负责系统相关配置,例如静态变量等。
CMS
常用配置,如文件上传目录,文件大小限制等
CONST
常用静态变量,例如系统名称,版权所有
InterceptorConfig
拦截器配置,静态资源映射
ShiroConfig
Spring 框架集成Shiro配置,参见官方文档,中文文档。
SystemConfig
系统配置文件,主要包含了对Ueditor,文件上传等系统通用配置信息的引入。
UEditor是由百度web前端研发部开发的所见即所得的开源富文本编辑器,具有轻量、可定制、用户体验优秀等特点。基于MIT开源协议,所有源代码在协议允许范围内可自由修改和使用。
UeConfig主要配置富文本中的文件上传目录。
//文件上传配置
public static String UPLOAD_PATH;
public static String UPLOAD_IMG_PATH;
public static String UPLOAD_IMG_MAX_SIZE;
public static String UPLOAD_FILE_PATH;
public static String UPLOAD_FILE_MAX_SIZE;
//站点信息配置
public static String SITE_TITLE;
public static String SITE_DESCRIPTION;
public static String SITE_KEY;
public static String SITE_CRPT;
public static int SITE_PAGE_SIZE;
public static String SITE_VERIFY_CODE;
public static int SITE_LATEST_YEAR_SETTING;
//UEditor 富文本配置
public static String UE_IMG_SAVE_PATH;
public static Long UE_IMG_MAX_SIZE;
public static String UE_IMG_ROOT_PATH;
public static String[] UE_IMG_ALLOW_FILES;
public static String UE_FILE_SAVE_PATH;
public static Long UE_FILE_MAX_SIZE;
public static String UE_FILE_ROOT_PATH;
public static String[] UE_FILE_ALLOW_FILES;
public static String UE_IMG_ACTION_NAME;
public static String UE_IMG_FIELD_NAME;
public static Boolean UE_IMG_COMPRESS_ENABLE;
public static Integer UE_IMG_COMPRESS_BORDER;
public static String UE_IMG_URL_PREFIX;
public static String UE_IMG_PATH_FORMAT;
public static String UE_IMG_INSERT_ALIGN;
public static String UE_FILE_ACTION_NAME;
public static String UE_FILE_FIELD_NAME;
public static String UE_FILE_URL_PREFIX;
public static String UE_FILE_PATH_FORMAT;
静态资源resources
config
基础配置(已舍弃)
static
静态文件,例如JS,CSS文件
templates
模板文件,控制器调用模板文件夹下的模板来渲染后返回到浏览器
例如 登录控制器:
项目路径/路径1/路径2 就是登录页面的浏览器访问地址
控制器接收到请求后,返回了模板,具体位置参考
resource/template/ + 模板位置
application.properties(application.dev.yml)
spring boot 配置文件,使用方法
logback.xml
日志配置文件,配置规则
其他功能简介
文件上传
html 代码
<div class="form-group">
<label class="col-xs-3 control-label no-padding-right">头像</label>
<div class="col-xs-9">
<input type="hidden" name="avatar" id="avatar" th:value="${item.avatar}"/>
<label class="ace-file-input" style="width:169px;float:left;">
<input type="file" data-id="avatar" class="upload-input" />
<span class="ace-file-container" data-title="Choose">
<span class="ace-file-name" data-title="No File ...">
<i class=" ace-icon fa fa-upload"></i>
</span>
</span>
<a class="remove" href="#">
<i class=" ace-icon fa fa-times"></i>
</a>
</label>
<img th:src="${item.avatar}" class="upload-preview" />
</div>
</div>
JS代码
// upload preview
$(".upload-input").change(function(){
var prev_img = $(this).parent().siblings("img.upload-preview");
var prev_download = $(this).parent().siblings(".preview-download");
var file_reader = new FileReader();
var file = $(this)[0].files[0];
var id = $(this).attr("data-id");
//文件转dataURL结构
file_reader.readAsDataURL(file);
file_reader.onload = function(){
var form_data = new FormData();
//表单中添加文件
form_data.append("file", file);
if(prev_img != null && prev_img.length > 0){
prev_img.attr("src", this.result);
}
//文件上传提交
$.ajax({
url:"/upload/do_upload",
type:"post",
data:form_data,
processData:false,
contentType:false,
dataType:"json",
success:function(data){
layer.msg(data['result_msg']);
$("#" + id).val(data['result_obj']['name']);
if(prev_download != null && prev_download.length > 0){
prev_download.html("<a target='_blank' href='"+data['result_obj']['name']+"'> 下载</a>")
}
}
})
}
});
java 代码 UploadController
@Log("上传文件")
@RequestMapping("do_upload")
@ResponseBody
public JsonResults doUpload(@RequestParam("file")MultipartFile file) throws IOException {
JsonResults res = new JsonResults();
//获取文件名
String name = file.getOriginalFilename();
log.debug("file-name:" + file.getName());
log.debug("file-origin:" + file.getOriginalFilename());
//获取后缀名
name = System.currentTimeMillis() + name.substring(name.lastIndexOf("."));
//保存至上传文件夹目录
file.transferTo(new File(SystemConfig.UPLOAD_PATH + SystemConfig.UPLOAD_IMG_PATH + name));
//返回结果
res.put("name", SystemConfig.UPLOAD_IMG_PATH + name);
res.setResult_msg("文件上传成功");
return res;
}
视频播放插件
jsmodern.js
邮件发送
详情参见:org.springframework.mail.javamail.JavaMailSender
public void sendMail(String to, String subject, String content) throws MessagingException {
//SimpleMailMessage message = new SimpleMailMessage();
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper;
helper = new MimeMessageHelper(message, true);
helper.setFrom(from);//发送自
helper.setTo(to);//发送给
helper.setSubject(subject);//主题
helper.setText(content, true);//内容
mailSender.send(message);//发送
}
密码邮箱找回
/**
* 重置密码页面
* @return
*/
@RequestMapping(value="reset_pwd", method = RequestMethod.GET)
public String rest_pwd_page(){
return "system/login/reset_pwd";
}
/**
* 重置密码
* @param code
* @return
*/
@RequestMapping(value="reset_pwd", method = RequestMethod.POST)
@ResponseBody
public JsonResults reset_pwd(String code, String email, String pwd){
//检验验证码
if(verifyEmailCode(code)){
//验证码正确,修改信息
Users user = usersService.findByEmail(email);
user.setLoginPwd(pwd);
//置空
SecurityUtils.getSubject().getSession().removeAttribute("email_code");
usersService.update(user);
jsonResults = new JsonResults();
}else{
jsonResults.setResult_msg("验证码错误");
jsonResults.setResult_code(false);
}
return jsonResults;
}
/**
* 发送邮件,找回密码
* @param email
* @param request
* @return
*/
@RequestMapping("send_mail")
@ResponseBody
public JsonResults sendMail(String email, HttpServletRequest request){
jsonResults = new JsonResults();
//验证邮箱的有效性
Users user = usersService.findByEmail(email);
if(user == null){
jsonResults.setResult_code(false);
jsonResults.setResult_msg("用户不存在");
return jsonResults;
}
String code = "";
for(int i=0; i<4; i++){
code += randomChar();
}
try {
SecurityUtils.getSubject().getSession().setAttribute("email_code", code);
mailService.sendMail(email, "找回密码", "验证码:<strong>" + code + "</strong>");
log.info("找回密码发送邮件:" + email);
jsonResults.setResult_msg("验证码已发送邮箱");
} catch (MessagingException e) {
e.printStackTrace();
jsonResults.setResult_code(false);
jsonResults.setResult_msg("邮件发送失败");
}
return jsonResults;
}
缓存策略
代码
package priv.dl.base.system.dao;
//缓存策略 永久缓存
@CacheNamespace(implementation = PerpetualCache.class)
public interface DictMapper
PerpetualCache 永久缓存
原理
封装了HashMap,用ID和其他缓存区分,数据存储到Map<Object,Object>中。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return this.cache.size();
}
public void putObject(Object key, Object value) {
this.cache.put(key, value);
}
public Object getObject(Object key) {
return this.cache.get(key);
}
public Object removeObject(Object key) {
return this.cache.remove(key);
}
public void clear() {
this.cache.clear();
}
public boolean equals(Object o) {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if (this == o) {
return true;
} else if (!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}
public int hashCode() {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}
系统标题修改
在application-dev.yml文件中
system:
site:
title: 系统标题