Jelajahi Sumber

1. 푸시 발송 기능 추가 (개발중 - user계정만 표출)
2. 환자정보에서 확인시 보던 리스트 페이지로 이동

huiwon.seo 4 tahun lalu
induk
melakukan
27d17bb141

+ 4 - 1
src/main/java/com/lemon/lifecenter/controller/PatientController.java

@@ -234,7 +234,9 @@ public class PatientController extends LifeCenterController {
     }
 
     @RequestMapping("/info")
-    public ModelAndView patientInfo( @ModelAttribute("dto") PatientDTO dto ) throws Exception {
+    public ModelAndView patientInfo( @ModelAttribute("dto") PatientDTO dto,
+            HttpServletRequest request,HttpServletResponse response ) throws Exception {
+        String referer = request.getHeader("referer");
         ModelAndView mv = setMV("patient/info");
 
         int patientIdx = dto.getPatientIdx();
@@ -289,6 +291,7 @@ public class PatientController extends LifeCenterController {
         mv.addObject( "info", dto );
         
         mv.addObject( "staffList", staffList );
+        mv.addObject( "referer", referer );
         
         return mv;
     }

+ 239 - 0
src/main/java/com/lemon/lifecenter/controller/PushController.java

@@ -0,0 +1,239 @@
+package com.lemon.lifecenter.controller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+import com.lemon.lifecenter.common.LifeCenterConfigVO;
+import com.lemon.lifecenter.common.LifeCenterController;
+import com.lemon.lifecenter.common.LifeCenterFunction;
+import com.lemon.lifecenter.common.LifeCenterPaging;
+import com.lemon.lifecenter.common.LifeCenterSessionController;
+import com.lemon.lifecenter.dto.PatientDTO;
+import com.lemon.lifecenter.dto.PushDTO;
+import com.lemon.lifecenter.service.PatientService;
+import com.lemon.lifecenter.service.PushService;
+
+@Controller
+@RequestMapping( "/push" )
+public class PushController extends LifeCenterController {
+    @Autowired
+    private PushService pushService;
+    
+    @Autowired
+    private PatientService patientService;
+    
+    @Autowired
+    private LifeCenterConfigVO config;
+    
+    private LifeCenterPaging paging;
+    
+    @RequestMapping( "/list" )
+    public ModelAndView list( @ModelAttribute("dto") final PushDTO dto,
+            @RequestParam(value="page", required=false, defaultValue="1") int page,
+            @RequestParam(value="y", required=false, defaultValue="") String y,
+            @RequestParam(value="m", required=false, defaultValue="") String m,
+            HttpServletRequest request,HttpServletResponse response  ) {
+        
+        String nowYear  = LifeCenterFunction.getNow( "yyyy" );
+        String nowMonth = LifeCenterFunction.getNow( "MM" );
+        
+        if( y.equals( "" ) ) {
+            y = nowYear;
+        }
+        
+        if( m.equals( "" ) ) {
+            m = nowMonth;
+        }
+        
+        System.err.println( "sendType : " + dto.getSendType() );
+        dto.setLimit( ( Integer.valueOf( page ) - 1 ) * config.pageDataSize );
+        dto.setLimitMax( config.pageDataSize );
+        dto.setYm( y + "" + m );
+        
+        int total = pushService.selectPushResultTableCount(dto);
+        
+        if( total > 0 ) {
+            total = pushService.selectPushListTotal(dto);
+        }
+        
+        List<PushDTO> pushList = new ArrayList<PushDTO>();
+        
+        if( total > 0 ) {
+            pushList = pushService.selectPushListData(dto);
+        }
+        
+        
+        ModelAndView mv = setMV("push/list");
+        
+        String param = "y=" + y + "&m=" + m + "&sendType=" + dto.getSendType() + "&targetType=" + dto.getTargetType() + "&startDate=" + dto.getStartDate() + "&endDate=" + dto.getEndDate() + "&q=" + dto.getQ() + "&searchType=" + dto.getSearchType();
+        paging = LifeCenterPaging.getInstance();
+        paging.paging(config, total, page, param);
+        
+        mv.addObject( "paging", paging );
+        
+        mv.addObject( "total", total );
+        mv.addObject( "pushList", pushList );
+        mv.addObject( "q", dto.getQ() );
+        mv.addObject( "searchType", dto.getSearchType() );
+        mv.addObject( "targetType", dto.getTargetType() );
+        mv.addObject( "sendType", dto.getSendType() );
+        mv.addObject( "startDate", dto.getStartDate() );
+        mv.addObject( "endDate", dto.getEndDate() );
+        
+        mv.addObject( "nowYear", nowYear );
+        mv.addObject( "nowMonth", nowMonth );
+        mv.addObject( "y", y );
+        mv.addObject( "m", m );
+        mv.addObject( "ym", y+""+m );
+        
+        return mv;
+    }
+    
+    @RequestMapping( "/send" )
+    public ModelAndView send( HttpServletRequest request,HttpServletResponse response ) {
+        String sesCenterCode  = LifeCenterSessionController.getSession( request, "sesCenterCode" );
+        String sesGroupIdx    = LifeCenterSessionController.getSession( request, "sesGroupIdx" );
+        
+        PatientDTO dto = new PatientDTO();
+        
+        dto.setCenterCode( Integer.parseInt( sesCenterCode ) );
+        dto.setGroupIdx( Integer.valueOf( sesGroupIdx ) );
+        dto.setState( "H" );
+        
+        int total = patientService.selectPatientCount(dto);
+        List<PatientDTO> result = new ArrayList<PatientDTO>();
+
+        if (total > 0) {
+            dto.setLimit( 0 );
+            dto.setLimitMax( total );
+            result = patientService.selectPatientList(dto);
+        }
+        
+        ModelAndView mv = setMV("push/send");
+        
+        mv.addObject( "total", total );
+        mv.addObject( "patientList", result );
+        
+        return mv;
+    }
+    
+    @RequestMapping( value="/getPushResultList", method = RequestMethod.POST )
+    @ResponseBody
+    public String getPushResultList(
+            @RequestParam(value="resultType", required=true ) String resultType,
+            @RequestParam(value="pushIdx", required=true ) int pushIdx,
+            @RequestParam(value="ym", required=true ) String ym ) {
+        
+        PushDTO dto = new PushDTO();
+        
+        dto.setPushIdx( pushIdx );
+        dto.setYm( ym );
+        
+        int total = pushService.selectPushResultTableCount(dto);
+        
+        JSONObject object = new JSONObject();
+        
+        if( resultType.toLowerCase().equals( "success" ) ) {
+            dto.setState( "C" );
+            dto.setSuccessYn( "Y" );
+        } else if( resultType.toLowerCase().equals( "fail" ) ) {
+            dto.setState( "C" );
+            dto.setSuccessYn( "N" );
+        } else if( resultType.toLowerCase().equals( "wait" ) ) {
+            dto.setState( "W" );
+            dto.setSuccessYn( "" );
+        }
+        
+        if( total != 0 ) {
+            total = pushService.selectPushResultTotal(dto);
+        }
+        
+        List<PushDTO> result = new ArrayList<PushDTO>();
+        
+        if( total > 0 ) {
+            result = pushService.selectPushResultList(dto);
+        }
+        
+        object.put( "total" , total );
+        object.put( "result" , result );
+        
+        return object.toString();
+    }
+    
+    @RequestMapping( value="/send/insert", method=RequestMethod.POST )
+    @Transactional(propagation=Propagation.REQUIRED)
+    public String insertPushData(
+            @ModelAttribute("dto") final PushDTO dto,
+            @RequestParam(value="patientIdx", required=false, defaultValue="") String[] patientIdx,
+            HttpServletRequest request,HttpServletResponse response ) {
+        int sesCenterCode = Integer.valueOf( LifeCenterSessionController.getSession( request, "sesCenterCode" ) ) ;
+        String sesId      = LifeCenterSessionController.getSession( request, "sesId" );
+        String remoteIp   = LifeCenterFunction.getRemoteAddr( request );
+        
+        dto.setCenterCode( sesCenterCode );
+        dto.setSender( sesId );
+        dto.setSenderIp( remoteIp );
+        dto.setSendState( "W" );
+        
+        pushService.insertPushData(dto);
+//        System.out.println( "PUSH IDX ::: " + dto.getPushIdx() );
+        
+        if( dto.getSelectType().equals( "PATIENT" ) && dto.getTargetType().equals( "P" ) ) {
+            for( int i = 0; i < patientIdx.length; i ++ ) {
+//                System.out.println( patientIdx[i] );
+                
+                dto.setPatientIdx( Integer.valueOf( patientIdx[i] ) );
+                
+                pushService.insertPushTargetTemp(dto);
+            }
+        }
+        
+        return "redirect:/push/list";
+    }
+    
+    @RequestMapping( "/info" )
+    public ModelAndView info() {
+        
+        ModelAndView mv = setMV("push/info");
+        
+        return mv;
+    }
+    
+    @RequestMapping( "/edit" )
+    public ModelAndView edit() {
+        
+        ModelAndView mv = setMV("push/edit");
+        
+        return mv;
+    }
+    
+    @RequestMapping( "/edit/update" )
+    public String updatePushData() {
+        
+        
+        return "";
+    }
+    
+    @RequestMapping( "/schedule/list" )
+    public ModelAndView schedule() {
+        
+        ModelAndView mv = setMV("push/schedule");
+        
+        return mv;
+    }
+}

+ 278 - 0
src/main/java/com/lemon/lifecenter/dto/PushDTO.java

@@ -0,0 +1,278 @@
+package com.lemon.lifecenter.dto;
+
+
+public class PushDTO {
+    private int idx;
+    private int pushIdx;
+    private int patientIdx;
+    private int centerCode;
+    
+    private String sendType="";
+    private String sendState="";
+    private String targetType="";
+    private String senderIp="";
+    private String sender="";
+    private String pushTitle="";
+    private String pushContent="";
+    private String sendDate=null;
+    private String sendTime=null;
+    private String createDate="";
+    private String selectType = "";
+    
+    private String q="";
+    private String searchType = "";
+    private String name="";
+    private String startDate="";
+    private String endDate="";
+    
+    private int totalCount;
+    private int successCount;
+    private int failCount;
+    private int waitCount;
+    
+    private String failCode="";
+    private String note="";
+    private String updateDate="";
+    private String centerName="";
+    private String patientName="";
+    private String wardNumber="";
+    private String roomNumber="";
+    private String jumin="";
+    private String gender="";
+    private String state="";
+    private String successYn="";
+    private String ym;
+    
+    public String getFailCode() {
+        return failCode;
+    }
+    public void setFailCode(String failCode) {
+        this.failCode = failCode;
+    }
+    public String getNote() {
+        return note;
+    }
+    public void setNote(String note) {
+        this.note = note;
+    }
+    public String getUpdateDate() {
+        return updateDate;
+    }
+    public void setUpdateDate(String updateDate) {
+        this.updateDate = updateDate;
+    }
+    public String getPatientName() {
+        return patientName;
+    }
+    public void setPatientName(String patientName) {
+        this.patientName = patientName;
+    }
+    public String getWardNumber() {
+        return wardNumber;
+    }
+    public void setWardNumber(String wardNumber) {
+        this.wardNumber = wardNumber;
+    }
+    public String getRoomNumber() {
+        return roomNumber;
+    }
+    public void setRoomNumber(String roomNumber) {
+        this.roomNumber = roomNumber;
+    }
+    public String getJumin() {
+        return jumin;
+    }
+    public void setJumin(String jumin) {
+        this.jumin = jumin;
+    }
+    public String getGender() {
+        return gender;
+    }
+    public void setGender(String gender) {
+        this.gender = gender;
+    }
+    public String getState() {
+        return state;
+    }
+    public void setState(String state) {
+        this.state = state;
+    }
+    public String getSuccessYn() {
+        return successYn;
+    }
+    public void setSuccessYn(String successYn) {
+        this.successYn = successYn;
+    }
+    public String getCenterName() {
+        return centerName;
+    }
+    public void setCenterName(String centerName) {
+        this.centerName = centerName;
+    }
+    
+    public String getYm() {
+        return ym;
+    }
+    public void setYm(String ym) {
+        this.ym = ym;
+    }
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public String getCreateDate() {
+        return createDate;
+    }
+    public void setCreateDate(String createDate) {
+        this.createDate = createDate;
+    }
+    public int getTotalCount() {
+        return totalCount;
+    }
+    public void setTotalCount(int totalCount) {
+        this.totalCount = totalCount;
+    }
+    public int getSuccessCount() {
+        return successCount;
+    }
+    public void setSuccessCount(int successCount) {
+        this.successCount = successCount;
+    }
+    public int getFailCount() {
+        return failCount;
+    }
+    public void setFailCount(int failCount) {
+        this.failCount = failCount;
+    }
+    public int getWaitCount() {
+        return waitCount;
+    }
+    public void setWaitCount(int waitCount) {
+        this.waitCount = waitCount;
+    }
+    public int getIdx() {
+        return idx;
+    }
+    public void setIdx(int idx) {
+        this.idx = idx;
+    }
+    private int limit;
+    private int limitMax;
+    
+    public int getLimit() {
+        return limit;
+    }
+    public void setLimit(int limit) {
+        this.limit = limit;
+    }
+    public int getLimitMax() {
+        return limitMax;
+    }
+    public void setLimitMax(int limitMax) {
+        this.limitMax = limitMax;
+    }
+    public String getStartDate() {
+        return startDate;
+    }
+    public void setStartDate(String startDate) {
+        this.startDate = startDate;
+    }
+    public String getEndDate() {
+        return endDate;
+    }
+    public void setEndDate(String endDate) {
+        this.endDate = endDate;
+    }
+    public String getSearchType() {
+        return searchType;
+    }
+    public void setSearchType(String searchType) {
+        this.searchType = searchType;
+    }
+    public String getQ() {
+        return q;
+    }
+    public void setQ(String q) {
+        this.q = q;
+    }
+    public int getPatientIdx() {
+        return patientIdx;
+    }
+    public void setPatientIdx(int patientIdx) {
+        this.patientIdx = patientIdx;
+    }
+    public String getSelectType() {
+        return selectType;
+    }
+    public void setSelectType(String selectType) {
+        this.selectType = selectType;
+    }
+    public int getPushIdx() {
+        return pushIdx;
+    }
+    public void setPushIdx(int pushIdx) {
+        this.pushIdx = pushIdx;
+    }
+    public String getSendType() {
+        return sendType;
+    }
+    public void setSendType(String sendType) {
+        this.sendType = sendType;
+    }
+    public String getSendState() {
+        return sendState;
+    }
+    public void setSendState(String sendState) {
+        this.sendState = sendState;
+    }
+    public String getTargetType() {
+        return targetType;
+    }
+    public void setTargetType(String targetType) {
+        this.targetType = targetType;
+    }
+    public int getCenterCode() {
+        return centerCode;
+    }
+    public void setCenterCode(int centerCode) {
+        this.centerCode = centerCode;
+    }
+    public String getSenderIp() {
+        return senderIp;
+    }
+    public void setSenderIp(String senderIp) {
+        this.senderIp = senderIp;
+    }
+    public String getSender() {
+        return sender;
+    }
+    public void setSender(String sender) {
+        this.sender = sender;
+    }
+    public String getPushTitle() {
+        return pushTitle;
+    }
+    public void setPushTitle(String pushTitle) {
+        this.pushTitle = pushTitle;
+    }
+    public String getPushContent() {
+        return pushContent;
+    }
+    public void setPushContent(String pushContent) {
+        this.pushContent = pushContent;
+    }
+    public String getSendDate() {
+        return sendDate;
+    }
+    public void setSendDate(String sendDate) {
+        this.sendDate = sendDate;
+    }
+    public String getSendTime() {
+        return sendTime;
+    }
+    public void setSendTime(String sendTime) {
+        this.sendTime = sendTime;
+    }
+}

+ 20 - 0
src/main/java/com/lemon/lifecenter/mapper/PushMapper.java

@@ -0,0 +1,20 @@
+package com.lemon.lifecenter.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+import com.lemon.lifecenter.dto.PushDTO;
+
+@Repository
+@Mapper
+public interface PushMapper {
+    public void insertPushData( PushDTO dto );
+    public void insertPushTargetTemp( PushDTO dto );
+    public int selectPushListTotal( PushDTO dto );
+    public List<PushDTO> selectPushListData( PushDTO dto );
+    public int selectPushResultTableCount( PushDTO dto );
+    public int selectPushResultTotal( PushDTO dto );
+    public List<PushDTO> selectPushResultList( PushDTO dto );
+}

+ 40 - 0
src/main/java/com/lemon/lifecenter/service/PushService.java

@@ -0,0 +1,40 @@
+package com.lemon.lifecenter.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.lemon.lifecenter.dto.PushDTO;
+import com.lemon.lifecenter.mapper.PushMapper;
+
+@Service
+public class PushService {
+    @Autowired
+    private PushMapper mapper;
+
+    public void insertPushData( PushDTO dto ) {
+        mapper.insertPushData(dto);
+    }
+    
+    public void insertPushTargetTemp( PushDTO dto ) {
+        mapper.insertPushTargetTemp(dto);
+    }
+    
+    public int selectPushListTotal( PushDTO dto ) {
+        return mapper.selectPushListTotal(dto);
+    }
+    public List<PushDTO> selectPushListData( PushDTO dto ){
+        return mapper.selectPushListData(dto);
+    }
+    
+    public int selectPushResultTableCount( PushDTO dto ) {
+        return mapper.selectPushResultTableCount(dto);
+    }
+    public int selectPushResultTotal( PushDTO dto ) {
+        return mapper.selectPushResultTotal(dto);
+    }
+    public List<PushDTO> selectPushResultList( PushDTO dto ){
+        return mapper.selectPushResultList(dto);
+    }
+}

+ 4 - 4
src/main/resources/logback-spring.xml

@@ -46,7 +46,7 @@
             <maxFileSize>50MB</maxFileSize>
          </timeBasedFileNamingAndTriggeringPolicy>
          <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
-         <maxHistory>3</maxHistory>
+         <maxHistory>15</maxHistory>
          <!--<MinIndex>1</MinIndex> <MaxIndex>10</MaxIndex>-->
       </rollingPolicy>
    </appender>
@@ -70,7 +70,7 @@
             <maxFileSize>10MB</maxFileSize>
          </timeBasedFileNamingAndTriggeringPolicy>
          <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
-         <maxHistory>3</maxHistory>
+         <maxHistory>15</maxHistory>
       </rollingPolicy>
    </appender>
    <!-- jdbc query log -->
@@ -93,12 +93,12 @@
             <maxFileSize>50MB</maxFileSize>
          </timeBasedFileNamingAndTriggeringPolicy>
          <!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거 -->
-         <maxHistory>3</maxHistory>
+         <maxHistory>15</maxHistory>
       </rollingPolicy>
    </appender>
    <!-- root레벨 설정 -->
    <root level="${LOG_LEVEL}">
-      <!-- <appender-ref ref="CONSOLE" /> -->
+      <appender-ref ref="CONSOLE" />
       <appender-ref ref="FILE_SAVE" />
       <appender-ref ref="Error" />
    </root>

+ 275 - 0
src/main/resources/mybatis/mapper/push/push.xml

@@ -0,0 +1,275 @@
+<?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.lemon.lifecenter.mapper.PushMapper">
+    <select id="selectPushResultTableCount" parameterType="PushDTO" resultType="int">
+        <![CDATA[
+            SELECT COUNT(*) total
+              FROM db_class
+             WHERE class_name LIKE '%push_result_${ym}%' 
+        ]]>
+    </select>
+    
+    <select id="selectPushListTotal" parameterType="PushDTO" resultType="int">
+        <![CDATA[
+            SELECT SUM( A.total ) total
+             FROM (
+                    SELECT COUNT(*) total
+                      FROM push_log PL
+                      LEFT JOIN member M
+                        ON M.id = PL.sender
+                     WHERE 1 = 1
+                       AND DATE_FORMAT( PL.start_date, '%Y%m' ) = #{ym}
+        ]]>
+        
+        <if test='sendType != null and sendType != ""'>
+            <![CDATA[
+                       AND PL.send_type = #{sendType}
+            ]]>
+        </if>
+        
+        <if test='targetType != null and targetType != ""'>
+            <![CDATA[
+                       AND PL.target_type = #{targetType}
+            ]]>
+        </if>
+        
+        <if test='startDate != null and startDate != ""'>
+            <![CDATA[
+                       AND DATE_FORMAT( PL.start_date, '%Y-%m-%d') >= #{startDate}
+            ]]>
+        </if>
+        
+        <if test='endDate != null and endDate != ""'>
+            <![CDATA[
+                       AND DATE_FORMAT( PL.start_date, '%Y-%m-%d') <= #{endDate}
+            ]]>
+        </if>
+        
+        <if test='searchType != null and searchType != ""'>
+            <if test='searchType == "title" and q != null and q !=""'>
+                <![CDATA[
+                       AND PL.push_title LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+            
+            <if test='searchType == "name" and q != null and q !=""'>
+                <![CDATA[
+                       AND M.name LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+            
+            <if test='searchType == "id" and q != null and q !=""'>
+                <![CDATA[
+                       AND PL.sender LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+        </if>
+        
+        <![CDATA[
+                     UNION
+                    SELECT COUNT(*) total
+                      FROM push_schedule
+                     WHERE send_type = 'D'
+                ) A
+        ]]>
+    </select>
+    
+    <select id="selectPushListData" parameterType="PushDTO" resultType="PushDTO">
+        <![CDATA[
+            SELECT PL.idx          AS idx,
+                   PL.push_idx     AS pushIdx,
+                   PL.send_type    AS sendType,
+                   PL.send_state   AS sendState,
+                   PL.target_type  AS targetType,
+                   PL.center_code  AS centerCode,
+                   PL.sender_ip    AS senderIp,
+                   PL.sender       AS sender,
+                   PL.push_title   AS pushTitle,
+                   PL.push_content AS pushContent,
+                   M.name           AS name,
+                   
+                   DATE_FORMAT( PL.create_date, '%Y-%m-%d %H:%i' )  AS createDate,
+                   DATE_FORMAT( PL.start_date, '%Y-%m-%d %H:%i' )   AS startDate,
+                   DATE_FORMAT( PL.end_date, '%Y-%m-%d %H:%i' )     AS endDate,
+                   
+                   PR.total_count   AS totalCount,
+                   PR.success_count AS successCount,
+                   PR.fail_count    AS failCount,
+                   PR.wait_count    AS waitCount,
+                   
+                   ( SELECT center_name FROM center_info WHERE center_code = PL.center_code  ) AS centerName
+              FROM push_log PL
+              LEFT JOIN member M
+                ON M.id = PL.sender
+              LEFT JOIN ( SELECT A.push_idx, 
+                                 COUNT( push_idx ) total_count,
+                                 COUNT( IF(success_yn='Y', 1, null) ) success_count,
+                                 COUNT( IF(success_yn='N', 1, null) ) fail_count,
+                                 COUNT( IF(state='W', 1, null) ) wait_count
+                            FROM push_result_${ym} A 
+                           WHERE push_idx = PL.push_idx 
+                           GROUP BY A.push_idx ) PR
+                ON PR.push_idx = PL.push_idx
+             WHERE 1 = 1
+               AND DATE_FORMAT( PL.start_date, '%Y%m' ) = #{ym}
+        ]]>
+        
+        <if test='sendType != null and sendType != ""'>
+            <![CDATA[
+                AND PL.send_type = #{sendType}
+            ]]>
+        </if>
+        
+        <if test='targetType != null and targetType != ""'>
+            <![CDATA[
+                AND PL.target_type = #{targetType}
+            ]]>
+        </if>
+        
+        <if test='startDate != null and startDate != ""'>
+            <![CDATA[
+                AND DATE_FORMAT( PL.start_date, '%Y-%m-%d') >= #{startDate}
+            ]]>
+        </if>
+        
+        <if test='endDate != null and endDate != ""'>
+            <![CDATA[
+                AND DATE_FORMAT( PL.start_date, '%Y-%m-%d') <= #{endDate}
+            ]]>
+        </if>
+        
+        <if test='searchType != null and searchType != ""'>
+            <if test='searchType == "title" and q != null and q !=""'>
+                <![CDATA[
+                AND PL.push_title LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+            
+            <if test='searchType == "name" and q != null and q !=""'>
+                <![CDATA[
+                AND M.name LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+            
+            <if test='searchType == "id" and q != null and q !=""'>
+                <![CDATA[
+                AND PL.sender LIKE CONCAT('%', #{q}, '%')
+                ]]>
+            </if>
+        </if>
+        
+        <![CDATA[
+              UNION
+              
+             SELECT 0 idx,
+                    push_idx     AS pushIdx,
+                    send_type    AS sendType,
+                    send_state   AS sendState,
+                    target_type  AS targetType,
+                    center_code  AS centerCode,
+                    sender_ip    AS senderIp,
+                    sender       AS sender,
+                    push_title   AS pushTitle,
+                    push_content AS pushContent,
+                    ( SELECT `name` FROM member WHERE id= sender ) AS `name`,
+                    DATE_FORMAT( create_date, '%Y-%m-%d %H:%i' )   AS createDate,
+                    null   AS startDate,
+                    null   AS endDate,
+                    0      AS totalCount,
+                    0      AS successCount,
+                    0      AS failCount,
+                    0      AS waitCount,
+                    
+                    ( SELECT center_name FROM center_info WHERE center_code =  push_schedule.center_code ) AS centerName
+               FROM push_schedule
+              WHERE send_type = 'D'
+              
+              ORDER BY createDate desc
+              LIMIT ${limit}, ${limitMax}
+        ]]>
+    </select>
+    
+    <select id="selectPushResultTotal" parameterType="PushDTO" resultType="int">
+        <![CDATA[
+            SELECT COUNT(*) total
+              FROM push_result_${ym} PR
+             WHERE 1=1
+               AND PR.push_idx = #{pushIdx}
+        ]]>
+                 
+        <if test='state != null and state !=""'>
+            <![CDATA[
+               AND PR.state = #{state}
+            ]]>
+        </if>
+        
+        <if test='successYn != null and successYn !=""'>
+            <![CDATA[
+               AND PR.success_yn = #{successYn}
+            ]]>
+        </if>
+    </select>
+    
+    <select id="selectPushResultList" parameterType="PushDTO" resultType="PushDTO">
+        <![CDATA[
+            SELECT PR.idx AS idx,
+                   PR.push_idx      AS pushIdx,
+                   PR.patient_idx   AS patientIdx,
+                   PR.device_key AS deviceKey,
+                   PR.state AS state,
+                   PR.success_yn AS successYn,
+                   PR.fail_code AS failCode,
+                   PR.note AS note,
+                   DATE_FORMAT( PR.create_date, '%Y-%m-%d %H:%i:%s' ) AS createDate,
+                   DATE_FORMAT( PR.update_date, '%Y-%m-%d %H:%i:%s' ) AS updateDate,
+                   PC.patient_name AS patientName,
+                   PC.ward_number AS wardNumber,
+                   PC.room_number AS roomNumber,
+                   PC.gender AS gender,
+                   PC.jumin AS jumin
+              FROM push_result_${ym} PR
+              LEFT JOIN patient_care PC
+                ON PR.patient_idx = PC.patient_idx
+             WHERE 1=1
+               AND PR.push_idx = #{pushIdx}
+        ]]>
+                 
+        <if test='state != null and state !=""'>
+            <![CDATA[
+               AND PR.state = #{state}
+            ]]>
+        </if>
+        
+        <if test='successYn != null and successYn !=""'>
+            <![CDATA[
+               AND PR.success_yn = #{successYn}
+            ]]>
+        </if>
+    </select>
+    
+    <insert id="insertPushData" parameterType="PushDTO" useGeneratedKeys="true">
+        <selectKey keyProperty="pushIdx" resultType="int" order="AFTER">
+            <![CDATA[
+                SELECT CURRENT_VAL AS pushIdx FROM db_serial WHERE NAME = 'push_schedule_ai_push_idx';
+            ]]>
+        </selectKey>
+        <![CDATA[
+            INSERT
+              INTO push_schedule 
+                   ( send_type,    send_state,     target_type,   center_code,   sender_ip,   sender,
+                     push_title,   push_content,   send_date,     send_time,     create_date )
+            VALUES ( #{sendType},  #{sendState},   #{targetType}, #{centerCode}, #{senderIp}, #{sender},
+                     #{pushTitle}, #{pushContent}, #{sendDate},   #{sendTime},   NOW() );
+        ]]>
+    </insert>
+    
+    <insert id="insertPushTargetTemp" parameterType="PushDTO">
+        <![CDATA[
+            INSERT
+              INTO push_target_temp
+                   ( push_idx,   patient_idx )
+            VALUES ( #{pushIdx}, #{patientIdx} )
+        ]]>
+    </insert>
+</mapper>

+ 6 - 3
src/main/webapp/WEB-INF/jsp/include/sidebar.jsp

@@ -37,9 +37,12 @@
                             <a class="sidebar-link" href="/cooperation/list">협력병원 관리</a>
                         </li>
                         
-<%--                         <li class="sidebar-item <c:if test='${data._MENUPATH eq "push"}'>active</c:if>"> --%>
-<!--                             <a class="sidebar-link" href="/push/list">푸시 서비스 관리</a> -->
-<!--                         </li> -->
+                    </c:if>
+                    
+                    <c:if test="${data._SES_ID eq 'user'}">
+                        <li class="sidebar-item <c:if test='${data._MENUPATH eq "push"}'>active</c:if>">
+                            <a class="sidebar-link" href="/push/list">푸시 서비스 관리(테스트)</a>
+                        </li>
                     </c:if>
                 </ul>
             </li>

+ 15 - 2
src/main/webapp/WEB-INF/jsp/patient/info.jsp

@@ -9,7 +9,7 @@ function deleteConfirm(){
     alertBox({ type : "confirm", 
                txt : "정말로 삭제할까요? (기록된 환자의 모든정보가 삭제되며 복구가 불가능합니다)", 
                callBack : function( result ){
-            	   console.log( result );
+                   console.log( result );
                    if( result ){
                        $( "#pForm" ).attr( "action", "./delete" );
                        $( "#pForm" ).submit();
@@ -18,6 +18,19 @@ function deleteConfirm(){
     });
     return false;
 }
+
+var listPage = '<c:out value="${referer}"/>'.replaceAll( "&amp;", "&" );
+if( listPage.includes( '/patient/list' ) ) {
+    encodeURIComponent(listPage);
+    setCookie( "listPage", listPage );
+}
+function goListPage(){
+    var referer = getCookie( "listPage" );
+    if( referer == "" ) {
+        referer = "/patient/list";
+    }
+    location.href = referer;
+}
 </script>
 </head>
 <body>
@@ -64,7 +77,7 @@ function deleteConfirm(){
                                                         <button type="button" class="btn btn-secondary w100" onclick="location.href='./edit?patientIdx=${patientIdx}';">수정</button>
                                                     </c:if>
                                                     
-                                                    <button type="button" class="btn btn-primary w100" onclick="location.href='./list';">확인</button>
+                                                    <button type="button" class="btn btn-primary w100" onclick="goListPage();">확인</button>
                                                 </div>
                                             </div>
                                         </div>

+ 0 - 6
src/main/webapp/WEB-INF/jsp/patient/list.jsp

@@ -305,13 +305,7 @@ tr.phr-info td span.no-data{color:#999999;}
                                                                 <td><c:out value="${l.confirmationDate}" /></td>
                                                                 <td><c:out value="${l.hospitalizationDate}" /></td>
                                                                 <td>
-                                                                    <c:if test="${l.expectedDischargeDate eq null or l.expectedDischargeDate eq ''}">
-                                                                        -
-                                                                    </c:if>
                                                                     <c:if test="${l.expectedDischargeDate ne ''}">
-                                                                        <fmt:parseDate var="dDate" value="${l.expectedDischargeDate}" pattern="yyyy-MM-dd" />
-                                                                        <c:set var="expectedDischargeDate"><fmt:formatDate value="${dDate}" pattern="yyyy-MM-dd" /></c:set>
-                                                                        <c:out value="${expectedDischargeDate}" />
                                                                     </c:if>
                                                                 </td>
                                                                 <td>

+ 578 - 0
src/main/webapp/WEB-INF/jsp/push/list.jsp

@@ -0,0 +1,578 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+    pageEncoding="UTF-8"%>
+<jsp:include page="${data._INCLUDE}/header.jsp"></jsp:include>
+<script type="text/javascript" src="/resources/js/common/jquery.tablesorter.min.js"></script>
+<script type="text/javascript">
+$( function(){
+    $( "select[name='y'], select[name='m']" ).change( function(){
+        var ym    = $( "select[name='y']" ).val() + "-" + $( "select[name='m']" ).val();
+        var sDate = moment( ym+"-01" ).startOf('month').format('YYYY-MM-DD');
+        var eDate = moment( ym+"-01" ).endOf('month').format('YYYY-MM-DD');
+        
+        $( "input[name='startDate']" ).val( sDate );
+        $( "input[name='endDate']" ).val( eDate );
+    });
+    
+    $( "input.date-no-req" ).daterangepicker({
+        singleDatePicker : true,
+        showDropdowns : true,
+        locale : {
+            format : "YYYY-MM-DD"
+        },
+        autoUpdateInput: false,
+        minDate: moment( $( "select[name='y']" ).val() + "-" + $( "select[name='m']" ).val() + "-01" ).startOf('month').format('YYYY-MM-DD'),
+        maxDate: moment( $( "select[name='y']" ).val() + "-" + $( "select[name='m']" ).val() + "-01" ).endOf('month').format('YYYY-MM-DD')
+    }).on('apply.daterangepicker', function(ev, picker) {
+        $(this).val(picker.startDate.format('YYYY-MM-DD'));
+        $(this).trigger( "change" );
+    });
+
+    $( "#searchForm" ).validate({
+        rules: {
+            startDate : {
+                date : true
+            },
+            endDate : {
+                date : true
+            }
+        },
+        onkeyup: function( element, event ) {
+            $( element ).valid();
+        },
+        onfocusout: function (element) {
+            $( element ).val( $.trim( $( element ).val() ) );
+            $( element ).valid();
+        },
+        submitHandler: function(form) {
+            form.submit();
+        }
+    });
+    
+    $( document ).on( "click", "span.push-result", function(){
+        var $this = $( this );
+        var key   = $this.prop( "accessKey" );
+        
+        var title     = $( "#sendResultList" ).find( ".modal-title" );
+        var pushIdx   = $this.closest( "tr" ).find( "input.push-idx" ).val();
+        var yearMonth = $( "#ym" ).val();
+        var resultAddClass = "";
+        var resultMessage  = "";
+        
+        console.log( key );
+        if( key == "success" ) {
+            title.text( "발송 성공 리스트" );
+            resultAddClass = "text-success";
+            resultMessage  = "성공";
+            
+        } else if( key == "fail" ) {
+            title.text( "발송 실패 리스트" );
+            resultAddClass = "text-danger";
+            resultMessage  = "실패";
+            
+        } else if( key == "wait" ) {
+            title.text( "발송 대기 리스트" );
+            resultAddClass = "text-warning";
+            resultMessage  = "대기";
+            
+        }
+        
+        //ajax -> get patientList
+        var loadingTr = $( "#loading-tr" ).clone().removeAttr( "id" );
+        $( "#patientList" ).empty().append( loadingTr );
+        
+        var url    = "./getPushResultList";
+        var vv     = "resultType=" + key + "&pushIdx=" + pushIdx + "&ym=" + yearMonth;
+        
+//         $( "#patientList" ).find( "tr" ).not( loadingTr ).remove();
+        
+        $( "#pushResultTable" ).trigger( "destroy" );
+        
+        getAjax( url, vv, function( data ){
+        	$( "#patientList" ).empty();
+        	
+            if( data.total > 0 ) {
+                for( var i = 1; i <= data.total; i ++ ) {
+                    var info = data.result[i - 1];
+                    var tr = $( "#patient-tr" ).clone().removeAttr( "id" );
+                    
+                    tr.find( "td.no" ).text( i );
+                    tr.find( "td.name" ).text( info.patientName );
+                    tr.find( "td.ward" ).text( info.wardNumber == "" ? info.roomNumber+"호" : info.wardNumber+"동 " + info.roomNumber+"호" );
+                    tr.find( "td.gender" ).text( info.gender );
+                    tr.find( "td.jumin" ).text( info.jumin );
+                    tr.find( "td.updateDate" ).text( info.updateDate );
+                    tr.find( "td.sendResult" ).text( resultMessage ).addClass( resultAddClass );
+                    tr.find( "td.failMsg" ).text( info.note ).attr( "data-content", info.note ).popover({
+                        container: 'body'
+                    });
+                    
+                    $( "#patientList" ).append( tr );
+                    
+                    if( i == ( data.total ) ) {
+                        $( "#pushResultTable" ).tablesorter();
+                    }
+                }
+            } else {
+            	$( "#patientList" ).append( "<tr><td colspan='8'>대상자가 존재하지않습니다</td></tr>" );
+            }
+        }, null, null );
+        
+        
+    });
+    
+    // 즉시발송건 존재할경우 5초마다 새로고침
+    setInterval(sendDirectCheck, 3000);
+});
+function getPushResult( type, pidx, ym ){
+	var url = "./getPushResultList";
+	var vv  = "resultType=success&pushIdx=1&ym=202012";
+	getAjax( url, vv, function( result ){
+		console.log( result );
+	}, null, null );
+}
+
+function sendDirectCheck(){
+    if( $( "div.send-loading" ).length > 0 ) {
+        $.ajax({
+            type: "GET",
+            url : "./list",
+            dataType : "text",
+            error : function(){
+                console.log( "error" );
+            },
+            success : function( data ){
+                var domNodes = $( $.parseHTML( data ) );
+                $( "#pushLogTable" ).html( domNodes.find( "#pushLogTable" ).html() );
+            }
+        })
+    };
+    
+    return false;
+};
+
+function pushDetail( t ){
+    var $this     = $( t );
+    var targetBox = $( "#defaultModalPrimary_1" );
+    
+    console.log( $this );
+    console.log( $this.parent( "td" ).siblings( "td-target-type" ).text() );
+    
+    targetBox.find( "td.center-name" ).text( $this.siblings( "input.center-name" ).val() );
+    targetBox.find( "td.sender" ).text( $this.siblings( "input.name" ).val() + " (" + $this.siblings( "input.sender" ).val() + ")" );
+    targetBox.find( "td.target-type" ).text( $this.parent( "td" ).siblings( "td.td-target-type" ).text() );
+    targetBox.find( "td.send-type" ).text( $this.parent( "td" ).siblings( "td.td-send-type" ).text() + " 발송" );
+    targetBox.find( "td.start-date" ).text( $this.parent( "td" ).siblings( "td.td-start-date" ).text() );
+    targetBox.find( "input.push-title" ).val( $this.text() );
+    targetBox.find( "textarea.push-content" ).val( $this.siblings( "input.push-content" ).val() );
+}
+</script>
+</head>
+<body>
+    <div class="modal fade" id="defaultModalPrimary_1" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">발송 정보</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span> </button>
+                </div>
+                <div class="modal-body m-4">
+                    <table class="table mobile-table">
+                        <colgroup>
+                            <col style="width: 30%">
+                            <col style="width: 70%">
+                        </colgroup>
+                        <tr>
+                            <th>생활치료센터명</th>
+                            <td class="center-name"></td>
+                        </tr>
+                        <tr>
+                            <th>발송등록자</th>
+                            <td class="sender"></td>
+                        </tr>
+                        
+                        
+                        <tr>
+                            <th>발송 대상</th>
+                            <td class="target-type"></td>
+                        </tr>
+                        
+                        <tr>
+                            <th>발송 구분</th>
+                            <td class="send-type"></td>
+                        </tr>
+                        
+                        <tr>
+                            <th>발송일</th>
+                            <td class="start-date"></td>
+                        </tr>
+                        
+                        <tr>
+                            <th>푸시 제목</th>
+                            <td>
+                                <div class="form-group mb-xl-0">
+                                    <input class="form-control push-title" type="text" value="" readonly>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th>푸시 내용</th>
+                            <td>
+                                <div class="form-group mb-xl-0">
+                                    <textarea  class="form-control push-content" rows="10" cols="" readonly></textarea>
+                                </div>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" data-dismiss="modal">확인</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+    
+    <div class="modal fade" id="sendResultList" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">발송 통계</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span> </button>
+                </div>
+                <div class="modal-body m-4">
+                    <table class="table mobile-table table-striped text-center" id="pushResultTable">
+                        <colgroup>
+                            <col style="width: 8%">
+                            <col style="width: 12%">
+                            <col style="width: 15%">
+                            <col style="width: 10%">
+                            <col style="width: 16%">
+                            <col style="width: 16%">
+                            <col style="width: 10%">
+                            <col style="width: 19%">
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <th>번호  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>환자명  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>호실  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>성별  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>생년월일  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>발송 완료시간  <i class="fa fa-fw fa-sort"></i></th>
+                                <th>발송 결과  <i class="fa fa-fw fa-sort"></i></th>
+                                <th style="max-width: 60px;">비고  <i class="fa fa-fw fa-sort"></i></th>
+                            </tr>
+                        </thead>
+                        <tbody id="patientList">
+                            <!-- 환자리스트 동적 생성 -->
+                        </tbody>
+                    </table>
+                    <table class="d-none" id="hide-table">
+                        <tr id="loading-tr">
+                            <td colspan="8">
+                                <div class="spinner-border text-primary" role="status">
+                                    <span class="sr-only">Loading...</span>
+                                </div>
+                            </td>
+                        </tr>
+                        
+                        <tr id="patient-tr">
+                            <td class="no"></td>
+                            <td class="name"></td>
+                            <td class="ward"></td>
+                            <td class="gender"></td>
+                            <td class="jumin"></td>
+                            <td class="updateDate"></td>
+                            <td class="sendResult"></td>
+                            <td class="failMsg" style="max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;" data-toggle="popover" title="실패 사유" data-content="And here's some amazing content. It's very engaging. Right?"></td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" data-dismiss="modal">확인</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+    <div class="wrapper">
+        <jsp:include page="${data._INCLUDE}/sidebar.jsp"></jsp:include>
+
+        <div class="main">
+            <jsp:include page="${data._INCLUDE}/top.jsp"></jsp:include>
+
+            <main class="content">
+                <div class="container-fluid p-0">
+                    <!-- 의료진 관리 START -->
+                    <div class="row">
+                        <div class="col-12 col-lg-6">
+                            <h1 class="h3 mb-3">월별 푸시 발송 현황 (${y}년 ${m}월)</h1>
+                        </div>
+                        <div class="col-12 col-lg-6  text-right">
+                            <nav aria-label="breadcrumb">
+                                <ol class="breadcrumb">
+                                    <li class="breadcrumb-item"><a href="javscript:;">Home</a></li>
+                                    <li class="breadcrumb-item">푸시서비스 관리</li>
+                                    <li class="breadcrumb-item active">발송 현황 리스트</li>
+                                </ol>
+                            </nav>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="col-12">
+                            <div class="card">
+                                <form action="?" method="get" id="searchForm">
+                                    <input type="hidden" id="ym" value="${ym}" />
+                                    <div class="card-body">
+                                        <table class="table mobile-table">
+                                            <colgroup>
+                                                <col style="width:10%">
+                                                <col style="width:40%">
+                                                <col style="width:10%">
+                                                <col style="width:40%">
+                                            </colgroup>
+                                            <tr>
+                                                <th>발송(연도)</th>
+                                                <td>
+                                                    <select class="custom-select  form-control" name="y">
+                                                        <c:forEach var="i" begin="2020" end="${nowYear}">
+                                                            <option value="${i}" <c:if test="${i eq y}">selected="selected"</c:if>>${i} 년</option>
+                                                        </c:forEach>
+                                                    </select>
+                                                </td>
+                                                <th>발송(월)</th>
+                                                <td>
+                                                    <select class="custom-select  form-control" name="m">
+                                                        <c:forEach var="j" begin="1" end="12">
+                                                            <option value="${j}" <c:if test="${j eq m}">selected="selected"</c:if>>${j} 월</option>
+                                                        </c:forEach>
+                                                    </select>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <th>발송(일)</th>
+                                                <td>
+                                                    <div class="row">
+                                                        <div class="col-5">
+                                                            <div class="form-group mb-xl-0">
+                                                                <input class="form-control date-no-req" type="text" name="startDate" value="${startDate}" onKeyup="inputYMDNumber(this);" autocomplete="off"  placeholder="검색 시작일자">
+                                                            </div>
+                                                        </div>
+                                                        <div
+                                                            class="col-2 text-center">
+                                                            ~</div>
+                                                        <div class="col-5">
+                                                            <div class="form-group mb-xl-0">
+                                                                <input class="form-control date-no-req" type="text" name="endDate" value="${endDate}" onKeyup="inputYMDNumber(this);" autocomplete="off" placeholder="검색 종료일자">
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                                
+                                                <th>수신 대상</th>
+                                                <td>
+                                                    <select class="custom-select  form-control" name="targetType">
+                                                        <option value="">대상자 선택</option>
+                                                        <option value="A" <c:if test="${targetType eq 'A'}">selected="selected"</c:if>>전체 환자</option>
+                                                        <option value="N" <c:if test="${targetType eq 'N'}">selected="selected"</c:if>>건강정보 미입력자</option>
+                                                        <option value="M" <c:if test="${targetType eq 'M'}">selected="selected"</c:if>>본인 관리 환자</option>
+                                                        <option value="P" <c:if test="${targetType eq 'P'}">selected="selected"</c:if>>환자 개별선택</option>
+                                                    </select>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <th>발송 구분</th>
+                                                <td>
+                                                    <select class="custom-select  form-control" name="sendType">
+                                                        <option value="">전체</option>
+                                                        <option value="D" <c:if test="${sendType eq 'D'}">selected="selected"</c:if>>즉시</option>
+                                                        <option value="R" <c:if test="${sendType eq 'R'}">selected="selected"</c:if>>예약</option>
+                                                        <option value="E" <c:if test="${sendType eq 'E'}">selected="selected"</c:if>>매일</option>
+                                                    </select>
+                                                </td>
+                                                
+                                                <th>검색어 조건</th>
+                                                <td>
+                                                    <div class="form-row">
+                                                        <div class="col-4">
+                                                            <select class="custom-select  form-control" id="" name="searchType">
+                                                                <option value="title" <c:if test="${searchType eq 'title'}">selected="selected"</c:if>>푸시 제목</option>
+                                                                <option value="name" <c:if test="${searchType eq 'name'}">selected="selected"</c:if>>발송자 이름</option>
+                                                                <option value="id" <c:if test="${searchType eq 'id'}">selected="selected"</c:if>>발송자 아이디</option>
+                                                            </select>
+                                                        </div>
+                                                        <div class="col-6">
+                                                            <input type="text" class="form-control" name="q" placeholder="" value="<c:out value='${q}'/>" autocomplete="off">
+                                                        </div>
+                                                        <div class="col-2">
+                                                            <button class="btn btn-primary">검색</button>
+                                                        </div>
+                                                    </div>
+                                                    
+                                                </td>
+                                            </tr>
+                                        </table>
+                                    </div>
+                                </form>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="col-12">
+                            <div class="card">
+                                <div class="card-body">
+                                    <div class="row mb-3">
+                                        <div class="col-6">전체 :<fmt:formatNumber value="${total}" pattern="#,###" />
+                                        </div>
+                                        <div class="col-6 text-right">
+<%--                                             <c:if test="${data._SES_GROUP_IDX ne 1}"> --%>
+<%--                                                 <c:if test="${sesGroupIdx eq '1' or sesGroupIdx eq '2'}"> --%>
+                                                    <button class="btn btn-secondary" onclick="location.href='./schedule/list';">예약 푸시 현황</button>
+                                                    <button class="btn btn-primary" onclick="location.href='./send';">신규 푸시 발송</button>
+<%--                                                 </c:if> --%>
+<%--                                             </c:if> --%>
+                                        </div>
+                                    </div>
+                                    <div class="table-responsive">
+                                        <table class="table table-striped text-center" id="pushLogTable">
+                                            <colgroup>
+                                                <col style=" width: 6%; ">
+                                                <col style=" width: 8%; ">
+                                                <col style=" width: 17%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 9%; ">
+                                                <col style=" width: 5%; ">
+                                                <col style=" width: 5%; ">
+                                                <col style=" width: 5%; ">
+                                                <col style=" width: 5%; ">
+                                                <col style=" width: 10%; ">
+                                            </colgroup>
+                                            <thead>
+                                                <tr>
+                                                    <th>번호</th>
+                                                    <th>발송 구분</th>
+                                                    <th>푸시제목</th>
+                                                    <th>수신 대상</th>
+                                                    <th>최초등록일</th>
+                                                    <th>발송시간</th>
+                                                    <th>발송상태</th>
+                                                    <th>Total</th>
+                                                    <th>성공</th>
+                                                    <th>실패</th>
+                                                    <th>대기</th>
+                                                    <th>발송등록자</th>
+                                                </tr>
+                                            </thead>
+                                            <tbody>
+                                                <c:if test="${total eq 0}">
+                                                    <tr>
+                                                        <td colspan="12">발송 이력이 없습니다</td>
+                                                    </tr>
+                                                </c:if>
+                                                <c:forEach var="pl" items="${pushList}" varStatus="lStatus">
+                                                    <tr>
+                                                        <td>
+                                                            <c:set var="pageNum" value="${total - lStatus.index}" />
+                                                            <fmt:formatNumber value="${pageNum}" pattern="#,###" />
+                                                        </td>
+                                                        <td class="td-send-type">
+                                                            <c:if test="${pl.sendType eq 'D'}">즉시</c:if>
+                                                            <c:if test="${pl.sendType eq 'R'}">예약</c:if>
+                                                            <c:if test="${pl.sendType eq 'E'}">매일</c:if>
+                                                        </td>
+                                                        <td class="td-push-title">
+                                                            <a href="javascript:;" data-toggle="modal" data-target="#defaultModalPrimary_1" onclick="pushDetail( this );"><c:out value="${pl.pushTitle}"/></a>
+                                                            <input type="hidden" class="push-idx" value="${pl.pushIdx}" />
+                                                            <input type="hidden" class="center-name" value="${pl.centerName}" />
+                                                            <input type="hidden" class="sender" value="${pl.sender}" />
+                                                            <input type="hidden" class="name" value="${pl.name}" />
+                                                            <input type="hidden" class="push-content" value="${pl.pushContent}" />
+                                                        </td>
+                                                        <td class="td-target-type">
+                                                            <c:if test="${pl.targetType eq 'A'}">전체 환자</c:if>
+                                                            <c:if test="${pl.targetType eq 'N'}">건강정보 미입력자</c:if>
+                                                            <c:if test="${pl.targetType eq 'M'}">본인 관리 환자</c:if>
+                                                            <c:if test="${pl.targetType eq 'P'}">환자 개별선택</c:if>
+                                                        </td>
+                                                        <td class="td-create-date"><c:out value="${pl.createDate}"/></td>
+                                                        
+                                                        <c:if test="${pl.sendState eq 'W'}">
+                                                            <td colspan="6">
+                                                            <div class="send-loading align-bottom spinner-border text-primary" role="status">
+                                                                <span class="sr-only">Loading...</span>
+                                                            </div>
+                                                            </td>
+                                                        </c:if>
+                                                        <c:if test="${pl.sendState ne 'W'}">
+                                                        <td class="td-start-date"><c:out value="${pl.startDate}"/></td>
+                                                        <td>
+                                                            <c:if test="${pl.sendState eq 'W'}">발송 대기</c:if>
+                                                            <c:if test="${pl.sendState eq 'C'}">발송 완료</c:if>
+                                                            <c:if test="${pl.sendState eq 'I'}">발송중</c:if>
+                                                        </td>
+                                                        <td class="text-right">
+                                                            <fmt:formatNumber value="${pl.totalCount}" pattern="#,###" />
+                                                        </td>
+                                                        <td class="text-right text-success">
+                                                            <span class="text-link push-result" accesskey="success"  data-toggle="modal" data-target="#sendResultList">
+                                                                <fmt:formatNumber value="${pl.successCount}" pattern="#,###" />
+                                                            </span>
+                                                        </td>
+                                                        <td class="text-right text-danger">
+                                                            <span class="text-link push-result" accesskey="fail" data-toggle="modal" data-target="#sendResultList">
+                                                                <fmt:formatNumber value="${pl.failCount}" pattern="#,###" />
+                                                            </span>
+                                                        </td>
+                                                        <td class="text-right text-warning">
+                                                            <span class="text-link push-result" accesskey="wait" data-toggle="modal" data-target="#sendResultList">
+                                                                <fmt:formatNumber value="${pl.waitCount}" pattern="#,###" />
+                                                            </span>
+                                                        </td>
+                                                        </c:if>
+                                                        <td><c:out value="${pl.name}"/><br/>(<c:out value="${pl.sender}"/>)</td>
+                                                    </tr>
+                                                </c:forEach>
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                    <div class="row mt-5">
+                                        <div class="col-12 col-lg-6 mb-2">
+<!--                                             <select class="custom-select form-control col-md-2" id="inputState" name="inputState"> -->
+<!--                                                 <option value="success" selected="">전체</option> -->
+<!--                                                 <option value="info">입소</option> -->
+<!--                                                 <option value="warning">퇴소</option> -->
+<!--                                             </select> -->
+                                        </div>
+                                        <div class="col-12 col-lg-6 mb-2">
+                                            <jsp:include page="${data._INCLUDE}/paging.jsp" flush="true">
+                                                <jsp:param name="firstPageNo" value="${paging.firstPageNo}" />
+                                                <jsp:param name="prevPageNo"  value="${paging.prevPageNo}" />
+                                                <jsp:param name="startPageNo" value="${paging.startPageNo}" />
+                                                <jsp:param name="pageNo"      value="${paging.pageNo}" />
+                                                <jsp:param name="endPageNo"   value="${paging.endPageNo}" />
+                                                <jsp:param name="nextPageNo"  value="${paging.nextPageNo}" />
+                                                <jsp:param name="finalPageNo" value="${paging.finalPageNo}" />
+                                                <jsp:param name="preFix"      value="${paging.preFix}" />
+                                                <jsp:param name="url"         value="${paging.url}" />
+                                                <jsp:param name="total"       value="${paging.totalCount}" />
+                                            </jsp:include>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <!-- 의료진 관리 END -->
+                </div>
+            </main>
+
+            <jsp:include page="${data._INCLUDE}/footer.jsp"></jsp:include>
+        </div>
+    </div>
+</body>
+</html>

+ 318 - 0
src/main/webapp/WEB-INF/jsp/push/schedule.jsp

@@ -0,0 +1,318 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+    pageEncoding="UTF-8"%>
+<jsp:include page="${data._INCLUDE}/header.jsp"></jsp:include>
+</head>
+<body>
+    <div class="modal fade" id="defaultModalPrimary_1" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">발송 정보</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span> </button>
+                </div>
+                <div class="modal-body m-4">
+                    <table class="table mobile-table">
+                        <colgroup>
+                            <col style="width: 30%">
+                            <col style="width: 70%">
+                        </colgroup>
+                        <tr>
+                            <th>생활치료센터명</th>
+                            <td>
+                                <div class="form-group mb-xl-0">레몬생활치료센터</div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th>발송등록자</th>
+                            <td>
+                                <div class="form-group mb-xl-0">이은숙수간호사 (admin0005)</div>
+                            </td>
+                        </tr>
+                        
+                        
+                        <tr>
+                            <th>발송 대상</th>
+                            <td>그룹 선택 / 본인 관리 환자</td>
+                        </tr>
+                        
+                        <tr>
+                            <th>발송 방식</th>
+                            <td>예약 발송</td>
+                        </tr>
+                        
+                        <tr>
+                            <th>발송일</th>
+                            <td>2020-12-14 18:20</td>
+                        </tr>
+                        
+                        <tr>
+                            <th>푸시 제목</th>
+                            <td>
+                                <div class="form-group mb-xl-0">
+                                    <input class="form-control" type="text" value="건강 정보를 입력해주세요." readonly>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th>푸시 내용</th>
+                            <td>
+                                <div class="form-group mb-xl-0">
+                                    <textarea  class="form-control" rows="10" cols="" readonly>건강 정보를 입력해주세요. 건강 정보를 입력해주세요. 건강 정보를 입력해주세요.건강 정보를 입력해주세요. </textarea>
+                                </div>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" data-dismiss="modal">확인</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+    
+    <div class="modal fade" id="sendResultList" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">발송 통계</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span> </button>
+                </div>
+                <div class="modal-body m-4">
+                    <table class="table mobile-table table-striped text-center">
+                        <colgroup>
+                            <col style="width: 5%">
+                            <col style="width: 14%">
+                            <col style="width: 14%">
+                            <col style="width: 10%">
+                            <col style="width: 16%">
+                            <col style="width: 16%">
+                            <col style="width: 10%">
+                            <col style="width: 19%">
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <th>번호</th>
+                                <th>환자명</th>
+                                <th>호실</th>
+                                <th>성별</th>
+                                <th>생년월일</th>
+                                <th>발송 완료시간</th>
+                                <th>발송 결과</th>
+                                <th>비고</th>
+                            </tr>
+                        </thead>
+                        <tbody id="patientList">
+                            <!-- 환자리스트 동적 생성 -->
+                        </tbody>
+                    </table>
+                    <table class="d-none" id="hide-table">
+                        <tr id="loading-tr">
+                            <td colspan="8">
+                                <div class="spinner-border text-primary" role="status">
+                                    <span class="sr-only">Loading...</span>
+                                </div>
+                            </td>
+                        </tr>
+                        
+                        <tr id="patient-tr">
+                            <td class="no"></td>
+                            <td class="name"></td>
+                            <td class="ward"></td>
+                            <td class="gender"></td>
+                            <td class="jumin"></td>
+                            <td class="updateDate"></td>
+                            <td class="sendResult"></td>
+                            <td class="failMsg"></td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-primary" data-dismiss="modal">확인</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+    <div class="wrapper">
+        <jsp:include page="${data._INCLUDE}/sidebar.jsp"></jsp:include>
+
+        <div class="main">
+            <jsp:include page="${data._INCLUDE}/top.jsp"></jsp:include>
+
+            <main class="content">
+                <div class="container-fluid p-0">
+                    <div class="row">
+                        <div class="col-12 col-lg-6">
+                            <h1 class="h3 mb-3">예약 푸시 현황</h1>
+                        </div>
+                        <div class="col-12 col-lg-6  text-right">
+                            <nav aria-label="breadcrumb">
+                                <ol class="breadcrumb">
+                                    <li class="breadcrumb-item"><a href="javscript:;">Home</a></li>
+                                    <li class="breadcrumb-item">푸시서비스 관리</li>
+                                    <li class="breadcrumb-item active">예약 푸시 현황</li>
+                                </ol>
+                            </nav>
+                        </div>
+                    </div>
+                    
+                    <div class="row">
+                        <div class="col-12">
+                            <div class="card">
+                                <div class="card-body">
+                                    <div class="row mb-3">
+                                        <div class="col-6">전체 :
+<%--                                             <fmt:formatNumber value="${total}" pattern="#,###" /> --%>
+                                        </div>
+                                        <div class="col-6 text-right">
+<%--                                             <c:if test="${data._SES_GROUP_IDX ne 1}"> --%>
+<%--                                                 <c:if test="${sesGroupIdx eq '1' or sesGroupIdx eq '2'}"> --%>
+                                                    <button class="btn btn-info" onclick="location.href='./list';">푸시 발송 현황</button>
+                                                    <button class="btn btn-primary" onclick="location.href='../send';">신규 푸시 발송</button>
+<%--                                                 </c:if> --%>
+<%--                                             </c:if> --%>
+                                        </div>
+                                    </div>
+                                    <div class="table-responsive">
+                                        <table class="table table-striped text-center">
+                                            <colgroup>
+                                                <col style=" width: 6%; ">
+                                                <col style=" width: 8%; ">
+                                                <col style=" width: 17%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                                <col style=" width: 10%; ">
+                                            </colgroup>
+                                            <thead>
+                                                <tr>
+                                                    <th>번호</th>
+                                                    <th>발송 구분</th>
+                                                    <th>푸시 제목</th>
+                                                    <th>수신 대상</th>
+                                                    <th>최초등록일자</th>
+                                                    <th>발송예정일</th>
+                                                    <th>발송예정시간</th>
+                                                    <th>발송등록자</th>
+                                                </tr>
+                                            </thead>
+                                            <tbody>
+                                                <tr>
+                                                    <td>1</td>
+                                                    <td>매일</td>
+                                                    <td>
+                                                        <a href="./edit">건강 정보를 업데이트 해주세요</a>
+                                                    </td>
+                                                    <td>건강정보 미입력자</td>
+                                                    <td>2020-12-10 15:30</td>
+                                                    <td>매일</td>
+                                                    <td>15:33</td>
+                                                    <td>이은숙수간호사<br/>(admin0005)</td>
+                                                </tr>
+                                                <c:forEach var="i" begin="2" end="10">
+                                                    <tr>
+                                                        <td>${i}</td>
+                                                        <td>즉시</td>
+                                                        <td>
+                                                            <a href="./edit">건강 정보를 업데이트 해주세요</a>
+                                                        </td>
+                                                        <td>건강정보 미입력자</td>
+                                                        <td>2020-12-10 15:30</td>
+                                                        <td>2020-12-10</td>
+                                                        <td>17:12</td>
+                                                        <td>이은숙수간호사<br/>(admin0005)</td>
+                                                    </tr>
+                                                </c:forEach>
+<%--                                                 <c:choose> --%>
+<%--                                                     <c:when test="${total > 0}"> --%>
+<%--                                                         <c:forEach var="l" items="${item}"> --%>
+<!--                                                             <tr> -->
+<%--                                                                 <td><c:out value="${l.num}" /></td> --%>
+<!--                                                                 <td> -->
+<%--                                                                     <a href="./info?staffId=${l.id}"><c:out value="${l.id}" /></a> --%>
+<!--                                                                 </td> -->
+<%--                                                                 <td><c:out value="${l.name}" /></td> --%>
+<%--                                                                 <td><c:out value="${l.centerName}" /></td> --%>
+<!--                                                                 <td> -->
+<%--                                                                     <c:if test="${l.groupIdx eq 1}">시스템관리자</c:if> --%>
+<%--                                                                     <c:if test="${l.groupIdx eq 2}">관리자</c:if> --%>
+<%--                                                                     <c:if test="${l.groupIdx eq 3}">일반사용자</c:if> --%>
+<!--                                                                 </td> -->
+<%--                                                                 <td><c:out value="${l.lastLoginTime}" /></td> --%>
+<%--                                                                 <td><c:out value="${l.createDate}" /></td> --%>
+<!--                                                                 <td> -->
+<%--                                                                     <c:choose> --%>
+<%--                                                                         <c:when test="${l.useYn == 'Y'}"> --%>
+<!--                                                                             <span class="text-success">활성</span> -->
+<%--                                                                         </c:when> --%>
+<%--                                                                         <c:otherwise> --%>
+<!--                                                                             <span class="text-danger">비활성</span> -->
+<%--                                                                         </c:otherwise> --%>
+                                                                    
+<%--                                                                     </c:choose> --%>
+<!--                                                                 </td> -->
+<!--                                                             </tr> -->
+<%--                                                         </c:forEach> --%>
+<%--                                                     </c:when> --%>
+<%--                                                     <c:otherwise> --%>
+<!--                                                         <tr> -->
+<!--                                                             <td colspan="11">등록된 푸시가 없습니다.</td> -->
+<!--                                                         </tr> -->
+<%--                                                     </c:otherwise> --%>
+<%--                                                 </c:choose> --%>
+                                                <!-- <tr>
+                                                    <td>9</td>
+                                                    <td>
+                                                        <a href="javscript:;">김레몬</a>
+                                                    </td>
+                                                    <td>남</td>
+                                                    <td>55</td>
+                                                    <td>1501</td>
+                                                    <td>2020-10-13 15:23</td>
+                                                    <td>2020-10-15 10:15</td>
+                                                    <td>-</td>
+                                                </tr> -->
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                    <div class="row mt-5">
+                                        <div class="col-12 col-lg-6 mb-2">
+<!--                                             <select class="custom-select form-control col-md-2" id="inputState" name="inputState"> -->
+<!--                                                 <option value="success" selected="">전체</option> -->
+<!--                                                 <option value="info">입소</option> -->
+<!--                                                 <option value="warning">퇴소</option> -->
+<!--                                             </select> -->
+                                        </div>
+                                        <div class="col-12 col-lg-6 mb-2">
+<%--                                             <jsp:include page="${data._INCLUDE}/paging.jsp" flush="true"> --%>
+<%--                                                 <jsp:param name="firstPageNo" value="${paging.firstPageNo}" /> --%>
+<%--                                                 <jsp:param name="prevPageNo"  value="${paging.prevPageNo}" /> --%>
+<%--                                                 <jsp:param name="startPageNo" value="${paging.startPageNo}" /> --%>
+<%--                                                 <jsp:param name="pageNo"      value="${paging.pageNo}" /> --%>
+<%--                                                 <jsp:param name="endPageNo"   value="${paging.endPageNo}" /> --%>
+<%--                                                 <jsp:param name="nextPageNo"  value="${paging.nextPageNo}" /> --%>
+<%--                                                 <jsp:param name="finalPageNo" value="${paging.finalPageNo}" /> --%>
+<%--                                                 <jsp:param name="preFix"      value="${paging.preFix}" /> --%>
+<%--                                                 <jsp:param name="url"         value="${paging.url}" /> --%>
+<%--                                                 <jsp:param name="total"       value="${paging.totalCount}" /> --%>
+<%--                                             </jsp:include> --%>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <!-- 의료진 관리 END -->
+                </div>
+            </main>
+
+            <jsp:include page="${data._INCLUDE}/footer.jsp"></jsp:include>
+        </div>
+    </div>
+</body>
+</html>

+ 445 - 0
src/main/webapp/WEB-INF/jsp/push/send.jsp

@@ -0,0 +1,445 @@
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+    pageEncoding="UTF-8"%>
+<jsp:include page="${data._INCLUDE}/header.jsp"></jsp:include>
+<script type="text/javascript" src="/resources/js/common/jquery.tablesorter.min.js"></script>
+<script>
+$( function(){
+    setEventInit();
+    setTargetSelect();
+    setSendSelect();
+    
+    $( "#sendForm" ).validate({
+        rules: {
+            sendDate : {
+                date : true
+            }
+        },
+        messages : {
+            targetPreview : {
+                required : "대상자를 1명 이상 선택해주세요"
+            }
+        },
+        onfocusout: function (element) {
+            $( element ).val( $.trim( $( element ).val() ) );
+            $( element ).valid();
+        },
+        submitHandler: function(form) {
+            console.log( $( form ).serialize() );
+            
+            alertBox({ 
+                txt: "발송하시겠습니까?",
+                type : "confirm",
+                callBack : function( result ){
+                    if( result ) {
+                        form.submit();
+                    }
+                }
+            })
+        }
+    });
+    
+    $( "input.date" ).daterangepicker({
+        singleDatePicker : true,
+//         timePicker: true,
+        showDropdowns : true,
+        locale : {
+            format : "YYYY-MM-DD"
+        },
+//         maxDate : ,
+        minDate : new Date()
+    }).on('apply.daterangepicker', function(ev, picker) {
+        
+        // 예약발송 -> 오늘날짜면 minDate 재설정
+        $( "input.date-time-r" ).daterangepicker({
+            singleDatePicker : true,
+            showDropdowns : true,
+            timePicker: true,
+            locale: {
+                format: 'HH:mm'
+            },
+            minDate : new Date()
+        }).on('show.daterangepicker', function(ev, picker) {
+            picker.container.find(".calendar-table").hide();
+        });
+        
+        if( new Date().format( "yyyy-MM-dd" ) < $( this ).val() ) {
+            $( "input.date-time-r" ).data( 'daterangepicker' ).minDate = "";
+        }
+    });
+    
+    // 예약 발송
+    $( "input.date-time-r" ).daterangepicker({
+        singleDatePicker : true,
+        showDropdowns : true,
+        timePicker: true,
+        locale: {
+            format: 'HH:mm'
+        },
+        minDate : new Date()
+    }).on('show.daterangepicker', function(ev, picker) {
+        picker.container.find(".calendar-table").hide();
+    });
+    
+    
+    // 매일 반복 발송
+    $( "input.date-time-e" ).daterangepicker({
+        singleDatePicker : true,
+        showDropdowns : true,
+        timePicker: true,
+        locale: {
+            format: 'HH:mm'
+        }
+    }).on('show.daterangepicker', function(ev, picker) {
+        picker.container.find(".calendar-table").hide();
+    });
+    
+    
+    // checkbox 전체선택, 해제 이벤트 처리
+    $( "#allCheck" ).click( function(){
+        $( "#patientList" ).find( "input[name='patientIdx']" ).prop( "checked", $( this ).prop( "checked" ) );
+    });
+    $( "#patientList" ).find( "input[name='patientIdx']" ).click( function(){
+        if( $( "#patientList" ).find( "input[name='patientIdx']:not(:checked)" ).length > 0 ){
+            $( "#allCheck" ).prop( "checked", false );
+        } else {
+            $( "#allCheck" ).prop( "checked", true );
+        }
+    });
+});
+
+function setEventInit(){
+    // 그룹선택 or 환자개별선택
+    $( "input.target-select" ).click( function(){
+        setTargetSelect();
+    });
+    
+    // 발송방식 선택
+    $( "input.send-select" ).click( function(){
+        setSendSelect();
+    });
+    
+    // 그룹선택이나 환자개별선택시 초기화 해야할부분 처리
+    $( "input[name='selectType']" ).on( "click", function(){
+        var selectType = $( this ).val();
+        
+        if( selectType == "GROUP" ) {
+            // 그룹 선택
+            $('#radio-target-a').click();
+            
+            initPatientSelect();
+        } else if( selectType == "PATIENT" ) {
+            // 환자 개별 선택
+            $('#radio-target-p').click();
+        }
+    });
+    
+    // 환자 개별 선택 팝업창 열때 현재 선택된 환자목록 기준으로 팝업창에서 선택한 환자 동기화
+    $( "#selectPatientList" ).on( "show.bs.modal", function(){
+        var $this               = $( this );
+        var listPushTargetTemp  = $( "#pushTargetTemp" );
+        var savedPushTargetTemp = listPushTargetTemp.find( "input[type='checkbox']:checked" ).serializeArray();
+        
+        $( "#patientList" ).find( "input[type='checkbox']:checked" ).prop( "checked", false );
+        
+        savedPushTargetTemp.forEach( function( item, index, arr2 ){
+//             console.log(item,index,arr2[index+1]);
+            $this.find( "input[type='checkbox']:input[value='"+ item.value +"']" ).prop( "checked", true );
+        });
+    });
+    
+    $( "#sortTable" ).tablesorter();
+}
+
+function setTargetSelect(){
+    $( "tr.target-select" ).hide();
+    $( "#" + $( "input.target-select:checked" ).prop( "accessKey" ) ).fadeIn( 100 );
+};
+
+function setSendSelect(){
+    $( "tr.send-select" ).hide().find( "input" ).prop( "disabled", true );
+    $( "#" + $( "input.send-select:checked" ).prop( "accessKey" ) ).fadeIn( 100 ).find( "input" ).prop( "disabled", false );
+}
+
+function initPatientSelect(){
+	$( "#allCheck" ).prop( "checked", false );
+    $( "#pushTargetTemp" ).empty();
+    $( "#target-preview" ).val( "" );
+    $( "#pushTargetTempTotal" ).text( "" ).hide();
+}
+
+function saveSelectedPatientList(){
+    alertBox({
+        txt : "선택된 환자를 발송 대상자로 적용할까요?",
+        type : "confirm", 
+        callBack : function( result ){
+            
+            console.log( result );
+            if( result ) {
+                $( "#selectPatientList" ).modal( "hide" );
+                $( "#pushTargetTemp" ).empty();
+                
+                var appendData = $( "#patientList" ).find( "input[type='checkbox']:checked" ).clone();
+                var name = appendData.map( function(){
+                    return $( this ).attr( "title" );
+                }).get().join( ", " );
+                
+                console.log( name );
+                
+                appendData.appendTo( "#pushTargetTemp" );
+                
+                if( appendData.length > 0 ){
+                    $( "#pushTargetTempTotal" ).text( appendData.length ).show();
+                } else {
+                    $( "#pushTargetTempTotal" ).text( "" ).hide();
+                }
+                 
+                $( "#target-preview" ).val( name ).valid();
+            } else {
+                console.log( "취소 -> no event" );
+            };
+        }
+    });
+    
+    $( "#selectPatientList" ).find( ".modal-title h3" ).remove();
+}
+</script>
+</head>
+<body>
+    <div class="modal fade" id="selectPatientList" tabindex="-1" role="dialog" aria-hidden="true">
+        <div class="modal-dialog modal-xl modal-dialog-scrollable" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">환자 선택 (레몬생활치료센터)</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span> </button>
+                </div>
+                <div class="modal-body m-4">
+                    <table class="table mobile-table table-striped text-center" id="sortTable">
+                        <colgroup>
+                            <col style="width: 5%">
+                            <col style="width: 10%">
+                            <col style="width: 15%">
+                            <col style="width: 25%">
+                            <col style="width: 10%">
+                            <col style="width: 10%">
+                            <col style="width: 25%">
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <th>
+                                    <input type="checkbox" id="allCheck" style="width:20px;height:20px;" >
+                                </th>
+                                <th>번호 <i class="fa fa-fw fa-sort"></i></th>
+                                <th>환자명 <i class="fa fa-fw fa-sort"></i></th>
+                                <th>호실 <i class="fa fa-fw fa-sort"></i></th>
+                                <th>성별 <i class="fa fa-fw fa-sort"></i></th>
+                                <th>나이 <i class="fa fa-fw fa-sort"></i></th>
+                                <th>입소일자 <i class="fa fa-fw fa-sort"></i></th>
+                            </tr>
+                        </thead>
+                        <tbody id="patientList">
+                            <c:choose>
+                                <c:when test="${total eq 0}">
+                                    <tr>
+                                        <td colspan="6">no data</td>
+                                    </tr>
+                                </c:when>
+                                <c:otherwise>
+                                    <c:forEach var="pl" items="${patientList}" varStatus="lStatus">
+                                        <tr>
+                                            <td class="no">
+                                                <input type="checkbox" name="patientIdx" title="[<c:if test="${pl.wardNumber ne '' and pl.wardNumber ne null}">${pl.wardNumber}동</c:if> ${pl.roomNumber}호:${pl.patientName}]" value="${pl.patientIdx}" style="width:20px;height:20px;" >
+                                            </td>
+                                            <td class="no">
+                                                <c:set var="pageNum" value="${total - lStatus.index}" />
+                                                <fmt:formatNumber value="${pageNum}" pattern="#,###" />
+                                            </td>
+                                            <td class="name"><c:out value="${pl.patientName}" /></td>
+                                            <td class="ward"><c:if test="${pl.wardNumber ne '' and pl.wardNumber ne null}">${pl.wardNumber}동</c:if> ${pl.roomNumber}호</td>
+                                            <td class="gender"><c:out value="${pl.gender}" /></td>
+                                            <td class="age"><c:out value="${pl.age}" /></td>
+                                            <td class="hospitalization-date"><c:out value="${pl.hospitalizationDate}" /></td>
+                                        </tr>
+                                    </c:forEach>
+                                </c:otherwise>
+                            </c:choose>
+                        </tbody>
+                    </table>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn border" data-dismiss="modal">취소</button>
+                    <button type="button" class="btn btn-primary" onclick="saveSelectedPatientList();">선택 완료</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+    
+    <div class="wrapper">
+        <jsp:include page="${data._INCLUDE}/sidebar.jsp"></jsp:include>
+        
+        <div class="main">
+            <jsp:include page="${data._INCLUDE}/top.jsp"></jsp:include>
+
+            <main class="content">
+                <div class="container-fluid p-0">
+                    <div class="row">
+                        <div class="col-12 col-lg-6">
+                            <h1 class="h3 mb-3">푸시 발송</h1>
+                        </div>
+                        <div class="col-12 col-lg-6  text-right">
+                            <nav aria-label="breadcrumb">
+                                <ol class="breadcrumb">
+                                    <li class="breadcrumb-item"><a href="javscript:;">Home</a></li>
+                                    <li class="breadcrumb-item">푸시서비스 관리</li>
+                                    <li class="breadcrumb-item active">푸시 발송</li>
+                                </ol>
+                            </nav>
+                        </div>
+                    </div>
+                    <div class="row">
+                        <div class="col-12">
+                            <div class="card">
+                                <form id="sendForm" action="./send/insert" method="post">
+                                    <div class="card-body">
+                                        <table class="table mobile-table">
+                                            <colgroup>
+                                                <col style="width:20%">
+                                                <col style="width:80%">
+                                            </colgroup>
+                                            <tr>
+                                                <th rowspan="2"><span class="fix">*</span>발송 대상</th>
+                                                <td>
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input target-select" accesskey="target-group" type="radio" name="selectType" value="GROUP" checked="checked">
+                                                        <span class="form-check-label">그룹 선택</span>
+                                                    </label>
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input target-select" accesskey="target-patient" type="radio" name="selectType" value="PATIENT">
+                                                        <span class="form-check-label">환자 개별 선택</span>
+                                                    </label>
+                                                </td>
+                                            </tr>
+                                            <tr class="target-select" id="target-group" style="display:none;">
+                                                <td>
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input" type="radio" id="radio-target-a" name="targetType" value="A" checked="checked" required>
+                                                        <span class="form-check-label">전체 환자</span>
+                                                    </label>
+                                                    
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input" type="radio" id="radio-target-n" name="targetType" value="N" required>
+                                                        <span class="form-check-label">건강정보 미입력 환자</span>
+                                                    </label>
+                                                    
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input" type="radio" id="radio-target-m" name="targetType" value="M" required>
+                                                        <span class="form-check-label">본인 관리 환자</span>
+                                                    </label>
+                                                </td>
+                                            </tr>
+                                            <tr class="target-select" id="target-patient" style="display:none;">
+                                                <td>
+                                                    <div class="form-row">
+                                                        <div class="col-7">
+                                                            <input class="form-check-input d-none" id="radio-target-p" type="radio" name="targetType" value="P">
+                                                            <input type="text" value="" class="form-control" name="targetPreview" id="target-preview" readonly required>
+                                                            
+                                                            <!-- 발송대상 checkboc동적 추가 hidden 영역 -->
+                                                            <div class="d-none" id="pushTargetTemp"></div>
+                                                        </div>
+                                                        <div class="col-4">
+                                                            <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#selectPatientList">환자 불러오기 <span id="pushTargetTempTotal" class="badge badge-danger" style="display:none;">2</span></button>
+                                                        </div>
+                                                    </div>
+                                                </td>
+                                            </tr>
+                                            
+                                            <tr>
+                                                <th rowspan="2"><span class="fix">*</span>발송 방식</th>
+                                                <td>
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input send-select" accesskey="send-d" type="radio" name="sendType" value="D" checked="checked" required>
+                                                        <span class="form-check-label">즉시 발송</span>
+                                                    </label>
+                                                    
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input send-select" accesskey="send-r" type="radio" name="sendType" value="R" required>
+                                                        <span class="form-check-label">예약 발송</span>
+                                                    </label>
+                                                    
+                                                    <label class="form-check form-check-inline">
+                                                        <input class="form-check-input send-select" accesskey="send-e" type="radio" name="sendType" value="E" required>
+                                                        <span class="form-check-label">매일 반복</span>
+                                                    </label>
+                                                </td>
+                                            </tr>
+                                            
+                                            <tr class="send-select" id="send-d">
+                                                <td><span>즉시 발송됩니다</span></td>
+                                            </tr>
+                                            
+                                            <tr class="send-select" id="send-r">
+                                                <td>
+                                                    <span class="mr-2">예약</span>
+                                                    <div class="form-check-inline calendar-bar mb-xl-0" style="width:120px;">
+                                                        <input type="text" class="form-control date" value="" name="sendDate" autocomplete="off" placeholder="예약 일자" required>
+                                                        <i class="align-middle mr-2 fas fa-fw fa-calendar-alt"></i>
+                                                    </div>
+                                                    <div class="form-check-inline calendar-bar mb-xl-0" style="width:80px;">
+                                                        <input type="text" class="form-control date-time-r" value="" name="sendTime" autocomplete="off" placeholder="매일 반복 시간" required>
+                                                        <i class="align-middle mr-2 fas fa-fw fa-clock"></i>
+                                                    </div>
+                                                    <span>에 발송됩니다</span>
+                                                </td>
+                                            </tr>
+                                            
+                                            <tr class="send-select" id="send-e">
+                                                <td>
+                                                    <span class="mr-2">매일</span>
+                                                    <div class="form-check-inline calendar-bar mb-xl-0" style="width:80px;">
+                                                        <input type="text" class="form-control date-time-e" value="" name="sendTime" autocomplete="off" placeholder="매일 반복 시간" required>
+                                                        <i class="align-middle mr-2 fas fa-fw fa-clock"></i>
+                                                    </div>
+                                                    <span>선택 시간에 반복 발송됩니다</span>
+                                                </td>
+                                            </tr>
+                                            
+                                            <tr>
+                                                <th><span class="fix">*</span>푸시(알림) 제목</th>
+                                                <td>
+                                                    <input type="text" name="pushTitle" class="form-control" placeholder="제목을 입력하세요" maxlength="80" required>
+                                                </td>
+                                            </tr>
+                                            
+                                            <tr>
+                                                <th><span class="fix">*</span>푸시(알림) 내용</th>
+                                                <td>
+                                                    <textarea  class="form-control" rows="10" cols="" name="pushContent" placeholder="내용을 입력하세요" maxlength="1000" required></textarea>
+                                                </td>
+                                            </tr>
+                                        </table>
+                                        
+                                        <div class="row mt-3">
+                                            <div class="col-12">
+                                                <div class="text-right">
+                                                    <button type="button" class="btn btn-outline-primary w100" onclick="history.back();">취소</button>
+                                                    <button type="submit" class="btn btn-primary w100">등록</button>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </form>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </main>
+
+            <jsp:include page="${data._INCLUDE}/footer.jsp"></jsp:include>
+        </div>
+    </div>
+</body>
+</html>

File diff ditekan karena terlalu besar
+ 2 - 0
src/main/webapp/resources/js/common/jquery.tablesorter.min.js