9:27 학원 도착
이미지는 1MB를 넘어가면 DataURL로 하면 보내지긴 하는데 그렇지 않으면 쪼개서 전달해야 함.
SpringBoot서버에 axios로 이미지를 업로드해놓고 이미지에 저장된 파일명만 알아내면 이미지 출력에 문제가 없다.
그래서 Change 파일 이벤트가 일어났을때 업로드하고 저장된 파일명을 응답받아서 저장된 파일명을 웹 소켓으로 전송해서 그 저장된 파일명을 여러 클라이언트에게 전송해서 그 파일명의 이미지를 열어서 바로 볼 수 있는 형태로 할 수 있다.
스프링부트에 설정해서 파일과 이미지 컨트롤러를 만들어야 한다는 것.
import { useEffect, useRef, useState } from 'react'
import { v4 as uuid } from 'uuid';
import { useWebSocket } from './hooks/useWebSocket';
import { Button, Form, InputGroup, ListGroup } from 'react-bootstrap';
import axios from 'axios';
interface Message{
id:string;
content:string;
sender?:string; //이 메세지를 누가 보냈는지 정보도 Message 객체에 담기 위해
isImage?:boolean; //이 메세지가 이미지인지 여부
}
function App3() {
const [msgs, setMsgs] = useState<Message[]>([]);
const inputRef=useRef<HTMLInputElement>(null);
//대화방에 입장한 userName 도 상태값으로 관리하기
const [userName, setUserName]=useState<string>();
// userName 을 useRef() 를 이용해서 관리하기
const userNameRef = useRef<string|null>(null);
//대화방 참여자 목록도 상태값으로 관리
const [userList, setUserList]=useState<string[]>([]);
// useWebSocket() hook 사용해서 웹소켓 연결하기
const {sendMessage, connected} = useWebSocket("ws://192.168.0.107:9000/ws", {
onOpen:()=>{
console.log("연결됨!");
},
onMessage:(event)=>{
//응답된 json 문자열을 실제 object로 변경한다.
const received = JSON.parse(event.data);
if(received.type === "enter"){
setIsEnter(true);
setMsgs(prevState=>{
const msg = received.payload.userName+" 님이 입장했습니다.";
return [...prevState, {id:uuid(), content:msg}]
});
//사용자 목록을 update 한다.
setUserList(received.payload.userList);
}else if(received.type == "leave"){
const msg = received.payload.userName+" 님이 퇴장했습니다.";
setMsgs(prevState => [...prevState, {id:uuid(), content:msg}]);
//leave 된 userName 을 userList 에서 제거한다
setUserList(prevState => prevState.filter(item => item !== received.payload.userName))
}else if(received.type ==="public"){
setMsgs(prevState=>{
//출력할 메세지를 구성한다.
const msg=received.payload.text;
return [...prevState, {id:uuid(), content:msg, sender:received.payload.userName}];
});
}else if(received.type === "whisper"){
//여기가 실행되는 경우는 귓말을 보낸 사람과, 받는 사람이다.
const msg = received.payload.userName === userNameRef.current ?
`${received.payload.text} => [귓말] ${received.payload.toUserName}`
:
`[귓말] => ${received.payload.text}`
;
setMsgs(prevState=>[...prevState, {id:uuid(), content:msg, sender:received.payload.userName}]);
}else if(received.type === "image"){
setMsgs(prevState => [...prevState, {
id:uuid(),
content: `/upload/${received.payload.saveFileName}`,
isImage: true,
sender: received.payload.userName
}]);
}
},
onClose:()=>{
console.log("연결끊김!");
}
});
//메세지 보내는 함수
const handleSend=()=>{
//입력한 메세지 읽어와서
const msg=inputRef.current?.value;
//서버에 전송할 정보를 담고 있는 object
let obj=null;
if(selectedUser){
obj={
path:"/chat/whisper",
data:{
userName,
text:msg,
toUserName:selectedUser
}
};
}else{
obj={
path:"/chat/public",
data:{
userName,
text:msg
}
}
}
//object를 json문자열로 변환해서 전송하기
sendMessage(JSON.stringify(obj));
//입력창 초기화
inputRef.current!.value="";
}
const divStyle={
height:"500px",
backgroundColor:"#cecece",
padding:"10px",
overflowY:"auto",
scrollBehavior:"smooth"
};
const divRef=useRef<HTMLDivElement>(null);
//자동 스크롤
useEffect(()=>{
if(divRef.current){
divRef.current!.scrollTop = divRef.current!.scrollHeight;
}
}, [msgs]);
const bubbleStyleBase: React.CSSProperties = {
borderRadius: "20px",
padding: "10px 16px",
marginBottom: "8px",
maxWidth: "70%",
wordBreak: "break-word",
fontSize: "0.95rem",
lineHeight: "1.4",
};
//내가 보낸 메세지 스타일
const myBubbleStyle: React.CSSProperties = {
...bubbleStyleBase,
backgroundColor: "#DCF8C6", // 연한 연두색 (WhatsApp 스타일)
alignSelf: "flex-end",
color: "#000",
};
//다른 사람이 보낸 메세지 스타일
const otherBubbleStyle: React.CSSProperties = {
...bubbleStyleBase,
backgroundColor: "#fff",
border: "1px solid #ccc",
alignSelf: "flex-start",
color: "#000",
};
//대화방에 입장했는지 여부
const [isEnter, setIsEnter] = useState<boolean>(false);
const inputUserRef = useRef<HTMLInputElement>(null);
const handleEnter = ()=>{
const obj={
path:"/chat/enter",
data:{
userName:inputUserRef.current?.value
}
};
sendMessage(JSON.stringify(obj));
//userName 을 상태값에 넣어주기
setUserName(obj.data.userName);
//userName 을 userNameRef 에도 넣어주기
userNameRef.current = inputUserRef.current!.value ;
}
//메세지를 보낸 사람을 출력할 스타일
const senderStyle={
fontSize:"0.75rem",
fontWeight:"bold",
marginBottom:"2px",
color:"#555"
};
//입장, 퇴장 메세지 스타일
const infoStyle={
textAlign:"center",
margin:"5px 0",
fontStyle:"italic",
color: "#888"
}
// 귓말 보내기 위해 선택된 userName 을 상태값으로 관리
const [selectedUser, setSelectedUser] = useState<string|null>(null);
// input type="file" 의 참조값
const fileInputRef = useRef<HTMLInputElement>(null);
const handleImageClick = ()=>{
// input type="file" 요소를 강제 클릭해서 이미지를 선택할수 있도록 한다.
fileInputRef.current?.click();
};
//이미지 파일을 선택했을때 실행되는 함수
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>)=>{
//선택된 파일 객체
const file = e.target.files?.[0];
fileUpload(file);
}
//매개변수에 전달된 파일 객체를 업로드 하는 함수
const fileUpload = async (file:File|undefined|null)=>{
if(!file)return;
//FormData
const formData = new FormData();
formData.append("image", file);
//axios 를 이용해서 multipart/form-data 요청해서 이미지 업로드
try{
const response = await axios.post("/api/image", formData, {
headers:{"Content-Type":"multipart/form-data"}
});
console.log(response.data);
// response.data 는 {saveFileName:"xxx.png"}
// 웹소켓을 이용해서 서버에 업로드된 파일 정보를 전송한다.
const obj={
path:"/chat/image",
data:{
userName,
saveFileName:response.data.saveFileName
}
}
sendMessage(JSON.stringify(obj));
}catch(err){
console.log("업로드 실패!", err);
}
}
// input 요소에 "paste" 이벤트 처리하는 함수
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>)=>{
//붙여 넣기한 item 목록 얻어내기
const items = e.clipboardData.items;
//반복문 돌면서
for(let i=0; i<items.length; i++){
const item=items[i];
if(item.kind === "file" && item.type.startsWith("image/")){
//실제 파일객체로 얻어낸다.
const file=item.getAsFile();
fileUpload(file);
}
}
};
return (
<div className='container'>
<h1>WebSocket 테스트3</h1>
<h2>WebSocket {connected ? "✅ 연결됨" : "❌ 끊김"} {userName}</h2>
{ isEnter ?
<div className='row'>
<div className="col-8">
<div style={divStyle} ref={divRef}>
{msgs.map(item => (
item.sender ?
<div key={item.id} style={{
display:"flex",
flexDirection:"column",
alignItems: item.sender === userName ? "flex-end" : "flex-start",
marginBottom: "10px"
}}>
{ item.sender !== userName && <div style={senderStyle}>{item.sender}</div>}
<div style={item.sender !== userName ? otherBubbleStyle : myBubbleStyle}>
{
item.isImage ?
<img src={item.content}
style={{maxWidth:"200px", borderRadius:"10px"}}
alt="업로드된 이미지"/>
:
item.content
}
</div>
</div>
:
<div key={item.id} style={infoStyle}>
{item.content}
</div>
))}
</div>
<InputGroup className="mb-3">
<Form.Control
placeholder={selectedUser ? selectedUser+" 님에게 귓말 보내기..." : "대화 입력..."}
ref={inputRef}
onKeyDown={(e)=>{
//Enter 키를 눌렀을때 handleSend() 함수 호출하기
if(e.key === "Enter")handleSend();
}}
onPaste={handlePaste}
/>
<Button variant='outline-success' onClick={handleImageClick}>이미지</Button>
<Button variant="outline-secondary" onClick={handleSend}>Send</Button>
</InputGroup>
<input type="file"
accept='image/*'
ref={fileInputRef}
style={{display:"none"}}
onChange={handleFileChange}/>
</div>
<div className='col-4'>
<h3>참여자 목록</h3>
<ListGroup as="ul">
{userList.map(item =>
<ListGroup.Item as="li"
key={uuid()}
action
style={{cursor:"pointer"}}
active={item === selectedUser}
onClick={()=>setSelectedUser(item === selectedUser ? null : item)}>
{item}
</ListGroup.Item>
)}
</ListGroup>
</div>
</div>
:
<>
<input ref={inputUserRef} type="text" placeholder='UserName 입력...' />
<button onClick={handleEnter}>입장</button>
</>
}
</div>
)
}
export default App3
import { useEffect, useRef, useState } from 'react'
import { v4 as uuid } from 'uuid';
import { useWebSocket } from './hooks/useWebSocket';
import { Button, Form, InputGroup, ListGroup } from 'react-bootstrap';
import axios from 'axios';
interface Message{
id:string;
content:string;
sender?:string; //이 메세지를 누가 보냈는지 정보도 Message 객체에 담기 위해
isImage?:boolean; //이 메세지가 이미지인지 여부
}
function App3() {
const [msgs, setMsgs] = useState<Message[]>([]);
const inputRef=useRef<HTMLInputElement>(null);
//대화방에 입장한 userName 도 상태값으로 관리하기
const [userName, setUserName]=useState<string>();
// userName 을 useRef() 를 이용해서 관리하기
const userNameRef = useRef<string|null>(null);
//대화방 참여자 목록도 상태값으로 관리
const [userList, setUserList]=useState<string[]>([]);
// useWebSocket() hook 사용해서 웹소켓 연결하기
const {sendMessage, connected} = useWebSocket("ws://192.168.0.107:9000/ws", {
onOpen:()=>{
console.log("연결됨!");
},
onMessage:(event)=>{
//응답된 json 문자열을 실제 object로 변경한다.
const received = JSON.parse(event.data);
if(received.type === "enter"){
setIsEnter(true);
setMsgs(prevState=>{
const msg = received.payload.userName+" 님이 입장했습니다.";
return [...prevState, {id:uuid(), content:msg}]
});
//사용자 목록을 update 한다.
setUserList(received.payload.userList);
}else if(received.type == "leave"){
const msg = received.payload.userName+" 님이 퇴장했습니다.";
setMsgs(prevState => [...prevState, {id:uuid(), content:msg}]);
//leave 된 userName 을 userList 에서 제거한다
setUserList(prevState => prevState.filter(item => item !== received.payload.userName))
}else if(received.type ==="public"){
setMsgs(prevState=>{
//출력할 메세지를 구성한다.
const msg=received.payload.text;
return [...prevState, {id:uuid(), content:msg, sender:received.payload.userName}];
});
}else if(received.type === "whisper"){
//여기가 실행되는 경우는 귓말을 보낸 사람과, 받는 사람이다.
const msg = received.payload.userName === userNameRef.current ?
`${received.payload.text} => [귓말] ${received.payload.toUserName}`
:
`[귓말] => ${received.payload.text}`
;
setMsgs(prevState=>[...prevState, {id:uuid(), content:msg, sender:received.payload.userName}]);
}else if(received.type === "image"){
setMsgs(prevState => [...prevState, {
id:uuid(),
content: `/upload/${received.payload.saveFileName}`,
isImage: true,
sender: received.payload.userName
}]);
}
},
onClose:()=>{
console.log("연결끊김!");
}
});
//메세지 보내는 함수
const handleSend=()=>{
//입력한 메세지 읽어와서
const msg=inputRef.current?.value;
//서버에 전송할 정보를 담고 있는 object
let obj=null;
if(selectedUser){
obj={
path:"/chat/whisper",
data:{
userName,
text:msg,
toUserName:selectedUser
}
};
}else{
obj={
path:"/chat/public",
data:{
userName,
text:msg
}
}
}
//object를 json문자열로 변환해서 전송하기
sendMessage(JSON.stringify(obj));
//입력창 초기화
inputRef.current!.value="";
}
const divStyle={
height:"500px",
backgroundColor:"#cecece",
padding:"10px",
overflowY:"auto",
scrollBehavior:"smooth"
};
const divRef=useRef<HTMLDivElement>(null);
//자동 스크롤
useEffect(()=>{
if(divRef.current){
divRef.current!.scrollTop = divRef.current!.scrollHeight;
}
}, [msgs]);
const bubbleStyleBase: React.CSSProperties = {
borderRadius: "20px",
padding: "10px 16px",
marginBottom: "8px",
maxWidth: "70%",
wordBreak: "break-word",
fontSize: "0.95rem",
lineHeight: "1.4",
};
//내가 보낸 메세지 스타일
const myBubbleStyle: React.CSSProperties = {
...bubbleStyleBase,
backgroundColor: "#DCF8C6", // 연한 연두색 (WhatsApp 스타일)
alignSelf: "flex-end",
color: "#000",
};
//다른 사람이 보낸 메세지 스타일
const otherBubbleStyle: React.CSSProperties = {
...bubbleStyleBase,
backgroundColor: "#fff",
border: "1px solid #ccc",
alignSelf: "flex-start",
color: "#000",
};
//대화방에 입장했는지 여부
const [isEnter, setIsEnter] = useState<boolean>(false);
const inputUserRef = useRef<HTMLInputElement>(null);
const handleEnter = ()=>{
const obj={
path:"/chat/enter",
data:{
userName:inputUserRef.current?.value
}
};
sendMessage(JSON.stringify(obj));
//userName 을 상태값에 넣어주기
setUserName(obj.data.userName);
//userName 을 userNameRef 에도 넣어주기
userNameRef.current = inputUserRef.current!.value ;
}
//메세지를 보낸 사람을 출력할 스타일
const senderStyle={
fontSize:"0.75rem",
fontWeight:"bold",
marginBottom:"2px",
color:"#555"
};
//입장, 퇴장 메세지 스타일
const infoStyle={
textAlign:"center",
margin:"5px 0",
fontStyle:"italic",
color: "#888"
}
// 귓말 보내기 위해 선택된 userName 을 상태값으로 관리
const [selectedUser, setSelectedUser] = useState<string|null>(null);
// input type="file" 의 참조값
const fileInputRef = useRef<HTMLInputElement>(null);
const handleImageClick = ()=>{
// input type="file" 요소를 강제 클릭해서 이미지를 선택할수 있도록 한다.
fileInputRef.current?.click();
};
//이미지 파일을 선택했을때 실행되는 함수
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>)=>{
//선택된 파일 객체
const file = e.target.files?.[0];
fileUpload(file);
}
//매개변수에 전달된 파일 객체를 업로드 하는 함수
const fileUpload = async (file:File|undefined|null)=>{
if(!file)return;
//FormData
const formData = new FormData();
formData.append("image", file);
//axios 를 이용해서 multipart/form-data 요청해서 이미지 업로드
try{
const response = await axios.post("/api/image", formData, {
headers:{"Content-Type":"multipart/form-data"}
});
console.log(response.data);
// response.data 는 {saveFileName:"xxx.png"}
// 웹소켓을 이용해서 서버에 업로드된 파일 정보를 전송한다.
const obj={
path:"/chat/image",
data:{
userName,
saveFileName:response.data.saveFileName
}
}
sendMessage(JSON.stringify(obj));
}catch(err){
console.log("업로드 실패!", err);
}
}
// input 요소에 "paste" 이벤트 처리하는 함수
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>)=>{
//붙여 넣기한 item 목록 얻어내기
const items = e.clipboardData.items;
//반복문 돌면서
for(let i=0; i<items.length; i++){
const item=items[i];
if(item.kind === "file" && item.type.startsWith("image/")){
//실제 파일객체로 얻어낸다.
const file=item.getAsFile();
fileUpload(file);
}
}
};
const [modalImageUrl, setModalImageUrl] = useState<string | null>(null);
const [scale, setScale] = useState(1);
const handleWheel = (e: React.WheelEvent<HTMLImageElement>) => {
e.preventDefault();
const delta = e.deltaY;
setScale(prev => {
const newScale = delta > 0 ? prev + 0.1 : prev - 0.1;
return Math.min(Math.max(newScale, 0.5), 3); // 최소 0.5배 ~ 최대 3배
});
};
useEffect(() => {
if (modalImageUrl) setScale(1);
}, [modalImageUrl]);
return (
<div className='container'>
<h1>WebSocket 테스트3</h1>
<h2>WebSocket {connected ? "✅ 연결됨" : "❌ 끊김"} {userName}</h2>
{ isEnter ?
<div className='row'>
<div className="col-8">
<div style={divStyle} ref={divRef}>
{msgs.map(item => (
item.sender ?
<div key={item.id} style={{
display:"flex",
flexDirection:"column",
alignItems: item.sender === userName ? "flex-end" : "flex-start",
marginBottom: "10px"
}}>
{ item.sender !== userName && <div style={senderStyle}>{item.sender}</div>}
<div style={item.sender !== userName ? otherBubbleStyle : myBubbleStyle}>
{
item.isImage ?
<img src={item.content}
style={{maxWidth:"200px", borderRadius:"10px"}}
alt="업로드된 이미지"/>
:
item.content
}
</div>
</div>
:
<div key={item.id} style={infoStyle}>
{item.content}
</div>
))}
</div>
<InputGroup className="mb-3">
<Form.Control
placeholder={selectedUser ? selectedUser+" 님에게 귓말 보내기..." : "대화 입력..."}
ref={inputRef}
onKeyDown={(e)=>{
//Enter 키를 눌렀을때 handleSend() 함수 호출하기
if(e.key === "Enter")handleSend();
}}
onPaste={handlePaste}
/>
<Button variant='outline-success' onClick={handleImageClick}>이미지</Button>
<Button variant="outline-secondary" onClick={handleSend}>Send</Button>
</InputGroup>
<input type="file"
accept='image/*'
ref={fileInputRef}
style={{display:"none"}}
onChange={handleFileChange}/>
</div>
<div className='col-4'>
<h3>참여자 목록</h3>
<ListGroup as="ul">
{userList.map(item =>
<ListGroup.Item as="li"
key={uuid()}
action
style={{cursor:"pointer"}}
active={item === selectedUser}
onClick={()=>setSelectedUser(item === selectedUser ? null : item)}>
{item}
</ListGroup.Item>
)}
</ListGroup>
</div>
</div>
:
<>
<input ref={inputUserRef} type="text" placeholder='UserName 입력...' />
<button onClick={handleEnter}>입장</button>
</>
}
</div>
)
}
export default App3
모달
<Modal
show={modalImageUrl !== null}
onHide={() => setModalImageUrl(null)}
centered
size="lg"
backdrop="static" // 배경 클릭 시 닫기 가능
keyboard // ESC로도 닫기 가능
>
<Modal.Header closeButton style={{ backgroundColor: "#222", borderBottom: "1px solid #444" }}>
<Modal.Title style={{ color: "#fff" }}>원본 이미지</Modal.Title>
</Modal.Header>
<Modal.Body style={{ padding: 0, backgroundColor: "#000"}}>
{modalImageUrl && (
<img
src={modalImageUrl}
alt="확대 이미지"
onWheel={handleWheel}
style={{
maxWidth: '100%',
margin: '0 auto',
height: 'auto',
display: 'block',
transform: `scale(${scale})`,
transformOrigin: 'center',
transition: 'transform 0.2s ease-in-out',
borderRadius: "0 0 10px 10px",
boxShadow: "0 0 10px rgba(0,0,0,0.5)"
}}
/>
)}
</Modal.Body>
</Modal>
<14:30~ 5교시~8교시> 프로젝트 회의 및 준비
AdminSalesDto
package com.example.FinalProject.dto;
import java.util.List;
import lombok.Data;
@Data
public class AdminSalesDto {
private int salesid;
private int storenum;
private int classid;
private int lectureid;
private int studentid;
private String salesdate;
private String updatedat;
private int salesamount;
private String sday;
private String syear;
private String smonth;
private String sdate;
private int dailySales;
private int monthlySales;
private int yearlySales;
private int salesByStore;
private int sal;
private int sumsal;
private int firsthalf;
private int secondhalf;
private String period;
private int price;
private int totalCost;
private String selectedLecture;
private String selectedClass;
private String title;
private String studentName;
private String teacherName;
private String username;
//페이징 처리를 위한 필드
private List<AdminSalesDto> list;
private int startRowNum;
private int endRowNum;
private String condition;//검색 조건
private String keyword;//검색 키워드 : writer 또는 title, 또는 title+content
private long prevNum;//이전글 글번호
private long nextNum;//다음글 글번호
}
AdminSalesMapper.java
package com.example.FinalProject.mappers;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import com.example.FinalProject.dto.AdminSalesDto;
import com.example.FinalProject.dto.ClassDto;
@Mapper
public interface AdminSalesMapper {
//일 매출 가져오기(~월의 일매출을 그래프로->일 매출이 한 기둥으로 리스트 = 한 테이블)
List<AdminSalesDto> getListDaily(Map<String, Object> smonthstorenum);
//월 매출 가져오기(~연도의 월매출을 그래프로 -> 월매출이 한 기둥으로 리스트 = 한 테이블)
List<AdminSalesDto> getListMonthly(Map<String,Object> syearstorenum);
//연 매출 가져오기(연도별 매출이 그래프 한 기둥 = 한 칼럼 )
List<AdminSalesDto> getListYearly(int storenum);
//강사별 월매출 가져오기
List<AdminSalesDto> getMonthlyByClass(int storenum);
//강사별 연매출 가져오기
List<AdminSalesDto> getYearlyByClass(int storenum);
//수업별 월매출 가져오기
List<AdminSalesDto> getMonthlyByLecture(int storenum);
//수업별 연매출 가져오기
List<AdminSalesDto> getYearlyByLecture(int storenum);
//연도별 상반기/하반기 가져오기(1월 ~6월, 7월 ~12월)
List<AdminSalesDto> getListHalfByYear(int storenum);
//강사별 상반기/하반기 가져오기(1월 ~6월, 7월 ~12월)
List<AdminSalesDto> getListHalfByClass(int storenum);
//수업별 상반기/하반기 가져오기(1월 ~6월, 7월 ~12월)
List<AdminSalesDto> getListHalfByLecture(int storenum);
//지출 연도별 -강사 월급 지출 가져오기
List<AdminSalesDto> getListCost(int storenum);
//이상 매출 현황 위한 dao
//--------------------------------------------------
//이하 매출 관리 위한 dao
//전체 매출 테이블 읽어오기
List<AdminSalesDto> getList(int storenum);
//매출 입력하기
int insert(AdminSalesDto dto);
//매출 수정하기
int update(AdminSalesDto dto);
//매출 삭제하기
int deleteSale(int sales_id);
//수업 가져오기
ClassDto getClassData(int storenum);
//수업 가격 가져오기
int getPrice(int lecture_id);
//분반의 수업 아이디 가져오기
int getLectureId(Map<String, Object> class_idstorenum);
//학생 번호 가져오기
int getStudentId(Map<String,Object> class_idstorenum);
//학생 이름 가져오기
String getStudentName(Map<String, Object>student_idstorenum);
//교사 번호 가져오기
int getTeacherId(Map<String, Object>class_idstorenum);
//매장 번호 가져오기
int getStorenum(String username);
//수업 아이디 가져오기
int getClassID(int storenum);
}
AdminSalesMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.FinalProject.mappers.AdminSalesMapper">
<select id="getListDaily" parameterType="map" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY-MM-DD') AS sday,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS dailySales
FROM
Sales
WHERE
TO_CHAR(salesdate, 'YYYY-MM') = #{smonth} AND storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY-MM-DD')
ORDER BY
sday ASC;
</select>
<select id="getListMonthly" parameterType="map" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
TO_CHAR(salesdate, 'YYYY') = #{syear} AND storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY')
ORDER BY
syear ASC;
</select>
<select id="getListYearly" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY')
ORDER BY
syear ASC;
</select>
<select id="getYearlyByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
classid,
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
classid, TO_CHAR(sales_date, 'YYYY')
ORDER BY
classid, syear;
</select>
<select id="getMonthlyByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
class_id,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
SUM(salesamount) AS monthlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
class_id, TO_CHAR(sales_date, 'YYYY-MM')
ORDER BY
class_id, smonth;
</select>
<select id="getYearlyByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
lectureid,
TO_CHAR(sales_date, 'YYYY') AS syear,
SUM(sales_amount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid, TO_CHAR(sales_date, 'YYYY')
ORDER BY
lecutureid, syear;
</select>
<select id="getMonthlyByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
lectureid,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
SUM(salesamount) AS monthlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid, TO_CHAR(salesdate, 'YYYY-MM')
ORDER BY
lectureid, smonth;
</select>
<select id="getListHalfByYear" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY'),
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
syear, period;
</select>
<select id="getListHalfByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear,
classid
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
classid,
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
classid, period;
</select>
<select id="getListHalfByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear,
lectureid
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid,
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
lectureid, period;
</select>
<select id="getListCost" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT t.name teachername, t.teacherid, t.salary*Floor((c.enddate-c.startdate)/30) as sal
FROM Teacher t
join class c on t.teacherid = c.teacherid
where storenum=#{storenum}
order by t.name asc
</select>
<select id="getList" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT To_char(sales_date, 'YYYY-MM-DD') as sday, salesid, classid, lectureid, salesdate, salesamount
FROM Sales
where storenum=#{storenum}
order by salesid, sday asc
</select>
<insert id="insert" parameterType = "com.example.FinalProject.dto.AdminSalesDto">
insert into sales(salesid, storenum, classid,lectureid,studentid,salesdate,salesamount)
values (sales_seq.nextval, #{storenum}, #{classid}, #{lectureid}, #{studentid}, TO_DATE(#{salesdate}, 'YYYY-MM-DD'), #{salesamount})
</insert>
<update id="update" parameterType="com.example.FinalProject.dto.AdminSalesDto">
update sales
set class_id=#{classid}, lecture_id=#{lectureid}, student_id=#{studentid},
TO_DATE(#{salesdate}, 'YYYY-MM-DD'), updatedat=sysdate, salesamount=#{salesamount}
where salesid=#{salesid}
</update>
<delete id="delete" parameterType="int">
delete from sales
where salesid=#{salesid}
</delete>
<select id="getClassData" parameterType="int" resultType="com.example.FinalProject.dto.ClassDto">
select classid, lectureid,teacherid, startdate,enddate
from class
where storenum = #{storenum} and
</select>
<select id="getPrice" parameterType="int" resultType="int">
select price
from lecture
where lectureid = #{lectureid}
</select>
<select id="getLectureId" parameterType="map" resultType="int">
select lectureid
from lecture
where title= #{title} and storenum = #{storenum}
</select>
<select id="getStudentId" parameterType="map" resultType="int">
select studentid from student
where classid= #{classid} and storenum = #{storenum}
</select>
<select id="getStudentName" parameterType="map" resultType="String">
select name from student
where studentid= #{studentid} and storenum = #{storenum}
</select>
<select id="getTeacherId" parameterType="map" resultType="int">
select teacherid from teacher
where teacherid= #{teacherid} and storenum = #{storenum}
</select>
<select id="getStorenum" parameterType="map" resultType="int">
select storenum
from users
where username= #{username}
</select>
<select id="getClassID" parameterType="" resultType="int">
SELECT t.name teachername, t.lectureid, c.classid, t.teacherid
FROM Teacher t
join class c on t.teacherid = c.teacherid
where storenum=#{storenum}
order by t.name asc
</select>
</mapper>
AdminSalesService
<?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.example.FinalProject.mappers.AdminSalesMapper">
<select id="getListDaily" parameterType="map" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY-MM-DD') AS sday,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS dailySales
FROM
Sales
WHERE
TO_CHAR(salesdate, 'YYYY-MM') = #{smonth} AND storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY-MM-DD')
ORDER BY
sday ASC;
</select>
<select id="getListMonthly" parameterType="map" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
TO_CHAR(salesdate, 'YYYY') = #{syear} AND storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY')
ORDER BY
syear ASC;
</select>
<select id="getListYearly" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY')
ORDER BY
syear ASC;
</select>
<select id="getYearlyByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
classid,
TO_CHAR(salesdate, 'YYYY') AS syear,
SUM(salesamount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
classid, TO_CHAR(sales_date, 'YYYY')
ORDER BY
classid, syear;
</select>
<select id="getMonthlyByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
class_id,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
SUM(salesamount) AS monthlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
class_id, TO_CHAR(sales_date, 'YYYY-MM')
ORDER BY
class_id, smonth;
</select>
<select id="getYearlyByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
lectureid,
TO_CHAR(sales_date, 'YYYY') AS syear,
SUM(sales_amount) AS yearlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid, TO_CHAR(sales_date, 'YYYY')
ORDER BY
lecutureid, syear;
</select>
<select id="getMonthlyByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
lectureid,
TO_CHAR(salesdate, 'YYYY-MM') AS smonth,
SUM(salesamount) AS monthlySales
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid, TO_CHAR(salesdate, 'YYYY-MM')
ORDER BY
lectureid, smonth;
</select>
<select id="getListHalfByYear" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
TO_CHAR(salesdate, 'YYYY'),
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
syear, period;
</select>
<select id="getListHalfByClass" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear,
classid
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
classid,
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
classid, period;
</select>
<select id="getListHalfByLecture" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END AS period,
SUM(salesamount) AS total,
TO_CHAR(salesdate, 'YYYY') AS syear,
lectureid
FROM
sales
WHERE
storenum = #{storenum}
GROUP BY
lectureid,
CASE
WHEN TO_NUMBER(TO_CHAR(salesdate, 'MM')) BETWEEN 1 AND 6 THEN 'firsthalf'
ELSE 'secondhalf'
END
ORDER BY
lectureid, period;
</select>
<select id="getListCost" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT t.name teachername, t.teacherid, t.salary*Floor((c.enddate-c.startdate)/30) as sal
FROM Teacher t
join class c on t.teacherid = c.teacherid
where storenum=#{storenum}
order by t.name asc
</select>
<select id="getList" parameterType="int" resultType="com.example.FinalProject.dto.AdminSalesDto">
SELECT To_char(sales_date, 'YYYY-MM-DD') as sday, salesid, classid, lectureid, salesdate, salesamount
FROM Sales
where storenum=#{storenum}
order by salesid, sday asc
</select>
<insert id="insert" parameterType = "com.example.FinalProject.dto.AdminSalesDto">
insert into sales(salesid, storenum, classid,lectureid,studentid,salesdate,salesamount)
values (sales_seq.nextval, #{storenum}, #{classid}, #{lectureid}, #{studentid}, TO_DATE(#{salesdate}, 'YYYY-MM-DD'), #{salesamount})
</insert>
<update id="update" parameterType="com.example.FinalProject.dto.AdminSalesDto">
update sales
set class_id=#{classid}, lecture_id=#{lectureid}, student_id=#{studentid},
TO_DATE(#{salesdate}, 'YYYY-MM-DD'), updatedat=sysdate, salesamount=#{salesamount}
where salesid=#{salesid}
</update>
<delete id="delete" parameterType="int">
delete from sales
where salesid=#{salesid}
</delete>
<select id="getClassData" parameterType="int" resultType="com.example.FinalProject.dto.ClassDto">
select classid, lectureid,teacherid, startdate,enddate
from class
where storenum = #{storenum} and
</select>
<select id="getPrice" parameterType="int" resultType="int">
select price
from lecture
where lectureid = #{lectureid}
</select>
<select id="getLectureId" parameterType="map" resultType="int">
select lectureid
from lecture
where title= #{title} and storenum = #{storenum}
</select>
<select id="getStudentId" parameterType="map" resultType="int">
select studentid from student
where classid= #{classid} and storenum = #{storenum}
</select>
<select id="getStudentName" parameterType="map" resultType="String">
select name from student
where studentid= #{studentid} and storenum = #{storenum}
</select>
<select id="getTeacherId" parameterType="map" resultType="int">
select teacherid from teacher
where teacherid= #{teacherid} and storenum = #{storenum}
</select>
<select id="getStorenum" parameterType="map" resultType="int">
select storenum
from users
where username= #{username}
</select>
<select id="getClassID" parameterType="" resultType="int">
SELECT t.name teachername, t.lectureid, c.classid, t.teacherid
FROM Teacher t
join class c on t.teacherid = c.teacherid
where storenum=#{storenum}
order by t.name asc
</select>
</mapper>
AdminSalesServiceImpl
package com.example.FinalProject.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.FinalProject.dto.AdminSalesDto;
import com.example.FinalProject.dto.ClassDto;
import com.example.FinalProject.mappers.AdminSalesMapper;
@Service
public class AdminSalesServiceImpl implements AdminSalesService {
@Autowired AdminSalesMapper mapper;
@Override
public int getStoreNum(String username) {
int storenum = mapper.getStorenum(username);
return storenum;
}
@Override
public int getLectureID(String selectedLecture) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getClassID(String selectedClass) {
int storenum = this.getStoreNum(selectedClass);
int classid=mapper.getClassID(storenum);
return classid;
}
@Override
public int getStudentID(String studentName) {
// TODO Auto-generated method stub
return 0;
}
@Override
public List<AdminSalesDto> getList(int storenum) {
storenum =1;
List<AdminSalesDto> list= mapper.getList(storenum);
for (AdminSalesDto dto : list) {
int studentId = dto.getStudentid();
Map<String, Object> param = new HashMap<>();
param.put("student_id", studentId);
param.put("storenum", storenum);
String studentname = mapper.getStudentName(param); // Mapper에서 이름 가져오기
dto.setStudentName(studentname); // DTO에 설정
}
return list;
}
@Override
public int addSales(AdminSalesDto dto) {
int storenum=1;
System.out.println(dto);
ClassDto classDto= mapper.getClassData(storenum);
System.out.println(classDto);
Map<String, Object> param = new HashMap<>();
param.put("class_id", classDto.getClassId());
param.put("storenum", storenum);
System.out.println(param);
int student_id= mapper.getStudentId(param);
Map<String, Object> student_idstorenum = new HashMap<>();
student_idstorenum.put("student_id", student_id);
student_idstorenum.put("storenum", storenum);
mapper.getStudentName(student_idstorenum);
return mapper.insert(dto);
}
@Override
public int updateSales(AdminSalesDto dto) {
return mapper.update(dto);
}
@Override
public int deleteSales(int sales_id) {
return mapper.deleteSale(sales_id);
}
@Override
public List<AdminSalesDto> getListDaily(String sales_date) {
String date = sales_date;
String smonth=date.substring(0,7);
int storenum=1;
Map<String, Object> smonthstorenum = new HashMap<>();
smonthstorenum.put("smonth", smonth);
smonthstorenum.put("storenum", storenum);
return mapper.getListDaily(smonthstorenum);
}
@Override
public List<AdminSalesDto> getListMonthly(String sales_date) {
String date = sales_date;
String syear=date.substring(0,4);
int storenum=1;
Map<String, Object> syearstorenum = new HashMap<>();
syearstorenum.put("syear", syear);
syearstorenum.put("storenum", storenum);
return mapper.getListMonthly(syearstorenum);
}
@Override
public List<AdminSalesDto> getListYearly(int storenum) {
return mapper.getListYearly(storenum);
}
@Override
public List<AdminSalesDto> getMonthlyByClass() {
int storenum=1;
return mapper.getMonthlyByClass(storenum);
}
@Override
public List<AdminSalesDto> getYearlyByClass() {
int storenum=1;
return mapper.getYearlyByClass(storenum);
}
@Override
public List<AdminSalesDto> getMonthlyByLecture() {
int storenum=1;
return mapper.getMonthlyByLecture(storenum);
}
@Override
public List<AdminSalesDto> getYearlyByLecture() {
int storenum=1;
return mapper.getYearlyByLecture(storenum);
}
@Override
public List<AdminSalesDto> getListHalf() {
int storenum=1;
return mapper.getListHalfByYear(storenum);
}
@Override
public List<AdminSalesDto> getListHalfByClass() {
int storenum=1;
return mapper.getListHalfByClass(storenum);
}
@Override
public List<AdminSalesDto> getListHalfByLecture() {
int storenum=1;
return mapper.getListHalfByLecture(storenum);
}
@Override
public List<AdminSalesDto> getListCost() {
int storenum=1;
return mapper.getListCost(storenum);
}
}
AdminSalesController
package com.example.FinalProject.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.FinalProject.dto.AdminSalesDto;
import com.example.FinalProject.service.AdminSalesService;
@RestController
public class AdminSalesController {
@Autowired private AdminSalesService service;
//매출 관리 페이지 기능
@GetMapping("/sales") //매출 리스트 가져오기 페이지
public List<AdminSalesDto> salesList(){
int storenum=1;
List<AdminSalesDto> list = service.getList(storenum);
System.out.println(list);
return list;
}
@PostMapping("/sales") //매출 추가하기
public AdminSalesDto addSale(AdminSalesDto dto) {
service.addSales(dto);
System.out.println(dto);
return dto;
}
}
'자바풀스택 과정 > 자바 풀 스택 : 수업내용정리' 카테고리의 다른 글
챗지피티에게 물어본 오라클에서 함수 만드는 방법 (0) | 2025.04.14 |
---|---|
자바 풀 스택 4/12 하루 기록 095(멘토링) (0) | 2025.04.12 |
자바 풀 스택 4/10 하루 기록 093 (0) | 2025.04.10 |
자바 풀 스택 4/9 하루 기록 092 (0) | 2025.04.09 |
자바 풀 스택 4/8 하루 기록 091 (0) | 2025.04.08 |