送水公司后台管理系统第三天 报表统计和工资计算

送水公司后台管理系统

1送水历史列表

​ 点击“送水历史管理”,显示“送水历史”列表。送水历史列表查询工作涉及到三张表联合查询,我们使用MyBatis-plus映射文件(HistoryMapper.xml)代替MyBatis-Plus提供的内置查询方法。

1635167301914

1.1 编写YML文件

配置Mapper映射文件的路径和包别名

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto
  # 配置Mapper.xml文件的路径,此时所有Mapper.xml映射文件都定义在resources/mapper路径下
  mapper-locations: classpath:/mapper/*Mapper.xml
  # 配置指定包中(com.shouyi.entities)不包括包名的简单类名作为包括包名的别名
  type-aliases-package: com.shouyi.entities
1.2 编写History实体类
package com.shouyi.entities;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

/**
 * TODO 送水历史管理实体类
 * @author caojie
 * @version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_history")
public class History {
    /**
     * 送水历史ID
     */
    private Integer hid;
    /**
     * 关联送水工
     */
    private Worker worker;
    /**
     * 关联客户
     */
    private Customer customer;

    /**
     * 送水时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date sendWaterTime;
    /**
     * 送水数量
     */
    private Integer sendWaterCount;
}
1.3 编写Mapper接口
package com.shouyi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shouyi.entities.History;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * TODO: 送水历史相关的映射器
 * @author caojie
 * @version 1.0
 */
@Repository
public interface HistoryMapper extends BaseMapper<History> {
    /**
     * 自定义查询方法来代替MyBaits-Plus内置的查询方法,查询所有的送水历史信息。
     * @return 送水历史列表
     */
    List<History> listHistory();
}
1.4 编写Mapper映射文件

​ 我们使用自定义查询方法来代替MyBaits-Plus内置的查询方法,所以要编写HistoryMapper接口对应的映射文件HistoryMapper.xml。该映射文件在resources/mapper路径下定义

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shouyi.mapper.HistoryMapper">
<!--   我们使用三张表进行联合查询,所以查询的送水历史列表返回一个结果集,将三张表查询的结果封装到resultMap结果集中。resultMap类似于JDBC的ResultSet对象
	   resultMap有两个核心属性:
		1) id表示resultMap在整个配置文件中的唯一性
		2) type表示返回值的全限定类名,我们在yml配置文件中配置了type-aliases-package: com.shouyi.entities。所以type属性值可以返回类型别名(省略了包名)	
	-->
    <resultMap id="selectHistoryMap" type="History">
        <id property="hid" column="hid" />
        <result column="send_water_time" property="sendWaterTime" />
        <result column="send_water_count" property="sendWaterCount"/>
<!--        送水历史关联送水工-->
        <association property="worker">
            <id column="wid" property="wid"></id>
            <result column="worker_name" property="workerName"></result>
        </association>
<!--        送水历史关联客户-->
        <association property="customer">
            <id column="cid" property="cid"></id>
            <result column="cust_name" property="custName"></result>
        </association>
    </resultMap>

    <!--
	select节点表示一个查询方法
	id属性必须跟HistoryMapper接口的方法名一致
	-->
    <select id="listHistory" resultMap="selectHistoryMap">
        SELECT h.hid,w.worker_name,h.send_water_time,c.cust_name,h.send_water_count
        FROM tb_history h, tb_worker w, tb_customer c
        WHERE h.worker_id = w.wid and h.cust_id = c.cid
    </select>
</mapper>
1.5 编写Service接口
package com.shouyi.service;

import com.shouyi.entities.History;

import java.util.List;

/**
 * TODO 送水历史管理业务逻辑接口
 * @author caojie
 * @version 1.0
 */
public interface HistoryService {
    /**
     查询所有送水历史信息
    */
    List<History> listHistory() ;
}

1.6 编写Service接口实现类
package com.shouyi.service.impl;

import com.shouyi.entities.History;
import com.shouyi.mapper.HistoryMapper;
import com.shouyi.service.HistoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * TODO:送水历史管理接口实现类
 * @author caojie
 * @version 1.0
 */
@Service
public class HistoryServiceImpl implements HistoryService {

    @Autowired
    private HistoryMapper historyMapper;
	/**
     查询所有送水历史信息
    */
    @Override
    public List<History> listHistory() {
        return historyMapper.listHistory();
    }
}
1.7 编写Controller控制器
package com.shouyi.controller;

import com.shouyi.entities.History;
import com.shouyi.service.HistoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * TODO
 *
 * @author caojie
 * @version 1.0
 * @date 2021/10/25 20:20
 */
@RequestMapping("/history")
@Controller
@Slf4j
public class HistoryController {

    @Autowired
    private HistoryService historyService;
	/**
	点击“送水历史管理”,调用HistoryService对象查询所有送水历史信息,然后将信息渲染到前端。最后返回“送水历史”页面
	*/
    @RequestMapping("/listHis")
    public String listHistory(Model model) {
        List<History> hisList = historyService.listHistory();
        log.info("history list size = "+hisList.size());
        model.addAttribute("hisList",hisList);
        return "historyList";
    }
}
1.8 编写送水历史管理列表页面

resources/water路径下创建客户管理列表页面historyList.html,显示送水历史信息。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title> XXX送水公司后台管理系统</title>

    <!--Bootstrap固定框架-->
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.css}">
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap-theme.css}">
    <!--图标库-->
    <link rel='stylesheet' th:href='@{/css/material-design-iconic-font.min.css}'>
    <!--核心样式-->
    <link rel="stylesheet" th:href="@{/css/style.css}">

</head>
<body>

<div id="viewport">

    <!-- Sidebar -->
    <div id="sidebar" th:replace="waterMainMenu::sidebar">

    </div>

    <!-- Content -->
    <div id="content">
        <nav class="navbar navbar-default" th:replace="waterMainMenu::navbar">

        </nav>
        <div class="container-fluid">
            <div class="row">

                <div class="col-md-12">
                    <table class="table table-hover table-striped">
                        <thead>
                        <tr>
                            <td>送水历史编号</td>
                            <td>送水工名称</td>
                            <td>客户名称</td>
                            <td>送水时间</td>
                            <td>送水数量</td>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="his : ${hisList}">
                            <td th:text="${his.hid}"></td>
                            <td th:text="${his.worker.workerName}"></td>
                            <td th:text="${his.customer.custName}"></td>
                            <td th:text="${#dates.format(his.sendWaterTime,'yyyy-MM-dd')}"></td>
                            <td th:text="${his.sendWaterCount}"></td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>

</div>

</body>
</html>
1.9 编写主页面

修改主页面waterMainMenu.html,在“送水历史管理”菜单里面定义超链接

      <li>
        <a th:href="@{/history/listHis}">
          <i class="zmdi zmdi-widgets"></i> 送水历史管理
        </a>
      </li>

2 添加送水历史信息

添加页面选择对应的“送水工”和“客户”,输入”送水时间“和“送水数量”

1635173638556

2.1编写Mapper接口

​ 由于History涉及到三张表,我们无法使用Mapper内置的添加方法。必须在HistoryMapper接口里面定义saveHistory方法来添加“送水历史”信息。

    /**
     * 添加送水历史
     * @param history 送水历史信息
     * @return  受影响行数,大于0添加成功,否则添加失败
     */
    int saveHistory(History history);
2.2 编写Mapper映射文件

HistoryMapper.xml映射文件中定义insert节点,添加“送水历史”信息。

    <insert id="saveHistory" >#{}
        insert into tb_history(worker_id,cust_id,send_water_time,send_water_count)
        values(#{worker.wid},#{customer.cid},#{sendWaterTime},#{sendWaterCount})
    </insert>
2.3 编写Service接口
    /**
     * 添加送水历史
     * @param history 表单采集的送水历史信息
     * @return 手影响行数,大于0添加成功,否则添加失败
     */
    int saveHistory(History history);
2.4 编写接口实现类
    /**
     * 添加送水历史
     *
     * @param history 表单采集的送水历史信息
     * @return 受影响行数,大于0添加成功,否则添加失败
     */
    @Override
    public int saveHistory(History history) {
        return historyMapper.saveHistory(history);
    }
2.5 编写Controller
 	/**
 	送水历史需要用到客户信息,自动装配CustomerService对象
 	*/
	@Autowired
    private CustomerService customerService;
 	/**
 	送水历史需要用到送水工信息,自动装配WorkerService对象
 	*/
    @Autowired
    private WorkerService workerService;

	/**
	点击“添加”按钮执行预修改操作
	步骤:
	1 查询客户列表和送水工列表
	2 将客户列表和送水工列表渲染到前端页面
	3 返回送水历史页面
	*/
    @RequestMapping("/preSaveHis")
    public String preSaveHistory(Model model) {
        // 查询客户列表和送水工列表,渲染到前端
        List<Customer> custList = customerService.listCustomer();
        List<Worker> workerList = workerService.listWorker();

        model.addAttribute("custList",custList);
        model.addAttribute("workerList",workerList);
        return "historySave";
    }
	/**
	点击“提交”按钮,将送水历史信息持久化到数据库,并重新查询送水历史列表,显示新增加的数据
	*/
    @RequestMapping(value="/saveHis",method = RequestMethod.POST)
    public String saveHistory(Integer workerId,Integer custId,History history) {
        log.info("save History workerId = "+workerId);
        log.info("save History customerId = "+custId);
        log.info("History history = "+history);
        // 将送水工编号和客户编号注入到History对象
        Worker worker = new Worker();
        worker.setWid(workerId);

        Customer customer = new Customer();
        customer.setCid(custId);

        history.setCustomer(customer);
        history.setWorker(worker);
        // 将History对象持久化到数据库
        int rows =historyService.saveHistory(history);
        log.info("save History rows = "+rows);
        return "redirect:/history/listHis";
    }

2.6 编写送水历史列表页面

在送水历史列表页面新增加一个“添加”按钮,单击“添加”按钮跳转到“添加送水历史”页面

                <div class="col-md-12">
                    <div class="col-md-4">
                        <a class="btn btn-primary"
                            th:href="@{/history/preSaveHis}">添加</a>
                    </div>
                </div>
2.7 编写添加送水历史页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title> XXX送水公司后台管理系统</title>

    <!--Bootstrap固定框架-->
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.css}">
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap-theme.css}">
    <!--图标库-->
    <link rel='stylesheet' th:href='@{/css/material-design-iconic-font.min.css}'>
    <!--核心样式-->
    <link rel="stylesheet" th:href="@{/css/style.css}">

</head>
<body>

<div id="viewport">

    <!-- Sidebar -->
    <div id="sidebar" th:replace="waterMainMenu::sidebar">

    </div>

    <!-- Content -->
    <div id="content">
        <nav class="navbar navbar-default" th:replace="waterMainMenu::navbar">

        </nav>
        <div class="container-fluid">
            <div class="row">
                <div class="col-md-12">
                    <form class="form-horizontal" method="post" th:action="@{/history/saveHis}" >
                        <div class="form-group">
                            <label  class="col-sm-2 control-label">送水工名称</label>
                            <div class="col-sm-5">
                               <select name="workerId" class="form-control">
                                   <option th:each="worker : ${workerList}"
                                            th:value="${worker.wid}" th:text="${worker.workerName}">
                                   </option>
                               </select>
                            </div>
                        </div>

                        <div class="form-group">
                            <label  class="col-sm-2 control-label">客户名称</label>
                            <div class="col-sm-5">
                                <select name="custId" class="form-control">
                                    <option th:each="cust : ${custList}"
                                            th:value="${cust.cid}"
                                            th:text="${cust.custName}">
                                    </option>
                                </select>
                            </div>
                        </div>

                        <div class="form-group">
                            <label  class="col-sm-2 control-label">送水时间</label>
                            <div class="col-sm-5">
                                <input type="date" class="form-control"
                                       name="sendWaterTime"  placeholder="送水时间">
                            </div>
                        </div>
                        <div class="form-group" >
                            <label  class="col-sm-2 control-label">送水数量</label>
                            <div class="col-sm-5">
                                <input type="text" class="form-control"
                                       name="sendWaterCount"  placeholder="送水数量">
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-sm-offset-2 col-sm-5">
                                <input type="submit" class="btn btn-primary" name="submit"  value="提交">
                                &nbsp;&nbsp;
                                <input type="reset" class="btn btn-warning" name="reset"  value="取消">
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</div>

</body>
</html>

3 修改送水历史信息

修改"送水历史"流程如下:

1635226213239

3.1 编写Mapper接口
3.2 编写Mppaer映射文件
3.3 编写Service接口
3.4 编写Service接口实现类
3.5 编写Controller控制器
3.6 编写送水历史列表页面

在页面上新增加一个“修改”按钮

3.7 编写修改送水历史页面

1 计算工资

老板为工人发工资,需要统计工资。统计结果如下:

1627395657605

1635259064479

 SELECT w.worker_name , w.worker_salary , w.worker_money,
 		-- 如果送水数量为null,默认为0
        ifnull(sum(h.send_water_count),0) as send_water_count,
        -- 如果实发工资为null,默认为基本工资
        ifnull(sum(h.send_water_count * w.worker_money)+w.worker_salary , w.worker_salary) as final_salary
        FROM tb_worker w left join tb_history h on w.wid = h.worker_id
        GROUP BY w.wid
        ORDER BY final_salary desc
1.1 定义计算工资Salary实体类
package com.minzu.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * TODO: 计算工资相关的实体类
 * @author caojie
 * @version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Salary {
    /**
     * 送水工名称
     */
    private String workerName;
    /**
     * 送水工底薪
     */
    private Integer workerSalary;

    /**
     * 送水工提成
     */
    private Double workerMoney;

    /**
     * 送水数量
     */
    private Integer sendWaterCount;

    /**
     * 实发工资
     */
    private Double finalSalary;
}
1.2 定义SalaryMapper接口
package com.minzu.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.minzu.entities.Customer;
import com.minzu.entities.Salary;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * TODO: 计算工资映射器
 * @author caojie
 * @version 1.0
 */
@Repository
public interface SalaryMapper extends BaseMapper<Customer> {
    /**
     * 计算所有送水工的工资
     * @return 工资列表
     */
    List<Salary> calcSalary();
}

1.3 定义映射文件

定义SalaryMapper接口对应的映射器SalaryMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.minzu.mapper.SalaryMapper">
    <select id="calcSalary" resultType="Salary">
        SELECT w.worker_name , w.worker_salary , w.worker_money,
        ifnull(sum(h.send_water_count),0) as send_water_count,
        ifnull(sum(h.send_water_count * w.worker_money)+w.worker_salary , w.worker_salary) as final_salary
        FROM tb_worker w left join tb_history h on w.wid = h.worker_id
        GROUP BY w.wid
        ORDER BY final_salary desc
    </select>
</mapper>
1.4 定义SalaryService接口
package com.minzu.service;

import com.minzu.entities.Salary;

import java.util.List;

/**
 * TODO:计算工资相关的业务逻辑
 * @author caojie
 * @version 1.0
 */
public interface SalaryService {
    /**
     * 计算所有送水工的工资
     * @return 工资列表
     */
    List<Salary> calcSalary();
}

1.5 定义SalaryService实现类
package com.minzu.service.impl;

import com.minzu.entities.Salary;
import com.minzu.mapper.SalaryMapper;
import com.minzu.service.SalaryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * TODO
 * @author caojie
 * @version 1.0
 */
@Service
public class SalaryServiceImpl implements SalaryService {

    @Autowired
    private SalaryMapper salaryMapper;
    /**
     * 计算所有送水工的工资
     *
     * @return 工资列表
     */
    @Override
    public List<Salary> calcSalary() {
        return salaryMapper.calcSalary();
    }
}
1.6 定义Controller控制器
package com.minzu.controller;

import com.minzu.entities.Salary;
import com.minzu.service.SalaryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * TODO:处理计算工资对应的请求
 * @author caojie
 * @version 1.0
 */
@RequestMapping("/salary")
@Controller
@Slf4j
public class SalaryController {

    @Autowired
    private SalaryService salaryService;

    /**
     * 点击"计算工资"显示所有送水工的工资
     * @param model
     * @return
     */
    @RequestMapping("/calcSalary")
    public String calcWorkerSalary(Model model) {
        List<Salary> salaryList = salaryService.calcSalary();
        log.info("worker salary list size = "+salaryList.size());
        model.addAttribute("salaryList",salaryList);
        return "salaryList";
    }
}
1.7 定义计算工资列表页面

定义计算工资列表页面salaryList.html,显示所有送水工的工资

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title> XXX送水公司后台管理系统</title>

    <!--Bootstrap固定框架-->
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.css}">
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap-theme.css}">
    <!--图标库-->
    <link rel='stylesheet' th:href='@{/css/material-design-iconic-font.min.css}'>
    <!--核心样式-->
    <link rel="stylesheet" th:href="@{/css/style.css}">

</head>
<body>

<div id="viewport">

    <!-- Sidebar
    客户列表页面使用th:replace属性替换成主菜单的侧边栏,让代码能够复用
    th:replace="waterMainMenu::sidebar"
    waterMainMenu表示主菜单页面的文件名称
    sidebar表示主菜单页面的片段名称
    -->
    <div id="sidebar" th:replace="waterMainMenu::sidebar">

    </div>

    <!-- Content -->
    <div id="content">
        <!--
th:replace="waterMainMenu::navbar"表示将nav标签里面所有的内容替换为主页面的navbar片段
        -->
        <nav class="navbar navbar-default" th:replace="waterMainMenu::navbar">

        </nav>
        <div class="container-fluid">
            <div class="row">


                <div class="col-md-12">
                    <table class="table table-hover table-striped">
                        <thead>
                        <tr>
                            <td>送水工姓名</td>
                            <td>基本工资</td>
                            <td>每桶提成</td>
                            <td>送水数量</td>
                            <td>实发工资</td>
                        </tr>
                        </thead>
                        <tbody>

                        <tr th:each="salary : ${salaryList}">
                            <td th:text="${salary.workerName}"></td>
                            <td th:text="${salary.workerSalary}"></td>
                            <td th:text="${salary.workerMoney}"></td>
                            <td th:text="${salary.sendWaterCount}"></td>
                            <td th:text="${salary.finalSalary}"></td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>

        </div>
    </div>

</div>

</body>
</html>
1.8 修改主界面

在主界面waterMainMenu.html增加“计算工资”对应的超链接

      <li>
        <a th:href="@{/salary/calcSalary}">
          <i class="zmdi zmdi-widgets"></i> 计算工资
        </a>
      </li>

2 根计算某一段时间的工人工资

1635261841604

2.1 编写SalaryMapper接口

SalaryMapper接口里面定义根据条件计算工资的方法。

注意:Mapper接口如果有多个方法必须定义@Param注解,否则无法传入Mapper映射文件

    /**
     * 根据搜索条件计算某一段时间的工资
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 工资列表
     */
    List<Salary> calcSalaryByCondition(@Param("startDate") String startDate,
                                       @Param("endDate") String endDate);
2.2 编写Mapper映射文件
    <select id="calcSalaryByCondition" parameterType="string" resultType="Salary">
        SELECT w.worker_name , w.worker_salary , w.worker_money,
        ifnull(sum(h.send_water_count),0) as send_water_count,
        ifnull(sum(h.send_water_count * w.worker_money)+w.worker_salary , w.worker_salary) as final_salary
        FROM tb_worker w left join tb_history h on w.wid = h.worker_id
        where h.send_water_time between #{startDate} and #{endDate}
        GROUP BY w.wid
        ORDER BY final_salary desc
    </select>
2.3 编写SalaryService接口
    /**
     * 根据搜索条件计算某一段时间的工资
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 工资列表
     */
    List<Salary> calcSalaryByCondition(String startDate,String endDate);
2.4 编写SalaryService实现类
    /**
     * 根据搜索条件计算某一段时间的工资
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 工资列表
     */
    @Override
    public List<Salary> calcSalaryByCondition(String startDate, String endDate) {
        // 条件成立:表示“结束时间”为空,系统当前时间为结束时间,然后再转换为字符串
        if (StrUtil.isEmpty(endDate)) {
            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            endDate = sdf.format(date);
        }
        return salaryMapper.calcSalaryByCondition(startDate,endDate);
    }
2.5 编写Controller控制器
    @RequestMapping("/searchCalcSalary")
    public String searchCalcWorkerSalary(String startDate, String endDate, Model model) {

        List<Salary> salaryList = salaryService.calcSalaryByCondition(startDate, endDate);
        model.addAttribute("salaryList",salaryList);
        return "salaryList";
    }
2.6 编写计算工资列表页面

salaryList.html页面中新增加搜索表单

                <div class="col-md-12">
                    <div class="col-md-8">
                        <form class="form-inline" th:action="@{/salary/searchCalcSalary}">
                            <input type="date" class="form-control" th:name="startDate" placeholder="请输入开始时间" required/>
                            <input type="date" class="form-control" th:name="endDate" placeholder="请输入结束时间"/>
                            <input type="submit" class="btn btn-primary" name="search" value="搜索"/>
                        </form>
                    </div>
                </div>

3 修复BUG

如图所示:查询2020-01-01 ~ 2020-01-31时间段送过水的送水工工资,在这个时间段没有为客户送过水的送水工工资却没有显示。这个时间段没有为为客户送过水的送水工应该显示基本工资

1635262702928

正确的查询结果应该是:

1635263959406

3.1 Mapper接口
/**
     * 查询没有为客户送水的送水工信息
     * @return 送水工列表
     */
    List<Worker> queryNotSendWaterWorker() ;
3.2 Mapper映射器配置文件
   <!-- 查询没有为客户送水的送水工信息-->
	<select id="queryNotSendWaterWorker" resultType="Worker">
        SELECT w.worker_name, w.worker_salary, w.worker_money
        FROM tb_worker w left join tb_history h on w.wid = h.worker_id
        where h.worker_id is null
    </select>
3.3 SalaryService接口实现类

修改SalaryService接口实现类的calcSalaryByCondition方法。计算没有送水的送水工的工资

 /**
     * 根据搜索条件计算某一段时间的工资
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 工资列表
     */
    @Override
    public List<Salary> calcSalaryByCondition(String startDate, String endDate) {
        // 条件成立:表示“结束时间”为空,系统当前时间为结束时间,然后再转换为字符串
        if (StrUtil.isEmpty(endDate)) {
            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            endDate = sdf.format(date);
        }
        List<Salary> salaryList = salaryMapper.calcSalaryByCondition(startDate, endDate);
        // 查询没有为客户送过水的送水工信息
        List<Worker> workerList = salaryMapper.queryNotSendWaterWorker();
        // 遍历每个没有为客户送过水的送水工信息,将它们注入到Salary对象。最后把Salary对象添加到salaryList集合
        workerList.forEach(worker ->{
            Salary salary = new Salary();
            salary.setWorkerName(worker.getWorkerName());
            salary.setWorkerSalary(worker.getWorkerSalary());
            salary.setWorkerMoney(worker.getWorkerMoney());
            // 没有送水的送水工实发工资就是他的底薪
            salary.setFinalSalary(Double.valueOf(worker.getWorkerSalary()));
            // 没有送水的送水工送水数量为0
            salary.setSendWaterCount(0);
            salaryList.add(salary);
        });
        // salaryList集合对象可能有重复的数据,将重复数据去掉
        Set set = new LinkedHashSet<>(salaryList);
        salaryList.clear();
        salaryList.addAll(set);
        return salaryList;
    }

1修复BUG

1.1 没有计算时间段外的送水工工资

​ 输入“起始时间”和“结束时间”,然后点击“搜索”。可以计算出在这个时间段的每个送水工的工资,还可以计算没有送水的送水工工资。

“小刘”没有在1月份为客户送水但是在其它时间段为客户送过水。下面的列表没有计算到小刘的工资。

1635337620028

​ 如何解决上述BUG? 使用union对多个select语句进行联合查询:查询没有为客户送过水的送水工列表 “联合” 没有在该时间段为客户送水的送水工列表。例如:

   SELECT  distinct w.worker_name,w.worker_salary,w.worker_money
   FROM tb_worker w left join tb_history h on w.wid = h.worker_id
   WHERE  not  h.send_water_time between  #{startDate} and #{endDate}
   union 	
   SELECT w.worker_name,w.worker_salary,w.worker_money
   FROM tb_worker w left join tb_history h on w.wid = h.worker_id
   where h.worker_id is null

​ Java代码也要做如下修改:

 /**
     * 根据条件计算某一段时间的送水工工资
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return 工资列表
     */
    @Override
public List<Salary> listCalcSalaryByCondition(String startDate, String endDate) {
        // 条件成立:表示输入的结束时间为Null,将系统当前时间作为结束时间
        if(StrUtil.isEmpty(endDate)){
            long currentTime = System.currentTimeMillis();
            Date dt = new Date(currentTime);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            endDate = sdf.format(dt);
        }
        // salaryList 在某个时间段已经为客户送过水的送水工信息
        List<Salary> salaryList = salaryMapper.listCalcSalaryByCondition(startDate, endDate);
        // sendWorkerList 在某个时间段已经为客户送过水的送水工名称列表
        List<String> sendWorkerList = salaryList.stream()
                .map(Salary::getWorkerName).collect(Collectors.toList());
        // 没有为客户送过水的送水工信息
        List<Worker> workerList = salaryMapper.queryNonSendWaterWorker(startDate,endDate);
        // 将没有送水的送水工信息合并到salaryList
        // 遍历workerList,将worker对象的数据注入到Salary对象中,让后添加到salaryList集合
        workerList.forEach(worker->{
            // 条件成立:表示没有送水的送水工在salaryList集合中不存在,将其添加到集合中
            if(!sendWorkerList.contains(worker.getWorkerName())){
                Salary sa = new Salary();
                sa.setWorkerName(worker.getWorkerName());
                sa.setWorkerSalary(worker.getWorkerSalary());
                sa.setWorkerMoney(worker.getWorkerMoney());
                // 没有送水的送水工默认送水数量为0
                sa.setSendWaterCount(0);
                // 没有送水的送水工默认实发工资为基本工资
                sa.setFinalSalary(Double.valueOf(worker.getWorkerSalary()));
                salaryList.add(sa);
            }
        });
        // salaryList集合可能有重复的数据,去掉重复的数据

        Collections.sort(salaryList,(o1,o2)->{
            if(o1.getFinalSalary() > o2.getFinalSalary()){
                return -1;
            }else if(o1.getFinalSalary() == o2.getFinalSalary()) {
                return 0;
            } else {
                return 1;
            }
        });
        return salaryList;
    }
1.2 没有对送水工工资进行排序

1635340187365

如何解决:按照送水工的工资进行比较,降序排序

        Collections.sort(salaryList,(o1,o2)->{
            if(o1.getFinalSalary() > o2.getFinalSalary()){
                return -1;
            }else if(o1.getFinalSalary() == o2.getFinalSalary()) {
                return 0;
            } else {
                return 1;
            }
        });

2 统计送水工送水数量

2.1 需求分析

原始需求:统计每个送水工为那些客户总共送了多少桶水

送水工名称客户列表送水数量
小刘老刘,老张761
小李李老,老陈66
小张老张60
小唐-0

需求分析:

  1. 首先确定表:tb_worker,tb_history,tb_customer
  2. 然后确定列:tb_worker表的worker_nametb_customer表的cust_nametb_history表的send_water_count
  3. 确定哪些列是原始列,哪些列是需要计算的:tb_worker表的worker_name列是原始列。使用group_concat函数对tb_customer表的cust_name列需要对每个客户进行拼接,然后汇总。使用聚合函数sumtb_history表的send_water_count列进行累加求和。
  4. 确定要分组的列:对tb_worker表的worker_name列进行分组
  5. 对“送水数量”进行降序排序
SELECT w.worker_name , 
			ifnull(GROUP_CONCAT(distinct c.cust_name),'-') as cust_details, 
			ifnull(sum(h.send_water_count),0) as send_water_count
FROM tb_worker w left join tb_history h  on w.wid = h.worker_id
		 left join tb_customer c on h.cust_id = c.cid
GROUP BY w.worker_name
ORDER BY send_water_count desc
2.2 操作步骤
2.2.1 编写实体类WaterDetails
package com.shouyi.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * TODO: 详细送水信息实体类
 * @author caojie
 * @version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WaterDetails {
    /**
     * 送水工名称
     */
    private String workerName;
    /**
     * 客户详细信息
     */
    private String custDetails;
    /**
     * 送水数量
     */
    private Integer sendWaterCount;
}

2.2.2 编写WaterDetailsMapper接口
package com.shouyi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shouyi.entities.WaterDetails;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * TODO 送水详细信息映射器
 * @author caojie
 * @version 1.0
 */
@Repository
public interface WaterDetailsMapper extends BaseMapper<WaterDetails> {

    /**
     * 查询每个送水工送水的详细信息
     * @return 送水详细信息列表
     */
    List<WaterDetails> querySendWaterDetails();
}

2.2.3 编写Mapper对应的映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shouyi.mapper.WaterDetailsMapper">
    <select id="querySendWaterDetails" resultType="WaterDetails">
        SELECT w.worker_name ,
			ifnull(GROUP_CONCAT(distinct c.cust_name),'-') as cust_details,
			ifnull(sum(h.send_water_count),0) as send_water_count
        FROM tb_worker w left join tb_history h  on w.wid = h.worker_id
		    left join tb_customer c on h.cust_id = c.cid
        GROUP BY w.worker_name
        ORDER BY send_water_count desc
    </select>
</mapper>
2.2.4 编写WaterDetailsService接口
package com.shouyi.service;

import com.shouyi.entities.WaterDetails;

import java.util.List;

/**
 * TODO: 送水详细信息业务逻辑接口
 * @author caojie
 * @version 1.0
 */
public interface WaterDetailsService {
    /**
     * 查询每个送水工送水的详细信息
     * @return 送水详细信息列表
     */
    List<WaterDetails> querySendWaterDetails();
}

2.2.5 编写接口实现类
package com.shouyi.service.impl;

import com.shouyi.entities.WaterDetails;
import com.shouyi.mapper.WaterDetailsMapper;
import com.shouyi.service.WaterDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * TODO
 * @author caojie
 * @version 1.0
 */
@Service
public class WaterDetailsServiceImpl implements WaterDetailsService {

    @Autowired
    private WaterDetailsMapper waterDetailsMapper;

    /**
     * 查询每个送水工送水的详细信息
     * @return 送水详细信息列表
     */
    @Override
    public List<WaterDetails> querySendWaterDetails() {
        return waterDetailsMapper.querySendWaterDetails();
    }
}

2.2.6 编写Controller控制器
package com.shouyi.controller;

import com.shouyi.entities.WaterDetails;
import com.shouyi.service.WaterDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * TODO: 统计送水信息的控制器
 * @author caojie
 * @version 1.0
 */
@RequestMapping("/stat")
@Controller
@Slf4j
public class WaterDetailsController {

    @Autowired
    private WaterDetailsService waterDetailsService;

    /**
     * 统计送水详细信息
     * @return “送水详细信息”页面
     */
    @RequestMapping("/statWaterDetails")
    public String statWaterDetails(Model model) {
        // 送水详细信息列表
        List<WaterDetails> waterList = waterDetailsService.querySendWaterDetails();
        model.addAttribute("waterList",waterList);
        return "waterDetailsList";
     }
}

2.2.7 编写前端页面

新建前端页面waterDetailsList.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title> XXX送水公司后台管理系统</title>

    <!--Bootstrap固定框架-->
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.css}">
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap-theme.css}">
    <!--图标库-->
    <link rel='stylesheet' th:href='@{/css/material-design-iconic-font.min.css}'>
    <!--核心样式-->
    <link rel="stylesheet" th:href="@{/css/style.css}">

</head>
<body>

<div id="viewport">

    <!-- Sidebar -->
    <div id="sidebar" th:replace="waterMainMenu::sidebar">

    </div>

    <!-- Content -->
    <div id="content">
        <nav class="navbar navbar-default" th:replace="waterMainMenu::navbar">

        </nav>
        <div class="container-fluid">
            <div class="row">

                <div class="col-md-12">
                    <table class="table table-hover table-striped">
                        <thead>
                        <tr>
                            <td>送水工名称</td>
                            <td>客户列表</td>
                            <td>送水数量</td>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="water : ${waterList}">
                            <td th:text="${water.workerName}"></td>
                            <td th:text="${water.custDetails}"></td>
                            <td th:text="${water.sendWaterCount}"></td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
</div>

</body>
</html>
2.2.8 修改主页面
      <li>
        <a th:href="@{/stat/statWaterDetails}">
          <i class="zmdi zmdi-widgets"></i> 统计送水数量
        </a>
      </li>

3调整工资

​ 进入“送水工列表”页面,点击“+”将工人工资增加100。然后弹出提示框“调整工资成功”。完成该功能前端需要使用jQuery框架。

1635393663494

3.1 导入jQuery框架
    <!--引入jQuery库-->
    <script th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
3.2 编写送水工页面

“送水工底薪”旁边添加+-

<!--                            <td th:text="${worker.workerSalary}"></td>-->
                            <td>
                                <span id="add" th:class="${worker.wid}" >+</span>
                                <span id="salary" th:text="${worker.workerSalary}"></span>
                                <span id="sub" th:class="${worker.wid}">-</span>
                            </td>
3.3 为span标签设置样式
        #add,#sub{
            font-weight: bold;
            font-size:20px;
            color:red;
        }
3.4 编写调整工资的前端代码
<script>
        $(function(){
            // 为“+”绑定“单击事件”
            $("span[id=add]").click(function(){
                // 获取送水工ID
                let workerid =$(this).attr("class");
                // 选择+下一个元素,获取送水工工资
                let workerSalary = $(this).next().text();
				// 计算调整之后的新工资
                workerSalary = parseInt(workerSalary);
                workerSalary+=100;
				// 将新工资设置到页面
                $(this).next().text(workerSalary);
                // 使用ajax技术向后端发起调整工资的异步请求
                $.ajax({
                    // 请求的URL
                    url:'/worker/adjustSalary',
                    // 提交给后端服务器的数据
                    data:{
                        wid:workerid,
                        workerSalary:workerSalary
                    },
                    // 请求的方式为“POST”
                    method:"POST",
                    //  请求成功后的回调函数
                    success:function(data,status) {
                        if(data =="OK") {
                            alert("调整工资成功");
                        } else {
                            alert("调整工资失败");
                        }
                    }
                })
            })
        })
    </script>
3.5 编写WorkerMapper接口

WorkerMapper接口新增加一个方法

    /**
     * 调整送水工工资,在原有基础上增加100
     * @param wid 送水工ID
     * @param workerSalary 送水工工资
     * @return 受影响行数。大于0调整工资成功,否则调整工资失败
     */
    int adjustSalary(@Param("wid")Integer wid,@Param("workerSalary")Integer workerSalary);
3.6 编写WorkerMapper映射文件

创建WorkerMapper.xml映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.shouyi.mapper.WorkerMapper">
    <update id="adjustSalary" parameterType="int">
        update tb_worker
        set worker_salary = #{workerSalary}
        where wid = #{wid}
    </update>
</mapper>
3.7 编写Service接口

WorkerService接口新增加一个调整工资的方法

    /**
     * 调整工资
     * @param wid 送水工ID
     * @param workerSalary 送水工工资
     * @return 受影响行数。大于0调整工资成功,否则调整工资失败
     */
    int adjustSalary(Integer wid,Integer workerSalary);
3.8编写接口实现类

WorkerServiceImpl接口实现类覆写WorkerService接口的adjustSalary方法

    /**
     * 调整工资
     *
     * @param wid          送水工ID
     * @param workerSalary 送水工工资
     * @return 受影响行数。大于0调整工资成功,否则调整工资失败
     */
    @Override
    public int adjustSalary(Integer wid, Integer workerSalary) {
        return workerMapper.adjustSalary(wid,workerSalary);
    }
3.9 编写Controller控制器

WorkerController新增一个方法,专门用来处理"调增工资"的请求

    /**
    处理"调增工资"的请求
    @ResponseBody表示将Java对象转换为json格式的数据渲染到前端页面
    */
	@RequestMapping(value="/adjustSalary",method = RequestMethod.POST)
    @ResponseBody
    public String adjustWorkerSalary(Integer wid,Integer workerSalary) {
        log.info("adjustWorkerSalary wid = "+wid);
        log.info("adjustWorkerSalary workerSalary = "+workerSalary);
        int rows = workerService.adjustSalary(wid, workerSalary);
        log.info("adjustWorkerSalary rows = "+rows );
        if (rows > 0) {
            return "OK";
        } else {
            return "Fail";
        }
    }

1 批量删除历史信息

效果如下

批量删除用到的技术“批处理”。一次数据库连接执行多次delete语句

1.1 前端页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title> XXX送水公司后台管理系统</title>

    <!--Bootstrap固定框架-->
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.css}">
    <link rel='stylesheet' th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap-theme.css}">
    <!--图标库-->
    <link rel='stylesheet' th:href='@{/css/material-design-iconic-font.min.css}'>
    <!--核心样式-->
    <link rel="stylesheet" th:href="@{/css/style.css}">
    <script th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
    <script>
        $(function() {
            // 为“selectAll”复选框绑定单击事件,一旦触发改时间,将下面的复选框“全选”or"全不选"
            $("#selectAll").click(function(){
                //  $("input[name=chkHistory]")表示选择“历史列表”所有的复选框,如果状态是“未选中”,将其
                // 设置为“选中”,如果状态是“已选择”,将其设置为“未选中”
                $("input[name=chkHistory]").prop("checked",$(this).prop("checked"))
            })
            /**
             * 为“批量删除”按钮绑定“单击”事件,将所有选中“的复选框”对应的“送水历史信息”批量删除
             * 步骤:
             * 1 获取所有“选中”的复选框
             * 2 如果一个都没选中,给出提示信息“至少选择一项”,结束操作。
             * 3 获取每个“选中”复选框的value值,进行拼接。将它作为参数传递给后端
             * 4 使用异步“Ajax”技术向后端发起请求进行“批量删除”
             * */
            $("#batchDelete").click(function () {
                // 获取所有“已选中”的复选框
                let checkedList = $("input[name=chkHistory]:checked");
                if (checkedList.length == 0) {
                    alert("请至少选择一项");
                    return;
                }
                // 存储所有已选中复选框的value属性值
                let ids ="";
                // 遍历每一个已选中复选框
                $(checkedList).each(function () {
                    // 获取复选框的value属性值
                    let hid = $(this).val();
                    ids += ","+hid;
                })
                // 使用
                $.ajax({
                    url:'/history/batchDelete',
                    data:{
                        idList:ids
                    },
                    method:'POST',
                    success:function(data,status){
                        if (data == "OK") {
                            alert("批量删除成功");
                            window.location.href='/history/listHis';
                        } else {
                            alert("批量删除失败");
                        }
                    }
                })
            })
        })
    </script>
</head>
<body>

<div id="viewport">

    <!-- Sidebar -->
    <div id="sidebar" th:replace="waterMainMenu::sidebar">

    </div>

    <!-- Content -->
    <div id="content">
        <nav class="navbar navbar-default" th:replace="waterMainMenu::navbar">

        </nav>
        <div class="container-fluid">
            <div class="row">
                <div class="col-md-12">
                    <div class="col-md-4">
                        <a class="btn btn-primary"
                            th:href="@{/history/preSaveHis}">添加</a>
                    </div>
                    <div class="col-md-4">
                        <a class="btn btn-danger" id="batchDelete"
                           href="#">批量删除</a>
                    </div>
                </div>
                <div class="col-md-12">
                    <table class="table table-hover table-striped">
                        <thead>
                        <tr>
                            <td>
                                <input type="checkbox" id="selectAll" value="selectAll"/>全选/全不选
                            </td>
                            <td>送水历史编号</td>
                            <td>送水工名称</td>
                            <td>客户名称</td>
                            <td>送水时间</td>
                            <td>送水数量</td>
                            <td>操作</td>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="his : ${hisList}">
                            <td>
                                <input type="checkbox" name="chkHistory" th:value="${his.hid}"/>
                            </td>
                            <td th:text="${his.hid}"></td>
                            <td th:text="${his.worker.workerName}"></td>
                            <td th:text="${his.customer.custName}"></td>
                            <td th:text="${#dates.format(his.sendWaterTime,'yyyy-MM-dd')}"></td>
                            <td th:text="${his.sendWaterCount}"></td>
                            <td>
                                <a class="glyphicon glyphicon-edit"
                                   th:href="@{'/history/preUpdateHis/'+${his.hid}}"></a>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>

</div>

</body>
</html>
2.2Mapper接口
    /**
     * 批量删除历史信息
     * @param ids 要删除的历史id。例如:1,2,3,5,6,7
     * @return 大于0批量删除成功,否则批量删除失败
     */
        int batchDeleteHistory(@Param("ids") List<Integer> ids);
2.3 Mapper映射文件
    <delete id="batchDeleteHistory" parameterType="list">
        delete from history where hid in
        <!--
			collection里面的值必须是一个集合类型
			注意:idList必须跟Mapper接口的@Param("idList")一致
			item里面的值表示集合的元素
			separator表示分隔符,多个id值使用半角逗号隔开
 			-->
        <foreach collection="ids" item="hid" open="(" close=")" separator=",">
            #{hid}
        </foreach>
    </delete>
2.4Service接口
    /**
     * 批量删除
     * @param idList 送水历史编号
     * @return 大于0删除成功,否则删除失败
     */
    int batchDeleteHistory(String idList);
2.5Service实现类
    /**
     * 批量删除
     * 步骤:
     *  1 字符串id转换为List集合
     *  2 调用Mapper的批量删除方法
     * @param ids 送水历史编号。例如:1,2,3,5,6,7
     * @return 大于0删除成功,否则删除失败
     */
    public int batchDeleteHistory(String idList) {
        List<Integer> ids = new ArrayList<>();
        // 使用逗号作为分割器,切割字符串。然后转换为数组
        String[] split = StrUtil.split(idList, ",");
        for (String  id : split) {
            if (StrUtil.isNotEmpty(id)) {
                ids.add(Integer.parseInt(id));
            }
        }
        return historyMapper.batchDeleteHistory(ids);
    }
2.6Controller控制器
	/**
	处理批量删除
	*/
@RequestMapping(value = "/batchDelete",method = RequestMethod.POST)
    @ResponseBody
    public String batchDeleteHistory(String idList) {
        log.info("batchDeleteHistory idList = "+idList);
        int rows = historyService.batchDeleteHistory(idList);
        if(rows > 0) {
            return "OK";
        } else {
            return "Fail";
        }
    }

2SpringBoot事务

​ 此前,我们主要通过XML配置Spring来托管事务。在SpringBoot则非常简单,只需在业务层添加事务注解(@Transactional )即可快速开启事务。接下来我们使用事务管理批量删除

2.1 Service实现类
	/**
     * 批量删除
     * 步骤:
     *  1 字符串id转换为List集合
     *  2 调用Mapper的批量删除方法
     * @param ids 送水历史编号。例如:1,2,3,5,6,7
     * @return 大于0删除成功,否则删除失败
       rollbackFor:触发回滚的异常。此时发生Exception异常或者它的子类就会触发事务回滚。
     */
    @Transactional(rollbackFor = {Exception.class,Error.class})
    @Override
    public int batchDeleteHistory(String ids) {
        // 第一个,替换为null
        ids =ids.replaceFirst(",","");
        String[] split = StrUtil.split(ids, ",");
        List<Integer> idList =  new ArrayList<>();
        for (String id : split) {
            idList.add(Integer.parseInt(id));
        }
        int rows =  historyMapper.deleteBatchHistory(idList);
        // 模拟执行失败,出现异常
        System.out.println(1/0);
        return rows;
    }
2.2 Controller 控制器

Service业务逻辑层发生异常,Controller在catch块回滚事务

    @RequestMapping(value="/batchDelete",method = RequestMethod.POST)
    @ResponseBody
    public String batchDeleteHistory(String idList)
        if(log.isInfoEnabled()) {
            log.info("deleteBatchHistory ids = "+ ids);
        }
        try {
            int rows = historyService.deleteBatchHistory(ids);
            if(rows > 0) {
                return "OK";
            } else {
                return "fail";
            }
        } catch (Exception e){
            log.error("删除失败...回滚事务..",e);
            return "fail";
        }
    }
2.3 事务的传播行为

​ 事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。org.springframework.transaction.annotation.Propagation 枚举类中定义了7个表示传播行为的枚举值:

public enum Propagation {  
    // 默认的事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
    REQUIRED(0),
    // 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    SUPPORTS(1),
    // 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
    MANDATORY(2),
    // 创建一个新的事务,如果当前存在事务,则把当前事务挂起
    REQUIRES_NEW(3),
    // 以非事务方式运行,如果当前存在事务,则把当前事务挂起
    NOT_SUPPORTED(4),
    // 以非事务方式运行,如果当前存在事务,则抛出异常
    NEVER(5),
    // 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
    NESTED(6);
}

ollbackFor:触发回滚的异常。此时发生Exception异常或者它的子类就会触发事务回滚。
*/
@Transactional(rollbackFor = {Exception.class,Error.class})
@Override
public int batchDeleteHistory(String ids) {
// 第一个,替换为null
ids =ids.replaceFirst(“,”,“”);
String[] split = StrUtil.split(ids, “,”);
List idList = new ArrayList<>();
for (String id : split) {
idList.add(Integer.parseInt(id));
}
int rows = historyMapper.deleteBatchHistory(idList);
// 模拟执行失败,出现异常
System.out.println(1/0);
return rows;
}


#### 2.2 Controller 控制器

Service业务逻辑层发生异常,Controller在catch块回滚事务

```java
    @RequestMapping(value="/batchDelete",method = RequestMethod.POST)
    @ResponseBody
    public String batchDeleteHistory(String idList)
        if(log.isInfoEnabled()) {
            log.info("deleteBatchHistory ids = "+ ids);
        }
        try {
            int rows = historyService.deleteBatchHistory(ids);
            if(rows > 0) {
                return "OK";
            } else {
                return "fail";
            }
        } catch (Exception e){
            log.error("删除失败...回滚事务..",e);
            return "fail";
        }
    }
2.3 事务的传播行为

​ 事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。org.springframework.transaction.annotation.Propagation 枚举类中定义了7个表示传播行为的枚举值:

public enum Propagation {  
    // 默认的事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
    REQUIRED(0),
    // 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    SUPPORTS(1),
    // 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
    MANDATORY(2),
    // 创建一个新的事务,如果当前存在事务,则把当前事务挂起
    REQUIRES_NEW(3),
    // 以非事务方式运行,如果当前存在事务,则把当前事务挂起
    NOT_SUPPORTED(4),
    // 以非事务方式运行,如果当前存在事务,则抛出异常
    NEVER(5),
    // 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
    NESTED(6);
}
深田之星管理系统网络版 2009 4.0 深田之星管理系统网络版,是一个面向社会桶装销售点的信息管理平台。软件集合各种管理功能与一体,极大的提高桶装销售的效率,并给管理者对管理数据进行各种分析,以便调整销售策略。总店-分店的连锁店管理模式分布式的软件操作更是为您的事业插上飞翔的翅膀。   一款真正的网络版桶装业务管理系统,适用于总店-分店模式的连锁店经营,软件能够通过互联网访问总部数据,数据既能集中化管理,各店又能独立运营,软件具有严格的权限分配,操作数据更加放心。   软件主要功能有来电显示及电话录音、客户管理、订单管理、票管理、权限管理、业务数据同步、工管理、库存管理、财务管理、售后管理、报表打印等众多实用有效的的功能。软件包括对来电显示、用户订单、发货订单、员业绩、库存信息以及包括各种数据统计分析等各种信息的管理,该软件支持各种报表的查询及打印,报表功能在本软件中体现的淋漓尽致,满足客户各种报表打印的需求。   软件支持对系统数据库的导出导入操作,也支持各种数据维护功能,为客户的数据管理提供了极大的方便,增加业务数据的安全性完整性。   详细请查看在线帮助文档。 ★系统需求   深田之星管理系统网络版 使用C#语言开发 适运行在 Microsoft WindowsNT/2000/XP/2003 等平台,但必须安装有.Net3.5平台SqlServer数据库.   该软件利用了微软.NET Framework3.5优秀的框架微软SQLServer数据库高性能的数据处理能力,因此在安装软件前,您需要花费一点时间来安装下面的组件(请您按照顺序安装即可): (1)安装MicroSoft .NET Framework 3.5。 (2)如果您的机器上没有安装MS SQLServer数据库,您可以选择下载微软MSDE组件进行安装,该安装包是微软发布的软件,网上随处可以找到,下载后默认进行安装即可,注意:MSDE安装后,必须重启机器,才能继续下面的安装。如果安装MSDE出现“为了安全起见,要求使用强SA密码。请使用SAPWD开关提供同一密码。”的提示,请找到msde安装目录下的setup.ini,打开修改成下面这个样子 [Options] SECURITYMODE=SQL SAPWD=123456 其中SAPWD后的"123456"是你的sa的密码。(你也可以改成你自己的)。 (3)最后下载 深田之星管理系统网络版,进行安装即完成整个软件的安装。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

办公模板库 素材蛙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值