文章目录

  • 前言
  • 项目展示
  • 技能要求
  • 一、开始前的准备
    • 1、OA系统是什么?
    • 2、人员权利与报销流程
    • 3、数据库设计
    • 4、创建项目及作用说明
    • 5、包与全局变量配置
    • 6、编写过滤器
    • 7、静态资源的复制与请求处理
    • 8、常量类
  • 二、功能实现
    • 1、部门管理
      • 部门信息的增删改查
      • (1)、实体类
      • (2)、dao接口与Sql映射文件
      • (3)、biz接口与其实现类
      • (4)、控制器
      • (5)、页面
    • 2、员工管理
      • 员工信息的增删改查
      • (1)、Sql映射文件
      • (2)、biz接口与实体类
      • (3)、控制器
      • (4)、页面
    • 3、登录及个人中心
      • (1)、登录的biz接口与实现类
      • (2)、控制器
      • (3)、登录拦截器
      • (4)、退出
      • (5)、修改密码
    • 4、报销单处理
      • (1)、报销单的持久层处理
      • (2)、填写报销单与报销单详情
      • (3)、个人报销单与待处理报销单
      • (4)、修改报销单
      • (5)、提交报销单
      • (6)、审核报销单与打款
  • 三、总结与源码
  • 加更来啦ヽ(゚∀゚)メ(゚∀゚)ノ
    • 1、源码的下载
    • 2、项目的启动

前言

SSM(Spring+SpringMVC+MyBatis)框架集由Spring、SpringMVC、MyBatis三个开源框架整合而成,常作为数据源较简单的web项目的框架。
其中Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。SpringMVC分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。
页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层向持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再调用视图展现数据。

写作动机:
这个项目是本小白学习完SSM后跟着视频做的第一个项目,但当时跟着做时出现了很多的bug无法更改,而且所提供的源码也是错误的,当时就直接把我搞得劝退了,而现在因为接触、学习了很多其他知识后,并且做了很多大大小小的项目有了些许经验,想着毕竟是第一个项目,要完成好,所以就靠自己写了出来,自己写出来后发现原来视频提供的源码是真的错了,难怪当时用不了,因为是自己敲的,所以会有很多缺陷,请见谅,下面便开始我的创作历程吧,希望对你有帮助(づ ̄3 ̄)づ╭❤~

相关博客:
Spring微总结
MyBatis微总结
SpringMVC微总结
SpringBoot+Mybatis实现用户账号CRUD系统+前后端交互(含源码)

项目展示

技能要求

前置条件:

MyBatis、Spring、Spring MVC、MySQL

主要技术:

Spring IOC、MyBatis+Spring整合、声明式事务、Spring标签库、Spring拦截器

一、开始前的准备

1、OA系统是什么?

OA系统(Office Automation),即办公自动化系统,是一种应用于办公领域的新型无纸化办公系统。
它利用计算机、通信等现代化技术来数字化地创建、收集、存储、处理办公任务所需的各种信息,代替办公人员传统的部分手动或重复性业务活动,极大地优化了以往复杂、低效的办公室工作过程,可以最大限度地提高工作效率和质量、改善工作环境。

2、人员权利与报销流程



报销流程:

3、数据库设计

4、创建项目及作用说明

oa:

父moudle
全局定义与组织

pom.xml:

<?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>com.oa</groupId><artifactId>oa</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><modules><module>oa_dao</module><module>oa_biz</module><module>oa_web</module></modules><properties><spring.version>4.0.2.RELEASE</spring.version></properties></project>

oa_dao:

持久层:持久化操作相关
NyBatis依赖、Spring依赖、MyBatis-Spring整合

pom.xml:

<?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"><parent><artifactId>oa</artifactId><groupId>com.oa</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>oa_dao</artifactId><dependencies><--MySQL驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.44</version></dependency><--MyBatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.4</version></dependency><--Spring基本的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><--Spring与MyBtis整合依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.1</version></dependency></dependencies></project>

oa_biz:

业务层:业务功能处理、AOP依赖、Aspectj依赖

pom.xml:

<?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"><parent><artifactId>oa</artifactId><groupId>com.oa</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>oa_biz</artifactId><dependencies><--依赖持久层--><dependency><groupId>com.oa</groupId><artifactId>oa_dao</artifactId><version>1.0-SNAPSHOT</version></dependency><--声明式事务的依赖包--><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><--AOP依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><--Aspectj依赖--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.0</version></dependency></dependencies>
</project>

oa_web:

表现层:与用户进行交互
Servlet依赖、Spring MVC依赖

pom.xml:

<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/maven-v4_0_0.xsd"><parent><artifactId>oa</artifactId><groupId>com.oa</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>oa_web</artifactId><packaging>war</packaging><name>oa_web Maven Webapp</name><url>http://maven.apache.org</url><dependencies><--依赖业务层--><dependency><groupId>com.oa</groupId><artifactId>oa_biz</artifactId><version>1.0-SNAPSHOT</version></dependency><--Servlet依赖--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.0</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><--spring相关依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><--SpringMVC依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency></dependencies><build><finalName>oa_web</finalName></build>
</project>

业务层、持久层、表现层所用到的模板:

5、包与全局变量配置

oa_dao:

dao、entity、global
数据源:指数据库应用程序所使用的数据库或者数据库服务器
Session(称域对象)工厂、映射器接口

resources/spring_dao.xml:

oa_biz:

biz
事务

resources/spring_biz.xml:

oa_web:

controller、dto、global
静态资源处理、视图转换器
Spring MVC加载

resources/spring_web.xml:

Spring MVC加载器:resources/webapp/WEB-INF/web.xml:

6、编写过滤器

oa_web/…/com/oa/global/EncodingFilter.java:

public class EncodingFilter implements Filter {private String encoding = "utf-8";public void init(FilterConfig filterConfig) throws ServletException {if(filterConfig.getInitParameter("encoding")!=null){    //获取encoding初始化参数encoding = filterConfig.getInitParameter("encoding");}}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;//设定字符集request.setCharacterEncoding(encoding);response.setCharacterEncoding(encoding);//调用拦拦截器链filterChain.doFilter(request,response);}public void destroy() {}
}

配置过滤器:resources/webapp/WEB-INF/web.xml:

<filter><filter-name>encoding</filter-name><filter-class>com.oa.global.EncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>

7、静态资源的复制与请求处理

静态资源复制后,因为如果所有的请求都交给了SpringMVC处理的话,效率就太慢了,所以写一个让这些静态资源默认给servlet进行处理(静态资源以放在GitHub里)。

都交给默认的servlet进行请求处理(因为在web中都会有一个default的servlet,所以就直接写了servlet-mapping):resources/webapp/WEB-INF/web.xml:

<servlet-mapping><servlet-name>default</servlet-name><url-pattern>/assets/*</url-pattern><url-pattern>/js/*</url-pattern><url-pattern>/vendor/*</url-pattern><url-pattern>*.js</url-pattern><url-pattern>*.jpg</url-pattern><url-pattern>*.gif</url-pattern><url-pattern>*.png</url-pattern><url-pattern>*.css</url-pattern></servlet-mapping>

8、常量类

都放在oa_dao层的com.oa.global.Contant.java:

报销单状态

//报销单状态
public static final String CLAIMVOUCHER_CREATED="新创建";
public static final String CLAIMVOUCHER_SUBMIT="已提交";
public static final String CLAIMVOUCHER_APPROVED="已审核";
public static final String CLAIMVOUCHER_BACK="已打回";
public static final String CLAIMVOUCHER_TERMINATED="已终止";
public static final String CLAIMVOUCHER_RECHECK="待复审";
public static final String CLAIMVOUCHER_PAID="已打款";

处理方式

//处理方式
public static final String DEAL_CREATE="创建";
public static final String DEAL_SUBMIT="提交";
public static final String DEAL_UPDATE="修改";
public static final String DEAL_BACK="打回";
public static final String DEAL_REJECT="拒绝";
public static final String DEAL_PASS="通过";
public static final String DEAL_PAID="打款";

职务、职务集合

//职务
public static final String POST_STAFF="员工";
public static final String POST_FM="部门经理";
public static final String POST_GM="总经理";
public static final String POST_CASHIER="财务";
public static List<String> getPosts(){List<String> list = new ArrayList<String>();list.add(POST_STAFF);list.add(POST_FM);list.add(POST_GM);list.add(POST_CASHIER);return list;
}

需复审额度

//审核额度
public static final double LIMIT_CHECK=5000.00;

费用类别集合

//费用类别
public static List<String> getItems(){List<String> list = new ArrayList<String>();list.add("交通");list.add("餐饮");list.add("住宿");list.add("办公");return list;
}

二、功能实现

1、部门管理

部门信息的增删改查

步骤:

(1)、实体类

根据数据库创建相对应的实体类:(都放在oa_dao层的com.oa.entity)
ClaimVoucher.java:(get、set)

public class ClaimVoucher {private Integer id;       //编号private String cause;       //说明private String createSn;        //创建者编号@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm")        //时间private Date createTime;            private String nextDealSn;      //待处理者编号private Double totalAmount;     //金额private String status;          //状态
}

ClaimVoucherItem.java:(get、set)

public class ClaimVoucherItem {private Integer id;       //编号private Integer claimVoucherId;     //报销单编号private String item;     //类型private Double amount;      //金额private String comment;     //说明
}

DealRecord.java:(get、set)

public class DealRecord {private Integer id;     //编号private Integer claimVoucherId;     //报销单编号private String dealSn;           //处理人编号@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm")private Date dealTime;          //处理时间private String dealWay;           //处理方式private String dealResult;        //处理结果private String comment;           //处理后的意见或备注
}

Department.java:(get、set)

public class Department {private String sn;private String name;private String address;
}

Employee.java:(get、set)

public class Employee {private String sn;private String password;private String name;private String departmentSn;private String post;
}

(2)、dao接口与Sql映射文件

dao接口:(都放在oa_dao层的com.oa.dao)

@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

ClaimVoucherDao:

@Repository("claimVoucherDao")
public interface ClaimVoucherDao {void insert(ClaimVoucher claimVoucher);       void update(ClaimVoucher claimVoucher);     void delete(int id);        ClaimVoucher select(int id);            List<ClaimVoucher> selectByCreateSn(String csn);      //根据创建者编号查询List<ClaimVoucher> selectByNextDealSn(String ndsn);        //根据待处理人编号查询
}

ClaimVoucherItemDao:

@Repository("claimVoucherItemDao")
public interface ClaimVoucherItemDao {void insert(ClaimVoucherItem claimVoucherItem);void update(ClaimVoucherItem claimVoucherItem);void delete(int id);List<ClaimVoucherItem> selectByClaimVoucher(int cvid);        //根据报销单编号进行查询
}

DealRecordDao:

@Repository("dealRecordDao")
public interface DealRecordDao {void insert(DealRecord dealRecord);List<DealRecord> selectByClaimVoucher(int cvid);
}

DepartmentDao:

@Repository("departmentDao")
public interface DepartmentDao {void insert(Department department);void update(Department department);void delete(String sn);Department select(String sn);List<Department> selectAll();
}

EmployeeDao:

@Repository("employeeDao")
public interface EmployeeDao {void insert(Employee employee);void update(Employee employee);void delete(String sn);Employee select(String sn);List<Employee> selectAll();List<Employee> selectByDepartmentAndPost(@Param("dsn") String dsn, @Param("post") String post);
}

Sql映射文件:

在全局配置文件中使用package标签统一配置dao包下接口对应的mapper.xml文件时,映射文件须与对应接口处在相同路径下且映射文件名与对应接口名须相同


使用mapper接口方式必须满足:

1.映射文件的namespace的值必须是接口的全路径名称 比如:com.oa.dao.XXXXXX
2.接口中的方法名在映射文件中必须有一个id值与之对应。
3.映射文件的名称必须和接口的名称一致

DepartmentDao.xml:

若对MySQL的一些简单操作不了解可以看看这个:一文解决MySQL基础的 ’ 增删改查 ’

(3)、biz接口与其实现类

biz接口:(放在oa_biz层的com.oa.dao)
DepartmentBiz:

public interface DepartmentBiz {void add(Department department);void edit(Department department);void remove(String sn);Department get(String sn);List<Department> getAll();
}

可以发现上面两个接口基本上是一一对应的,那它又有什么存在的必要呢,其实呀,因为这个部门管理没什么业务功能,也没什么特殊的业务流程,所以这个接口会与持久层功能匹配,在后面完成登录功能时便会介绍比较特殊的业务功能。

实现类:(放在oa_biz层的com.oa.dao.impl)
简单的增删改查的操作:DepartmentBizImpl.java:

@Service("departmentBiz")
public class DepartmentBizImpl implements DepartmentBiz {@Autowiredprivate DepartmentDao departmentDao;public void add(Department department) {departmentDao.insert(department);}public void edit(Department department) {departmentDao.update(department);}public void remove(String sn) {departmentDao.delete(sn);}public Department get(String sn) {return departmentDao.select(sn);}public List<Department> getAll() {return departmentDao.selectAll();}
}

可以发现这个模块是很简单的,当然也就这个模块比较简单ヾ(。`Д´。)ノ彡
上面已经完成了持久层和业务层的程序,下面我们来完成表现层┗(•ω•;)┛

(4)、控制器

DepartmentController.java:(展现员工列表)[oa_web/…/com.oa.controller]

@Controller("departmentController")
@RequestMapping("/department")
public class DepartmentController {@Autowiredprivate DepartmentBiz departmentBiz;@RequestMapping("/list")public String list(Map<String,Object> map){map.put("list",departmentBiz.getAll());return "department_list";}
}

(5)、页面

实现相对应的JSP:
department_list.jsp和公用的jsp:top.jsp、botton.jsp请看最后源码展示

对于部门增删改的控制器如下,department_add.jsp、department_update.jsp就看源码吧:
简单介绍一下:

redirect(重定向):
是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.

 @RequestMapping("/to_add")public String toAdd(Map<String,Object> map){map.put("department",new Department());return "department_add";}@RequestMapping("/add")public String add(Department department){departmentBiz.add(department);return "redirect:list";}@RequestMapping(value = "/to_update",params = "sn")        //在RequestMapping进行过滤,用属性params强制要求传sn这个参数public String toUpdate(String sn,Map<String,Object> map){map.put("department",departmentBiz.get(sn));return "department_update";}@RequestMapping("/update")public String update(Department department){departmentBiz.edit(department);return "redirect:list";}@RequestMapping(value = "/remove",params = "sn")       //在RequestMapping进行过滤,用属性params强制要求传sn这个参数public String remove(String sn){departmentBiz.remove(sn);return "redirect:list";}

以上便是部门管理的功能实现,效果展现:

下面便进行员工管理。

2、员工管理

员工信息的增删改查

(1)、Sql映射文件

在上面的部门管理功能实现时就已经完成了实体类、dao接口,下面来编写SQL的映射文件:
EmployeeDao.xml:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.oa.dao.EmployeeDao"><resultMap id="employee" type="Employee"><id property="sn" column="sn" javaType="String"/><result property="password" column="password" javaType="String"/><result property="name" column="name" javaType="String"/><result property="departmentSn" column="department_sn" javaType="String"/><result property="post" column="post" javaType="String"/><association property="department" column="department_sn" javaType="Department" ><id property="sn" column="dsn" javaType="String"/><result property="name" column="dname" javaType="String"/></association></resultMap><insert id="insert" parameterType="Employee">insert into employee values(#{sn},#{password},#{name},#{departmentSn},#{post})</insert><update id="update" parameterType="Employee">update employee set name=#{name},password=#{password},department_sn=#{departmentSn},post=#{post} where sn=#{sn}</update><delete id="delete" parameterType="String">delete from employee where sn=#{sn}</delete><select id="select" parameterType="String" resultMap="employee">select e.*,d.sn dsn,d.name dname from employee e left join department d on d.sn=e.department_snwhere e.sn=#{sn}</select><select id="selectAll" resultMap="employee">select e.*,d.sn dsn,d.name dname from employee e left join department d on d.sn=e.department_sn</select><select id="selectByDepartmentAndPost" resultMap="employee">select e.*,d.sn dsn,d.name dname from employee e left join department d on d.sn=e.department_snwhere e.sn is not NULL<if test="dsn!=null">and e.department_sn=#{dsn}</if><if test="post!=null">and e.post=#{post}</if></select>
</mapper>

员工与部门的关联关系

下面便实现biz接口与其实现类

(2)、biz接口与实体类

(3)、控制器

EmployeeController.java:(放在oa_web/…/com.oa.controller,这些都比较简单就不详细解释了)

@Controller("employeeController")
@RequestMapping("/employee")
public class EmployeeController {@Autowiredprivate DepartmentBiz departmentBiz;@Autowiredprivate EmployeeBiz employeeBiz;@RequestMapping("/list")public String list(Map<String,Object> map){map.put("list",employeeBiz.getAll());return "employee_list";}@RequestMapping("/to_add")public String toAdd(Map<String,Object> map){map.put("employee",new Employee());map.put("dlist",departmentBiz.getAll());      //传递所有部门到添加页面map.put("plist", Contant.getPosts());        //传递所有职位到添加页面return "employee_add";}@RequestMapping("/add")public String add(Employee employee){employeeBiz.add(employee);return "redirect:list";}@RequestMapping(value = "/to_update",params = "sn")public String toUpdate(String sn,Map<String,Object> map){map.put("employee",employeeBiz.get(sn));map.put("dlist",departmentBiz.getAll());map.put("plist", Contant.getPosts());return "employee_update";}@RequestMapping("/update")public String update(Employee employee){employeeBiz.edit(employee);return "redirect:list";}@RequestMapping(value = "/remove",params = "sn")public String remove(String sn){employeeBiz.remove(sn);return "redirect:list";}
}

(4)、页面

页面的JSP请看最后的源码,这里我就不展示了
效果展示:

3、登录及个人中心

(1)、登录的biz接口与实现类

GlobalBiz:

public interface GlobalBiz {Employee login(String sn, String password);      //登录账户void changePassword(Employee employee);       //修改密码
}

GlobalBizImpl.java:

@Service("globalBiz")
public class GlobalBizImpl implements GlobalBiz {@Autowiredprivate EmployeeDao employeeDao;public Employee login(String sn, String password) {Employee employee = employeeDao.select(sn);if(employee != null && employee.getPassword().equals(password)){        return  employee;}return null;}public void changePassword(Employee employee) {employeeDao.update(employee);}
}

便完成了biz业务层的登录程序,下面继续编写web表现层的登录程序。

(2)、控制器

简单了解:

@RequestParam从请求中提取查询参数,表单参数甚至文件.

转载一下这位大佬博主解释得很透彻了, 点我:彻底弄清楚session是什么?
GloablController.java:

@Controller("globalController")
public class GloablController {@Autowiredprivate GlobalBiz globalBiz;@RequestMapping("/to_login")public String toLogin(){return "login";}@RequestMapping("/login")public String login(HttpSession session, @RequestParam String sn, @RequestParam String password){      //从jsp获取sn和passwordEmployee employee = globalBiz.login(sn,password);       //调用login方法判断用户是否存在if (employee == null) {return "redirect:to_login";}session.setAttribute("employee",employee);return "redirect:self";}@RequestMapping("/self")public String self(){return  "self";}
}

(3)、登录拦截器

oa_web/…/com.oa.global.LoginInterceptor.java:

public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {//获取URL判断是否拦截String url = httpServletRequest.getRequestURI();if(url.toLowerCase().indexOf("login") >= 0){return true;}//判断当前状态是否登录HttpSession session = httpServletRequest.getSession();if(session.getAttribute("employee") != null){return true;}httpServletResponse.sendRedirect("/to_login");return false;}public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}

拦截器的程序就是这样,但要想其生效就需要在spring中进行配置并进行所有拦截(spring-web.xml):

<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/>        <bean class="com.oa.global.LoginInterceptor"/></mvc:interceptor></mvc:interceptors>

登录页面login.jsp、self.jsp便不在这里展示了,请到最后看源码

(4)、退出

控制器:oa_web/…/com.oa.global.LoginInterceptor.java:

@RequestMapping("/quit")
public String quit(HttpSession session){session.setAttribute("employee",null);return "redirect:to_login";
}

(5)、修改密码

控制器:oa_web/…/com.oa.global.LoginInterceptor.java:

@RequestMapping("/to_change_password")public String toChangePassword(){return "change_password";}@RequestMapping("/change_password")
public String changePassword(HttpSession session, @RequestParam String old, @RequestParam String new1 ,@RequestParam String new2){Employee employee = (Employee)session.getAttribute("employee");if(employee.getPassword().equals(old)){if(new1.equals(new2)){employee.setPassword(new1);globalBiz.changePassword(employee);return "redirect:self";}}return "redirect:to_change_password";
}

修改密码的页面change_password.jsp便不展示了,最后请参考源码,效果展示:

终于到了最后一个模块也是最核心最复杂的模块报销单处理,下面我们便开始吧。

4、报销单处理

(1)、报销单的持久层处理

先从持久层入手,上面我们关于持久层的大部分都已经实现了,但还需要添加一些关联对象的属性:
oa_dao/…/com.oa.entity.ClaimVoucher.java:(创建者、处理人)(get、set)

private Employee creater;        //创建者
private Employee dealer;        //处理人

DealRecord.java的dealSn是处理人编号,但处理人在表现层呈现的时候不能为编号,所以Employee dealer进行传递(get、set):

private Employee dealer;

dao接口已经在上面实现了,下面便是关于Sql映射文件:
ClaimVoucherDao.xml:


下面这两个就没有什么特殊性,所以就不解释了
下面是报销单条目的Sql映射文件ClaimVoucherItemDao.xml:


再下面是处理记录的Sql映射文件DealRecordDao.xml:

在这里关于oa_dao持久层的程序实现就完成了,下面便开始报销单的功能实现,业务层与表现层交际的实行

(2)、填写报销单与报销单详情

首先是实现oa_biz业务层的开发:
biz接口:
ClaimVoucherBiz:

public interface ClaimVoucherBiz {//保存报销单void save(ClaimVoucher claimVoucher, List<ClaimVoucherItem> items);//获取报销单对象ClaimVoucher get(int id);//获取报销单条目List<ClaimVoucherItem> getItems(int cvid);//审核记录List<DealRecord> getRecords(int cvid);
}

还有实现类:

@Qualifier 注释:
可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注释和 @Autowired 注释通过指定哪一个真正的 bean 将会被装配来消除混乱。

ClaimVoucherBizImpl.java:

@Service("cliamVoucherBiz")
public class ClaimVoucherBizImpl implements ClaimVoucherBiz {@Qualifier("claimVoucherDao")@Autowiredprivate ClaimVoucherDao claimVoucherDao;@Qualifier("claimVoucherItemDao")@Autowiredprivate ClaimVoucherItemDao claimVoucherItemDao;@Qualifier("dealRecordDao")@Autowiredprivate DealRecordDao dealRecordDao;public void save(ClaimVoucher claimVoucher, List<ClaimVoucherItem> items) {claimVoucher.setCreateTime(new Date());            //设置创建时间claimVoucher.setNextDealSn(claimVoucher.getCreateSn());     //设置待处理人(创建者)claimVoucher.setStatus(Contant.CLAIMVOUCHER_CREATED);        //设置状态(新创建)//保存到数据库claimVoucherDao.insert(claimVoucher);          for(ClaimVoucherItem item:items){item.setClaimVoucherId(claimVoucher.getId());claimVoucherItemDao.insert(item);}}public ClaimVoucher get(int id) {return claimVoucherDao.select(id);}public List<ClaimVoucherItem> getItems(int cvid) {return claimVoucherItemDao.selectByClaimVoucher(cvid);}public List<DealRecord> getRecords(int cvid) {return dealRecordDao.selectByClaimVoucher(cvid);}
}

关于填写报销单与报销单详情业务层就完成啦,下面便进行表现层的编写:
如何获取用户填写的报销单信息呢???
oa_web/…/com.oa.dto.ClaimVoucherInfo.java:(get、set)

public class ClaimVoucherInfo {//用一下的方式获取用户提交的信息private ClaimVoucher claimVoucher;      //报销单对象private List<ClaimVoucherItem> items;       //items集合
}

控制器:oa_web/…/com.oa.controller.ClaimVoucherController.java:

@Controller("claimVoucherController")
@RequestMapping("/claim_voucher")
public class ClaimVoucherController {@Autowiredprivate ClaimVoucherBiz claimVoucherBiz;//去填写@RequestMapping("/to_add")public String toAdd(Map<String,Object> map){map.put("items", Contant.getItems());     //传递常量类所有定义的类型map.put("info",new ClaimVoucherInfo());     //以info为键,创建ClaimVoucherInfo()return "claim_voucher_add";}//填写完后保存@RequestMapping("/add")public String add(HttpSession session, ClaimVoucherInfo info){     //info是要与上面的创建ClaimVoucherInfo()的键想对应Employee employee = (Employee)session.getAttribute("employee");     //创建者编号,表现层通过session拿出employeeinfo.getClaimVoucher().setCreateSn(employee.getSn());claimVoucherBiz.save(info.getClaimVoucher(),info.getItems());return "redirect:detail?id=" + info.getClaimVoucher().getId();}//保存完后跳转到详情界面@RequestMapping("/detail")public String detail(int id, Map<String,Object> map){map.put("claimVoucher",claimVoucherBiz.get(id));map.put("items",claimVoucherBiz.getItems(id));map.put("records",claimVoucherBiz.getRecords(id));return "claim_voucher_detail";}
}

至于页面claim_voucher_add.jsp、claim_voucher_detail.jsp请参考源码。
看看视频效果:

(3)、个人报销单与待处理报销单

与上面的填写报销单与报销单详情相同,首先是实现oa_biz业务层的开发:
biz接口:
ClaimVoucherBiz:

//获取个人报销单
List<ClaimVoucher> getForSelf(String sn);
//获取待处理报销单
List<ClaimVoucher> getForDeal(String sn);

还有实现类:
ClaimVoucherBizImpl.java:

public List<ClaimVoucher> getForSelf(String sn) {return claimVoucherDao.selectByCreateSn(sn);
}public List<ClaimVoucher> getForDeal(String sn) {return claimVoucherDao.selectByNextDealSn(sn);
}

关于个人报销单与待处理报销单业务层就完成啦,下面便进行表现层的编写:
控制器:oa_web/…/com.oa.controller.ClaimVoucherController.java:

//个人报销单
@RequestMapping("/self")
public String self(HttpSession session, Map<String,Object> map){Employee employee = (Employee)session.getAttribute("employee");    //获取当前用户的编号map.put("list",claimVoucherBiz.getForSelf(employee.getSn()));return "claim_voucher_self";
}//待处理报销单
@RequestMapping("/deal")
public String deal(HttpSession session, Map<String,Object> map){Employee employee = (Employee)session.getAttribute("employee");        //获取当前用户的编号map.put("list",claimVoucherBiz.getForDeal(employee.getSn()));return "claim_voucher_deal";
}

至于页面claim_voucher_self.jsp、claim_voucher_deal.jsp请参考源码。
看看视频效果:

(4)、修改报销单

与上面相同,首先是实现oa_biz业务层的开发:
biz接口:
ClaimVoucherBiz:

//修改报销单
void update(ClaimVoucher claimVoucher, List<ClaimVoucherItem> items);

还有实现类:
ClaimVoucherBizImpl.java:

public void update(ClaimVoucher claimVoucher, List<ClaimVoucherItem> items) {claimVoucher.setNextDealSn(claimVoucher.getCreateSn());claimVoucher.setStatus(Contant.CLAIMVOUCHER_CREATED);claimVoucherDao.update(claimVoucher);List<ClaimVoucherItem> olds = claimVoucherItemDao.selectByClaimVoucher(claimVoucher.getId());   //获取数据库已有的条目集合//删除我不要的条目for(ClaimVoucherItem old : olds){     //迭代其集合boolean isHave = false;for(ClaimVoucherItem item : items){       //寻找条目集合是否存在if(item.getId() == old.getId()){isHave = true;break;}}if(!isHave){claimVoucherItemDao.delete(old.getId());      //在迭代和新的集合都没有找到,所以删掉这个条目}}for(ClaimVoucherItem item : items){item.setClaimVoucherId(claimVoucher.getId());if(item.getId() != null && item.getId() > 0){//更新已经有了的条目claimVoucherItemDao.update(item);}else{//插入之前不存在的新条目claimVoucherItemDao.insert(item);}}
}

关于个人报销单与待处理报销单业务层就完成啦,下面便进行表现层的编写:
控制器:oa_web/…/com.oa.controller.ClaimVoucherController.java:

@RequestMapping("/to_update")
public String toUpdate(int id,Map<String,Object> map){map.put("items", Contant.getItems());ClaimVoucherInfo info =new ClaimVoucherInfo();info.setClaimVoucher(claimVoucherBiz.get(id));        //通过业务处理对象使用get(id)来获取报销单info.setItems(claimVoucherBiz.getItems(id));       //获取条目信息map.put("info",info);return "claim_voucher_update";
}
@RequestMapping("/update")
public String update(HttpSession session, ClaimVoucherInfo info){Employee employee = (Employee)session.getAttribute("employee");info.getClaimVoucher().setCreateSn(employee.getSn());claimVoucherBiz.update(info.getClaimVoucher(),info.getItems());return "redirect:deal";
}

至于页面claim_voucher_update.jsp请参考源码,效果展示:

(5)、提交报销单

提交报销单的持久层的操作在部门管理和员工管理就已经完成了,下面我们来进行oa_biz业务层:
biz接口:
ClaimVoucherBiz:

//提交报销单
void submit(int id);

还有实现类:
ClaimVoucherBizImpl.java:

@Qualifier("employeeDao")
@Autowired
private EmployeeDao employeeDao;public void submit(int id) {ClaimVoucher claimVoucher = claimVoucherDao.select(id);Employee employee = employeeDao.select(claimVoucher.getCreateSn());//报销单状态更新claimVoucher.setStatus(Contant.CLAIMVOUCHER_SUBMIT);//通过部门(同部门)与职位(部门经理),设置待处理人编号claimVoucher.setNextDealSn(employeeDao.selectByDepartmentAndPost(employee.getDepartmentSn(),Contant.POST_FM).get(0).getSn());//更新claimVoucherDao.update(claimVoucher);//进行记录保存DealRecord dealRecord = new DealRecord();dealRecord.setDealWay(Contant.DEAL_SUBMIT);        //设置提交参数dealRecord.setDealSn(employee.getSn()); //设置处理人(当前员工)dealRecord.setClaimVoucherId(id);      //报销单编号dealRecord.setDealResult(Contant.CLAIMVOUCHER_SUBMIT);       //设置提交后的状态dealRecord.setDealTime(new Date());       //设置当前时间dealRecord.setComment("无");       //备注设置为无dealRecordDao.insert(dealRecord);       //保存
}

关于个人报销单与待处理报销单业务层就完成啦,下面便进行表现层的编写:
控制器:oa_web/…/com.oa.controller.ClaimVoucherController.java:

@RequestMapping("/submit")
public String submit(int id){claimVoucherBiz.submit(id);return "redirect:deal";
}

效果展示:

(6)、审核报销单与打款

与上面相同,首先是实现oa_biz业务层的开发:
biz接口:
ClaimVoucherBiz:

//审核报销单
void deal(DealRecord dealRecord);

还有实现类:
ClaimVoucherBizImpl.java:

public void deal(DealRecord dealRecord) {ClaimVoucher claimVoucher = claimVoucherDao.select(dealRecord.getClaimVoucherId());Employee employee = employeeDao.select(dealRecord.getDealSn());//设置处理时间dealRecord.setDealTime(new Date());if(dealRecord.getDealWay().equals(Contant.DEAL_PASS)){//金额小于5000或者处理报销单的人是总经理就不需要复审if(claimVoucher.getTotalAmount()<=Contant.LIMIT_CHECK || employee.getPost().equals(Contant.POST_GM)){//修改报销单状态:已审核claimVoucher.setStatus(Contant.CLAIMVOUCHER_APPROVED);//设置待处理人为财务claimVoucher.setNextDealSn(employeeDao.selectByDepartmentAndPost(null,Contant.POST_CASHIER).get(0).getSn());//设置处理结果dealRecord.setDealResult(Contant.CLAIMVOUCHER_APPROVED);}else{      //需要复审的claimVoucher.setStatus(Contant.CLAIMVOUCHER_RECHECK);claimVoucher.setNextDealSn(employeeDao.selectByDepartmentAndPost(null,Contant.POST_GM).get(0).getSn());dealRecord.setDealResult(Contant.CLAIMVOUCHER_RECHECK);}}else if(dealRecord.getDealWay().equals(Contant.DEAL_BACK)){        //打回的操作claimVoucher.setStatus(Contant.CLAIMVOUCHER_BACK);claimVoucher.setNextDealSn(claimVoucher.getCreateSn());dealRecord.setDealResult(Contant.CLAIMVOUCHER_BACK);}else if(dealRecord.getDealWay().equals(Contant.DEAL_REJECT)){      //拒绝的操作claimVoucher.setStatus(Contant.CLAIMVOUCHER_TERMINATED);claimVoucher.setNextDealSn(null);dealRecord.setDealResult(Contant.CLAIMVOUCHER_TERMINATED);}else if(dealRecord.getDealWay().equals(Contant.DEAL_PAID)){        //打款的操作claimVoucher.setStatus(Contant.CLAIMVOUCHER_PAID);claimVoucher.setNextDealSn(null);dealRecord.setDealResult(Contant.CLAIMVOUCHER_PAID);}//报销单更新claimVoucherDao.update(claimVoucher);//审核记录插入dealRecordDao.insert(dealRecord);
}

关于个人报销单与待处理报销单业务层就完成啦,下面便进行表现层的编写:
控制器:oa_web/…/com.oa.controller.ClaimVoucherController.java:

@RequestMapping("/to_check")
public String toCheck(int id,Map<String,Object> map){map.put("claimVoucher",claimVoucherBiz.get(id));map.put("items",claimVoucherBiz.getItems(id));map.put("records",claimVoucherBiz.getRecords(id));DealRecord dealRecord = new DealRecord();dealRecord.setClaimVoucherId(id);map.put("record",dealRecord);return "claim_voucher_check";
}
@RequestMapping("/check")
public String check(HttpSession session, DealRecord dealRecord){Employee employee = (Employee)session.getAttribute("employee");dealRecord.setDealSn(employee.getSn());       //设置当前处理人claimVoucherBiz.deal(dealRecord);      //调用处理方法审核报销单return "redirect:deal";
}

至于页面claim_voucher_check.jsp请参考源码,效果展示:

在此所以的业务功能就完成啦ヾ(o´∀`o)ノ

三、总结与源码

总结:
开发过程中遇到的bug:

严重: Servlet.service() for servlet [SpringMVC] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
这个报错是在开发修改报销单的时候出现的,百度了很多都没有找到问题所在,不知道为什么会报一个空指针异常,不断的调试和观看日志后,最后发现原来是ClaimVoucherBizImpl的update方法中判断条目是否存在时少了一个条件,原:if(item.getId() > 0)改正后:if(item.getId() != null && item.getId() > 0),害,是自己考虑不周全
难易的bug有很多这里就记录这一个(其实就是其它的bug忘记记录了)┭┮﹏┭┮

通过这个项目的实现操作对于MVC架构有了更深层的了解,在本项目中,V即是JSP为视图,C即是Controller控制器,M即是oa_dao持久层、oa_biz业务层就是对用户的请求进行实际处理,不过JSP已经很老了,日后可以用SpringBoot+Trymeleaf+Vue进行代替。
在此项目中的功能有很多不足,如对于部门、员工的添加、删除、修改没有特指谁可以进行,所以导致只要是该公司人员就可进行操作,还有很多方面可以完善项目功能,请自由发挥ヾ(◍°∇°◍)ノ゙

源码:(*❦ω❦)
点个赞呗,创作不易٩( ‘ω’ )و 蟹蟹!


加更来啦ヽ(゚∀゚)メ(゚∀゚)ノ


鉴于有许多小伙伴都问项目启动流程,下面便是启动流程操作:

1、源码的下载

首先我们点击源码下载进入到github里,点击Code下载源码

下载的方式我知道的有两种,第一种:

第二种则是使用Git工具进行下载,Git下载链接,首先复制下载链接:
然后在要下载的磁盘里选择Git Bash Here,输入git clone +复制的下载链接(注意:粘贴是不支持Ctrl+V的,所以要用鼠标右键选择Paste):


以上便是源码下载的两种方式。

2、项目的启动

我使用的编译器是IDEA,直接使用IDEA打开刚才下载的文件:

打开之后点击Import Changes下载依赖的包:

下载好后便是配置tomcat服务器,至于每一层(biz、dao、web)的作用在本文章中已有解释:




然后点击Apply,在点击OK即可

其中我们可以看到我们的源码下载里有一个sql文件:

这个SQL文件可以在Navicat for MySQL的可视化工具里运行


打开运行即可,这样MySQL的数据便存在了(倘若运行SQL文件后任然没有,可以刷新或者重启可视化工具,如果还是没有则可以自己创建一个oa数据库在运行SQL文件在刷新表就可以了)

而因为每个人的MySQL的密码不同,所以还需要在oa_dao层进行修改数据库的连接密码:

数据库的相关操作就是这些,下面就是最后一步啦[]( ̄▽ ̄)*
最后启动tomcat, 启动完成后输入网址http://127.0.0.1:8080/to_login


这样项目的启动便完成了ヾ(◍°∇°◍)ノ゙
给个三连呗,创作不易(๑Ő௰Ő๑)

手把手教你SSM整合开发办公系统(OA)——报销单(含源码)相关推荐

  1. 用python画皇冠_【推荐】手把手教你如何用Python画一棵漂亮樱花树含源码

    最近给大家整理了一下,挑了一些我觉得不错的代码分享给大家手把手教你如何用Python画一棵漂亮樱花树含源码. 动态生成樱花 效果图(这个是动态的): import turtle as T import ...

  2. python樱花树代码_【推荐】手把手教你如何用Python画一棵漂亮樱花树含源码

    最近,我整理出来,并选择一些代码,我觉得是好与你分享教你如何画一个美丽的樱花与Python源代码树.动态生成樱花进口龟Timport randomimport时间#画樱花的躯干(60 t) def树( ...

  3. Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑 【文末源码】

    Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑 [文末源码] 前言 一,效果展示 二,场景搭建 三,代码逻辑 四,完善场景 五,使用小结 前言 还在看别人的寻路逻辑?保姆级教程,一步步教 ...

  4. java ssm框架的点歌系统的设计与实现源码

    项目名称 java ssm框架的点歌系统的设计与实现源码 下载地址 下载地址 系统说明 4.2 系统功能 4.2.1 登录与注册功能 系统的登录分为了前台登录和后台登录两个模块,都分别处在不同的界面上 ...

  5. 【Chrome浏览器插件开发】浏览器插件运行机制03之实战使用Vue.js 3 + Vite 2开发出简易的浏览器插件(含源码)

    文章目录 知识点: 一.使用 vite 创建项目 1.1 环境搭建 1.2 安装vite工具 1.3 创建vite项目 1.4 进入项目并安装依赖 1.5 修改端口 1.6 运行项目 二.创建项目资源 ...

  6. 手把手教你 SSM 整合(非常非常非常非常非常详细)

    comi single blog(欢迎访问我的个人博客) SSM 整合   整合的思路是:    先创建spring框架    通过spring整合spring mvc    通过spring整合my ...

  7. SSM+医院故障报修系统小程序 毕业设计-附源码191734

    基于SSM医院故障报修小程序 摘  要 随着互联网大趋势的到来,社会的方方面面,各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去,而其中最好的方式就是建立网络管理系统,并对其进行信息 ...

  8. jsp+ssm计算机毕业设计中医药系统论文2022【附源码】

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  9. javaWeb基于SSM框架开发的社区医疗数据管理系统【项目源码+数据库脚本+报告】

    一.项目简介 本项目是一套基于SSM框架开发的社区医疗数据管理系统,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者. 包含:项目源码.数据库脚本等,该项目可以直接作为 ...

最新文章

  1. 软硬兼施极限轻量BERT!能比ALBERT再轻13倍?!
  2. HDU4619--Warm up 2
  3. 程序员常见的健康问题
  4. 笔记本电池续航测试软件,电池续航测试和试用总结
  5. 带你穿越古罗马,元宇宙巴士来啦 #Invisible Cities
  6. 12.2 剪贴板的高级用法
  7. 汉语拼音—韦氏拼音对照表
  8. html阅读是什意思,HTML是什么意思?什么是HTML5?什么是H5? | 前端面试题
  9. note-PythonCookbook-第十一章 网络与WEB编程
  10. 实验一-Bomblab(炸弹实验)
  11. Windows 2003 下手动关闭危险端口
  12. oracle上机题库_Oracle笔试题库附参考答案
  13. 使用netstat命令验证DDOS入侵
  14. [COGS2652]秘术「天文密葬法」-长链剖分-01分数规划
  15. 软件测试仿真系统,嵌入式系统软件仿真自动化黑盒测试平台
  16. 有限自动机和右线性文法笔记
  17. 明瞳智控最佳实践--国标设备实战接入
  18. php汉字转拼音百家姓版,Excel 将中文名改成拼音,并将姓氏放后面
  19. 移动、联通和电信,哪家的宽带好,看完你就知道该怎么选了!
  20. SQLite实用武器库(1)利用dump命令和read命令导出数据、导入数据

热门文章

  1. QT实现图片的滚轮缩放、框选放大、拖拽移动
  2. python的matplotlib绘图(双坐标轴)
  3. 10.java基础----继承、抽象类- 编程
  4. clickHouse副本和同步机制
  5. python求均值 有限存储量_python计算均值
  6. 【jzoj4598】【准备食物】【字典树】
  7. 微信小程序云函数如何返回参数?
  8. 电脑桌面计算机文件打不开怎么办,电脑开机桌面文件都点不开的解决方法
  9. matlab左侧显示当前文件夹,matlab还原默认布局,matlab左侧显示
  10. 小米手机开发者选项怎么打开