문자 agent 테스트 mms 파일 전송 진행중

This commit is contained in:
hylee 2024-08-19 15:47:55 +09:00
parent 2954e6273f
commit 2d42bb1348
22 changed files with 781 additions and 656 deletions

View File

@ -160,6 +160,11 @@
<version>2.3.1</version>
</dependency>
<!-- Spring WebFlux 의존성 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>

View File

@ -21,6 +21,7 @@ public abstract class AbstractAgentService<T, M> implements AgentService<T> {
List<T> agentVOL = new ArrayList<>();
int sendCnt = parseSendCount(agentVO);
for (int i = 0; i < sendCnt; i++) {
T paramVO = createCopy(agentVO, i);
agentVOL.add(paramVO);

View File

@ -51,6 +51,12 @@ public class AgentCOneServiceImpl extends AbstractAgentService<AgentCOneVO, Agen
@Override
protected AgentCOneVO createCopy(AgentCOneVO originalVO, int index) {
if (!originalVO.getMessage().startsWith("ITN")) {
return originalVO;
}
AgentCOneVO paramVO = new AgentCOneVO();
String msgType = originalVO.getMsgType();

View File

@ -52,6 +52,11 @@ public class AgentCTwoServiceImpl extends AbstractAgentService<AgentCTwoVO, Agen
@Override
protected AgentCTwoVO createCopy(AgentCTwoVO originalVO, int index) {
if (!originalVO.getMessage().startsWith("ITN")) {
return originalVO;
}
AgentCTwoVO paramVO = new AgentCTwoVO();
String msgType = originalVO.getMsgType();

View File

@ -4,14 +4,29 @@ import com.itn.admin.agent.client.two.mapper.domain.AgentCTwoVO;
import com.itn.admin.agent.client.two.service.AgentCTwoService;
import com.itn.admin.cmn.msg.RestResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class AgentCTwoRestController {
private AgentCTwoService agentCTwoService;
private final String UPLOAD_DIR = "/home/mjon_client_agent_1/mmsfile";
@Autowired
@ -53,4 +68,54 @@ public class AgentCTwoRestController {
public ResponseEntity<RestResponse> findByLogMoveCnt(@RequestBody AgentCTwoVO agentCTwoVO) throws Exception {
return ResponseEntity.ok().body(agentCTwoService.findAllLogMoveCnt(agentCTwoVO));
}
@PostMapping("/agent/two/uploadFiles")
public ResponseEntity<RestResponse> uploadFiles(@RequestParam("filename01") MultipartFile file1,
@RequestParam("filename02") MultipartFile file2,
@RequestParam("filename03") MultipartFile file3) {
try {
Map<String, String> fileNames = new HashMap<>();
// 파일을 업로드하고 파일명을 수집
String fileName = "";
if (!file1.isEmpty()) {
fileName = uploadSingleFile(file1);
fileNames.put("fileName01", fileName);
}
if (!file2.isEmpty()) {
fileName = uploadSingleFile(file2);
fileNames.put("fileName02", fileName);
}
if (!file3.isEmpty()) {
fileName = uploadSingleFile(file3);
fileNames.put("fileName03", fileName);
}
// 경로와 파일명 반환
Map<String, Object> response = new HashMap<>();
response.put("fileNames", fileNames);
response.put("status", "OK");
return ResponseEntity.ok(new RestResponse(HttpStatus.OK, "",response) );
} catch (IOException ex) {
return ResponseEntity.ok(new RestResponse(HttpStatus.INTERNAL_SERVER_ERROR, "저장실패","") );
}
}
private String uploadSingleFile(MultipartFile file) throws IOException {
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
String uploadDir = "user-files/";
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Path filePath = uploadPath.resolve(fileName);
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
return fileName;
}
}

View File

@ -4,6 +4,7 @@ import com.itn.admin.agent.server.mapper.AgentSMapper;
import com.itn.admin.agent.server.mapper.domain.AgentSVO;
import com.itn.admin.agent.server.service.AgentSService;
import com.itn.admin.cmn.msg.RestResponse;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
@ -24,7 +25,9 @@ public class AgentSServiceImpl implements AgentSService {
// int cnt = agentSMapper.countByCurStateAndUserId(agentSVO);
String cntTxt = agentSMapper.findByCurStateAndUserIdAndSmsTxt(agentSVO);
if(StringUtils.isNotEmpty(cntTxt) ){
cntTxt = cntTxt.replace(agentSVO.getMessage(),"").trim();
}
return new RestResponse(HttpStatus.OK,"", cntTxt);
}

View File

@ -0,0 +1,14 @@
package com.itn.admin.cmn.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@ -19,7 +19,12 @@ import javax.sql.DataSource;
@Configuration
@MapperScan(value = "com.itn.admin.itn.*.mapper", sqlSessionFactoryRef = "factory")
@MapperScan(basePackages = {
"com.itn.admin.itn.dict.mapper",
"com.itn.admin.itn.mjon.spam.mapper",
"com.itn.admin.itn.user.mapper"
}
, sqlSessionFactoryRef = "factory")
class MainDatabaseConfig {
private final String MAIN_DATA_SOURCE = "MainDatabase";
@ -38,27 +43,11 @@ class MainDatabaseConfig {
@Primary
@Bean(name="factory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
/*
* MyBatis JdbcTemplate 대신 Connection 객체를 통한 질의를 위해서 SqlSession 사용한다.
* 내부적으로 SqlSessionTemplate SqlSession 구현한다.
* Thread-Safe 하고 여러 개의 Mapper 에서 공유할 있다.
*/
// SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
// bean.setDataSource(dataSource);
// MyBatis Mapper Source
// MyBatis SqlSession 에서 불러올 쿼리 정보
// Resource[] res = new PathMatchingResourcePatternResolver().getResources("classpath:mapper/itn/dictionary/*Mapper.xml");
// bean.setMapperLocations(res);
// MyBatis Config Setting
// MyBatis 설정 파일
// bean.setConfigLocation(myBatisConfig);
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setTypeAliasesPackage("com.itn.admin.itn.*");
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/itn/*/*Mapper.xml"));
sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/itn/**/*Mapper.xml"));
sqlSessionFactory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
return sqlSessionFactory.getObject();

View File

@ -3,7 +3,6 @@ package com.itn.admin.itn.dict.mapper;
import com.itn.admin.itn.dict.mapper.domain.DictionaryVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;

View File

@ -6,9 +6,7 @@ import com.itn.admin.itn.dict.mapper.domain.DictionaryVO;
import com.itn.admin.itn.dict.service.DictionaryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;
import java.util.List;

View File

@ -0,0 +1,19 @@
package com.itn.admin.itn.mjon.spam.mapper;
import com.itn.admin.itn.dict.mapper.domain.DictionaryVO;
import com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface SpamMapper {
List<SpamVO> getList(SpamVO spamVO);
List<SpamVO> getListWhereKeywordsIsNull(SpamVO spamVO);
void update(SpamVO spamVO);
}

View File

@ -0,0 +1,43 @@
package com.itn.admin.itn.mjon.spam.mapper.domain;
import lombok.*;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* packageName : com.itn.admin.itn.mjon.spam.mapper.domain
* fileName : SpamVO
* author : hylee
* date : 2024-08-14
* description : 문자온 스팸관련
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2024-08-14 hylee 최초 생성
*
*/
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
@Setter
@ToString
public class SpamVO implements Serializable {
private static final long serialVersionUID = 1L;
private int spamId; // 스팸 ID
private String msgGroupId; // 문자그룹ID
private String smsTxt; // 문자내용
private String spamRsn; // 스팸사유
private String keywords; // 문자에 포함된 단어
private String chcKeywords; // 스팸에 해당하는 단어
private LocalDateTime timestamp; // 요청 시간
private String categ; // 스팸종류
private String frstRegisterId; // 최초등록자ID
private LocalDateTime frstRegistPnttm; // 최초등록일자
private String lastUpdusrId; // 최종수정자ID
private LocalDateTime lastUpdtPnttm; // 최종수정일자
}

View File

@ -0,0 +1,14 @@
package com.itn.admin.itn.mjon.spam.service;
import com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO;
import java.util.Map;
public interface SpamService {
Map<String, Object> getList(SpamVO spamVO);
void analyze();
}

View File

@ -0,0 +1,114 @@
package com.itn.admin.itn.mjon.spam.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itn.admin.itn.mjon.spam.mapper.SpamMapper;
import com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO;
import com.itn.admin.itn.mjon.spam.service.SpamService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class SpamServiceImpl implements SpamService {
@Autowired
SpamMapper spamMapper;
@Autowired
private RestTemplate restTemplate;
final static private String PYTHON_ANALYZE_URL= "http://192.168.0.78:5000/word_analyze";
public SpamServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public Map<String, Object> getList(SpamVO spamVO) {
List<SpamVO> spamList = spamMapper.getList(spamVO);
Map<String, Object> map = new HashMap<>();
map.put("spamList", spamList);
return map;
}
@Override
public void analyze() {
// List<SpamVO> spamList = spamMapper.getList(new SpamVO());
// for (SpamVO spamVO : spamList) {
// API 호출 데이터 수신
List<SpamVO> spamList = spamMapper.getListWhereKeywordsIsNull(new SpamVO());
for (SpamVO spamVO : spamList) {
String text = spamVO.getSmsTxt();
try {
// 요청 데이터 준비
Map<String, String> requestBody = new HashMap<>();
requestBody.put("text", text);
// ObjectMapper를 사용하여 JSON 문자열 생성
ObjectMapper objectMapper = new ObjectMapper();
String jsonData = objectMapper.writeValueAsString(requestBody);
// HTTP 헤더 설정
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
// 요청 본문 설정
HttpEntity<String> requestEntity = new HttpEntity<>(jsonData, headers);
// API 호출 응답 처리
ResponseEntity<String> responseEntity = restTemplate.exchange(
PYTHON_ANALYZE_URL,
HttpMethod.POST,
requestEntity,
String.class
);
String responseBody = responseEntity.getBody();
// JSON 응답을 Map으로 변환
Map<String, Object> responseMap = objectMapper.readValue(responseBody, Map.class);
// words 리스트 추출
List<String> wordsList = (List<String>) responseMap.get("words");
// 가나다순으로 정렬
Collections.sort(wordsList);
// List를 , 구분된 단일 String으로 변환
String wordsString = String.join(", ", wordsList);
// 정렬된 결과 출력
System.out.println("Sorted Words: " + wordsString);
spamVO.setKeywords(wordsString);
spamMapper.update(spamVO);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,47 @@
package com.itn.admin.itn.mjon.spam.web;
import com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO;
import com.itn.admin.itn.mjon.spam.service.SpamService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.lang.module.ModuleDescriptor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class RestSpamController {
private SpamService spamService;
@Autowired
public void setSpamService(SpamService spamService) {
this.spamService = spamService;
}
@GetMapping(value = "/mjon/spam/analyze")
public void analyze(Model model) {
spamService.analyze();
}
}

View File

@ -0,0 +1,40 @@
package com.itn.admin.itn.mjon.spam.web;
import com.itn.admin.itn.dict.service.DictionaryService;
import com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO;
import com.itn.admin.itn.mjon.spam.service.SpamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.Map;
@Controller
public class SpamController {
private SpamService spamService;
@Autowired
public void setSpamService(SpamService spamService) {
this.spamService = spamService;
}
@GetMapping(value = "/mjon/spam/select")
public String select(@ModelAttribute("spamVO") SpamVO spamVO, Model model) {
//
//
// Map<String, Object> resultMap = spamService.getList(spamVO);
// model.addAttribute("spamList", resultMap.get("spamList"));
//
// model.addAttribute("list", resultMap.get("resultList"));
return "mjon/spam/select";
}
}

View File

@ -0,0 +1,72 @@
<?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.itn.admin.itn.mjon.spam.mapper.SpamMapper">
<select id="getList" parameterType="spamVO" resultType="spamVO">
SELECT
SPAM_ID,
MSG_GROUP_ID,
SMS_TXT,
SPAM_RSN,
KEYWORDS,
CHC_KEYWORDS,
TIMESTAMP,
CATEG,
FRST_REGISTER_ID,
FRST_REGIST_PNTTM,
LAST_UPDUSR_ID,
LAST_UPDT_PNTTM
FROM
spam_keywords
<!-- <if test="params.koreanWord != null and params.koreanWord != ''">
AND a.KOREAN_WORD LIKE CONCAT('%', #{params.koreanWord}, '%')
</if>
<if test="params.englishWord != null and params.englishWord != ''">
AND a.ENGLISH_WORD LIKE CONCAT('%', #{params.englishWord}, '%')
</if>
<if test="params.abbreviation != null and params.abbreviation != ''">
AND a.ABBREVIATION LIKE CONCAT('%', #{params.abbreviation}, '%')
</if>
<if test="params.isConfirmed != null and params.isConfirmed != ''">
AND a.IS_CONFIRMED = #{params.isConfirmed}
</if>
<if test="params.lastUpdusrName != null and params.lastUpdusrName != ''">
AND b.USER_NAME LIKE CONCAT('%', #{params.lastUpdusrName}, '%')
</if>
<if test="params.lastUpdtPnttm != null and params.lastUpdtPnttm != ''">
AND a.LAST_UPDT_PNTTM LIKE CONCAT('%', #{params.lastUpdtPnttm}, '%')
</if>-->
</select>
<select id="getListWhereKeywordsIsNull" parameterType="spamVO" resultType="spamVO">
SELECT
SPAM_ID,
MSG_GROUP_ID,
SMS_TXT,
SPAM_RSN,
KEYWORDS,
CHC_KEYWORDS,
TIMESTAMP,
CATEG,
FRST_REGISTER_ID,
FRST_REGIST_PNTTM,
LAST_UPDUSR_ID,
LAST_UPDT_PNTTM
FROM
spam_keywords
WHERE
KEYWORDS IS NULL
</select>
<update id="update">
UPDATE spam_keywords
SET keywords = #{keywords}
WHERE spam_id = #{spamId}
</update>
</mapper>

View File

@ -16,6 +16,7 @@
<typeAliases>
<typeAlias type="com.itn.admin.commute.mapper.domain.CommuteVO" alias="commuteVO"/>
<typeAlias type="com.itn.admin.itn.dict.mapper.domain.DictionaryVO" alias="dictionaryVO"/>
<typeAlias type="com.itn.admin.itn.mjon.spam.mapper.domain.SpamVO" alias="spamVO"/>
<typeAlias type="com.itn.admin.itn.user.mapper.domain.UserVO" alias="userVO"/>
<typeAlias type="com.itn.admin.agent.client.one.mapper.domain.AgentCOneVO" alias="agentCOneVO"/>
<typeAlias type="com.itn.admin.agent.client.two.mapper.domain.AgentCTwoVO" alias="agentCTwoVO"/>

View File

@ -85,14 +85,22 @@ $(function () {
var msgType = $(this).val();
var tagId = getParentsId($(this));
// 제목
$(tagId+' .subject').closest('.form-group').hide();
// 파일 그룹
$(tagId+' .fileUploadGroup').hide();
$(tagId+' .fileUploadGroup input[type="file"]').val("");
if(msgType === 'L'
||msgType === 'M'
||msgType === 'A'
||msgType === 'F'
) {
$(tagId+' .subject').closest('.form-group').show();
}else{
$(tagId+' .subject').closest('.form-group').hide();
}else if(msgType === 'M'){
$(tagId+' .subject').closest('.form-group').show();
$(tagId+' .fileUploadGroup').show();
}
var $message = $(tagId + ' .message');

View File

@ -187,6 +187,18 @@
<input type="text" class="form-control subject" id="subject1" name="subject" placeholder="제목 - SUBJECT">
</div>
</div>
<div class="form-group fileUploadGroup" style="display: none;">
<label for="file2">파일첨부</label>
<div class="input-group">
<input type="file" class="form-control file1" id="file2" name="filename01">
</div>
<div class="input-group mt-2">
<input type="file" class="form-control file2" name="filename02">
</div>
<div class="input-group mt-2">
<input type="file" class="form-control file3" name="filename03">
</div>
</div>
<div class="form-group">
<label for="sendPhone1">메세지 - MESSAGE</label>
<div class="input-group">
@ -282,6 +294,18 @@
<input type="text" class="form-control subject" id="subject" name="subject" placeholder="제목 - SUBJECT">
</div>
</div>
<div class="form-group fileUploadGroup" style="display: none;">
<label for="file1">파일첨부</label>
<div class="input-group">
<input type="file" class="form-control file1" id="file1" name="filename01">
</div>
<div class="input-group mt-2">
<input type="file" class="form-control file2" name="filename02">
</div>
<div class="input-group mt-2">
<input type="file" class="form-control file3" name="filename03">
</div>
</div>
<div class="form-group">
<label for="message">메세지 - MESSAGE</label>
<div class="input-group">
@ -486,9 +510,16 @@
</div>
<!-- Reporting start 버튼 -->
<div class="row">
<div class="col-12 text-left">
<button class="btn btn-primary rprtAllStrtBtn" data-tagid="oneUserId">Reporting start</button>
<button class="btn btn-success rprtCrrntStrtBtn" data-tagid="oneUserId">
<i class="fas fa-chart-line"></i> Report Current Data
</button>
<button class="btn btn-danger rprtAllStrtBtn" data-tagid="oneUserId">
<i class="fas fa-chart-line"></i> Report All Data
</button>
</div>
</div>
</div>
</div>
@ -804,23 +835,59 @@
* client_2 msg insert
* */
$("#divTwoSms .sendBtn").on("click", function () {
console.log('#divTwoSms .sendBtn ');
// 폼 데이터를 수집
var formData = new FormData($("#divTwoSms .sendForm")[0]);
// 먼저 파일을 업로드하고 파일명만 받음
var fileUploadForm = new FormData();
fileUploadForm.append("filename01", formData.get("filename01"));
fileUploadForm.append("filename02", formData.get("filename02"));
fileUploadForm.append("filename03", formData.get("filename03"));
$.ajax({
type: "POST",
url: "/agent/two/uploadFiles",
data: fileUploadForm,
processData: false,
contentType: false,
success: function(response) {
if (response.status === 'OK') {
// 파일명만 formData에 추가
// 파일명 formData에 추가
if (response.fileNames && response.fileNames.fileName01) {
formData.append("fileName01", response.fileNames.fileName01);
} else {
formData.append("fileName01", "");
}
if (response.fileNames && response.fileNames.fileName02) {
formData.append("fileName02", response.fileNames.fileName02);
} else {
formData.append("fileName02", "");
}
if (response.fileNames && response.fileNames.fileName03) {
formData.append("fileName03", response.fileNames.fileName03);
} else {
formData.append("fileName03", "");
}
var jsonObject = {};
formData.forEach((value, key) => {
jsonObject[key] = value;
});
});
if(jsonObject['recvPhone'] === ""){
alert('정보를 입력하거나 예시입력을 클릭해주세요.')
return false;
}
console.log('two sendBtn : ', jsonObject);
console.log('jsonObject : ', jsonObject);
/*
$.ajax({
type: "POST",
url: "/agent/two/send",
@ -850,6 +917,15 @@
complete : function(xhr, textStatus) {
oneStopInsertTimer();
}
});*/
} else {
alert("파일 업로드 실패: " + response.msg);
}
},
error: function(e) {
alert("파일 업로드에 실패하였습니다.");
console.log("ERROR : " + JSON.stringify(e));
}
});
});

View File

@ -42,7 +42,7 @@
with font-awesome or any other icon font library -->
<li class="nav-item">
<a href="#" class="nav-link" >
<i class="nav-icon fas fa-tachometer-alt"></i>
<i class="nav-icon fas fa-cogs"></i>
<p>
관리
<i class="right fas fa-angle-left"></i>
@ -52,7 +52,7 @@
<li class="nav-item">
<a th:href="@{/commute/list}" class="nav-link">
<!-- <i class="far fa-circle nav-icon"></i>-->
<i class="far fa-circle nav-icon"></i>
<i class="far fa-clock nav-icon"></i>
<p>출퇴근 관리</p>
</a>
</li>
@ -60,9 +60,7 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a th:href="@{/admin/user/list}" class="nav-link">
<!-- <i class="far fa-circle nav-icon"></i>-->
<!-- <i class="nav-icon fab fa-user"></i>-->
<i class="nav-icon far fa-circle text-danger"></i>
<i class="nav-icon fas fa-user-cog"></i>
<p>사용자 관리</p>
</a>
</li>
@ -72,16 +70,11 @@
<a th:href="@{/dict/list}" class="nav-link">
<i class="nav-icon fas fa-book"></i>
<p>단어사전</p>
<!-- <i class="nav-icon fas fa-th"></i>-->
<!-- <p>-->
<!-- Widgets-->
<!-- </p>-->
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link" >
<i class="nav-icon fas fa-edit"></i>
<!-- <i class="nav-icon fas fa-tachometer-alt"></i>-->
<i class="nav-icon fas fa-vial"></i>
<p>
테스트
<i class="right fas fa-angle-left"></i>
@ -90,607 +83,29 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a th:href="@{/agent/view}" class="nav-link">
<!-- <i class="far fa-circle nav-icon"></i>-->
<i class="far fa-circle nav-icon"></i>
<i class="far fa-paper-plane nav-icon"></i>
<p>agent발송</p>
</a>
</li>
</ul>
</li>
<!--
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-copy"></i>
<a href="#" class="nav-link" >
<i class="nav-icon fas fa-sms"></i>
<p>
Layout Options
<i class="fas fa-angle-left right"></i>
<span class="badge badge-info right">6</span>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/layout/top-nav.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Top Navigation</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/top-nav-sidebar.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Top Navigation + Sidebar</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/boxed.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Boxed</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/fixed-sidebar.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Fixed Sidebar</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/fixed-sidebar-custom.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Fixed Sidebar <small>+ Custom Area</small></p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/fixed-topnav.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Fixed Navbar</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/fixed-footer.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Fixed Footer</p>
</a>
</li>
<li class="nav-item">
<a href="pages/layout/collapsed-sidebar.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Collapsed Sidebar</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-chart-pie"></i>
<p>
Charts
문자온[WEB] 관련
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/charts/chartjs.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>ChartJS</p>
</a>
</li>
<li class="nav-item">
<a href="pages/charts/flot.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Flot</p>
</a>
</li>
<li class="nav-item">
<a href="pages/charts/inline.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Inline</p>
</a>
</li>
<li class="nav-item">
<a href="pages/charts/uplot.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>uPlot</p>
<a th:href="@{/mjon/spam}" class="nav-link">
<i class="fas fa-exclamation-triangle nav-icon"></i>
<p>스팸문자</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-tree"></i>
<p>
UI Elements
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/UI/general.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>General</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/icons.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Icons</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/buttons.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Buttons</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/sliders.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Sliders</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/modals.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Modals & Alerts</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/navbar.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Navbar & Tabs</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/timeline.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Timeline</p>
</a>
</li>
<li class="nav-item">
<a href="pages/UI/ribbons.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Ribbons</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-edit"></i>
<p>
Forms
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/forms/general.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>General Elements</p>
</a>
</li>
<li class="nav-item">
<a href="pages/forms/advanced.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Advanced Elements</p>
</a>
</li>
<li class="nav-item">
<a href="pages/forms/editors.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Editors</p>
</a>
</li>
<li class="nav-item">
<a href="pages/forms/validation.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Validation</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-table"></i>
<p>
Tables
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/tables/simple.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Simple Tables</p>
</a>
</li>
<li class="nav-item">
<a href="pages/tables/data.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>DataTables</p>
</a>
</li>
<li class="nav-item">
<a href="pages/tables/jsgrid.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>jsGrid</p>
</a>
</li>
</ul>
</li>
<li class="nav-header">EXAMPLES</li>
<li class="nav-item">
<a href="pages/calendar.html" class="nav-link">
<i class="nav-icon far fa-calendar-alt"></i>
<p>
Calendar
<span class="badge badge-info right">2</span>
</p>
</a>
</li>
<li class="nav-item">
<a href="pages/gallery.html" class="nav-link">
<i class="nav-icon far fa-image"></i>
<p>
Gallery
</p>
</a>
</li>
<li class="nav-item">
<a href="pages/kanban.html" class="nav-link">
<i class="nav-icon fas fa-columns"></i>
<p>
Kanban Board
</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon far fa-envelope"></i>
<p>
Mailbox
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/mailbox/mailbox.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Inbox</p>
</a>
</li>
<li class="nav-item">
<a href="pages/mailbox/compose.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Compose</p>
</a>
</li>
<li class="nav-item">
<a href="pages/mailbox/read-mail.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Read</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-book"></i>
<p>
Pages
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/examples/invoice.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Invoice</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/profile.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Profile</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/e-commerce.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>E-commerce</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/projects.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Projects</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/project-add.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Project Add</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/project-edit.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Project Edit</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/project-detail.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Project Detail</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/contacts.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Contacts</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/faq.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>FAQ</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/contact-us.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Contact us</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon far fa-plus-square"></i>
<p>
Extras
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>
Login & Register v1
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/examples/login.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Login v1</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/register.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Register v1</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/forgot-password.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Forgot Password v1</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/recover-password.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Recover Password v1</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>
Login & Register v2
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/examples/login-v2.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Login v2</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/register-v2.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Register v2</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/forgot-password-v2.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Forgot Password v2</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/recover-password-v2.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Recover Password v2</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="pages/examples/lockscreen.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Lockscreen</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/legacy-user-menu.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Legacy User Menu</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/language-menu.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Language Menu</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/404.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Error 404</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/500.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Error 500</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/pace.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Pace</p>
</a>
</li>
<li class="nav-item">
<a href="pages/examples/blank.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Blank Page</p>
</a>
</li>
<li class="nav-item">
<a href="starter.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Starter Page</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-search"></i>
<p>
Search
<i class="fas fa-angle-left right"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="pages/search/simple.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Simple Search</p>
</a>
</li>
<li class="nav-item">
<a href="pages/search/enhanced.html" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Enhanced</p>
</a>
</li>
</ul>
</li>
<li class="nav-header">MISCELLANEOUS</li>
<li class="nav-item">
<a href="iframe.html" class="nav-link">
<i class="nav-icon fas fa-ellipsis-h"></i>
<p>Tabbed IFrame Plugin</p>
</a>
</li>
<li class="nav-item">
<a href="https://adminlte.io/docs/3.1/" class="nav-link">
<i class="nav-icon fas fa-file"></i>
<p>Documentation</p>
</a>
</li>
<li class="nav-header">MULTI LEVEL EXAMPLE</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="fas fa-circle nav-icon"></i>
<p>Level 1</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon fas fa-circle"></i>
<p>
Level 1
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Level 2</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>
Level 2
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-dot-circle nav-icon"></i>
<p>Level 3</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-dot-circle nav-icon"></i>
<p>Level 3</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-dot-circle nav-icon"></i>
<p>Level 3</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="far fa-circle nav-icon"></i>
<p>Level 2</p>
</a>
</li>
</ul>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="fas fa-circle nav-icon"></i>
<p>Level 1</p>
</a>
</li>
<li class="nav-header">LABELS</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon far fa-circle text-danger"></i>
<p class="text">Important</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon far fa-circle text-warning"></i>
<p>Warning</p>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="nav-icon far fa-circle text-info"></i>
<p>Informational</p>
</a>
</li>-->
</ul>
</nav>
<!-- /.sidebar-menu -->

View File

@ -0,0 +1,191 @@
<!DOCTYPE html>
<!-- 관련 Namespace 선언 및 layout:decorate 추가 -->
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="layout">
<head>
<!-- layout.html 에 들어간 head 부분을 제외하고 개별 파일에만 적용되는 head 부분 추가 -->
<title>스팸 단어 선택</title>
<!-- 필요하다면 개별 파일에 사용될 css/js 선언 -->
<link rel="stylesheet" th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet" th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet" th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
<style>
/* 고정된 헤더를 위한 스타일 */
.table-fixed thead {
position: sticky;
top: 0;
z-index: 1020;
background-color: #fff;
}
/*.table-fixed tbody {*/
/* display: block;*/
/* height: 400px; !* 고정된 높이 *!*/
/* overflow-y: auto;*/
/* width: 100%;*/
/*}*/
.table-fixed thead, .table-fixed tbody tr {
display: table;
width: 100%;
table-layout: fixed; /* 고정된 너비를 위해 */
}
.table-fixed tbody td, .table-fixed thead th {
text-align: center;
vertical-align: middle;
}
</style>
<script>
</script>
</head>
<body layout:fragment="body">
<div class="wrapper">
<div th:replace="~{fragments/top_nav :: topFragment}"/>
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4"
th:insert="~{fragments/mainsidebar :: sidebarFragment}">
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">스팸 단어 선택</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">스팸 단어 선택</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<!-- /.card -->
<div class="card">
<div class="card-header">
<h3 class="card-title">스팸 단어 선택</h3>
</div>
<!-- /.card-header -->
<div class="card-body p-0">
<table class="table table-hover table-fixed">
<thead>
<tr>
<th style="width: 5%;">번호</th>
<th style="width: 25%;">스팸문자</th>
<th style="width: 25%;">복합명사</th>
<th style="width: 25%;">핵심 단어</th>
<th style="width: 20%;">스팸분류</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>(광고)♥봄맞이 특별이벤트 진행중♥ ...</td>
<td>'바다', '서비스', '갯수', '이건조제한', ...</td>
<td>'바다', '서비스', '갯수', '이건조제한', 'EVENT 1' ...</td>
<td>
<select class="form-control">
<option>선택</option>
<option>성인</option>
<option>불법</option>
<option>해당없음</option>
</select>
</td>
</tr>
<tr>
<td>2</td>
<td>[국민건강보험]...</td>
<td>'바다', '서비스', '갯수'...</td>
<td>'바다', '서비스'...</td>
<td>
<select class="form-control">
<option>선택</option>
<option>성인</option>
<option>불법</option>
<option>해당없음</option>
</select>
</td>
</tr>
<!-- 추가 행을 여기에 추가 -->
</tbody>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
<!-- /Main content -->
</div>
<!-- /.content-wrapper -->
<footer class="main-footer"
th:insert="~{fragments/footer :: footerFragment}">
</footer>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- ./wrapper -->
<!-- &lt;!&ndash; DataTables & Plugins &ndash;&gt;-->
<!-- <script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>-->
<!-- <script th:src="@{/plugins/jszip/jszip.min.js}"></script>-->
<!-- <script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>-->
<!-- <script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>-->
<!-- <script th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>-->
<script>
$(function () {
});
</script>
</body>
</html>