8-17项目总结

本文总结了一个项目中SpringDataJpa的使用,包括CRUD、分页查询和高级搜索。介绍了如何扩展SpringDataJpa,并实现BaseRepository。此外,还涉及Shiro的身份认证、授权及密码加密。同时,讨论了模板技术如Freemarker和Velocity,以及代码生成器的开发,如easycode和自定义代码生成。项目还包括权限判断、菜单读取、Excel操作和报表展示,如HighChart和ECharts的应用。
摘要由CSDN通过智能技术生成

一,什么是SpringDataJpa
咱们要集成三大框架(Spring+SpringMVC+SpringDataJpa)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.itsource</groupId>
  <artifactId>yxb</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <org.springframework.version>4.2.5.RELEASE</org.springframework.version>
    <org.hibernate.version>4.3.8.Final</org.hibernate.version>
    <spring-data-jpa.version>1.9.0.RELEASE</spring-data-jpa.version>
    <com.fasterxml.jackson.version>2.5.0</com.fasterxml.jackson.version>
    <org.slf4j.version>1.6.1</org.slf4j.version>
  </properties>
  <dependencies>
    <!-- Spring的支持包 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${org.springframework.version}</version>
      <scope>test</scope>
    </dependency>
    <!-- 引入web前端的支持 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${org.springframework.version}</version>
    </dependency>
    <!-- SpringMCV上传需要用到io包-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-io</artifactId>
      <version>1.3.2</version>
    </dependency>
    <!-- 文件上传用到的包 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.2.2</version>
    </dependency>
    <!-- SpringMVC的json支持包 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${com.fasterxml.jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${com.fasterxml.jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${com.fasterxml.jackson.version}</version>
    </dependency>
    <!-- hibernate的支持包 -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${org.hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${org.hibernate.version}</version>
    </dependency>
    <!-- SpringData的支持包 -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>${spring-data-jpa.version}</version>
    </dependency>
    <!-- SpringData的擴展包 -->
    <dependency>
      <groupId>com.github.wenhao</groupId>
      <artifactId>jpa-spec</artifactId>
      <version>3.1.1</version>
      <!-- 把所有的依賴都去掉 -->
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.2.2</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
    </dependency>
    <!-- 測試包 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <!-- 这个scope 只能作用在编译和测试时,同时没有传递性。表示在运行的时候不添加此jar文件 -->
      <scope>provided</scope>
    </dependency>
    <!-- 日志文件 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${org.slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${org.slf4j.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>
    <!-- 代码生成器模版技术 -->
    <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.6</version>
    </dependency>
  <!-- shiro的支持包 -->
   <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-all</artifactId>
      <version>1.4.0</version>
      <type>pom</type>
  </dependency>
    <!-- shiro与Spring的集成包 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.4.0</version>
    </dependency>
    <!-- poi支持的jar包 -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>3.11</version>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>3.11</version>
    </dependency>
    <!-- 图片压缩功能 -->
    <!-- 缩略图 -->
    <dependency>
      <groupId>net.coobird</groupId>
      <artifactId>thumbnailator</artifactId>
      <version>0.4.6</version>
    </dependency>
    <!-- 定时调度 -->
    <dependency>
      <groupId>quartz</groupId>
      <artifactId>quartz</artifactId>
      <version>1.5.2</version>
    </dependency>
    <!-- 邮件支持 -->
    <dependency>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>1.4.1</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>yxb</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>8.1.15.v20140411</version>
        <configuration>
          <stopPort>9966</stopPort>
          <stopKey>foo</stopKey>
          <webAppConfig>
            <contextPath>/</contextPath>
          </webAppConfig>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

二,SpringDataJpa的基本使用(掌握)
抽取id

@MappedSuperclass
public class BaseDomain {
    @Id
    @GeneratedValue
    public Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

抽取查询方法
BaseQuery公共查询对象,完成分页查询和排序

public abstract class BaseQuery {
    //当前页数
    private Integer currentPage=1;
    //每页条数
    private Integer pageSize=10;
    //排序字段
    private String orderByName;
    //排序类型默认 降序
    private String orderByType="DESC";
    public Integer getJpaPage(){
        return currentPage -1;
    }

    public Integer getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(Integer currentPage) {
        this.currentPage = currentPage;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public String getOrderByName() {
        return orderByName;
    }

    public void setOrderByName(String orderByName) {
        this.orderByName = orderByName;
    }

    public String getOrderByType() {
        return orderByType;
    }

    public void setOrderByType(String orderByType) {
        this.orderByType = orderByType;
    }
    //排序方法,设置需要排序才排序,不需要就不排序
    public Sort createSort(){
        Sort sort = null;
        if (orderByName!=null){
            //三目运算
            Sort.Direction type ="ASC".equals(orderByType.toUpperCase())?Sort.Direction.ASC:Sort.Direction.DESC;
            //组装sort
            sort = new Sort(type,orderByName);
        }
        return sort;
    }


    //该方法定义成抽象的目的,让子类都必须覆写该方法
    public abstract Specification createSpecification();
}

三,EmployeeRepository extends JpaRepository

//想通过员工的名称来查询员工 -- 如果按照一定的规范来写 底层就会解析完成
    List<Employee> findEmployeeByUsername(String username);
    //like模糊查询
    List<Employee> findEmployeeByUsernameLike(String username);
    //查询数据--Query注解查询
    @Query("select o from Employee o where o.username like ?1")
    List<Employee> findEmpByUsername(String username);
    //根据顺序查询
    @Query("select o from Employee o where o.username like ?1 and o.email like ?2")
    List<Employee> query02(String username,String emial);
    //根据名称来查询
    @Query("select o from Employee o where  o.username like :username and o.email like :email")
    List<Employee> query03(@Param("username") String username1, @Param("email")String email1);
    //对原生sql语句
    @Query(value="select count(*) from employee ",nativeQuery=true)
    Long query04();

还可以继承多个接口

public interface EmployeeRepository1 extends JpaRepository<Employee,Long>,
        JpaSpecificationExecutor<Employee> {
}

四,各种CRUD ,分页查询,高级搜索,

CRUD

 @Autowired
    private EmployeeRepository employeeRepository;

    //查询所有
    @Test
    public void testFindAll() throws Exception{
        List<Employee> employees = employeeRepository.findAll();
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
    //批量查询
    @Test
    public void testFindAll1() throws Exception{
        List list = Arrays.asList(55L,66L,77L);
        List<Employee> employees = employeeRepository.findAll(list);
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
     @Autowired
    private EmployeeRepository employeeRepository;
    //查询所有
    @Test
    public void testJpa() throws Exception{
        List<Employee> lists = employeeRepository.findAll();
        System.out.println(employeeRepository.getClass());
        for (Employee employee :lists ) {
            System.out.println(employee);
        }
    }


    @Test
    public void testfindAll() throws Exception{
        //表示多条查询查询25,26,27
        List lists = Arrays.asList(25L,26L,27L);
        List<Employee> list = employeeRepository.findAll(lists);
        for (Employee o : list) {
            System.out.println(o);
        }
    }
    //查询一条数据
    @Test
    public void testFindOne() throws Exception{
        Employee employee = employeeRepository.findOne(6L);
        System.out.println(employee);
    }

    //保存
    @Test
    public void testsave() throws Exception{
        Employee employee = new Employee();
        employee.setUsername("公鸡");
        employee.setPassword("傻逼");
        employee.setAge(22);
        employeeRepository.save(employee);
    }
    //修改  如果有id 就修改 没有id 就新增
    @Test
    public void testUpdate() throws Exception{
        Employee employee = new Employee();
        employee.setId(275L);
        employee.setUsername("大鸡鸡");
        employee.setPassword("大傻逼");
        employee.setAge(18);
        employeeRepository.save(employee);
    }

    //删除
    @Test
    public void testDelete() throws Exception{
        employeeRepository.delete(275L);
    }

    @Test
    public void testDeleteInBatch() throws Exception{
        //指定删除两条
        List<Employee> list = employeeRepository.findAll(Arrays.asList(272L, 273L));
        employeeRepository.deleteInBatch(list);
    }
    //总条数
    @Test
    public void testCount() throws Exception{
        System.out.println(employeeRepository.count());
    }

JpaSpecificationExecutor

@Autowired
    private EmployeeRepository1 employeeRepository1;
    @Test
    public void testSpec1() throws Exception{

        EmployeeQuery employeeQuery = new EmployeeQuery();
        employeeQuery.setUsername("呵呵");
        employeeQuery.setAge(234);
        Specification<Employee> spe = Specifications.<Employee>and().
                like(StringUtils.isNotBlank(employeeQuery.getUsername()),
                        "username", "%" + employeeQuery.getUsername() + "%")
                .gt(employeeQuery.getAge() != null, "age", employeeQuery.getAge())
                .build();
        //排序
        Sort sort = null;
        if(employeeQuery.getOrderByName()!= null){
            String orderByName = employeeQuery.getOrderByName();
            Sort.Direction direction = null;
            if(employeeQuery.getOrderByType().equals("DESC")){
                direction = Sort.Direction.DESC;
            }else{
                direction = Sort.Direction.ASC;
            }
            //组装Sort
            sort =new Sort(direction,orderByName);
        }
        //分页
        Pageable pageable = new PageRequest(employeeQuery.getJpaPage(), employeeQuery.getPageSize(), sort);
        Page<Employee> employees = employeeRepository1.findAll(spe, pageable);
        for (Employee employee : employees) {
            System.out.println(employee);
        }

    }

    @Test
    public void testSpec2() throws Exception{
        EmployeeQuery employeeQuery = new EmployeeQuery();
        employeeQuery.setUsername("1");
        employeeQuery.setAge(18);
        Specification spec = employeeQuery.createSpecification();
        Sort sort = employeeQuery.createSort();
        Pageable pageable = new PageRequest(employeeQuery.getJpaPage(), employeeQuery.getPageSize(), sort);
        Page<Employee> lists = employeeRepository1.findAll(spec, pageable);
        for (Employee employee : lists) {
            System.out.println(employee);
        }
    }

Query的抽取

@Autowired
    private EmployeeRepository employeeRepository;

    //按照规范查询
    @Test
    public void testQuery() throws Exception{
        List<Employee> employees = employeeRepository.findEmployeeByUsername("acac");
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

    //按照规范查询
    @Test
    public void testQuery1() throws Exception{
        //模糊查询
        List<Employee> employees = employeeRepository.findEmployeeByUsernameLike("%bb%");
        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }

    //Query注解查询
    @Test
    public void testQuery2() throws Exception{
        List<Employee> emps= employeeRepository.findEmpByUsername("%bbbbbb%");
        for (Employee emp : emps) {
            System.out.println(emp);
        }
    }
    //根据顺序来查询
    @Test
    public void testQuery3() throws Exception{
        //多条件查询
        List<Employee> emps= employeeRepository.query02("%cc%","%33%");
        for (Employee emp : emps) {
            System.out.println(emp);
        }
    }


    @Test
    public void testQuery4() throws Exception{
        List<Employee> emps= employeeRepository.query03("b","%2%");
        for (Employee emp : emps) {
            System.out.println(emp);
        }
    }

    @Test
    public void testQuery5() throws Exception{
        System.out.println(employeeRepository.query04());

    }

springdatajpa的扩展–抽取

抽取一个BaseRepository – 写三个方法

@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>, JpaSpecificationExecutor<T> {

    //根据Query拿到分页对象(分页)
    Page findPageByQuery(BaseQuery baseQuery);

    //根据Query拿到对应的所有数据(不分页)
    List<T> findByQuery(BaseQuery baseQuery);

    //根据jpql与对应的参数拿到数据
    List findByJpql(String jpql,Object... values);


}

然后实现BaseRepositoryImpl

public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID>
        implements BaseRepository<T,ID>  {
    //让spring把entityManager给我注入过来 --factoryBean
    private final EntityManager entityManager;

    //必需要实现父类的这个构造器
    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
    }

    //分页查询的方法
    @Override
    public Page  findPageByQuery(BaseQuery baseQuery) {
        //得到Sort
        Sort sort = baseQuery.createSort();
        //条件
        Specification spec = baseQuery.createSpecification();
        //分页
        Pageable pageable = new PageRequest(baseQuery.getJpaPage(),baseQuery.getPageSize() , sort);
        //查询
        Page<T> page = super.findAll(spec,pageable);
        return page;

    }
    //不分页
    @Override
    public List<T> findByQuery(BaseQuery baseQuery) {
        Sort sort = baseQuery.createSort();
        Specification specs = baseQuery.createSpecification();
        //条件和排序
        List<T> list = super.findAll(specs,sort);
        return list;
    }

    //select o from Employee o where o.username=? and o.email=?
    //xxx 111@email.com 2
    @Override
    public List findByJpql(String jpql, Object... values) {
        //得到query对象
        Query query = entityManager.createQuery(jpql);
        //设值
        for (int i = 0; i < values.length; i++) {
            query.setParameter(i+1,values[i] );
        }

        return query.getResultList();
    }

然后管理起来自己用

二,创建web前端结构
完成Service层
创建IBaseService

public interface IBaseService<T,ID extends Serializable> {
    //新增和修改
    void save(T t);

    //删除 --Long
    void delete(ID id);

    //查询
    T findOne(ID id);

    //查询所有
    List<T> findAll();

    //根据Query拿到分页对象(分页)
    Page findPageByQuery(BaseQuery baseQuery);

    //根据Query拿到对应的所有数据(不分页)
    List<T> findByQuery(BaseQuery baseQuery);

    //根据jpql与对应的参数拿到数据
    List findByJpql(String jpql,Object... values);
}

创建BaseServiceImpl

@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public class BaseServiceImpl<T,ID extends Serializable> implements IBaseService<T,ID> {

    //spring 4之后 支持泛型的注解
    @Autowired
    private BaseRepository<T,ID> baseRepository;

    @Override
    @Transactional
    public void save(T t) {
        baseRepository.save(t);
    }

    @Override
    @Transactional
    public void delete(ID id) {
        baseRepository.delete(id);
    }

    @Override
    public T findOne(ID id) {
        return baseRepository.findOne(id);
    }

    @Override
    public List<T> findAll() {
        return baseRepository.findAll();
    }

    @Override
    public Page findPageByQuery(BaseQuery baseQuery) {
        return baseRepository.findPageByQuery(baseQuery);
    }

    @Override
    public List<T> findByQuery(BaseQuery baseQuery) {
        return baseRepository.findByQuery(baseQuery);
    }

    @Override
    public List findByJpql(String jpql, Object... values) {
        return baseRepository.findByJpql(jpql,values);
    }
}

创建IEmployeeService

public interface IEmployeeService extends IBaseService<Employee,Long> {
}

创建EmployeeServiceImpl

@Service
public class EmployeeServiceImpl extends BaseServiceImpl<Employee,Long>
        implements IEmployeeService {
}

配置applicationContext-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/mvc
         http://www.springframework.org/schema/mvc/spring-mvc.xsd
    ">
    <!-- 对静态资源进行放行 -->
    <mvc:default-servlet-handler />
    <!-- 扫描controller部分的包 -->
    <!-- @Component组件, @Repository持久层, @Service业务逻辑层, and @Controller控制器 -->
    <context:component-scan base-package="cn.itsource.aisell.web.controller" />
    <!-- 添加mvc对@RequestMapping等注解的支持 -->
    <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:配置 setObjectMapper(new CustomMapper)objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
                <property name="objectMapper">
                    <bean class="cn.itsource.aisell.common.CustomMapper"></bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- ViewResolver 视图解析器 (struts2视图类型类似) -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 设置视图路径的前后缀,该配置可以让我们写视图路径的时候更简单。 -->
        <!-- 希望跳转jsp是[/WEB-INF/views/前缀][xxx变量][.jsp后缀] -->
        <!-- * @see #setPrefix -->
        <property name="prefix" value="/WEB-INF/views/" />
        <!-- * @see #setSuffix -->
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 错误:提示告诉开发者你没有配置文件上传解析器。 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置上传文件的最大尺寸为1MB -->
        <property name="maxUploadSize">
            <value>1048576</value>
        </property>
    </bean>
</beans>

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">


</web-app>

完成Controller层

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService employeeService;

    @RequestMapping("/index")
    public String index() {
        //根据配置,这里会跳到/WEB-INF/views/employee/employee.jsp页面
        return "employee/employee";
    }
    @RequestMapping("/list")
    @ResponseBody
    public List<Employee> list(){
        return employeeService.findAll();
    }

}

三,完成员工页面前端展示和分页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@include file="/WEB-INF/views/head.jsp"%>

<html>
<head>
    <title>Title</title>
    <script  type="text/javascript" src="/js/model/employee.js"></script>
</head>
<body>
    <!--
     datagrid  fit 填充父容器
     rownumbers 行号
     pagination  分页

    -->
    <table id="employeeGrid" class="easyui-datagrid" data-options="fit:false,fixed:true,
            fitColumns:true,toolbar:'#tb',singleSelect:true";
           url="/employee/page"
           iconCls="icon-save"
           rownumbers="true" pagination="true">
        <thead>
        <tr>
            <th width="20"  field="headImage" align="right" formatter="formatImage">头像</th>
            <th width="20"  field="username" >用户名</th>
            <th width="20"  field="password">密码</th>
            <th width="20"  field="email">邮件</th>
            <th width="20"  field="age" align="right">年龄</th>
            <th width="20"  field="department" align="right" formatter="formatObj">部门名称</th>
        </tr>
        </thead>
    </table>

    <div id="tb" style="padding:5px;height:auto">
        <!-- 这部分是加上增删改的按键:现在没有功能,我们先不管它 -->
        <div style="margin-bottom:5px">
            <a href="#"  data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
            <a href="#"  data-method="edit" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
            <a href="#"  data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
        </div>
        <!-- 这部门是查询的功能  -->
        <div>
            <form id="searchForm" action="/employee/download" method="post">
                用户名: <input name="username" class="easyui-textbox" style="width:80px;height:32px">
                邮件: <input name="email" class="easyui-textbox" style="width:80px;height:32px">
                部门 :
                <input  class="easyui-combobox" name="departmentId"
                        data-options="valueField:'id',textField:'name',panelHeight:'auto',url:'/util/departmentList'">
                <a href="#" data-method="search"  class="easyui-linkbutton" iconCls="icon-search">查找</a>
            </form>
        </div>
    </div>
</body>
</html>

解决懒加载问题

<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>
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);
    }
}

加入高级查询条件
UtilController

@Controller
@RequestMapping("/util")
public class UtilController {
    //注入部门信息
    @Autowired
    private IDepartmentService departmentService;

    //departmentList
    @RequestMapping("/departmentList")
    @ResponseBody
    public List<Department> queryDepartmentList(){
        return departmentService.findAll();
    }
}

$(function(){
    //定义form表单
    var searchForm = $("#searchForm");
    var employeeGrid = $("#employeeGrid");

    //绑定事件 easyui 第二天的时候
    $("a[data-method]").on('click',function(){
       //获取 data-method属性 <a data-method="seacher">
        var methodName = $(this).data("method");
        //动态调用方法 itsource["seacher"]
        itsource[methodName]();
    })
    //对象
    var itsource = {
        search:function(){
          //怎么完成高级查询 jquery.jdirk.js 这个方法 这是jquery扩展方法
            //该方法返回一个 JSON Object,返回对象中的每个数据都表示一个表单控件值。
            var param = searchForm.serializeObject();
            //发送查询数据库 --加载表格 发送请求 /employee/page
            employeeGrid.datagrid('load',param);
        },
        add:function(){
            alert(1);
        }
    }

public class EmployeeQuery extends BaseQuery{

    /**
     * username	admin
     * email	xx@qq.com
     * departmentId	1
     */
    //自身条件
    private String username;
    private String email;
    private Integer age;

    private Integer departmentId;


    public Integer getDepartmentId() {
        return departmentId;
    }

    public void setDepartmentId(Integer departmentId) {
        this.departmentId = departmentId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    //抽取查询
    @Override
    public Specification createSpecification() {
        Specification<Employee> spe = Specifications.<Employee>and().
                like(StringUtils.isNotBlank(username), "username", "%" + username + "%")
                .like(StringUtils.isNotBlank(email), "email", "%" + email + "%")
                .eq(departmentId!=null,"department.id",departmentId)
                .gt(age != null, "age", age)
                .build();

        return spe;
    }
}

一,智能销售CRUD

后台

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService employeeService;
    //加载页面
    @RequestMapping("/index")
    public String index(){
        return "employee";
    }

    //分页处理
    @RequestMapping("/page")
    @ResponseBody
    public UiPage page(EmployeeQuery employeeQuery){

        Page page = employeeService.findPageByQuery(employeeQuery);
        UiPage uipage = new UiPage(page);
        return uipage;
    }

    //方法 delete
    @RequestMapping("/delete")
    @ResponseBody
    public Map delete(Long id){
        Map mp = new HashMap();
        try {
            employeeService.delete(id);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }

    //保存方法-- 修改和新增保存
    @RequestMapping("/save")
    @ResponseBody
    public Map save(Employee employee){
        Map mp = new HashMap();
        try {
            employeeService.save(employee);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }

    @RequestMapping("/update")
    @ResponseBody
    public Map update(@ModelAttribute("editEmployee") Employee employee){
        Map mp = new HashMap();
        try {
            employeeService.save(employee);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }

    //去重
    @RequestMapping("/checkUsername")
    @ResponseBody
    public boolean checkUsername(String username,Long id){
        if(id != null && !"".equals(id)){
            //修改
            Employee employee = employeeService.findOne(id);
            String username_db = employee.getUsername();
            if(username.equals(username_db)){
                return true;
            }
        }

        return employeeService.checkUsername(username);
    }


    //删除方法
    @ModelAttribute("editEmployee")
    public Employee beforeEdit(Long id,String cmd){
        Employee employee = null;
        if("update".equals(cmd) && id != null && !"".equals(id) ){
            employee = employeeService.findOne(id);
            employee.setDepartment(null);
        }
        return employee;
    }

}

前台处理

/表格数据格式化
function formatObj(value){
    if(value){
        return value.name;
    }
}
//formatImage头像格式化
function formatImage(value){
   if(value){
       return "<img width='100px' height='100px' src='"+value+"' alt='没有图片'/>"
   }
}

//自己去扩展easyui验证的功能
$.extend($.fn.validatebox.defaults.rules, {
    equals: {
        validator: function(value,param){

            return value == $(param[0]).val();
        },
        message: '密码不匹配.'
    }
});

//扩展用户名是否重复的功能
$.extend($.fn.validatebox.defaults.rules, {
    checkUsername: {
        validator: function(value,param){
            //当然输入框value 和数据库的username进行比较
            //发送ajax请求 --异步(ajax同步)
            var id = $("#employeeId").val();
           var result =  $.ajax({
                type: "POST",
                url: "/employee/checkUsername",
                data: "username="+value+"&id="+id,
                async: false //同步
            }).responseText;

            return result == 'true';
        },
        message: '用户名重复了.'
    }
});


$(function(){
    //定义form表单
    var searchForm = $("#searchForm");
    var employeeGrid = $("#employeeGrid");
    //获取employeeDialog
    var employeeDialog = $("#employeeDialog");
    //定义表单新增的form
    var employeeForm = $("#employeeForm");

    //绑定事件 easyui 第二天的时候
    $("a[data-method]").on('click',function(){
       //获取 data-method属性 <a data-method="seacher">
        var methodName = $(this).data("method");

        itsource[methodName]();
    });
    //对象
    var itsource = {
        search:function(){

            var param = searchForm.serializeObject();

            employeeGrid.datagrid('load',param);
        },
        add:function(){
            //隐藏数据 display:none
            $("tr[data-save]").show();
            //取消密码验证
            $("*[data-save] input").validatebox("enableValidation");
            //清空表单
            employeeForm.form('clear');
            //新增 --弹出一个对话框--装一个表单
            employeeDialog.dialog('center').dialog('open');

        },
        edit:function(){
            //选择一条数据进行修改
            var row = employeeGrid.datagrid('getSelected');
            if(row){
                //隐藏数据 display:none
                $("tr[data-save]").hide();
                //取消密码验证
                $("*[data-save] input").validatebox("disableValidation");
                //部门回显
                if(row.department){
                  row["department.id"] = row.department.id;
                }
                //修改 -- 回显示数据
                employeeForm.form('load',row);
            }else{
                //提示用户
                $.messager.alert('温馨提示:','请修改其中一条','info');
                return;
            }
          //弹出对话框
            employeeDialog.dialog('center').dialog('open');
        },
        del:function(){
            //判断表格里面是否选中得数据
            var row = employeeGrid.datagrid('getSelected');
            if(row){
                //是否确认要删除数据
                $.messager.confirm('温馨提示','是否要删除?',function(value){
                    if(value){
                        //获取id值

                        //发送ajax到后台进行删除数据
                        $.get('/employee/delete',{"id":row.id},function(data){
                            //返回json对象
                            if(data.success){
                                $.messager.alert('温馨提示:','删除成功','info');
                                //重新加载数据
                                employeeGrid.datagrid('reload');
                            }else{
                                $.messager.alert('温馨提示:','删除失败'+data.msg,'error');
                            }
                        });
                    }
                })

            }else{
                //提示用户
                $.messager.alert('温馨提示:','选中一条进行删除','info');
                return;
            }

        },
        save:function(){
            var url = "/employee/save";
            //获取隐藏域里面id值
            var id = $("#employeeId").val();
            if(id){
                url = "/employee/update?cmd=update"
            }
            //保存方法 --提交表单的数据到后台
            employeeForm.form('submit', {
                    url:url,
                    onSubmit: function(){
                        // 提交之前的验证
                       return employeeForm.form('validate');
                     },
                success:function(data){
                        //字符串 转换成json对象
                      var dataJson =  $.parseJSON(data);
                      if(dataJson.success){
                          $.messager.alert('温馨提示:','操作成功','info');
                          //重新加载数据
                          employeeGrid.datagrid('reload');
                          //关闭对话框
                          employeeDialog.dialog('close');
                      }else{
                          $.messager.alert('温馨提示:','保存失败'+dataJson.msg,'error');
                          employeeDialog.dialog('close');
                      }
                }
            });
        }
    }



})

二.模板技术(freemarker-velocity)

模板技术怎样输出数据
数据+模板(html文件,vm文件,ftl文件)=输出文本
Velocity模板技术可以实现的功能
动态页面静态化:xxx.html --动态页面页面 静态化
pom.xml:添加jar文件

<!-- 代码生成器模版技术 -->
<dependency>
	<groupId>org.apache.velocity</groupId>
	<artifactId>velocity</artifactId>
	<version>1.6</version>
</dependency>

测试Velocity功能

public class VelocityTest {
    @Test
    public void testVelocity01() throws Exception {
        //创建模板应用上下文
        VelocityContext context = new VelocityContext();
        context.put("msg", "花好月圆");
        //拿到相应的模板(需要设置好编码)
        Template template = Velocity.getTemplate("temptest/hello.html","UTF-8");
        //准备输出流
        StringWriter writer = new StringWriter();
        template.merge(context, writer);
        System.out.println(writer);
    }


    @Test
    public void testVelocity02() throws Exception {
        //创建模板应用上下文
        VelocityContext context = new VelocityContext();
        context.put("msg", "男上加男");
        //拿到相应的模板(需要设置好编码)
        Template template = Velocity.getTemplate("temptest/hello.html","UTF-8");
        //准备输出流
        File file = new File("temptest/helloNew.html");
        FileWriter writer = new FileWriter(file);
        template.merge(context, writer);
        writer.close();
    }

}

三,代码生成器(手动写代码生成代码生成器)

一.开发步骤
1.准备模板(把每个模块需要改的地方确定好)
2.确定所有模板的生成顺序
3.确定所有模板的生成位置
4.确定要生成的基本功能的domain(Dept)
5.根据Domain名称与模板结合,在相应位置生成文件
6.解决如果父文件夹不存在的问题
7.解决如果文件已经存在的问题
8.排错(有错先改模板)
按照课件走,

@Test
public void testCreate() throws Exception{
    //创建模板应用上下文
    VelocityContext context = new VelocityContext();
    //一.遍历所有的Domain
    for (int i = 0; i < domains.length; i++) {
        //1.1拿到大写的domain
        String domainBig = domains[i];//Dept
        //1.2拿到小写的domain  //dept
        String domainSmall = domainBig.substring(0,1).toLowerCase() + domainBig.substring(1);
        //System.out.println(domainBig);
        //System.out.println(domainSmall);
        //1.3设置上下文的替换名称
        context.put("Domain",domainBig);
        context.put("domain",domainSmall);
        //二.遍历所有的路径
        for (int j = 0; j < paths.length; j++) {
            //2.1拿到相应的路径
            String path =paths[j];
            //2.2拿到相应的模板名称
            String tempName = tempNames[j];
            //2.3拼接回我们需要的位置文件
            String realPath = (path + tempName).replaceAll("Domain",domainBig).replaceAll("domain",domainSmall);

            //三.准备相应文件与模板进行组合
            //3.1准备相应的文件(要生成的文件)
            File file = new File(realPath);
            //  如果父文件不存在,我们创建一个
            File parentFile = file.getParentFile();
            if(!parentFile.exists()){
                parentFile.mkdirs();
            }
            //3.2拿到相应的模板(需要设置好编码)
            Template template = Velocity.getTemplate("template/"+tempName,"UTF-8");
            FileWriter writer = new FileWriter(file);
            template.merge(context, writer);
            writer.close();
        }
    }
}

easycode

支持的数据库类型
因为是基于Database Tool开发,所有Database Tool支持的数据库都是支持的。
包括如下数据库:
1.MySQL
2.SQL Server
3.Oracle
4.PostgreSQL
5.Sqlite
6.Sybase
7.Derby
8.DB2
9.HSQLDB
10.H2
安装按照课件和老师讲的走

代码生成器模板

domain

##引入宏定义
$!define
##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))
##使用宏定义设置回调(保存位置与文件后缀)
##注意:为了路径,我现在都从main的位置开始
#save("/main/java/cn/itsource/zx/domain", ".java")

##使用宏定义设置包后缀
#setPackageSuffix("domain")

##使用全局变量实现默认包导入
$!autoImport
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;

##使用宏定义实现类注释信息
#tableComment("实体类")
@Entity
@Table(name="$!{lowerTableInfo}")
public class $!{tableInfo.name} extends BaseDomain {

##实现列进行排除
#set($temp = $tool.newHashSet("id"))
#foreach($item in $temp)
    #set($newList = $tool.newArrayList())
    #foreach($column in $tableInfo.fullColumn)
        #if($column.name!=$item)
        ##带有反回值的方法调用时使用$tool.call来消除返回值
            $tool.call($newList.add($column))
        #end
    #end
##重新保存
    $tableInfo.setFullColumn($newList)
#end

#foreach($column in $tableInfo.fullColumn)
    #if(${column.comment})//${column.comment}#end
    private $!{tool.getClsNameByFullName($column.type)} $!{column.name};
#end


#foreach($column in $tableInfo.fullColumn)
##使用宏定义实现get,set方法
    #getSetMethod($column)
#end

}

Query查询
##定义初始变量
#set($tableName = t o o l . a p p e n d ( tool.append( tool.append(tableInfo.name, “Query”))
##设置文件名
! c a l l b a c k . s e t F i l e N a m e ( !callback.setFileName( !callback.setFileName(tool.append($tableName, “.java”))
##设置文件保存的位置(依然是从src开始)
! c a l l b a c k . s e t S a v e P a t h ( !callback.setSavePath( !callback.setSavePath(tool.append($tableInfo.savePath, “/main/java/cn/itsource/zx/query”))

##拿到首字母小写的表单
#set($lowerTableInfo = t o o l . f i r s t L o w e r C a s e ( tool.firstLowerCase( tool.firstLowerCase(!{tableInfo.name}))

##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}query;
##使用全局变量实现默认包导入
! a u t o I m p o r t i m p o r t c n . i t s o u r c e . z x . d o m a i n . !autoImport import cn.itsource.zx.domain. !autoImportimportcn.itsource.zx.domain.!{tableInfo.name};
import com.github.wenhao.jpa.Specifications;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;

public class KaTeX parse error: Expected '}', got '#' at position 45: …s BaseQuery { #̲#注:如果生成的表没有name…!{tableInfo.name}>and()
.like(StringUtils.isNotBlank(name),“name”, “%”+name+"%")
.build();
}

public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

}
Repository数据层

##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "Repository"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/repository"))

##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))

##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}repository;
import cn.itsource.zx.domain.$!{tableInfo.name};

public interface $!{tableInfo.name}Repository extends BaseRepository<$!{tableInfo.name},Long>{

}

IService业务层接口

##定义初始变量
#set($tableName = $tool.append("I",$tableInfo.name, "Service"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/service"))

##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))

##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service;

import cn.itsource.zx.domain.$!{tableInfo.name};

public interface I$!{tableInfo.name}Service extends IBaseService<$!{tableInfo.name},Long> {

}

Service

##定义初始变量
#set($tableName = $tool.append($tableInfo.name, "ServiceImpl"))
##设置回调
$!callback.setFileName($tool.append($tableName, ".java"))
$!callback.setSavePath($tool.append($tableInfo.savePath, "/main/java/cn/itsource/zx/service/impl"))

##拿到首字母小写的表单
#set($lowerTableInfo = $tool.firstLowerCase($!{tableInfo.name}))

##引入相应的包
#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.#{end}service.impl;

import cn.itsource.zx.domain.$!{tableInfo.name};
import cn.itsource.zx.repository.$!{tableInfo.name}Repository;
import cn.itsource.zx.service.I$!{tableInfo.name}Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class $!{tableInfo.name}ServiceImpl extends BaseServiceImpl<$!{tableInfo.name},Long> implements I$!{tableInfo.name}Service {

    @Autowired
    private $!{tableInfo.name}Repository $!{lowerTableInfo}Repository};
    
}

一,shiro

Apache Shiro是一个强大且易用的架,有身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Spring security 重量级安全框架Java安全框
Apache Shiro轻量级安全框架
Shiro:是一个apache的安全框架,还有spring security;
Shiro:能做:身份认证(登录)authentication,授权authorization,密码学,会话管理。
应用–》Subject(当前用户)—》Security Mananger管理–》Realm----》db
导入jar

<dependencies>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
[users]
# 用户 'root' 密码是 'secret' and the 'admin' 角色
root = secret, admin
# 用户 'guest' 密码 'guest' 和'guest'角色
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# 用户 'darkhelmet' with password 'ludicrousspeed' and 角色 'darklord' and 'schwartz'
darkhelmet = 123, darklord, schwartz
# 用户 'lonestarr' 密码 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz

[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# 这个 'schwartz' 角色 能干lightsaber下面的所有事情:
schwartz = lightsaber:*
# goodguy这个角色 能干winnebago下面的drive权限,操作eagle5这个资源 user:delete:5
goodguy = winnebago:drive:eagle5

测试

 @Test
    public void test() throws Exception{
        //填入路径到工厂对象中去
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //得到SecutiryManager 核心对象
        SecurityManager securityManager = factory.getInstance();
        //设置到shiro环境中去
        SecurityUtils.setSecurityManager(securityManager);
        //获得主体
        Subject subject = SecurityUtils.getSubject();
        //创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("darkhelmet", "123");
        try {
            subject.login(token);
            System.out.println("登陆成功");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("该账号不存在");
        }catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误");
        }catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("其他认证错误");
        }
        //角色判断
        if (subject.hasRole("admin")){
            System.out.println("这个角色有admin");
        }else{
            System.out.println("没有该角色");
        }
        //权限判断
        if (subject.isPermitted("lightsaber:*")){
            System.out.println("该角色有lightsaber权限");
        }else{
            System.out.println("没有权限");
        }
    }

自定义Realm

public class MyRealm extends AuthorizingRealm {
    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //得到用户名
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        //拿到权限去数据库查询
        Set<String> role = getRoleByPrincipal(primaryPrincipal);
        //拿到角色去数据库查询
        Set<String> permission = getPermissionByPrincipal(primaryPrincipal);


        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //设置角色
        simpleAuthorizationInfo.setRoles(role);
        //设置权限
        simpleAuthorizationInfo.setStringPermissions(permission);

        return simpleAuthorizationInfo;
    }

    //通过用户拿到角色
    private Set<String> getRoleByPrincipal(String primaryPrincipal){
        Set<String> roles = new HashSet<String>();
        if("gongji".equals(primaryPrincipal)){
            roles.add("admin");
            return roles;
        }else{
            return null;
        }
    }
    //通过用户获取权限
    private Set<String> getPermissionByPrincipal(String primaryPrincipal){
        Set<String> permissions = new HashSet<String>();
        if("gongji".equals(primaryPrincipal)){
            permissions.add("admin");
            return permissions;
        }else{
            return null;
        }
    }

    //身份认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //得到令牌
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //主体
        Object principal = token.getPrincipal();
        System.out.println(principal);

        //获取用户名 zhangsan
        String username = token.getUsername();
        System.out.println(username);

        //通过用户名 去数据库查询数据 -- 查询用户的信息
        // 没有加密返回 String dbpwd= getPwdByUsername(username);
        String md5Pwd = getMD5PwdByUsername(username);
        //处理用户名问题 用户名是否存在的问题
        if(md5Pwd==null){
            //用户不存在
            return null;
        }
        //ByteSource
        ByteSource salt = ByteSource.Util.bytes("itsource");
        //下面进行密码的比对  shiro 底层它会自动进行比对 -- 处理密码问题
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, md5Pwd,salt, getName());

        return simpleAuthenticationInfo;
    }

    //通过用户名去拿到密码
    public String getPwdByUsername(String username){
        if("gongji".equals(username)){
            return "123";
        }else{
            return null;
        }
    }
    //加密返回密码
    public String getMD5PwdByUsername(String username){
        if("gongji".equals(username)){
            return "d5a3fedf6c59c2ecbe7f7a6c1a22da37";
        }else{
            return null;
        }
    }

    public static void main(String[] args) {
        //MD5 本身是不可破解 ..不可逆  网上看到破解方式 都是穷举把
        //(1)普通加密+202cb962ac59075b964b07152d234b70 -123
        SimpleHash simpleHash = new SimpleHash("MD5","123" );
        //(2)加密+加次数
        SimpleHash simpleHash1 = new SimpleHash("MD5","123",null,10 );
        //5371007260db2b98e3f7402395c45f28
        //(3)加密123 加盐 + itsource +加次数 --d5a3fedf6c59c2ecbe7f7a6c1a22da37 -- 最安全
        SimpleHash simpleHash2 = new SimpleHash("MD5","123","itsource",10 );
        System.out.println(simpleHash.toString());
        System.out.println(simpleHash1.toString());
        System.out.println(simpleHash2.toString());
    }
}

Shiro中密码加密

 @Test
    public void testDiyRealm() throws Exception{

        //创建自定义myRealm
        MyRealm myRealm  = new MyRealm();
        //得到SecutiryManager 核心对象
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        securityManager.setRealm(myRealm);
        //设置到shiro的环境里面 才能运行
        SecurityUtils.setSecurityManager(securityManager);
        //密码匹配器
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("MD5");
        matcher.setHashIterations(10);
        //在realm设置匹配器
        myRealm.setCredentialsMatcher(matcher);

        //完成一些认证 拿到主体 -- 游客
        Subject subject = SecurityUtils.getSubject();

        //创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("gongji","123");
        try {
            subject.login(token);
            System.out.println("登陆成功");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户不存在");
        }catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误..");
        }catch (AuthenticationException e) {
            e.printStackTrace();
            System.out.println("其他认证错误");
        }
        System.out.println(subject);
        //判断角色 --shiro底层 自动找到myRealm 返回AuthorizationInfo 信息 进行判断比较
        if(subject.hasRole("admin")){
            System.out.println("豪哥有:admin角色");
        }
        //判断主题是否有权限
        if(subject.isPermitted("driver:xx")){
            System.out.println("公鸡可以开车");
        }



        subject.logout();//退出


    }

二,Shiro集成Spring

配置导包

<!-- shiro的支持包 -->
 <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.4.0</version>
    <type>pom</type>
</dependency>
  <!-- shiro与Spring的集成包 -->
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
  </dependency>
<filter>
  <filter-name>shiroFilter</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  <init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>shiroFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

三,Authentication(身份认证)和Authorizing(授权)

public class MyRealm extends AuthorizingRealm {

    //登陆认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       
        //身份 username
        Object username = authenticationToken.getPrincipal();
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username1 = token.getUsername();

        //通过用户名得到密码
        String pwd = getPwdByUsername(username1);
        if(pwd == null){
            return null;
        }
        //添加颜值
        ByteSource salt = ByteSource.Util.bytes("itsource");
        //得到simpleAuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,pwd,salt,getName());


        return simpleAuthenticationInfo;
    }

    public String getPwdByUsername(String username){
        if("bb".equals(username)){
            return "607ca42819a4452f620bf721b7558c18";
        }else{
            return null;
        }
    }

    public static void main(String[] args) {
        SimpleHash simpleHash = new SimpleHash("MD5", "520520", "itsource", 10);
        System.out.println(simpleHash);
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //得到主体/用户
        String username =(String)principalCollection.getPrimaryPrincipal();
        //根据用户 从数据库去拿到权限 加入到shiro里面
        Set<String> permissions = new HashSet<>();
        if(username.equals("bb")){
            permissions.add("user:*");
            permissions.add("employee:*");
        }

        SimpleAuthorizationInfo simpleAuthorizationInfo  = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissions);

        //返回到shiro里面 就会进行权限的判断
        return simpleAuthorizationInfo;
    }
}

授权数据库查询

public class AisellFilterChainDefinitionMapBuilder {


    public Map buildFilterChaiDefinitionMap(){
        //map要顺序要求
        /**
         */

        Map mp = new LinkedHashMap();
        mp.put("/s/login.jsp","anon");
        mp.put("/login","anon");
        mp.put("/s/permission.jsp","perms[user:*]");
        mp.put("/**","authc");
        return mp;


    }
}

一,登录功能

把数据库里面的内容加密

public class MD5Util {
   // String algorithmName, Object source, Object salt, int hashIterations
    //设置盐值
    public static final String SALT = "itsource";
    //设置遍历次数
    public static final int HASHITERATIONS = 10;
    //传入一个字符串,返回一个md5编码的值
    public static String createMd5Str(String str){
        SimpleHash hash = new SimpleHash("MD5",str,SALT,HASHITERATIONS);
        return hash.toString();
    }
}
@Test
public void testUpdatePwd() throws Exception{
    List<Employee> list = employeeService.findAll();
    for (Employee employee : list) {
        employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
        employeeService.save(employee); //注:save是添加与修改
    }
}

准备登陆页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>源码智销系统</title>
    <%@ include file="/WEB-INF/views/head.jsp"%>
    <script type="text/javascript">

        //判断当前是否是顶层窗口
        if (top != window){
            window.top.location.href = window.location.href;
        }
        //登陆方法
        function submitForm(){
            //提交表单
            $("#loginForm").form('submit',{
                url:"/login",
                success:function(data){
                    
                    console.log(data);
                    console.log(typeof data);
                    var jsonObj = $.parseJSON(data);
                    console.log(jsonObj.success);
                    if(jsonObj.success){
                        //进入成功页面
                        location.href="/main/index";
                    }else{
                        $.messager.alert('温馨提示:',"登录失败"+jsonObj.msg,"error");
                    }
                }

            });
        }

        // //按回车进行登陆 绑定键盘事件 keyon keydown keyup 按完抬起
        $(document.documentElement).on('keyup',function(event){
            //event 键盘事件对象
            if(event.keyCode == 13){
                //提交表单
                submitForm();
            }
        })

    </script>
</head>
<body>
<div align="center" style="margin-top: 100px;">
    <div class="easyui-panel" title="智销系统用户登陆" style="width: 350px; height: 240px;">
        <form id="loginForm" class="easyui-form" method="post">
            <table align="center" style="margin-top: 15px;">
                <tr height="20">
                    <td>用户名:</td>
                </tr>
                <tr height="10">
                    <td><input name="username" class="easyui-validatebox" required="true" value="admin" /></td>
                </tr>
                <tr height="20">
                    <td>密&emsp;码:</td>
                </tr>
                <tr height="10">
                    <td><input name="password" type="password" class="easyui-validatebox" required="true" value="123456" /></td>
                </tr>
                <tr height="40">
                    <td align="center"><a href="javascript:;" class="easyui-linkbutton" onclick="submitForm();">登陆</a> <a
                            href="javascript:;" class="easyui-linkbutton" onclick="resetForm();">重置</a></td>
                </tr>
            </table>
        </form>
    </div>
</div>
</body>
</html>

修改applicationContext-shiro.xml

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"></property>
        <!-- 认证没有成功跳转的页面-->
        <property name="loginUrl" value="/login"></property>
        <!-- 登陆成功进入页面-->
        <property name="successUrl" value="/main/index"></property>
        <!-- 没有权限 进入页面 -->
        <property name="unauthorizedUrl" value="/s/unauthorized.jsp"></property>

静态资源放行

  Map mp = new LinkedHashMap();
        mp.put("/login","anon");
        mp.put("*.js","anon");
        mp.put("*.css","anon");
        mp.put("/css/**","anon");
        mp.put("/js/**","anon");
        mp.put("/easyui/**","anon");
        mp.put("/images/**","anon");

        mp.put("/s/login.jsp","anon");

        mp.put("/s/permission.jsp","perms[user:*]");
        mp.put("/**","authc");
        return mp;

完成登陆功能

public class JpaRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;

    。。。

    //AuthenticationInfo:认证; 身份验证; 证明
    //登录的时候就会调用这个方法来做验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //身份认证(用户名)
        // 1.拿到用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        String username = usernamePasswordToken.getUsername();
        // 2.根据用户名到数据库拿到用户
        Employee loginUser = employeeService.findByUsername(username);
        if(loginUser==null){
            return null; //该用户名不存在
        }
        //从数据库中拿到密码
        Object credentials = loginUser.getPassword();
        //加盐的数据
        ByteSource byteSource = ByteSource.Util.bytes("itsource");
        return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
    }
}

json返回封装数据

public class JsonResult {
    //是否成功
    private boolean success = true;
    //消息
    private String msg ;

    public JsonResult() {
    }


    public JsonResult(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

登陆功能

 @RequestMapping(value = "/login",method = RequestMethod.POST)
       @ResponseBody
        public JsonResult login(String username, String password) {

           //得到主体
           Subject subject = SecurityUtils.getSubject();
           //判断改主体是否已经认证过
           if (!subject.isAuthenticated()) {
               //完成认证
               try {
                   UsernamePasswordToken token = new UsernamePasswordToken(username, password);
                   subject.login(token);

               } catch (UnknownAccountException e) {
                   e.printStackTrace();
                   System.out.println("账号不存在");
                   return new JsonResult(false, "账号不存在");

               } catch (IncorrectCredentialsException e) {
                   e.printStackTrace();
                   System.out.println("密码错误");
                   return new JsonResult(false, "密码错误");
               } catch (AuthenticationException e) {
                   e.printStackTrace();
                   System.out.println("其他问题");
                   return new JsonResult(false, "其他问题");
               }


           }
           //表示成功
           return new JsonResult();

       }

       //注销
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "forward:/WEB-INF/views/login.jsp";
    }

二,权限domain部分

创建domain使用代码生成器完成

权限完善

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>permission管理</title>
    <%@include file="/WEB-INF/views/head.jsp" %>
    <script type="text/javascript" src="/js/model/permission.js"></script>
</head>
<body>
<table id="permissionGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true"
       url="/permission/page"
       iconCls="icon-save"
       enableHeaderClickMenu="true"
       rownumbers="true" pagination="true"
>
    <thead>
    <tr>
        <th width="20"  field="name" sortable="true">权限名称</th>
        <th width="20"  field="url" sortable="true">资源路径</th>
        <th width="20"  field="descs" sortable="true">描述</th>
        <th width="20"  field="sn" sortable="true">权限编号</th>
        <%-- <th width="20"  field="menuId" sortable="true">menuId</th>--%>
    </tr>
    </thead>
</table>
<div id="tb" style="padding:5px;height:auto">
    <!-- 这部分是加上增删改的按键:现在没有功能,我们先不管它 -->
    <div style="margin-bottom:5px">
        <a href="#"  data-method="add" class="easyui-linkbutton" iconCls="icon-add" plain="true">添加</a>
        <a href="#"  data-method="edit" class="easyui-linkbutton" iconCls="icon-edit" plain="true">修改</a>
        <a href="#"  data-method="del" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
    </div>
    <!-- 这部门是查询的功能 -->
    <div>
        <form id="searchForm" action="/permission/download" method="post">
            名称: <input name="name" class="easyui-textbox" style="width:80px;height:32px">
            <a href="#" data-method="search"  class="easyui-linkbutton" iconCls="icon-search">查找</a>
        </form>
    </div>
</div>

<!-- 弹出相应的功能框 -->
<div id="permissionDialog" class="easyui-dialog" title="数据操作" data-options="closed:true,modal:true"  style="width:400px;padding:10px">
    <form id="permissionForm" method="post">
        <input id="permissionId" type="hidden" name="id">
        <table cellpadding="5">
            <tr>
                <td>name:</td>
                <td><input class="easyui-validatebox" type="text" name="name"></input></td>
            </tr>
            <tr>
                <td>url:</td>
                <td><input class="easyui-validatebox" type="text" name="url"></input></td>
            </tr>
            <tr>
                <td>descs:</td>
                <td><input class="easyui-validatebox" type="text" name="descs"></input></td>
            </tr>
            <tr>
                <td>sn:</td>
                <td><input class="easyui-validatebox" type="text" name="sn"></input></td>
            </tr>
            <tr>
                <td>menuId:</td>
                <td><input class="easyui-validatebox" type="text" name="menuId"></input></td>
            </tr>
        </table>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#permissionDialog').dialog('close')">取消</a>
        </div>
    </form>
</div>


<div id="gridMenu" class="easyui-menu" style="width:120px;">
    <div data-options="iconCls:'icon-add'"  data-method="add" >添加</div>
    <div data-options="iconCls:'icon-edit'"  data-method="edit">修改</div>
    <div data-options="iconCls:'icon-remove'" data-method="del">删除</div>
</div>


</body>
</html>

三,角色管理(页面功能)

<div id="roleDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:900px">
    <div style="padding:10px 60px 20px 40px">
        <form id="roleForm" class="easyui-form" method="post" data-options="">
            <input type="hidden" id="roleId" name="id" >
            <table cellpadding="5" style="width: 100%;">
                <tr>
                    <td>
                        名称:<input class="easyui-validatebox" type="text" name="name" data-options="required:true"></input>
                        编码:<input class="easyui-validatebox" type="text" name="sn" data-options="required:true"></input>
                    </td>
                </tr>
                <tr>
                   <td colspan="2">
                       <div id="cc" class="easyui-layout" style="width: 100%;height:400px;">
                           <div data-options="region:'west',split:true" style="width:50%;">
                               <table id="userPermissionGrid">
                                   <thead>
                                   <tr>
                                       <th width="10"  field="name" >名称</th>
                                       <th width="20"  field="url" >对应的资源</th>
                                       <th width="20"  field="sn" >对象的权限</th>
                                   </tr>
                                   </thead>
                               </table>
                           </div>
                           <div data-options="region:'center'" style="padding:5px;background:#eee;">
                               <table id="allPermissionGrid">
                                   <thead>
                                   <tr>
                                       <th width="10"  field="name" >名称</th>
                                       <th width="20"  field="url" >对应的资源</th>
                                       <th width="20"  field="sn" >对象的权限</th>
                                   </tr>
                                   </thead>
                               </table>
                           </div>
                       </div>
                   </td>
                </tr>

            </table>
        </form>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#roleDialog').dialog('close')">取消</a>
        </div>
    </div>
</div>

role.js
…
//用户权限列表
var userPermissionGrid =$("#userPermissionGrid");
//所有权限列表
var allPermissionGrid =$("#allPermissionGrid");

…
var itsource={
    
…    //添加数据(弹出添加的模态框)
    add:function () {
        //如果有data-save属性,我们把它展示出来
        $("*[data-save]").show();
        //启动有data-save属性的input元素的验证功能
        $("*[data-save] input").validatebox("enableValidation");
        roleForm.form("clear");//清除数据
        //弹出表单窗口
        roleDialog.dialog("center").dialog('open');
        //当前用户权限清空
        userPermissionGrid.datagrid("loadData",[]);
    },
    //修改数据(弹出修改的模态框)
    edit:function () {
        //选中了某一条数据才删除
        var row = roleGrid.datagrid("getSelected");
        if(row){
            //隐藏有data-save属性的元素
            $("*[data-save]").hide();
            //禁用有data-save属性的input元素的验证功能
            $("*[data-save] input").validatebox("disableValidation");

            roleForm.form("clear");//清除数据
            roleDialog.dialog("center").dialog('open');
            //为form加载数据
            roleForm.form("load",row);

            //单独解决权限的回显问题(注意,这里要准备一个新的权限,以免删除时出问题)
            var permissions = [];
            $.extend(permissions,row.permissions);
            userPermissionGrid.datagrid("loadData",permissions);

        }else{
            $.messager.alert('提示信息','请选择一行再进行修改!','info');
        }
    },
   …
    
    //双击所有权限,把一个权限交给这个角色
    addPermission:function(rowIndex, rowData){
        //判断是否有重复的行
        //1.拿到角色权限所有的行数据
        var rows = userPermissionGrid.datagrid("getRows");
        //2.遍历rows拿到每一个员工数据
        for(var i=0;i<rows.length;i++){
            var row = rows[i];                //3.判断如果出现相等数据的情况

            if(row.id == rowData.id){
                //不做任何操作(也可以给出提示)
                $.messager.show({
                    title:'提示',
                    msg:'该权限已经添加!',
                    timeout:2000,
                    showType:'slide'
                });
                return;
            }
        }
        //把数据加到相应角色权限Grid中
        userPermissionGrid.datagrid("appendRow",rowData);
    },
    //双击后移动相应的权限
    removePermission:function (rowIndex, rowData) {
        userPermissionGrid.datagrid("deleteRow",rowIndex);
    }
}

//我们在这里创建两个Grid
//1.创建当前角色的权限Grid
userPermissionGrid.datagrid({
    fit:true,
    fixed:true,
    fitColumns:true,
    //双击后移除权限
    onDblClickRow:itsource.removePermission
})

//2.获取所有权限的Grid
allPermissionGrid.datagrid({
    fit:true,
    fixed:true,
    fitColumns:true,
    //toolbar:'#tb',
    // singleSelect:true";
    url:"/permission/page",
    rownumbers:"true" ,
    pagination:"true",
    //双击后添加权限
    onDblClickRow:itsource.addPermission
})

一,权限判断

拿到所有用户

 //登陆认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //身份 username
        Object username = authenticationToken.getPrincipal();
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username1 = token.getUsername();

        //通过用户名得到密码 admin 从数据库读取数据 --根据用户名去查询用户的方法

        Employee employee = employeeService.getEmployeeByUsername(username1);
        if(employee == null){
            return null;
        }
        System.out.println(employee.getPassword());
        //添加颜值
        ByteSource salt = ByteSource.Util.bytes("itsource");
        //得到simpleAuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());


        return simpleAuthenticationInfo;
    }

UserContext:设置与拿到当前登录用户
放入到session中去

//工具
public class UserContext {

    public static final String USER_IN_SESSION = "loginUser";

    //存入session
    public static void setUser(Employee loginUser){
        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute(USER_IN_SESSION, loginUser);
    }

    //取出session
    public static Employee getUser(){
        Subject subject = SecurityUtils.getSubject();
        return (Employee) subject.getSession().getAttribute(USER_IN_SESSION);
    }
}

根据当前用户拿到当前用户对应的id


public interface IPermissionService extends IBaseService<Permission,Long>{

    //根据当前登录的用户 查询用户自己的权限
    public Set<String> findPermissionsByLoginUser(Long id);

}

权限判断Ajax请求和自定义权限拦截器

public class AisellPermissionAuthorizationFilter extends PermissionsAuthorizationFilter {
    //Ctrl+o复写方法
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        //得到主体
        Subject subject = this.getSubject(request, response);
        //判断主体是否为空
        if(subject.getPrincipal()==null){
        //登录失败的操作
            this.saveRequestAndRedirectToLogin(request,response);
        }else {
            //如果拦截请求是ajax请求,就返回json处理 否者就返回页面

            HttpServletRequest req = (HttpServletRequest) request;

            HttpServletResponse resp =(HttpServletResponse) response;

            //获取请求头
            String header = req.getHeader("X-Requested-With");


            if ("XMLHttpRequest".equals(header)){
                //设置类型和编码

                resp.setContentType("text/json;charset=UTF-8");
                resp.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;


    }

}

静态资源放行

public class AisellFilterChainDefinitionMapBuilder {
    //注入
    @Autowired
    private IPermissionService permissionService;


    public Map buildFilterChaiDefinitionMap(){
        //map要顺序要求
        /**
         */
        Map mp = new LinkedHashMap();
        mp.put("/login","anon");
        mp.put("*.js","anon");
        mp.put("*.css","anon");
        mp.put("/css/**","anon");
        mp.put("/js/**","anon");
        mp.put("/easyui/**","anon");
        mp.put("/images/**","anon");

        mp.put("/s/login.jsp","anon");

       //从数据库查询出来放入shiro不能写死
        List<Permission> permissions = permissionService.findAll();
        //取出所有url和sn
        for (Permission permission : permissions) {
            String url = permission.getUrl();
            String sn = permission.getSn();
            mp.put(url,"aisellPerm["+sn+"]");


        }
        mp.put("/**","authc");
        return mp;


    }
}

二,菜单读取

menu

@Entity
@Table(name = "menu")
public class Menu extends BaseDomain{
    private String name;
    private String url;
    private String icon;

    //懒加载
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    //加载json时忽略
    private Menu parent;


    //表示临时的属性
    @Transient
    private List<Menu> children = new ArrayList<>();
    下面是get,set

然后配置permission

 //加载方式懒加载
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "menu_id")
    private Menu menu;

MenuRepository

public interface MenuRepository extends BaseRepository<Menu,Long>{

    @Query("select m from Employee e join e.roles er join er.permissions p join p.menu m where e.id = ?1 ")
    public List<Menu> findMenuByLoginUser(Long userid);
}

MenuService
思路就是,判断每个不同用户下面有没有父菜单,有就添加子菜单,没有就添加父菜单

public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService {

    @Autowired
    private MenuRepository menuRepository;



    @Override
    public List<Menu> findMenuByLoginUser(Long userid) {
        //查询所有子菜单
        List<Menu> childrenList = menuRepository.findMenuByLoginUser(userid);

        //创建一个集合放父菜单
        List<Menu> perentList = new ArrayList<>();
        //拿到所有菜单
        for (Menu childrenMenu : childrenList) {
            //得到父菜单
            Menu parent = childrenMenu.getParent();
            //判断时候有父菜单,没有就添加,有就添加子菜单
            if (!perentList.contains(parent)){

                perentList.add(parent);
            }
                parent.getChildren().add(childrenMenu);

        }

        return perentList;
    }
}

MenuController

 @RequestMapping("/findMenuByLoginUser")
    @ResponseBody
    public List<Menu> findMenuByLoginUser(){
        Employee employee = UserContext.getUser();
        //根据登陆用户的id 查询菜单
        return  menuService.findMenuByLoginUser(employee.getId());
    }

一,配置使用poi的包

两款操作java操作办公框架
jxl: jxl早期只对excel操作,在处理excel上面有很多的优势 jxl在写入上要快一点
缺点:效率低,图片支持不完善,对格式的支持不如POI强大
poi:excel和word,ppt都可以才有 poi在读取上面要快一点
缺点:不成熟,代码不能跨平台,兼容性不是那么好

<!-- poi支持的jar包 -->
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>3.11</version>
</dependency>
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>3.11</version>
</dependency>

使用poi完成九九乘法表
导出

 @Test
    public void testPoiWrite99() throws Exception{
        //创建一个工作薄
        XSSFWorkbook workbook = new XSSFWorkbook();
        //创建一个表格
        XSSFSheet sheet = workbook.createSheet("九九乘法表");
        for (int i = 1; i <=9 ; i++) {
            XSSFRow row = sheet.createRow(i-1);
            for (int j = 1; j <= i ; j++) {
                //创建一个单元格
                XSSFCell cell = row.createCell(j-1);
                cell.setCellValue(i+"*"+j+"="+(i*j));
            }

        }
        //输出
        FileOutputStream out = new FileOutputStream(
                new File("99.xlsx"));
        workbook.write(out);
        out.close();
    }

用poi导入办公文件

 @Test
    public void testPoiRead() throws Exception{
        //读取
        FileInputStream fis = new FileInputStream(
                new File("empread.xlsx"));
        //得到工作薄
        XSSFWorkbook workbook = new XSSFWorkbook(fis);
        //得到第一个表格
        XSSFSheet spreadsheet = workbook.getSheetAt(0);
        //获取表格所有的row
        Iterator <Row> rowIterator = spreadsheet.iterator();
        int count = 0;
        //循环行
        while (rowIterator.hasNext()) {
               count++;
                //取出具体哪一个行
                XSSFRow row = (XSSFRow) rowIterator.next(); //取出两行进行抛弃
            if (count > 2) {
                //得到单元格
                Iterator<Cell> cellIterator = row.cellIterator();
                while (cellIterator.hasNext()) {
                    //取出单元格
                    Cell cell = cellIterator.next();
                    //判断单元格类型
                    switch (cell.getCellType()) {
                        case Cell.CELL_TYPE_NUMERIC:
                            System.out.print(
                                    cell.getNumericCellValue() + " \t\t ");
                            break;
                        case Cell.CELL_TYPE_STRING:
                            System.out.print(
                                    cell.getStringCellValue() + " \t\t ");
                            break;
                    }
                }
                System.out.println();
            }
        }
        fis.close();
    }

二,easypoi的基本使用
基本导出

 @Test
    public void testEasyuiPoi() throws Exception{

        List<EasyEmployee> list = new ArrayList<>();
        EasyEmployee easyEmployee1 = new EasyEmployee();
        easyEmployee1.setName("草拟大爷");
        easyEmployee1.setAge(30);
        easyEmployee1.setSex(false);
        easyEmployee1.setBirthday(new Date());
        easyEmployee1.setHeadImage("4.bmp");

        EasyEmployee easyEmployee2 = new EasyEmployee();
        easyEmployee2.setName("小泽玛利亚");
        easyEmployee2.setAge(20);
        easyEmployee2.setSex(false);
        easyEmployee2.setBirthday(new Date());
        easyEmployee2.setHeadImage("6.bmp");
        list.add(easyEmployee1);
        list.add(easyEmployee2);
        //导出代码
        //ExportParams 设置标题 设置表名
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("av演员","女优"),
                EasyEmployee.class, list);

        //输出效果
        FileOutputStream outputStream = new FileOutputStream(new File("employee导出.xlsx"));
        workbook.write(outputStream);
        outputStream.close();
    }

测试关联对象,只导入部门和部门下面的东西

//关联对象 --表示这个对象里面也有导出的字段 深入导出
    @ExcelEntity
    private EasyDept easyDept;

测试代码

 @Test
    public void testEasyuiPoi2() throws Exception{

        List<EasyDept> list = new ArrayList<>();

        EasyDept easyDept1 = new EasyDept();
        easyDept1.setName("无痛人流部");
        EasyDept easyDept2 = new EasyDept();
        easyDept2.setName("包皮包茎部");

        list.add(easyDept1);
        list.add(easyDept2);

        //导出代码
        //ExportParams 设置标题 设置表名
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("部门信息","部门"), EasyDept.class, list);

        //输出效果
        FileOutputStream outputStream = new FileOutputStream(new File("employee导出1.xlsx"));
        workbook.write(outputStream);
        outputStream.close();
    }

解决图像问题

 //2表示选择图片导出 1表示的文本
    @Excel(name="头像",type = 2 ,width = 40 , height = 20)

测试代码

 @Test
    public void testEasyuiPoi1() throws Exception{

        List<EasyEmployee> list = new ArrayList<>();
        EasyEmployee easyEmployee1 = new EasyEmployee();
        easyEmployee1.setName("草拟大爷");
        easyEmployee1.setAge(30);
        easyEmployee1.setSex(false);
        easyEmployee1.setBirthday(new Date());
        easyEmployee1.setHeadImage("4.bmp");
        EasyDept dept1 = new EasyDept();
        dept1.setName("男优部");

        easyEmployee1.setEasyDept(dept1);
        EasyEmployee easyEmployee2 = new EasyEmployee();
        easyEmployee2.setName("小泽玛利亚");
        easyEmployee2.setAge(20);
        easyEmployee2.setSex(false);
        easyEmployee2.setBirthday(new Date());
        easyEmployee2.setHeadImage("6.bmp");

        EasyDept dept2 = new EasyDept();
        dept2.setName("女优部");

        easyEmployee2.setEasyDept(dept2);

        list.add(easyEmployee1);
        list.add(easyEmployee2);
        //导出代码
        //ExportParams 设置标题 设置表名
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("av演员","女优"),
                EasyEmployee.class, list);

        //输出效果
        FileOutputStream outputStream = new FileOutputStream(new File("employee导出.xlsx"));
        workbook.write(outputStream);
        outputStream.close();
    }

三,和springmvc集成关联

首先配置Controller
测试代码(没有开启验证)

@Controller
@RequestMapping("/import")
public class ImportController {

    //自定义验证类
    @Autowired
    private AisellEmployeeVerifyHander aisellEmployeeVerifyHander;

    @Autowired
    private IDepartmentService departmentService;

    @Autowired
    private IEmployeeService employeeService;

    @RequestMapping("/index")
    public String importPage(){

        return "import";
    }

    //导入功能 --没有验证功能
    @RequestMapping("/employeeXlsx1")
    public String employeeXlsx1(MultipartFile empFile) throws Exception {
        //这个文件怎么
        ImportParams params = new ImportParams();
        params.setTitleRows(1);

        //导入
        List<Employee> list = ExcelImportUtil.importExcel(empFile.getInputStream(),
                Employee.class, params);

        for (Employee employee : list) {
            System.out.println(employee);
            //根据部门名称查询的部门对象
            String deptName = employee.getDepartment().getName();
            Department department = departmentService.findDepartmentByName(deptName);
            //设置部门
            employee.setDepartment(department);
            employee.setPassword("123456");
            employeeService.save(employee);
        }
        return "import";
    }

开启验证

 //具备验证功能
    @RequestMapping("/employeeXlsx")
    public String employeeXlsx(MultipartFile empFile, HttpServletResponse response) throws Exception {
        //这个文件怎么
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        //开启验证
        params.setNeedVerfiy(true);
        //开启自定义验证
        params.setVerifyHandler(aisellEmployeeVerifyHander);
        //导入
        ExcelImportResult<Employee> result = ExcelImportUtil.importExcelMore(empFile.getInputStream(),
                Employee.class, params);
        //正确数据
        for (Employee employee : result.getList()) {
            System.out.println(employee);
            //根据部门名称查询的部门对象
            String deptName = employee.getDepartment().getName();
            Department department = departmentService.findDepartmentByName(deptName);
            //设置部门
            employee.setDepartment(department);
            employee.setPassword("123456");

            employeeService.save(employee);
        }

        //错误的数据
        for (Employee employee : result.getFailList()) {
            System.out.println("错误数据:"+employee);
        }
        //传回前台 让用户查看 修改
        if(result.isVerfiyFail()){
            //有时候信息
            Workbook failWorkbook = result.getFailWorkbook();
            //输出设置一堆参数
            //把这个文件导出
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //mime类型
            response.setHeader("Content-disposition", "attachment;filename=error.xlsx");
            response.setHeader("Pragma", "No-cache");//设置不要缓存
           //输出内容
            OutputStream ouputStream = response.getOutputStream();
            failWorkbook.write(ouputStream);
            ouputStream.flush();
            ouputStream.close();

        }
        return "import";
    }

最后自定义验证

public class AisellEmployeeVerifyHander implements IExcelVerifyHandler<Employee> {

    @Autowired
    private IEmployeeService employeeService;
    @Override
    public ExcelVerifyHandlerResult verifyHandler(Employee employee) {
        ExcelVerifyHandlerResult result = new ExcelVerifyHandlerResult(true);
        boolean flag = employeeService.checkUsername(employee.getUsername());
        if(!flag){
            result.setSuccess(false);
            result.setMsg("用户名重复");
        }

        return result;
    }
}

一,系统数据字典

SystemDictionaryType

@Entity
@Table(name = "systemdictionarydetail")
public class Systemdictionarydetail extends BaseDomain{

    //定义两个常量
    public static final String PRODUCT_BRAND = "productBrand";
    public static final String PRODUCT_UNIT = "productUnit";
    private String name;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "types_id")
    private Systemdictionarytype types;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Systemdictionarytype getTypes() {
        return types;
    }

    public void setTypes(Systemdictionarytype types) {
        this.types = types;
    }
}

query

public class SystemdictionarydetailQuery extends BaseQuery{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Specification createSpecification() {
        Specification<Systemdictionarydetail> spe = Specifications.<Systemdictionarydetail>and().
                like(StringUtils.isNotBlank(name), "name", "%" + name + "%")
                .build();

        return spe;
    }
}

Repository

public interface SystemdictionarydetailRepository extends BaseRepository<Systemdictionarydetail,Long>{

    @Query("select d from Systemdictionarydetail d join d.types t where t.sn=?1")
    List<Systemdictionarydetail> findDetailsBySn(String sn);
}

service

public interface ISystemdictionarydetailService extends IBaseService<Systemdictionarydetail,Long> {

    /**
     * 根据编号查询对应的数据字典明细
     * @param sn   数据字典类型的编号
     * @return
     */
    List<Systemdictionarydetail> findDetailsBySn(String sn);
}
@Service
public class SystemdictionarydetailServiceImpl extends BaseServiceImpl<Systemdictionarydetail,Long> implements ISystemdictionarydetailService {

    @Autowired
    private SystemdictionarydetailRepository systemdictionarydetailRepository;

    @Override
    public List<Systemdictionarydetail> findDetailsBySn(String sn) {
        return systemdictionarydetailRepository.findDetailsBySn(sn);
    }
}

Controller

@Controller
@RequestMapping("/systemdictionarydetail")
public class SystemdictionarydetailController {
    @Autowired
    private ISystemdictionarydetailService systemdictionarydetailService;
    @RequestMapping("/index")
    public String index(){
        return "systemdictionarydetail/systemdictionarydetail";
    }

    //写一个方法查询数据 返回json --查数据库 findPage
    @RequestMapping("/page")
    @ResponseBody
    public UiPage page(SystemdictionarydetailQuery systemdictionarydetailQuery){
        //List<Systemdictionarydetail> systemdictionarydetails = systemdictionarydetailService.findAll();
        Page page = systemdictionarydetailService.findPageByQuery(systemdictionarydetailQuery);
        UiPage uipage = new UiPage(page);
        return uipage;
    }

    //方法 delete
    @RequestMapping("/delete")
    @ResponseBody
    public Map delete(Long id){
        Map mp = new HashMap();
        try {
            systemdictionarydetailService.delete(id);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }

    //保存方法-- 修改和新增保存
    @RequestMapping("/save")
    @ResponseBody
    public Map save(Systemdictionarydetail systemdictionarydetail){
        Map mp = new HashMap();
        try {
            systemdictionarydetailService.save(systemdictionarydetail);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }

    @RequestMapping("/update")
    @ResponseBody
    public Map update(@ModelAttribute("editSystemdictionarydetail") Systemdictionarydetail systemdictionarydetail){
        Map mp = new HashMap();
        try {
            //Systemdictionarydetail systemdictionarydetail = findOne(systemdictionarydetail.getId())
            systemdictionarydetailService.save(systemdictionarydetail);
            mp.put("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            mp.put("success", false);
            mp.put("msg", e.getMessage());
        }
        return mp;

    }



    //删除方法
    @ModelAttribute("editSystemdictionarydetail")
    public Systemdictionarydetail beforeEdit(Long id, String cmd){
        Systemdictionarydetail systemdictionarydetail = null;
        if("update".equals(cmd) && id != null && !"".equals(id) ){
            systemdictionarydetail = systemdictionarydetailService.findOne(id);

        }
        return systemdictionarydetail;
    }
}

二,自主完成CRUD

使用代码生成器完成:
domain
query
repository
service
controller
还有前台页面

产品页面展示

<table id="productGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true";
       url="/product/page"
       iconCls="icon-save"
       rownumbers="true" pagination="true">
    <thead>
    <tr>
        <th width="20"  field="name"  >名称</th>
        <th width="20"  field="color"  data-options="formatter:formatColor"  >颜色</th>
        <th width="20"  field="smallpic"  data-options="formatter:formatImg"  >图片</th>
        <th width="20"  field="costprice"  >成本价</th>
        <th width="20"  field="saleprice"  >销售价</th>
        <th width="20"  field="types"  data-options="formatter:formatObj"  >类型</th>
        <th width="20"  field="unit"  data-options="formatter:formatObj" >单位</th>
        <th width="20"  field="brand"  data-options="formatter:formatObj" >品牌</th>
    </tr>
    </thead>
</table>

处理大图展示方法
function loadSuccess(data) {
//看是否进来了
alert(进来了)
var rows = data.rows;
for(var i=0;i<rows.length;i++){
var result = rows[i];
. e a s y u i . t o o l t i p . i n i t ( .easyui.tooltip.init( .easyui.tooltip.init((“img[src=’”+result.smallpic+"’]"), {
position: “rigth”,
content: “<div style=“width:600px;height:480px;”>
});
}
}
添加与修改页面

<div id="productDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:450px">
    <div style="padding:10px 60px 20px 40px">
        <form id="productForm" class="easyui-form" method="post" data-options="">
            <input type="hidden" id="productId" name="id" >
            <table cellpadding="5">
                <tr>
                    <td>名称:</td>
                    <td><input class="easyui-validatebox" type="text" name="name" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>颜色:</td>
                    //注意颜色在回显的时候不用去写,,,type=color可以直接回显
                    <td><input class="easyui-validatebox" type="color" name="color"></input></td>
                </tr>
                <tr>
                    <td>成本价:</td>
                    <td><input class="easyui-validatebox" type="text" name="costprice"></input></td>
                </tr>
                <tr>
                    <td>销售价:</td>
                    <td><input class="easyui-validatebox" type="text" name="saleprice"></input></td>
                </tr>
                <tr>
                    <td>产品图片:</td>
                    <td>
                        <input name="fileImage" class="easyui-filebox" style="width:100%">
                    </td>
                </tr>
            </table>
        </form>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#productDialog').dialog('close')">取消</a>
        </div>
    </div>
</div>

产品修改时回显

edit:function () {
    //选中了某一条数据才删除
    var row = productGrid.datagrid("getSelected");
    if(row){
        //隐藏有data-save属性的元素
        $("*[data-save]").hide();
        //禁用有data-save属性的input元素的验证功能
        $("*[data-save] input").validatebox("disableValidation");

        productForm.form("clear");//清除数据
        productDialog.dialog("center").dialog('open');
        //解决单位,品牌,类型的回显问题
        if(row.unit){
            row["unit.id"] = row.unit.id;
        }
        if(row.brand){
            row["brand.id"] = row.brand.id;
        }
        if(row.types){
            row["types.id"] = row.types.id;
        }
        //为form加载数据
        productForm.form("load",row);
    }else{
        $.messager.alert('提示信息','请选择一行再进行修改!','info');
    }
},

保存

@RequestMapping("/save")
@ResponseBody
public Map<String,Object> save(Product product,HttpServletRequest req){
    return saveOrUpdate(product,req);
}

最后删除(注意删除时把图片也删除了)

//删除功能
@RequestMapping("/delete")
@ResponseBody
public Map<String,Object> delete(Long id,HttpServletRequest req){
    Map<String,Object> map = new HashMap<>();
    try{
        Product product = productService.findOne(id);
        productService.delete(id);
        // 删除图片的代码,写在delete方法之后
        String webapp = req.getServletContext().getRealPath("/");
        if (id != null && StringUtils.isNotBlank(product.getPic())) {
            File deleteFile = new File(webapp, product.getPic());
            if (deleteFile.exists()) {
                deleteFile.delete();
            }
            File deleteSmallFile = new File(webapp, product.getSmallPic());
            if (deleteSmallFile.exists()) {
                deleteSmallFile.delete();
            }
        }
        map.put(SUCCESS,true);
    }catch (Exception e){
        map.put(SUCCESS,false);
        map.put("msg",e.getMessage());
        e.printStackTrace();
    }
    return map;
}

一,采购单模型设计

判断数据库联系
如果是下拉列表:一般是多对一,一对一
如果是复选框:一般是多对多,一对多
采购单组合关系
主表

@Entity
@Table(name="purchasebill")
public class Purchasebill extends BaseDomain {

    private Date vdate; // 交易时间 前台传过来
    private BigDecimal totalAmount; //订单总金额 --后台计算出来
    private BigDecimal totalNum; //订单数量 --后台计算出来
    private Date inputtime = new Date();//采购单录入时间 -- 后台生成
    private Date auditortime; //审核时间 --- 后台生成
    private Integer status = 0; //单据状态 0 表示待审核 1 表示审核 -1表作废
    //private Long supplierId;
   @ManyToOne(fetch = FetchType.LAZY,optional = false)
   @JoinColumn(name="supplier_id")
   private Supplier supplier;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="auditor_id")
    private Employee auditor;//审核人 录入可以为null

    @ManyToOne(fetch = FetchType.LAZY,optional = false)
    @JoinColumn(name="inputUser_id")
    private Employee inputUser;//录入人 不能为null 当前登陆用户

    @ManyToOne(fetch = FetchType.LAZY,optional = false)
    @JoinColumn(name="buyer_id")
    private Employee buyer;//采购员

    //明細  强级联  orphanRemoval 一方解除关系 去删除
    @OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY,mappedBy = "bill",
    orphanRemoval = true)
    private List<Purchasebillitem> items = new ArrayList<>();

    @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;
    }

    public BigDecimal getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }

    public BigDecimal getTotalNum() {
        return totalNum;
    }

    public void setTotalNum(BigDecimal totalNum) {
        this.totalNum = totalNum;
    }

    public Date getInputtime() {
        return inputtime;
    }

    public void setInputtime(Date inputtime) {
        this.inputtime = inputtime;
    }

    public Date getAuditortime() {
        return auditortime;
    }

    public void setAuditortime(Date auditortime) {
        this.auditortime = auditortime;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Supplier getSupplier() {
        return supplier;
    }

    public void setSupplier(Supplier supplier) {
        this.supplier = supplier;
    }

    public Employee getAuditor() {
        return auditor;
    }

    public void setAuditor(Employee auditor) {
        this.auditor = auditor;
    }

    public Employee getInputUser() {
        return inputUser;
    }

    public void setInputUser(Employee inputUser) {
        this.inputUser = inputUser;
    }

    public Employee getBuyer() {
        return buyer;
    }

    public void setBuyer(Employee buyer) {
        this.buyer = buyer;
    }

    public List<Purchasebillitem> getItems() {
        return items;
    }

    public void setItems(List<Purchasebillitem> items) {
        this.items = items;
    }
}

采购明细表

@Entity
@Table(name="purchasebillitem")
public class Purchasebillitem extends BaseDomain {
        private BigDecimal price;//产品价格
        private BigDecimal num;//产品数量

        private BigDecimal amount;//产品小计
        private String descs; //产品描述
        // private Long productId;
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name="product_id")
        private Product product;

        //订单 bill_id
       @ManyToOne(fetch = FetchType.LAZY,optional = false)
       @JoinColumn(name="bill_id")
       @JsonIgnore //返回页面 不展示出来
       private Purchasebill bill;


    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public BigDecimal getNum() {
        return num;
    }

    public void setNum(BigDecimal num) {
        this.num = num;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public String getDescs() {
        return descs;
    }

    public void setDescs(String descs) {
        this.descs = descs;
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    public Purchasebill getBill() {
        return bill;
    }

    public void setBill(Purchasebill bill) {
        this.bill = bill;
    }
}

订单明细表展示页面

<table id="purchasebillGrid" class="easyui-datagrid" data-options="fit:true,fixed:true,fitColumns:true,toolbar:'#tb',singleSelect:true";
       url="/purchasebill/page"
       iconCls="icon-save"
       rownumbers="true" pagination="true">
    <thead>
    <tr>
        <th width="20"  field="vdate">交易时间</th>
        <th width="20"  field="supplier" data-options="formatter:formatObj">供应商</th>
        <th width="20"  field="buyer"  data-options="formatter:formatEmp">采购员</th>
        <th width="20"  field="totalNum">总数量</th>
        <th width="20"  field="totalAmount" >总金额</th>
        <th width="20"  field="status" data-options="formatter:formatStatus" >状态</th>
    </tr>
    </thead>
</table>

处理明细单审核状态

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.结束时间是不能成功获取查询的值.
如果是月底,时间会自动添加到下一个月

 @Override
    public Specification createSpecification() {

        Date tempDate = null;
        if(this.endDate!=null){
            tempDate =  DateUtils.addDays(this.endDate,1 );
            System.out.println(tempDate);
        }

        Specification<Purchasebill> spe = Specifications.<Purchasebill>and().
                ge(this.beginDate != null, "vdate",this.beginDate ).
                lt(this.endDate!=null,"vdate",tempDate).
                eq(this.status!=null && !"".equals(this.status),"status",this.status)
                .build();

        return spe;
    }

弹出采购对话框
加载采购员 和 供应商

<tr>
                <td>供应商:</td>
                <td><input class="easyui-combobox" type="text" name="supplier.id"
                           data-options="panelHeight:'auto',valueField:'id',textField:'name',url:'/util/findAllSupplier'"
                ></input></td>
            </tr>
            <tr>
                <td>采购员:</td>
                <td><input class="easyui-combobox" type="text" name="buyer.id"  data-options="valueField:'id',textField:'username',url:'/util/findAllBuyer'"></input></td>
            </tr>

二,采购单明细表格处理(难点)

表单样式可以从网上下载,也可以应课件准备好的

<div id="purchasebillDialog" class="easyui-dialog" data-options="closed:true,modal:true" title="功能操作" style="width:800px">
    <div style="padding:10px 60px 20px 40px">
        <form id="purchasebillForm" class="easyui-form" method="post" data-options="">
            <input type="hidden" id="purchasebillId" name="id" >
            <table cellpadding="5">
                <tr>
                    <td>交易时间:</td>
                    <td><input class="easyui-datebox"name="vdate" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>供应商:</td>
                    <td>
                        <input class="easyui-combobox" name="supplier.id"
                               data-options="valueField:'id',textField:'name',panelHeight:'auto',url:'/util/findAllSupplier'">
                    </td>
                </tr>
                <tr>
                    <td>采购员:</td>
                    <td>
                        <input class="easyui-combobox" name="buyer.id"
                               data-options="valueField:'id',textField:'username',url:'/util/getBuyer'">
                    </td>
                </tr>
            </table>
        </form>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="$('#purchasebillDialog').dialog('close')">取消</a>
        </div>
    </div>
</div>

明细单保存
准备额外参数

 //保存方法 --提交表单的数据到后台
            purchasebillForm.form('submit', {
                    url:url,
                    onSubmit: function(param){
                        //提交 封装 items
                        //得到明细表格所有的数据 price num descs
                        var rows = $("#gridItem").datagrid('getRows');
                        for(var i=0;i<rows.length;i++){
                            var rowData = rows[i];
                            param["items["+i+"].descs"] = rowData.descs;
                            param["items["+i+"].price"] = rowData.price;
                            param["items["+i+"].num"] = rowData.num;
                            param["items["+i+"].product.id"] = rowData.productId.id;
                        }

                        // 提交之前的验证
                       return purchasebillForm.form('validate');

解决修改回显问题
准备一个临时副本存放数据,
用的时候才拿出来,这样就不会出现明明没有删除操作,数据却显示不出来的问题

 edit:function(){
            //选择一条数据进行修改
            var row = purchasebillGrid.datagrid('getSelected');
            if(row){
                //弹出对话框
                purchasebillDialog.dialog('center').dialog('open');
               //供货商
                if(row.supplier){
                    row["supplier.id"] = row.supplier.id;
                }
                //供货商
                if(row.buyer){
                    row["buyer.id"] = row.buyer.id;
                }
                for(var i=0;i<row.items.length;i++){
                    var item = row.items[i];
                    item["productId"] = item.product;
                }


                var items = $.extend([], row.items);
                //明细表格回显
                $("#gridItem").datagrid('loadData',items);
                //修改 -- 回显示数据
                purchasebillForm.form('load',row);
            }else{
                //提示用户
                $.messager.alert('温馨提示:','请选中一条数据进行修改','info');
                return;
            }

最后解决n to n问题
设置关联对象为null,在每次用之前清理一次
就不会出现n-to-n问题了

purchasebill.setBuyer(null);
purchasebill.setSupplier(null);
purchasebill.getItems().clear();

一,采购单报表页面查询

datagrid
页面效果展示js

$(function(){
    $('#purchasebillitemDatagrid').datagrid({
        width:500,
        height:250,
        fit:true,
        rownumbers:true,
        remoteSort:false,
        nowrap:false,
        fitColumns:true,
        toolbar:'#tb',
        url:'/purchasebillitem/findAllItemVo',
        columns:[[
            {field:'id',title:'编号',width:100,sortable:true},
            {field:'supplier',title:'供应商名称',width:80,align:'right',sortable:true},
            {field:'buyer',title:'采购员名称',width:80,align:'right',sortable:true},
            {field:'product',title:'产品名称',width:150,sortable:true},
            {field:'productType',title:'产品分类',width:60,align:'center'},
            {field:'vdate',title:'交易时间',width:180,align:'right',sortable:true},
            {field:'num',title:'数量',width:50,sortable:true},
            {field:'price',title:'价格',width:60,align:'center'},
            {field:'amount',title:'小计',width:60,align:'center'},
            {field:'status',title:'状态',width:60,align:'center',formatter:statusFormatter}
        ]],
        groupField:'groupField',
        view: groupview,
        groupFormatter:function(value, rows){
            var totalNum =0;
            var totalAmount = 0;
            //rows表示当前分组下面的行
            for(var i=0;i<rows.length;i++){
                var row = rows[i];//循环每一行
                totalNum += row.num;
                totalAmount += row.amount;
            }
            return value + ' - ' + rows.length + ' 条数据' +"  <span style='color:green;'>共"+totalNum+"件商品</span>" +"<span style='color:#6b3980;'>总金额:"+totalAmount+"</span>";
        }
    });
});

后台数据
定义一个PurchaseBillItemVo 类
用来处理分组问题, 还有date时间问题,
用构造方法创建对象,并赋值

public class PurchasebillitemVo {

    private Long id; //编号
    private String supplier; //供应商名称
    private String buyer; //采购员名称
    private String product; //产品名称
    private String productType; //产品分类
    private Date vdate; //交易时间
    private BigDecimal num; //采购数量
    private BigDecimal price; //价格
    private BigDecimal amount; //小计 = 价格*数量
    private Integer status;

    private String groupField = ""; //分组字段

    //构造方法 创建对象或者赋值
    public PurchasebillitemVo(Purchasebillitem item){
        this.id = item.getId();
        this.supplier = item.getBill().getSupplier().getName();
        this.buyer = item.getBill().getBuyer().getUsername();
        this.product = item.getProduct().getName();
        this.productType = item.getProduct().getTypes().getName();
        this.vdate = item.getBill().getVdate();
        this.num = item.getNum();
        this.price = item.getPrice();
        this.amount = item.getAmount();
        this.status = item.getBill().getStatus();
    }

    public PurchasebillitemVo(Purchasebillitem item,String groupBy){
        this.id = item.getId();
        this.supplier = item.getBill().getSupplier().getName();
        this.buyer = item.getBill().getBuyer().getUsername();
        this.product = item.getProduct().getName();
        this.productType = item.getProduct().getTypes().getName();
        this.vdate = item.getBill().getVdate();
        this.num = item.getNum();
        this.price = item.getPrice();
        this.amount = item.getAmount();
        this.status = item.getBill().getStatus();
        //根据传入的条件 进行分组
        if("o.bill.supplier.name".equals(groupBy)){
            this.groupField = this.supplier;
        }else if("o.bill.buyer.username".equals(groupBy)){
            this.groupField = this.buyer;
        }else if("MONTH(o.bill.vdate)".equals(groupBy)){
            //this.groupField = this.vdate; 7 8
            this.groupField = (DateUtils.toCalendar(this.vdate).get(Calendar.MONTH)+1)+"月";
        }else{
            this.groupField = this.supplier;
        }
    }

    public static void main(String[] args) {
        System.out.println(DateUtils.toCalendar(new Date()).get(Calendar.MONTH)+1);
    }

    public  PurchasebillitemVo(){}



    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getSupplier() {
        return supplier;
    }

    public void setSupplier(String supplier) {
        this.supplier = supplier;
    }

    public String getBuyer() {
        return buyer;
    }

    public void setBuyer(String buyer) {
        this.buyer = buyer;
    }

    public String getProduct() {
        return product;
    }

    public void setProduct(String product) {
        this.product = product;
    }

    public String getProductType() {
        return productType;
    }

    public void setProductType(String productType) {
        this.productType = productType;
    }

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    public Date getVdate() {
        return vdate;
    }

    public void setVdate(Date vdate) {
        this.vdate = vdate;
    }

    public BigDecimal getNum() {
        return num;
    }

    public void setNum(BigDecimal num) {
        this.num = num;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getGroupField() {
        //设置分组字段 默认按照供应商分组
       // this.groupField = this.supplier;
        return groupField;
    }

    public void setGroupField(String groupField) {
        this.groupField = groupField;
    }
}

根据前台传来的日期,状态,和用户名来查询query

public class PurchasebillitemQuery extends BaseQuery{


    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //接收时间和状态

    private Date beginDate;

    private Date endDate;

    private Integer status;

    //介绍分组字段
    private String groupBy = "o.bill.supplier.name";

    public String getGroupBy() {
        return groupBy;
    }

    public void setGroupBy(String groupBy) {
        this.groupBy = groupBy;
    }

    public Date getBeginDate() {
        return beginDate;
    }

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    public void setBeginDate(Date beginDate) {
        this.beginDate = beginDate;
    }

    public Date getEndDate() {
        return endDate;
    }

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    //抽取查询
    @Override
    public Specification createSpecification() {
        Date tempDate = null;
        if(this.endDate!=null){
            tempDate =  DateUtils.addDays(this.endDate,1 );
            System.out.println(tempDate);
        }
        //查询 Purchasebillitem select o from purchasebillitem o where o.bill.vdate = 1? and  o.bill.status
        Specification<Purchasebillitem> spe = Specifications.<Purchasebillitem>and().
                ge(this.beginDate != null, "bill.vdate",this.beginDate ).
                lt(this.endDate!=null,"bill.vdate",tempDate).
                eq(this.status!=null && !"".equals(this.status),"bill.status",this.status)
                .build();

        return spe;
    }

service层处理

 @Autowired
    private PurchasebillitemRepository purchasebillitemRepository;

    @Override
    public List<PurchasebillitemVo> findItems(PurchasebillitemQuery itemQuery) {
        //查询所有的采购的明细
        List<Purchasebillitem> items = purchasebillitemRepository.findByQuery(itemQuery);
        List<PurchasebillitemVo> itemsVoList = new ArrayList<>();
        for (Purchasebillitem item : items) {
            PurchasebillitemVo itemVo = new PurchasebillitemVo(item,itemQuery.getGroupBy());
            itemsVoList.add(itemVo);
        }
        return itemsVoList;
    }

controller层处理
这里需要注意的是分组报表查询,因为前台页面效果是这样的
所以需要把查询出来的List放入map里去
在这里插入图片描述

 //分组报表查询
   
    @RequestMapping("/findAllItemVo")
    @ResponseBody
    public Map findPurchaseBillItemVo(PurchasebillitemQuery billitemQuery){

        Map mp = new HashMap();
        List<PurchasebillitemVo> items = purchasebillitemService.findItems(billitemQuery);
        mp.put("total", items.size());
        mp.put("rows", items);

        return mp;
    }

前台数据完成
引入js

<script type="text/javascript" src="/js/model/purchasebillitem.js"></script>

js

$(function(){
    $('#purchasebillitemDatagrid').datagrid({
        width:500,
        height:250,
        fit:true,
        rownumbers:true,
        remoteSort:false,
        nowrap:false,
        fitColumns:true,
        toolbar:'#tb',
        url:'/purchasebillitem/findAllItemVo',
        columns:[[
            {field:'id',title:'编号',width:100,sortable:true},
            {field:'supplier',title:'供应商名称',width:80,align:'right',sortable:true},
            {field:'buyer',title:'采购员名称',width:80,align:'right',sortable:true},
            {field:'product',title:'产品名称',width:150,sortable:true},
            {field:'productType',title:'产品分类',width:60,align:'center'},
            {field:'vdate',title:'交易时间',width:180,align:'right',sortable:true},
            {field:'num',title:'数量',width:50,sortable:true},
            {field:'price',title:'价格',width:60,align:'center'},
            {field:'amount',title:'小计',width:60,align:'center'},
            {field:'status',title:'状态',width:60,align:'center',formatter:statusFormatter}
        ]],
        groupField:'groupField',
        view: groupview,
        groupFormatter:function(value, rows){
            var totalNum =0;
            var totalAmount = 0;
            //rows表示当前分组下面的行
            for(var i=0;i<rows.length;i++){
                var row = rows[i];//循环每一行
                totalNum += row.num;
                totalAmount += row.amount;
            }
            return value + ' - ' + rows.length + ' 条数据' +"  <span style='color:green;'>共"+totalNum+"件商品</span>" +"<span style='color:#6b3980;'>总金额:"+totalAmount+"</span>";
        }
    });
});

二,HighChart,全球使用,echart(百度开发,开源免费)

引入,HighChart
3D报表图,2D只需改变 alpha到0度就可以了

 charts3D:function(){
            //发送ajax请求到后台查询的数据吧

            var param = searchForm.serializeObject();
            $.post("/purchasebillitem/findAllGraphic",param,function(result){

                Highcharts.chart('purchasebillitemDialog', {
                    chart: {
                        type: 'pie',
                        options3d: {
                            enabled: true,
                            alpha: 45, //倾斜角度
                            beta: 0
                        }
                    },
                    title: {
                        text: '消费图'
                    },
                    tooltip: {
                        pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
                    },
                    plotOptions: {
                        pie: {
                            allowPointSelect: true,
                            cursor: 'pointer',
                            depth: 35,//深度
                            dataLabels: {
                                enabled: true,
                                format: '{point.name}'
                            }
                        }
                    },
                    series: [{
                        type: 'pie',
                        name: '消费比例',
                        data:result
                    }]
                });
            })
		    //3D图表 --打开对话框
            purchasebillitemDialog.dialog('center').dialog('open');

通过文档看到其展示数据的类型是[{},{},{}]这样,所以我们需要相应的查询语句
拼接sql语句

 //定义一个容器
  List params = new ArrayList<>();
  
    public String getWhereSql(){

        String sql = "";

        if(beginDate != null && !"".equals(beginDate)){
            sql += " and b.vdate >= ?";
            params.add(beginDate);
        }

        if(endDate != null && !"".equals(endDate)){
            sql += " and b.vdate < ?";
            params.add(endDate);
        }

        if(status != null && !"".equals(status)){
            sql += " and b.status = ?";
            params.add(status);
        }

        return sql.replace("and", "where");

    }

    public List getParams() {
        return params;
    }

    public void setParams(List params) {
        this.params = params;
    }

查询到了图标需要的值后,在把数据弄成我们想要的样子
这里要注意jpql版本不同的bug,//JPA框架的bug的
group by的语句 不要写? group by 直接就拼接sql,会导致数据的丢失,
查询不出来

 //根据jpql语句查询的数据
    @Override
    public List findItemsByJql(PurchasebillitemQuery itemQuery) {
		  List<Map> mpList = new ArrayList<>();
        String groupBy = itemQuery.getGroupBy(); 
        //根据jpql语句查询 
        String jpql = "select "+groupBy+",sum(b.totalAmount) from Purchasebillitem o join o.bill b "+itemQuery.getWhereSql()+"  group by "+groupBy;
        //查询出来的list list->数组 list.toArray()
        List<Object[]> list = purchasebillitemRepository.findByJpql(jpql,itemQuery.getParams().toArray());
		 String jpql1 = "select o.bill.supplier.name,sum(b.totalAmount) from Purchasebillitem o join o.bill b group by ?";

        List<Object[]> list1 = purchasebillitemRepository.findByJpql(jpql1,groupBy);
        System.out.println("list1:"+list1);

        for (Object[] objects : list) {
            Object name = objects[0];
            BigDecimal y = (BigDecimal)objects[1];
            Map mp = new HashMap();
            mp.put("name", name);
            mp.put("y", y);
            mpList.add(mp);
        }
        return mpList;
    }

最后controlller层处理

 //查询图形报表
    @RequestMapping("/findAllGraphic")
    @ResponseBody
    public List findAllGraphic(PurchasebillitemQuery billitemQuery){

        List itemsListMap = purchasebillitemService.findItemsByJql(billitemQuery);

        return itemsListMap;
    }

一,整个项目的基本流程

基础数据模块:,产品类型,供应商(采购),客户(销售)
权限管理 产品 数据字典 员工 部门–shiro(身份认证和授权)

采购模块
在这里插入图片描述
请购单录入 
采购请购是指企业内部向采购部门提出采购申请,或采购部门汇总企业内部采购需求提出采购清单。 
在采购管理模块——请购——请购单中,点击增加,出现空白单据 
在单据中录入请购部门、请购人员、存货编码、数量等,确认无误后保存单据; 2、请购单审核 
打开请购单列表弹出查询条件选择,录入过滤条件点击确定
程序将符合条件的单据过滤出来然后选择要审核的单据进行审核

库存模块
入库数据: 保存(stockincomebill/stockincomebillitem)
出库数据:
入库审核:
审核完之后, 进入productstock(即时库存表)和 depot(仓库表) 同时 (stockincomebill)入库单据状态变成已审核
出库审核之后:
审核完之后, 进入productstock和 depot 同时 (stockincomebill)入库单据状态变成已审核
盘点模块:(提了需求 crud)
清点模块 – 清点单(每个月有那么一两天清点)
盘盈盘亏数据 --单据
清点完之后–还有同步数据
及时库存表:(账面库存)
同一个仓库里面的产品是唯一
合并原则:(入库 通过同一个产品 同一个仓库) – 价格(加权平均法)
数量*价格 +库存数量 *价格 /数量+库存数量
保存入库–和采购单保存一样的
审核入库单(重点)审核流程:
单据变成已审核 ,改变状态 添加审核人 审核时间
更新即时库存表
更新仓库表
所以我们需要配置三张表课件上有拷过来直接改

@Entity
@Table(name="stockincomebill")
public class Stockincomebill extends BaseDomain {


    private Date vdate;// 交易时间
    private BigDecimal totalAmount;//总金额
    private BigDecimal totalNum;//总数量
    private Date inputTime = new Date();//入库事件
    private Date auditorTime;//审核时间
  
    private Integer status = 0;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "supplier_id")
    private Supplier supplier;// 多对一,非空
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "auditor_id")
    private Employee auditor;// 审核人 多对一,可以为空
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "inputUser_id")
    private Employee inputUser;// 入库人员 多对一,非空
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "keeper_id")
    private Employee keeper;// 多对一,非空 //仓管员
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "depot_id")
    private Depot depot;// 多对一,非空
    // 一般组合关系使用List
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "bill", fetch = FetchType.LAZY, orphanRemoval = true)
    private List<Stockincomebillitem> items = new ArrayList<Stockincomebillitem>();
@Entity
@Table(name = "stockincomebillitem")
public class Stockincomebillitem extends BaseDomain {
    private BigDecimal price;
    private BigDecimal num;
    private BigDecimal amount;
    private String descs;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "product_id")
    private Product product;// 多对一,非空
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "bill_id")
    private Stockincomebill bill;// 组合关系,非空

仓库

@Entity
@Table(name="depot")
public class Depot extends BaseDomain {

    private String name;
    private BigDecimal maxcapacity;
    private BigDecimal currentcapacity;
    private BigDecimal totalamount;

    public Depot(long deptid) {
        this.id = deptid;
    }

    public Depot(){}

保存操作,配置了强级联
测试下

 @Test
    public void save() throws Exception {
        //创建入库主单
        Stockincomebill bill = new Stockincomebill();

        bill.setDepot(new Depot(1L));
        //入库人员 登陆人员 入库 (仓库管理人员 --登陆人员)
        bill.setInputUser(new Employee(1L));
        //入库人员 登陆人员 入库 (仓库管理人员)
        bill.setKeeper(new Employee(2L));
        //供货商
        bill.setSupplier(new Supplier(2L));
        //交易时间(入库事件)  --  在表里面添加字段 入库时间
        bill.setVdate(new Date());
		 List<Stockincomebillitem> items = new ArrayList<Stockincomebillitem>();
        Stockincomebillitem billItem = new Stockincomebillitem();
        billItem.setDescs("备注1");
        billItem.setNum(new BigDecimal(1));
        billItem.setPrice(new BigDecimal(1));
        billItem.setProduct(new Product(1L));
        items.add(billItem);

        Stockincomebillitem billItem2 = new Stockincomebillitem();
        billItem2.setDescs("备注2");
        billItem2.setNum(new BigDecimal(2));
        billItem2.setPrice(new BigDecimal(2));
        billItem2.setProduct(new Product(2L));
        items.add(billItem2);

        BigDecimal totalAmount = new BigDecimal(0);// 总金额
        BigDecimal totalNum = new BigDecimal(0);// 总数量
        for (Stockincomebillitem item : items) {
            // 设置多方到一方的关系 --双向关系 级联保存
            item.setBill(bill);
            // 计算小计 price*num
            item.setAmount(item.getPrice().multiply(item.getNum()));
            // 累加
            totalAmount = totalAmount.add(item.getAmount());
            totalNum = totalNum.add(item.getNum());
        }
        // 设置总金额,总数量
        bill.setTotalAmount(totalAmount);
        bill.setTotalNum(totalNum);

        // 设置一方到多方的关系
        bill.setItems(items);

        // 级联保存
        stockincomebillService.save(bill);

接下来是审核操作

@Transactional
    public void auditStockincomebill(Long billid, Employee auditor){

        //需要审核单据
        Stockincomebill bill = stockincomebillRepository.findOne(billid);
        if(bill == null){
            throw new RuntimeException("该单据不存在");
        }
        //0表示 待审 1 表示已审 -1表示作废
        if(bill.getStatus() == 1){
            throw new RuntimeException("该单据已审核");
        }

        if(bill.getStatus() == -1){
            throw new RuntimeException("该单据已作废");
        }

        //单据里面 里面审核的时间 审核人 状态
        bill.setAuditorTime(new Date());
        bill.setAuditor(auditor);
        bill.setStatus(1);

        //保存数据 更新数据
        stockincomebillRepository.save(bill);

        //  (2)仓库变化
        Depot depot = bill.getDepot();
        //当前容量
        depot.setCurrentcapacity(depot.getCurrentcapacity().add(bill.getTotalNum()));
        depot.setTotalamount(depot.getTotalamount().add(bill.getTotalAmount()));

        //保存仓库
        depotRepository.save(depot);

        //(3)及时库存表变化 同一个仓库 同一个产品合并
        // 第一次入及时库存  直接新增 如果不是第一次 需要进行合并
        List<Stockincomebillitem> items = bill.getItems();
        //查询当前这个产品在即时库存表离是否存在
        String jpql = "select o from Productstock o where o.product=? and o.depot=?";
        for (Stockincomebillitem item : items) {
            Product product = item.getProduct();

            List productStockList = productstockRepository.findByJpql(jpql, product, depot);
            BigDecimal totalNum = new BigDecimal("0");
            BigDecimal totalAmount = new BigDecimal("0");

            if(productStockList.size() == 0){
                //第一次  直接新增
                Productstock productStock = new Productstock();
                productStock.setDepot(depot);//仓库
                productStock.setNum(item.getNum());
                productStock.setPrice(item.getPrice());
                productStock.setAmount(item.getAmount());
                productStock.setIncomedate(new Date());//入即时库存时间
                productStock.setProduct(product);//产品
                productstockRepository.save(productStock);

            }else if(productStockList.size() == 1){
                //合并
                Productstock productstock = (Productstock)productStockList.get(0);

                totalNum =  productstock.getNum().add(item.getNum());
                totalAmount = productstock.getAmount().add(item.getAmount());
                //总数量
                productstock.setNum(productstock.getNum().add(item.getNum()));
                //总金额
                productstock.setAmount(productstock.getAmount().add(item.getAmount()));
                //加权平均价格 --四舍五入
                productstock.setPrice(totalAmount.divide(totalNum,2,BigDecimal.ROUND_HALF_EVEN));

               productstockRepository.save(productstock);
         }else{
                throw new RuntimeException("仓库出问题");
            }

        }

测试审核

 //测试审核
    @Test
    public void testAuditor() throws Exception{
        stockincomebillService.auditStockincomebill(1L, new Employee(1));
    }

二,库存预警和发送邮件

Java自带的timer不好做复杂的预警,所以
我们用了一个框架叫Qz

导包,配置文件

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>${org.springframework.version}</version>
</dependency>
<dependency>
	<groupId>quartz</groupId>
	<artifactId>quartz</artifactId>
	<version>1.5.2</version>
</dependency>

引入配置交给spring管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
				http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
				http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">


    <!-- cron表达式:在每天早上8点到晚上8点期间每1分钟触发一次 -->
    <!--value>0 0/1 8-20 * * ?</value -->
    <!-- cron表达式:每5分钟触发一次 -->
    <!-- <value>0 0/5 * * * ?</value> -->

    <task:scheduled-tasks>
        <!-- 执行quartzJob里面的work方法,执行频率是cron表达式 -->
        <task:scheduled ref="quartzJob" method="work" cron="0 0/1 * * * ?" />
    </task:scheduled-tasks>
</beans>

简单邮件,复杂邮件
引入配置,交给spring管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
				http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <!-- 163邮箱,smtp.163.com -->
        <!-- admin@163.com 用户名:admin 授权码:xxx -->
        <!-- smtp邮件发送协议 -->
        <!-- pop3收邮件协议 -->
        <property name="host" value="smtp.163.com" />
        <property name="username" value="zytestitsource" />
        <property name="password" value="zytest8888" />
        <property name="javaMailProperties">
            <props>
                <!-- 必须进行授权认证,它的目的就是阻止他人任意乱发邮件 -->
                <prop key="mail.smtp.auth">true</prop>
                <!-- SMTP加密方式:连接到一个TLS保护连接 -->
                <prop key="mail.smtp.starttls.enable">true</prop>
            </props>
        </property>
    </bean>

</beans>

测试

public class MailTest extends BaseTest {
   //注入一个对象
  /* @Autowired
   JavaMailSender mailSender;

   //简单发送邮件
   @Test
   public void testName() throws Exception {
   //JavaMailSenderImpl xxx = (JavaMailSenderImpl)mailSender
      // 简单邮件对象
      SimpleMailMessage msg = new SimpleMailMessage();
      // 发送人:和配置一致
      msg.setFrom("1141275667@qq.com");
      // 收件人
      msg.setTo("876009440@qq.com");
      //抄送其他人
    
      String[] strs =  new String[]{"1127560384@qq.com","763379508@qq.com","937783535@qq.com","346820640@qq.com"};
      msg.setCc(strs);

      // 主题
      msg.setSubject("好热哦");
      // 内容
      msg.setText("好想去 游泳");
      // 设置固定回邮地址
      //msg.setReplyTo("xxxx@xxx.com");
      // 发送
      mailSender.send(msg);
   }

   *//**
    * 发送复杂邮件
    *//*
   @Test
   public void testSendMsg2() throws MessagingException {
      //创建复杂的邮件发送
      MimeMessage mimeMessage = mailSender.createMimeMessage();

      //复杂邮件的工具类 设置编码格式
      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,"UTF-8");

      helper.setFrom("1141275667@qq.com");

      helper.setTo("876009440@qq.com");

      helper.setSubject("热热热热热");
      helper.setText("<h1>这是主题</h1>",true);

      //发送附件
      helper.addAttachment("热成狗.jpg",new File("E:/b3317f52.bmp"));
     
      mailSender.send(mimeMessage);
   }*/
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值