day 1
集成Spring +SpringMVC+SpringDataJpa
1 .创建maven项目 配置pom.xml文件,导入整个项目需要用到的jar包
2. 配置applicationContext.xml文件 配置Spring注解扫描,连接池,集成hibernate的jpa功能,配置 JPA事务
3. 使用jpa实现对数据库的持久化操作,配置核心文件persistence.xml
4. 提取一个分页类,用于对查询数据的分页
5. jpa与jdbc的优缺点
6. 配置多表的映射关系配置
7. 抽取工具类 BaseDomain BaseTest JpaRepostory Basequery head.jsp
问题难点总结:
高级查询
easyui的增加,删除,修改操作,dialog会话框数据回显,layout布局,左侧菜单的加载
部门,头像 的 显示
8. 解决no session的问题
<!-- 加上OpenEntityManager -->
<filter>
<filter-name>openEntity</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEntity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
9 .解决 No serializer问题
创建一个新的类(重写com.fasterxml.jackson.databind.ObjectMapper)
package cn.itsource.pss.common;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class CustomMapper extends ObjectMapper {
public CustomMapper() {
this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 设置 SerializationFeature.FAIL_ON_EMPTY_BEANS 为 false 对null的bean 不做序列化
this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
}
第二步:在applicationContext-mvc.xml中配置一下即可
<!-- Spring MVC 配置 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json; charset=UTF-8</value>
<value>application/x-www-form-urlencoded; charset=UTF-8</value>
</list>
</property>
<!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
<property name="objectMapper">
<bean class="cn.itsource.pss.common.CustomMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
显示效果图
**day 2 **
- 添加或修改employee表,用户名重复验证 checkName
- 添加用户,重复输入密码的验证
- 修改操作,因为我们没有修改图片,导致数据丢失问题的解决方法
先查询数据库,获取持久状态的对象,然后把页面的数据set到对象里面
(这种方案也是用得比较多的一种方案)
Employee tempEmployee = employeeService.get(employee.getId());
//需要修改的值就从页面里面的employee放入tempEmployee
tempEmployee.setUsername(employee.getUsername());
employeeService.save(tempEmployee);
- 修改部门信息遇到的 n -to -n 问题
解决方案,将关联对象设置为 null; - velocity 模板技术,与easycode代码生成器
day3
- shiro 身份认证,与权限
- 自定义realm 集成 AuthorizingRealm 就可以操作身份认证,跟权限
- MD5 密码加密,加盐 抽取工具类UtilMD5,给数据库所有的密码加密加盐,后面的添加或修改操作,密码都需要加密
- 实现登录验证,跟权限拦截
- 绑定回车键登录事件
- 解决登录过期,登录页面嵌套的问题
// 检查自己是否是顶级页面
if (top != window) {// 如果不
是顶级
//把子页面的地址,赋值给顶级页面显示
window.top.location.href = window.location.href;
}
- 展示用户名与注销登录操作
<shiro:user>
欢迎[<shiro:principal />]登录,<a href="${pageContext.request.contextPath}/logout">退出</a>
</shiro:user>
- 角色管理,修改权限的操作,实现如下面的效果
双击右边的一行,添加到左边表格,不能重复添加,双击左边的,移除行
注意这里修改操作的数据回显问题
day4
1. UserContext:设置与拿到当前登录用户
2. 查询登录的用户的权限,将权限信息以固定格式存入到map 将map配置 给shiro
public Map<String,String> createFilterChainDefinitionMap(){
Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
//注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
filterChainDefinitionMap.put("/login","anon");
filterChainDefinitionMap.put("*.js","anon");
filterChainDefinitionMap.put("*.css","anon");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/easyui/**","anon");
filterChainDefinitionMap.put("/images/**","anon");
filterChainDefinitionMap.put("/logout","logout"); //不登录也可以访问
//这个值之后从数据库中查询到【用户-角色-权限-资源】
//从数据库拿到数据,放到咱们的Map中
//1.拿到所有权限
List<Permission> permissions = permissionService.findAll();
//2.遍历权限,拿到权限 资源
for (Permission permission : permissions) {
String url = permission.getUrl();//资源
String sn = permission.getSn();//权限
//把路径与资源放到拦截中去
filterChainDefinitionMap.put(url,"yxbPerms["+sn+"]");
}
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
实现不同的登录用户权限不一样,可以执行的操作也不一样
3. 解决没有权限发送ajax请求,返回页面的问题
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190817231008670.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByaW1hcnlrZXkxMjM=,size_16,color_FFFFFF,t_70)解决方式,判断如果发送的是ajax请求【请求头】,出现异常就返回 ‘没有权限’
自定义过滤器
public class ItsourceYxbPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
Subject subject = this.getSubject(request, response);
if (subject.getPrincipal() == null) {
//没有登录成功后的操作
this.saveRequestAndRedirectToLogin(request, response);
} else {
//登录成功后没有权限的操作
//1.转成http的请求与响应操作
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
//2.根据请求确定是什么请求
String xRequestedWith = httpRequest.getHeader("X-Requested-With");
if (xRequestedWith != null &&"XMLHttpRequest".equals(xRequestedWith)) {
//3.在这里就代表是ajax请求
//表示ajax请求 {"success":false,"message":"没有权限"}
httpResponse.setContentType("text/json; charset=UTF-8");
httpResponse.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
}else {
String unauthorizedUrl = this.getUnauthorizedUrl();
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
WebUtils.toHttp(response).sendError(401);
}
}
}
return false;
}
}
<!-- 引用自定义的权限过滤器 -->
<property name="filters">
<map>
<entry key="yxbPerms" value-ref="yxbPermissionsAuthorizationFilter"></entry>
</map>
</property>
</bean>
<!-- 配置自定义权限过滤器 -->
<bean id="yxbPermissionsAuthorizationFilter" class="cn.itsource.yxb.shiro.filter.ItsourceYxbPermissionsAuthorizationFilter"></bean>
-
不同的登录用户的菜单读取
构建tree需要的json格式,注意理解构建父菜单与子菜单的方法 -
没有操作权限,就不显示相应的功能键
day5 -
使用poi完成Excel的创建于读取
-
easypoi的导出功能实现
相关的注解 @Excel(name=“用户名”) @Excel(name=“邮件”,width = 25)
导入图片@Excel(name = “头像”,type = 2,savePath = “/images/head”,height = 23) -
导入功能实现
-
自带的注解验证 @NotNull(message = “用户名不为空”)
@Max(value = 80,message = “max 最大值不能超过80”) -
为了解决用户名重名问题,我们还需要自定义验证【自定义的类中写验证规则】
实现IExcelVerifyHandler接口 扫描它,把它交给Spring管理 -
数据字典 系统的初始数据
day6 -
自定义产品模块 【增删改查功能】
-
产品的图片,大小图片的显示问题
-
颜色显示 【span】
-
添加修改功能产品类型 产品品牌的回显
@Query("select o from Producttype o where o.parent.id is not null")
List<Producttype> findChildren();
<tr>
<td>类型:</td>
<td>
<input class="easyui-combobox" name="types.id"
data-options="groupField:'group',valueField:'id',textField:'name',panelHeight:'auto',url:'/util/findChildren'">
</td>
</tr>
- 删除和修改需要删除原来的 大小图片
- 上传的 图片需要使用工具压缩出小图片
效果展示
day7
采购模块 模型图
1. 参构订单 与订单详情表之间配置强级联 和孤儿删除 - 日期时间,配置注解如下
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
public Date getVdate() {
return vdate;
}
@DateTimeFormat(pattern = "yyyy-MM-dd")
public void setVdate(Date vdate) {
this.vdate = vdate;
}
这里配置错误会导致保存数据时类型不一致,前台报400错误
- 状态信息的显示 0 :待审 1:已审 -1:作废
function formatStatus(action) {
var data = {
0:"<div style='color:red;'>待审</div>",
1:"<div style='color: green'>已审</div>",
"-1":"<div><s>作废</s></div>"
};
return data[action];
}
- 订单管理页面的高级查询,这里需要注意,如果查询的起始时间与结束时间是同一天,
需要在后台对结束时间 +1天
public Specification createSpecification(){
//如果日期不为空,结束日期加1
Date tempDate = null;
if(endDate!=null){
tempDate = DateUtils.addDays(endDate, 1);
}
//根据条件把数据返回即可
Specification<Purchasebill> spec = Specifications.<Purchasebill>and()
.eq(status!=null,"status",status )
.ge(beginDate!=null, "vdate",beginDate) //大于等于
.lt(endDate!=null, "vdate",tempDate) //小于
.build();
return spec;
}
- 添加,修改功能实现需要使用http://www.easyui-extlib.com/ ->Datagrid-Edit -单元格编辑
效果如下
这里注意上面三个下拉列表【combobox】数据的回显 整个dialog是一个form表单,下面部分是一个 datadrid数据表格
保存数据时需要提交额外的参数
save:function () {
var url = "/purchasebill/save";
var id = $("#purchasebillId").val();
if(id){
url = "/purchasebill/update?cmd=update";
}
purchasebillForm.form('submit', {
url:url,
onSubmit: function(param){
//这里加上param这个参数,为它设置值,就可以做额外的参数提交
//拿到当前页的所有参数 并进行名称修改 items[0].product.id /item[1].price,...
var rows = gridItems.datagrid('getRows');
//param.items[0].product.id = 1;
for (var i = 0; i < rows.length; i++) {
var rowData = rows[i];
param["items[" + i + "].product.id"] = rowData.productId.id;
param["items[" + i + "].price"] = rowData.price;
param["items[" + i + "].num"] = rowData.num;
param["items[" + i + "].descs"] = rowData.descs;
//完成当前 这一行数据的验证
if(!gridItems.datagrid("validateRow",i)){
alert("你的明细数据有还有问题!");
return false;
}
}
return purchasebillForm.form("validate");
day8
-
订单详情报表 需要使用3. EasyUI(datagrid-groupview) 插件 效果如下
-
创建一个报表的类,其中有一个分组字段 private String groupField = “”; //分组字段
-
带条件查询就是拼接sql 的查询条件
注意,jpql中 order by后面不要跟 ? 这里有个bug
还要注意拼接出来的sql 语句中,每个单词之间的缺少空格可能会导致sql语句失效 -
HighChart 统计图
这个引入相关的支持,扒下源码,构建它需要的数据格式就行
效果如下
//查询到图表需要的值,并且封装成相应的格式[{name:aa,y:43},{name:bb,y:20},...]
public List<Map> findCharts(PurchaseBillItemQuery itemQuery){
List<Map> chartList = new ArrayList<>();
//完成查询的JPQL拼接
String jpql = "select "+itemQuery.getGroupBy()+",sum(o.amount) from PurchaseBillItem o "+itemQuery.getWhereSql()+" group by "+itemQuery.getGroupBy();
//System.out.println(jpql);
List<Object[]> list = findByJpql(jpql, itemQuery.getParams().toArray());
for (Object[] objects : list) {
Map map = new HashMap();
map.put("name",objects[0]);
map.put("y",objects[1]);
chartList.add(map);
}
return chartList;
}
day9
- 入库操作:涉及到入库订单,和入库订单详情这两张表的数据更新
- 审核操作:涉及到仓库表,即时库存表的数据更新
即时库存表,同一产品同一仓库,如果没有这一产品,就直接保存
如果有这一产品,需要使用加权平均操作 - 解决spring测试时没有tomcat环境 的el 问题
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
- 到库存不足时,需要预警
我们使用Quartz 来预警操作 - Spring发送邮件