Phát triển hệ thống y tế trực tuyến dựa trên reflection (Phần 3)

Giới thiệu:

Bài viết này sẽ thực hiện chức năng danh sách và phân trang cho trang quản lý, cùng với các thao tác thêm, sửa, xóa và tìm kiếm thông tin. Chúng ta sẽ lấy quản lý bác sĩ khám bệnh làm ví dụ, các phần khác tương tự nên không trình bày chi tiết. Bắt đầu luôn với các chức năng cụ thể.

I. Triển khai danh sách và phân trang cho quản lý bác sĩ khám bệnh

1. Yêu cầu AJAX từ phía frontend

Cần lưu ý rằng chúng ta gộp chức năng hiển thị danh sách và tìm kiếm vào một phương thức duy nhất là list. Phương thức này cần nhận ba tham số: trang hiện tại page, khoa department và tên bác sĩ name. Trong file doctor/index.jsp:

Mã nguồn của doctor/index.jsp:

function showList(page, department, name) {
    $.ajax({
        url: "${path}doctors/list?page=" + page + "&department=" + department + "&name=" + name,
        type: "GET",
        dataType: "json",
        success: function (message) {
            $("#doctor-table tr").remove();
            console.log(message);
            $("#current").text(message.count); // Tổng số bản ghi
            $("#pageTotal").text(message.countPage); // Tổng số trang
            $("#firstPage").attr("page", 1);
            $("#prePage").attr("page", message.prePage);
            $("#nextPage").attr("page", message.nextPage);
            $("#lastPage").attr("page", message.countPage);

            var datas = message.pageList;
            $.each(datas, function (index, item) {
                card(item);
            })

        }
    });
}

 function card(item) {
        var tr = "<tr>" +
            "<th><label class='my_protocol'><input class='input_agreement_protocol' type='checkbox' value='" + item.did + "'" + "  /><span></span></label></th>" +
            "<td>" + item.did + "</td>" +
            "<td>" + item.name + "</td>" +
            "<td>" + item.phone + "</td>" +
            "<td>" +"<span class='"+item.tag_color+"' style='padding: 6px;'>" + item.dname +"</span>"+ "</td>" +
            "<td>" +
            "<button type='button' class='btn btn-info' onclick='doctor_detail(" + item.did + ")' >Chi tiết</button>" +
            "<button class='btn btn-warning' type='button' onclick='doctor_edit(" + item.did + ")' >Sửa</button>" +
            "<button type='button' class='btn btn-danger' onclick='doctor_delete(" + item.did + ")'>Xóa</button>" +

            "</td>" +
            "</tr>";
        $("#doctor-table").append($(tr));
    }

2. Lớp phân trang Pages

Chúng ta cần tạo một lớp thực thể phân trang có kiểu gen (Generic), với các thuộc tính: initSize (số lượng bản ghi mỗi trang), count (tổng số bản ghi), countPage (tổng số trang), currentPage (trang hiện tại), prePage (trang trước), nextPage (trang sau), và pageList (dữ liệu trang).

Mã nguồn đầy đủ:

import java.util.List;

/**
 *  Page: Lớp thực thể phân trang
 * @author Doan Zhenbiao
 *
 */
public class Pages<T> {
	
	private Integer initSize = 5; // Số bản ghi mỗi trang
	
	private Long count ; // Tổng số bản ghi
	
	private Integer countPage ; // Tổng số trang
	
	private Integer currentPage ; // Trang hiện tại
	
	private Integer prePage; // Trang trước
	
	private Integer nextPage; // Trang sau
	
	private List<T> pageList; // Dữ liệu trang

	public Pages() {

	}

	public Pages(Integer initSize, Long count, Integer currentPage, List<T> pageList) {

		this.initSize = initSize;
		this.count = count;
		Long num  = count / initSize;
		this.countPage = (int)(count % initSize == 0 ? num : num + 1);
		this.currentPage = currentPage < 0 ? 1: currentPage > countPage ? countPage : currentPage;
		// Nếu trang hiện tại lớn hơn 1 thì trang trước là trang hiện tại - 1, ngược lại là 1
		this.prePage = currentPage > 1 ? currentPage -1 : 1;
		// Nếu trang hiện tại nhỏ hơn tổng số trang thì trang sau là trang hiện tại + 1, ngược lại là tổng số trang.
		this.nextPage = currentPage < this.countPage ? currentPage +1 : this.countPage;
		this.pageList = pageList;
	}

	public Integer getInitSize() {
		return initSize;
	}

	public void setInitSize(Integer initSize) {
		this.initSize = initSize;
	}

	public Long getCount() {
		return count;
	}

	public void setCount(Long count) {
		this.count = count;
	}

	public Integer getCountPage() {
		return countPage;
	}
	public void setCountPage(Integer countPage) {
		this.countPage = countPage;
	}
	public Integer getCurrentPage() {
		return currentPage;
	}
	public void setCurrentPage(Integer currentPage) {
		this.currentPage = currentPage;
	}

	public Integer getPrePage() {
		return prePage;
	}

	public void setPrePage(Integer prePage) {
		this.prePage = prePage;
	}

	public Integer getNextPage() {
		return nextPage;
	}

	public void setNextPage(Integer nextPage) {
		this.nextPage = nextPage;
	}

	public List<T> getPageList() {
		return pageList;
	}

	public void setPageList(List<T> pageList) {
		this.pageList = pageList;
	}

	@Override
	public String toString() {
		return "Pages [initSize=" + initSize + ", count=" + count + ", countPage=" + countPage + ", currentPage="
				+ currentPage + ", prePage=" + prePage + ", nextPage=" + nextPage + ", pageList=" + pageList + "]";
	}	
}


3. Phương thức list

DoctorServlet.java

public void list(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  {
		// Thực thể thông báo
		Message message = new Message();
		ObjectMapper mapper  = new ObjectMapper();
		String json = null;
		// Trang hiện tại
		String pageStr = request.getParameter("page");
		if(pageStr == null || pageStr.length() == 0) {pageStr = "1";} // Mặc định trả về trang đầu tiên nếu không có tham số page.
		// Thiết lập số lượng bản ghi mỗi trang
		int SIZE = 5; // tạm thời đặt là 5;
		int page = Integer.parseInt(pageStr);// Ép kiểu
		
		// Khoa
		String department = request.getParameter("department");
		// Tên bác sĩ
		String docName = request.getParameter("name");
		Doctor doctor = new Doctor(docName);
		doctor.setDepartment(Integer.parseInt(department));
		// Sử dụng kiểu gen Map<String, Object>
		Pages<Map<String, Object>> pages = doctorService.doctorList(doctor,page,SIZE);
		
		// Ghi phản hồi
		json = mapper.writeValueAsString(pages);
		response.getWriter().print(json);
		
	}

Giao diện DoctorService: (tạo lớp DoctorService trong gói service)

Pages<Map<String, Object>> doctorList(Doctor doctor, int page, int sIZE);

Lớp thực thi DoctorServiceImpl: (tạo lớp DoctorServiceImpl trong gói service.Impl)

DoctorDao doctorDao = new DoctorDaoImpl();

@Override
public Pages<Map<String, Object>> doctorList(Doctor doctor, int page, int sIZE) {
    return doctorDao.doctorList( doctor, page, sIZE);
}

Giao diện DoctorDao: (tạo lớp DoctorDao trong gói dao)

List<Map<String, Object>> selectDoctors(Doctor doctor, int start, int sIZE);

Pages<Map<String, Object>> doctorList(Doctor doctor, int page, int sIZE);


Lớp thực thi DoctorDaoImpl:

// Lấy kết nối tài nguyên
private QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

@Override
public Pages<Map<String, Object>> doctorList(Doctor doctor, int page, int sIZE) {

    System.out.println("*******************************");
    // Lấy tổng số bác sĩ phù hợp điều kiện
    Long count = getDoctorCount(doctor);

    int start = (page - 1)*sIZE;

    List<Map<String, Object>> ls = selectDoctors(doctor,start,sIZE);

    Pages<Map<String, Object>> pages = new Pages<Map<String, Object>>(sIZE,count,page,ls);

    return pages;
}

// Lấy tổng số bác sĩ phù hợp điều kiện
private Long getDoctorCount(Doctor doctor) {

    StringBuilder sb = new StringBuilder("select count(*) from doctor where 1=1 ");
    String sql = contionSql(doctor,sb);
    System.out.println(sql);

    Long count = null ;
    try {
        count = qr.query(sql, new ScalarHandler<Long>());
    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return count;
}
/**
 * Lấy danh sách bác sĩ phù hợp điều kiện
 */
@Override
public List<Map<String, Object>> selectDoctors(Doctor doctor, int start, int sIZE) {

    StringBuilder sb = new StringBuilder("select * from doctor,department where 1=1 and doctor.department = department.id ");
    String sql = contionSql(doctor,sb)+" order by -did " + " limit ?,?";

    System.out.println("sql:"+sql);
    List<Doctor> doctors = null;

    List<Map<String, Object>> mapList = null;
    try {
        mapList = qr.query(sql, new MapListHandler(),start,sIZE);
    } catch (SQLException e) {
        System.out.println("Lỗi phương thức selectDoctors:" );
    }
    System.out.println(mapList);
    return mapList;
}

// Xây dựng chuỗi SQL
private String contionSql(Doctor doctor, StringBuilder sb) {

    String name = doctor.getName();
    if(name != null && name.trim() != "") {
        sb.append(" and name like '%" + name + "%'");
    }

    int department = doctor.getDepartment();

    if(department != 0) {
        sb.append(" and department=" + department);

    }

    String sql = sb.toString();

    return sql;

}

4. Cấu trúc dữ liệu trả về từ response

Sau khi hoàn thành logic của phương thức list, yêu cầu URL http://localhost:8080/hospital/doctors/list?page=1&department=0&name= sẽ trả về cấu trúc dữ liệu như sau:

Trong quá trình này, có thể gặp phải vấn đề mã hóa tiếng Trung bị lỗi khi gọi API backend, nhưng dữ liệu hiển thị trên trang JSP vẫn bình thường vì đã thiết lập UTF-8. Giải pháp là tạo lớp EncodingFilter trong gói filter:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Bộ lọc mã hóa: xử lý lỗi mã hóa request.
 */
@WebFilter("/*")
public class EncodingFilter implements Filter {


    public EncodingFilter() {}

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		
	}

	/**
	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
	 */
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
		// Ép kiểu thành HttpServletRequest
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		request.setCharacterEncoding("UTF-8");
		// Thiết lập mã hóa để tránh lỗi tiếng Trung.
		response.setContentType("text/html;charset=UTF-8");
	    response.setCharacterEncoding("UTF-8");
		chain.doFilter(request, response);
	}

	public void init(FilterConfig fConfig) throws ServletException {
		// TODO Auto-generated method stub
	}

}


5. Logic phân trang phía frontend

Cần thêm ID để xác định vị trí phần phân trang và gắn sự kiện click.

Thêm vào trang doctor/index.jsp

 // Khởi tạo trang: danh sách
$(function () {
    var page = 1;
    var department = $("#department option:selected").val();
    var name = $("#name").val();
    showList(page, department, name);

});
// Trang đầu
$("#firstPage").click(function () {

    var page = $(this).attr("page");
    var department = $("#department option:selected").attr("value");
    var name = $("#name").val();
    showList(1, department, name);
});
// Trang trước:
$("#prePage").click(function () {

    var page = $(this).attr("page");
    var department = $("#department option:selected").attr("value");

    var name = $("#name").val();
    showList(page, department, name);
});
// Trang sau:
$("#nextPage").click(function () {
    var page = $(this).attr("page");
    var department = $("#department option:selected").attr("value");
    var name = $("#name").val();
    showList(page, department, name);
});
// Trang cuối
$("#lastPage").click(function () {
    var page = $(this).attr("page");
    var department = $("#department option:selected").attr("value");

    var name = $("#name").val();
    showList(page, department, name);
});

II. Thêm mới

1. Yêu cầu AJAX từ phía frontend

Nút "Thêm" kích hoạt phương thức chuyển đến trang add.jsp, chúng ta thêm phương thức submit trong trang add.jsp.

// Nút thêm, href thay đổi thành doctor/add.jsp
$("#newNav").click(function () {
    window.location.href = "${path}doctor/add.jsp";
});

2. Kiểm tra form

Kiểm tra form sử dụng plugin jQuery-validate, tương tự như đăng nhập và đăng ký. Không trình bày lại mã nguồn ở đây, mã nguồn có thể tìm thấy tại: https://gitee.com/duanxiaobiao/hospital-jsp/blob/master/WebContent/doctor/add.jsp

3. Phương thức thêm và xóa
public void add(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  {
		
    String name = request.getParameter("name");
    String cardno = request.getParameter("cardno");
    String phone = request.getParameter("phone");
    String sex = request.getParameter("sex");
    String age = request.getParameter("age");
    String birthday = request.getParameter("birthday");
    String email = request.getParameter("email");
    String department = request.getParameter("department");
    String education = request.getParameter("education");
    String remark = request.getParameter("remark");

    int sexInt = Integer.parseInt(sex);
    int ageInt = Integer.parseInt(age);
    int departmentInt = Integer.parseInt(department);
    int educationInt = Integer.parseInt(education);
    Date date = null;
    try {
        date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(birthday);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Doctor doctor = new Doctor(name,cardno,phone,sexInt,ageInt,date,email,departmentInt,educationInt,remark);
    System.out.println(doctor);
    int result = doctorService.addDoctor(doctor);
    Message message = new Message();
    if(result == 1) {
        System.out.println("Thêm thành công");
        message.setStatus_code(200);
        message.setMessage("Thêm thành công");
    }else {
        // Thêm thất bại
        System.out.println("Thêm thất bại.");
        message.setStatus_code(100);
        message.setMessage("Thêm thất bại");
    }

    ObjectMapper mapper  = new ObjectMapper();
    String json = mapper.writeValueAsString(message);
    response.getWriter().print(json);

}

public void delete(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  {
    request.setCharacterEncoding("utf-8");
    System.out.println("Phương thức delete được gọi...............................................");
    String id = request.getParameter("did");
    System.out.println("DID:" + id);
    Message message = new Message();
    int result = doctorService.delete(id);
    System.out.println(result);
    if(result > 0) {
        message.setStatus_code(200);
        message.setMessage("Thực hiện thành công!");
    }else {
        message.setStatus_code(100);
        message.setMessage("Thực hiện thất bại!");
    }
    ObjectMapper mapper  = new ObjectMapper();
    String json = mapper.writeValueAsString(message);
    response.getWriter().print(json);
}


Giao diện DoctorService

int addDoctor(Doctor doctor);

int delete(String id);

Lớp thực thi DoctorServiceImpl:

@Override
public int addDoctor(Doctor doctor) {
    // TODO Auto-generated method stub
    return doctorDao.addDoctor(doctor);
}

@Override
public int delete(String id) {

    return doctorDao.delete(id);
}

Giao diện DoctorDao

int addDoctor(Doctor doctor);

int delete(String id);

Lớp thực thi DoctorDaoImpl

@Override
public int addDoctor(Doctor doctor) {
    String sql = "insert into doctor(name,cardno,phone,sex,age,birthday,email,department,education,remark)"
        + "values(?,?,?,?,?,?,?,?,?,?)";
    int result = 0;
    try {
        result = qr.update(sql,doctor.getName(),doctor.getCardno(),doctor.getPhone(),
                          doctor.getSex(),doctor.getAge(),doctor.getBirthday(),doctor.getEmail(),doctor.getDepartment(),doctor.getEducation(),doctor.getRemark());
    } catch (SQLException e) {
        System.out.println(e);
    }
    return result;
}


@Override
public int delete(String id) {
    String sql = "delete from doctor where did in (" + id + ")";
    int result = 0;
    try {
        result = qr.update(sql);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return result;
}


Sự kiện xóa:

Lưu ý cần chèn đoạn script vào index.jsp để hiển thị thông báo:

<!--Hộp thông báo -->
<script type="text/javascript" src="../static/js/coco-message.js"></script>

// Xóa
function doctor_delete(did) {
    if (confirm("Bạn chắc chắn muốn xóa bản ghi này?")) {
        $.ajax({
            url: "${path}doctors/delete",
            type: "post",
            data: {"did": did},
            dataType: "json",
            success: function (message) {
                // Nếu xóa thành công
                if (message.status_code == 200) {
                    cocoMessage.success("Xóa thành công",30000);
                    location.reload();

                } else {
                    cocoMessage.error("Xóa thất bại!",30000);
                }
            }, error: function () {
                alert("Lỗi")
                cocoMessage.warning("Lỗi!",30000);

            }

        });
    } else {
        console.log("Người dùng hủy bỏ việc xóa bản ghi.");
    }
}

III. Sửa đổi

Quy trình sửa cũng tương tự, bỏ qua ở đây để tiết kiệm thời gian. (Sẽ bổ sung khi có thời gian, đồng thời chỉnh sửa lại giao diện...)

Thẻ: Java JSP Servlet Reflection mysql

Đăng vào ngày 6 tháng 7 lúc 07:52