junekeunsong 4 년 전
부모
커밋
6fb2daa811

+ 7 - 0
log.config.path_IS_UNDEFINED/log.config.filename_IS_UNDEFINED.log

@@ -0,0 +1,7 @@
+DEBUG 2020-12-09 20:12:59[RMI TCP Connection(273)-127.0.0.1] [AnnotationConfigServletWebServerApplicationContext:1056] - Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@716a7124, started on Wed Dec 09 15:48:44 KST 2020
+DEBUG 2020-12-09 20:12:59[RMI TCP Connection(273)-127.0.0.1] [DefaultLifecycleProcessor:365] - Stopping beans in phase 2147483647
+DEBUG 2020-12-09 20:12:59[RMI TCP Connection(273)-127.0.0.1] [DefaultLifecycleProcessor:238] - Bean 'webServerGracefulShutdown' completed its stop procedure
+DEBUG 2020-12-09 20:12:59[RMI TCP Connection(273)-127.0.0.1] [DefaultLifecycleProcessor:365] - Stopping beans in phase 2147483646
+DEBUG 2020-12-09 20:13:00[RMI TCP Connection(273)-127.0.0.1] [DefaultLifecycleProcessor:238] - Bean 'webServerStartStop' completed its stop procedure
+DEBUG 2020-12-09 20:13:00[RMI TCP Connection(273)-127.0.0.1] [AnnotationMBeanExporter:452] - Unregistering JMX-exposed beans on shutdown
+DEBUG 2020-12-09 20:13:00[RMI TCP Connection(273)-127.0.0.1] [AnnotationMBeanExporter:186] - Unregistering JMX-exposed beans

+ 0 - 0
log.config.path_IS_UNDEFINED/push_err_log.log


+ 0 - 0
log.config.path_IS_UNDEFINED/push_mybatis.log


+ 13 - 0
pom.xml

@@ -87,6 +87,19 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jdbc</artifactId>
         </dependency>
+        
+        <!-- FCM -->
+        <dependency>
+            <groupId>com.google.firebase</groupId>
+            <artifactId>firebase-admin</artifactId>
+            <version>7.0.1</version>
+        </dependency>
+        <!-- APNS -->
+        <dependency>
+            <groupId>com.notnoop.apns</groupId>
+            <artifactId>apns</artifactId>
+            <version>0.2.3</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 26 - 3
src/main/java/com/lemon/lifecenter/scheduler/common/HomeController.java

@@ -1,9 +1,10 @@
 package com.lemon.lifecenter.scheduler.common;
 
+import java.io.IOException;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
@@ -15,13 +16,35 @@ public class HomeController {
     
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
     
+    @Autowired
+    private PushUtils pushUtils;
+    
     @Autowired
     private TestService service;
     
     @RequestMapping(value = "/test", method = RequestMethod.GET)
     public String index() {
-        String result = service.testSelect();
-        logger.error("result -- > " + result);
+//        String result = service.testSelect();
+//        logger.error("result -- > " + result);
+        //sendFcm(String deviceKey, String title, String content)
+        String title = "오늘의";
+        String content = " 점심은??";
+        
+        pushUtils.sendFcm("dC7nrWbLRquoaOoWdMAq2x:APA91bFqjD4LIHgoJU6s0ptR0MDqHqw8WxdjxRLsL8lpo5Do-RV8_NZ_MYXDM3xD0ex_Z8zaijvDKXhGvFPIUaZ4Dv8ilZqxcJmC5WXTlYNgetvFihQoQ-w25c4VbheYCXjJG5SXFCY4",
+                title, content);
+        
+        pushUtils.sendFcm("d_N0ErUTTk2QiRCur8-AcA:APA91bGLm2v9F2e4Sl-NIXXtPt6MzXze9NrdV6OltzyBo-DUGI_r9DGicQM6-yQ05T8M4KVN4XqpIkNx6nvbWZ7MKYNeGzs-9r44mjBW2nqApyIVRcFMTHSUhD58xSaumejuiW6K2Diq",
+                title, content);
+        
+        pushUtils.sendFcm("fGKpg4m8SUSiaclDxhDUlS:APA91bFDbbSVtyA3mFFEE7q7DTBYMJkv9E_qBxeCM0lztuE1VyO4RbA1dSc8NC9QLbvV2TGf79eDFpZy2kh-wCkjlYYssIiX0DtIRkqvNgbtfJdosuF0Y8tjlsJua4r4-cAY_xldXsrU",
+                title, content);
+        
+        pushUtils.sendFcm("fA-RwuMFQJO3E76CuyTqyS:APA91bFaPwatHGIPiRs9MWVPGXb7szXZHUrjPWuRafXjOPLO1BSR5CZgwNuFcXb1x7fFbTnjaF_o7FSPka3ucJ3RJJN0Ujr_rQ2PBMFuGuNtpoy6oWLxltty4nw4_x1zJ-cYi7WsV3t8",
+                title, content);
+        
+        pushUtils.sendFcm("cuLTEbJThUvWlaBtvOHqfJ:APA91bEuOtQN6mnw9fqcS5wA6VLSEDulyg54nczI5KNN_0BaQWO-Fn5O92lxycfSdPrw_9wC2mGcH2nkIsWhayv3RXDnXQDAGN5r3uKJ-wrVtM2HC7j7OHGbiuTexwADBAo4ehHJHkZh",
+                title, content);
+        
         return "helloWorld";
     }
     

+ 135 - 0
src/main/java/com/lemon/lifecenter/scheduler/common/PushUtils.java

@@ -0,0 +1,135 @@
+package com.lemon.lifecenter.scheduler.common;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Component;
+
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.messaging.AndroidConfig;
+import com.google.firebase.messaging.AndroidNotification;
+import com.google.firebase.messaging.ApnsConfig;
+import com.google.firebase.messaging.Aps;
+import com.google.firebase.messaging.ApsAlert;
+import com.google.firebase.messaging.AndroidNotification.Priority;
+import com.google.firebase.messaging.FirebaseMessaging;
+import com.google.firebase.messaging.FirebaseMessagingException;
+import com.google.firebase.messaging.Message;
+import com.notnoop.apns.APNS;
+import com.notnoop.apns.ApnsService;
+
+@Component
+public class PushUtils {
+    
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    
+    public void sendFcm(String deviceKey, String title, String content) {
+        
+        try {
+            ClassPathResource cpr = new ClassPathResource("firebase/firebase_service_key.json");
+            FirebaseOptions options = new FirebaseOptions.Builder()
+                    .setCredentials(GoogleCredentials.fromStream(cpr.getInputStream()))
+                    .setDatabaseUrl("https://fcmtest-ddc32.firebaseio.com/")
+                    .build();
+            
+            if (FirebaseApp.getApps().isEmpty()) {
+                FirebaseApp.initializeApp(options);
+            }
+            
+            String registrationToken = deviceKey;
+            
+            AndroidNotification notification = AndroidNotification.builder().setTitle(title).setBody(content).setPriority(Priority.MAX).build();
+            AndroidConfig androidConfig = AndroidConfig.builder().setPriority(AndroidConfig.Priority.HIGH).setNotification(notification).setTtl(7200).build();
+            
+            ApsAlert apsAlert = ApsAlert.builder().setTitle(title).setBody(content).build();
+            Aps aps = Aps.builder().setSound("default").setAlert(apsAlert).build();
+            ApnsConfig apnsConfig = ApnsConfig.builder().setAps(aps).build();
+//            Message message = Message.builder().setApnsConfig(apnsConfig).setToken(registrationToken).build();
+            
+            Message message = Message.builder()
+                    .setAndroidConfig(androidConfig)
+                    .setApnsConfig(apnsConfig)
+                    .setToken(registrationToken).build();
+ 
+            String response = FirebaseMessaging.getInstance().send(message);
+            logger.error("response -- > " + response);
+            
+        } catch (FileNotFoundException e) {
+            logger.error("fileNotFound -- > " + e);
+            errorMsg("FileNotFound");
+        } catch (IOException e) {
+            logger.error("IOException -- > " + e);
+            errorMsg("IOException");
+        } catch (FirebaseMessagingException e) {
+            logger.error("FirebaseMessagingException -- > " + e);
+            errorMsg(e.getErrorCode().toString().trim());
+        }
+    }
+    
+    private void errorMsg(String e) {
+        String msg = "";
+        if (fcmErrorCode().get(e) != null) {
+            msg = fcmErrorCode().get(e);
+        } else {
+            msg = fcmErrorCode().get("FATAL");
+        }
+    }
+    
+    private HashMap<String, String> fcmErrorCode() {
+         HashMap<String, String> code = new HashMap<String, String>();
+         code.put("UNSPECIFIED_ERROR", "이 오류에 대한 추가 정보가 없습니다.");
+         code.put("INVALID_ARGUMENT", "(HTTP 오류 코드 = 400) 요청 매개 변수가 유효하지 않습니다. google.rpc.BadRequest 유형의 확장자가 리턴되어 유효하지 않은 필드를 지정합니다.");
+         code.put("UNREGISTERED", " (HTTP 오류 코드 = 404) FCM에서 앱 인스턴스가 등록 해제되었습니다. 이것은 일반적으로 사용 된 토큰이 더 이상 유효하지 않으며 새로운 토큰을 사용해야 함을 의미합니다.");
+         code.put("SENDER_ID_MISMATCH", "(HTTP 오류 코드 = 403) 인증 된 발신자 ID가 등록 토큰의 발신자 ID와 다릅니다.");
+         code.put("QUOTA_EXCEEDED", "(HTTP 오류 코드 = 429) 메시지 대상에 대한 전송 제한이 초과되었습니다. 초과 한 할당량을 지정하기 위해 google.rpc.QuotaFailure 유형의 확장이 반환됩니다..");
+         code.put("UNAVAILABLE", "  (HTTP 오류 코드 = 503) 서버가 과부하되었습니다.");
+         code.put("INTERNAL", "(HTTP 오류 코드 = 500) 알 수없는 내부 오류가 발생했습니다.");
+         code.put("THIRD_PARTY_AUTH_ERROR", "(HTTP 오류 코드 = 401) APN 인증서 또는 웹 푸시 인증 키가 잘못되었거나 없습니다.");
+         code.put("FATAL", "알수없는 오류");
+         code.put("FileNotFound", "FileNotFound");
+         code.put("IOException", "IOException");
+         return code;
+    }
+    
+    public void apnsSend() {
+        ClassPathResource cpr = new ClassPathResource("apns/dev/certify.p12");
+        logger.error("name -- > " + cpr.getFilename());
+        logger.error("path -- > " + cpr.getPath());
+        try {
+            logger.error("file -- > " + cpr.getFile().getAbsolutePath());
+            
+            ApnsService service =
+                    APNS.newService()
+                            .withCert(cpr.getFile().getAbsolutePath(), "jksong092") // 指定p12文件及密钥
+                            .withSandboxDestination() // 使用苹果推送测试服务器
+                            //.withProductionDestination() // 使用苹果推送生产服务器
+                            .build();
+
+            String payload = APNS.newPayload()
+//                    .alertTitle("推送标题") // 标题
+                    .alertBody("푸시발송 테스트") // 内容
+                    .customField("sourceCode", "01") // 自定义字段 
+                    .customField("url", "https://www.qq.com") // 自定义字段
+                    .sound("default") // 提示声音
+                    //.sound("msgsound.caf") // 提示声音(自定义)
+                    .badge(1) // 应用角标
+                    .build();
+
+            String token = "1EDAE24F474C7CB5497AD92BB92C506033218BF37E7C808C8CD3B28154D41B07";
+            
+            service.push(token, payload);
+            
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+
+    }
+
+}

+ 28 - 0
src/main/java/com/lemon/lifecenter/scheduler/common/WebConfigurations.java

@@ -0,0 +1,28 @@
+package com.lemon.lifecenter.scheduler.common;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfigurations implements WebMvcConfigurer {
+//    @Autowired
+//    LifeCenterInterCeptor lifeCenterInterCeptor;
+    
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
+    }
+    
+//    /*
+//     * Interceptor 설정
+//     * */
+//    @Override
+//    public void addInterceptors(InterceptorRegistry registry) {
+//        registry.addInterceptor(lifeCenterInterCeptor)
+//            .addPathPatterns( "/**/*" )
+//            .excludePathPatterns( "/resources/css/**", "/resources/download/**", "/resources/js/**", "/resources/images/**", "/resources/fonts/**" );
+//    }
+}

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

@@ -0,0 +1,81 @@
+package com.lemon.lifecenter.scheduler.controller;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.lemon.lifecenter.scheduler.dto.DeviceInfoDTO;
+import com.lemon.lifecenter.scheduler.service.PushService;
+
+@RestController
+public class PushController {
+    
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+    @Autowired
+    private PushService service;
+    
+    /**
+     * 디바이스 정보 저장
+     * @param dto
+     * patientIdx : 환자등록 고유번호
+     * deviceType : 안드로이드 : AND, 아이폰 : IOS
+     * deviceToken : 푸시 수신을 위한 토큰
+     * macAddress : 디바이스 맥 어드레스
+     */
+    @RequestMapping(value="/insertDeviceInfo", method=RequestMethod.POST)
+    public Map<String, String> setDeviceInfo(@RequestBody DeviceInfoDTO dto) {
+        HashMap<String, String> result = new HashMap<>();
+        String code = "99";
+        
+        int patientIdx = dto.getPatientIdx();
+        String deviceType = dto.getDeviceType();
+        String deviceToken = dto.getDeviceToken();
+        String macAddress = dto.getMacAddress();
+        
+        if (patientIdx == 0 || deviceType.equals("") || deviceToken.equals("") || macAddress.equals("")) {
+            code = "01";
+            result.put("code", code);
+            result.put("message", "Lack of parameters");
+            return result;
+        }
+        
+        int cnt = service.selectDeviceInfoCount(dto);
+        int rCnt = 0;
+        if (cnt == 0) {
+            service.insertDeviceInfo(dto);
+            rCnt = dto.getQueryCount();
+        } else {
+            rCnt = service.updatedeviceInfo(dto);
+        }
+        
+        if (rCnt == 0) {
+            code = "02";
+            result.put("code", code);
+            result.put("message", "Token storage failure");
+        } else {
+            code = "00";
+            result.put("code", code);
+            result.put("message", "success");
+        }
+        
+        logger.error("patientIdx -- > " + dto.getPatientIdx());
+        logger.error("deviceType -- > " + dto.getDeviceType());
+        logger.error("deviceToken -- > " + dto.getDeviceToken());
+        logger.error("macAddress -- > " + dto.getMacAddress());
+        
+        return result;
+    }
+    
+    @RequestMapping(value = "/selectSendPushList", method = RequestMethod.POST)
+    public void selectSendPushList() {
+        
+    }
+
+}

+ 41 - 0
src/main/java/com/lemon/lifecenter/scheduler/dto/DeviceInfoDTO.java

@@ -0,0 +1,41 @@
+package com.lemon.lifecenter.scheduler.dto;
+
+public class DeviceInfoDTO {
+    private int patientIdx     = 0;
+    private String deviceType  = "";
+    private String deviceToken = "";
+    private String macAddress  = "";
+    private int queryCount     = 0;
+    
+    public int getPatientIdx() {
+        return patientIdx;
+    }
+    public void setPatientIdx(int patientIdx) {
+        this.patientIdx = patientIdx;
+    }
+    public String getDeviceType() {
+        return deviceType;
+    }
+    public void setDeviceType(String deviceType) {
+        this.deviceType = deviceType;
+    }
+    public String getDeviceToken() {
+        return deviceToken;
+    }
+    public void setDeviceToken(String deviceToken) {
+        this.deviceToken = deviceToken;
+    }
+    public String getMacAddress() {
+        return macAddress;
+    }
+    public void setMacAddress(String macAddress) {
+        this.macAddress = macAddress;
+    }
+    public int getQueryCount() {
+        return queryCount;
+    }
+    
+    public void setQueryCount(int queryCount) {
+        this.queryCount = queryCount;
+    }
+}

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

@@ -0,0 +1,14 @@
+package com.lemon.lifecenter.scheduler.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+import com.lemon.lifecenter.scheduler.dto.DeviceInfoDTO;
+
+@Repository
+@Mapper
+public interface PushMapper {
+    public int selectDeviceInfoCount(DeviceInfoDTO dto);
+    public void insertDeviceInfo(DeviceInfoDTO dto);
+    public int updatedeviceInfo(DeviceInfoDTO dto);
+}

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

@@ -0,0 +1,25 @@
+package com.lemon.lifecenter.scheduler.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.lemon.lifecenter.scheduler.dto.DeviceInfoDTO;
+import com.lemon.lifecenter.scheduler.mapper.PushMapper;
+
+@Service
+public class PushService {
+    @Autowired
+    private PushMapper mapper;
+    
+    public int selectDeviceInfoCount(DeviceInfoDTO dto) {
+        return mapper.selectDeviceInfoCount(dto);
+    }
+    
+    public void insertDeviceInfo(DeviceInfoDTO dto) {
+        mapper.insertDeviceInfo(dto);
+    }
+    
+    public int updatedeviceInfo(DeviceInfoDTO dto) {
+        return mapper.updatedeviceInfo(dto);
+    }
+}

BIN
src/main/resources/apns/dev/certify.p12


+ 3 - 3
src/main/resources/application.properties

@@ -1,12 +1,12 @@
 spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
-#spring.datasource.url=jdbc:log4jdbc:cubrid:61.97.184.187:30000:LIFE_CENTER:::?charset=UTF-8
-spring.datasource.url=jdbc:log4jdbc:cubrid:localhost:30000:LIFE_CENTER:::?charset=UTF-8
+spring.datasource.url=jdbc:log4jdbc:cubrid:61.97.184.187:30000:LIFE_CENTER:::?charset=UTF-8
+#spring.datasource.url=jdbc:log4jdbc:cubrid:localhost:30000:LIFE_CENTER:::?charset=UTF-8
 
 spring.datasource.username=dba
 spring.datasource.password=#zo240s!
 spring.datasource.sql-script-encoding=UTF-8
 
-#mybatis.type-aliases-package=com.lemon.lifecenter.dto
+mybatis.type-aliases-package=com.lemon.lifecenter.scheduler.dto
 mybatis.mapper-locations=mybatis/mapper/**/*.xml
 
 #logging.level.com.lemon.lifecenter.mapper=DEBUG

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 12 - 0
src/main/resources/firebase/firebase_service_key.json


+ 1 - 1
src/main/resources/logback-local.properties

@@ -1,3 +1,3 @@
 #config
-log.config.path=./logs/local
+log.config.path=./logs/push
 log.config.filename=scheduler_log

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

@@ -15,9 +15,9 @@
     <!-- log file name -->
     <property name="LOG_FILE_NAME" value="${log.config.filename}" />
     <!-- err log file name -->
-    <property name="ERR_LOG_FILE_NAME" value="err_log" />
+    <property name="ERR_LOG_FILE_NAME" value="push_err_log" />
     <!-- jdb sql log file name -->
-    <property name="SQL_LOG_FILE_NAME" value="mybatis" />
+    <property name="SQL_LOG_FILE_NAME" value="push_mybatis" />
     <!-- pattern -->
     <!-- property name="LOG_PATTERN" value="%-5level %d{yy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n" / -->
     <property name="LOG_PATTERN" value="%-5level %d{yyyy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n" />

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

@@ -0,0 +1,36 @@
+<?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.scheduler.mapper.PushMapper">
+
+    <select id="selectDeviceInfoCount" parameterType="DeviceInfoDTO" resultType="int">
+        <![CDATA[
+            SELECT COUNT(*) AS CNT
+              FROM PUSH_DEVICE_INFO
+             WHERE 1 = 1
+               AND PATIENT_IDX = #{patientIdx}
+        ]]>
+    </select>
+    <insert id="insertDeviceInfo" parameterType="DeviceInfoDTO">
+        <selectKey keyProperty="queryCount" resultType="int" order="AFTER">
+            <![CDATA[
+                SELECT COUNT(*) queryCount FROM PUSH_DEVICE_INFO WHERE PATIENT_IDX = #{patientIdx}
+            ]]>
+        </selectKey>
+        <![CDATA[
+            INSERT INTO PUSH_DEVICE_INFO
+                        (PATIENT_IDX,   DEVICE_TYPE,   DEVICE_KEY,     MAC_ADDRESS,   CREATE_DATE)
+                 VALUES (#{patientIdx}, #{deviceType}, #{deviceToken}, #{macAddress}, NOW())
+        ]]>
+    </insert>
+    <update id="updatedeviceInfo" parameterType="DeviceInfoDTO">
+        <![CDATA[
+            UPDATE PUSH_DEVICE_INFO
+               SET UPDATE_DATE  = NOW(),
+                   DEVICE_TYPE  = #{deviceType},
+                   DEVICE_KEY   = #{deviceToken},
+                   MAC_ADDRESS  = #{macAddress}
+             WHERE PATIENT_IDX  = #{patientIdx}
+        ]]>
+    </update>
+</mapper>