게스트ID: rkwlakzpt(가지마켓)
비밀번호: rkwlakzpt123(가지마켓123)
- 팀장 : 김종호
- 팀원 : 천영준, 신정훈, 백의헌
Category | Detail |
---|---|
FrontEnd | HTML5, CSS, JavaScript, JQuery |
BackEnd | Java(JDK 11.0.2), Spring Framework(5.3.19) |
OS | Windows 10, MacOS |
IDE | SpringToolSuite3, VSCode ,SQL Developer, DBeaver |
Server | Tomcat 9.0 |
DateBase | Oracle(21c) |
API | IamPort, CK Editor, Kakao Map, cloudinary, ipstack, Kakao(Daum) PostCode, Gmail SMPT |
Library | AspectJ 1.9.19 ojdbc8 23.2.0.0 commons-lang3 3.8.1 slf4j 1.6.6 mybatis 3.5.9 spring-session 2.3.3.RELEASE log4j 1.2.15 mybatis-spring 2.0.6 inject 1.0.0 lombok 1.18.28 Servlet API 4.0.1 spring-security 5.7.5 jackson 2.14.2 JSP API2.1 gson2.8.9 JSTL1.2 DBCP1.4 jackson2.10.2 iamport0.2.14 commons-fileupload 1.4 commons-io 2.6 cloudinary-http44 1.32.2 cloudinary-taglib 1.32.2 dotenv-java 2.2.4 javax.mail 1.4.7 spring-context-support 5.3.19 spring-websocket 5.3.19 stomp-websocket 2.3.3-1 |
채팅방에 따라 DB값 불러오기(Click)
채팅방 리스트를 클릭하면 내 아이디 값과 상대방 아이디 값을 가져오게 됨. 판매자와 구매자를 구별하기 위해 union사용.
<select id="selectChatListByUserId" parameterType="string" resultType="ChatRoomDto">
SELECT c.chat_id, c.goods_id, u.nickname,c.seller_id, c.created_at
FROM chat_room c
JOIN users u ON (u.user_id = c.buyer_id)
WHERE c.seller_id = #{userId}
UNION
SELECT c.chat_id, c.goods_id, u.nickname,c.seller_id, c.created_at
FROM chat_room c
JOIN users u ON (u.user_id = c.seller_id)
WHERE c.buyer_id = #{userId}
ORDER BY created_at
</select>
채팅방 리스트 시간 변경
메시지를 입력하면 priview와 현재시간을 채팅방 리스트에 최신화
<c:forEach var="item2" items="${item1.chatInfo}">
<span class="time">
<c:choose>
<c:when test="${item2.createAt != null}">
<script>
var dateString = "${item2.createAt}";
var date = new Date(dateString);
var today = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hours = date.getHours();
var minutes = date.getMinutes();
if (today.getDate() == day) {
if (hours > 11) {
document.write("오후 " + (hours - 12) + ":" + minutes);
} else {
document.write("오전 " + hours + ":" + minutes);
}
} else {
document.write(month + "월 " + day + "일");
}
</script>
</c:when>
</c:choose>
</span>
<span class="preview">
<c:choose>
<c:when test="${item2.message.startsWith('https://res.cloudinary.com/')}">
이미지 파일
</c:when>
<c:otherwise>
${item2.message}
</c:otherwise>
</c:choose>
</span>
</c:forEach>
## 관리자 페이지
검색 조건에 따른 데이터를 불러오기 위한 동적 쿼리문(Click)
동적 쿼리문을 통해 여러가지 입력 가능한 정렬 조건중 입력된 값에 대해서만 where절이 적용 되도록 하였습니다.
<select id="getGoodsList" parameterType="map"
resultType="GoodsListDto">
select list2.*,(select f.url from goods_file f where
f.goods_id=list2.goods_id and rownum between 0 and 1) url from
(select
list.*,rownum rn from(
(select
g.view_count,g.title,g.price,g.goods_Id,u.nickname,g.status,g.safe_Trading_Yn,d.dong_Name,g.refreshed_at,
g.created_at,
(select count(*) from wishList w where
g.goods_id=w.goods_id) likeCount
from goods g join users u
using(user_id) join dong d on g.dong_id=d.dong_id
where
active_status='Y'
<if test='category!=null'>
and category_id=#{category}
</if>
<if test='searchWord!=null'>
and title like #{searchWord}
</if>
<choose>
<when test="priceCeiling==100000">
and price <= 100000
</when>
<when test="priceCeiling==300000">
and price >= 100000 and price <= 300000
</when>
<when test="priceCeiling==500000">
and price >= 300000 and price <= 500000
</when>
<when test="priceCeiling==700000">
and price >= 500000
</when>
</choose>
<choose>
<when test='onsale!=null'>
and g.status=1
</when>
<otherwise>
and g.status!=4
</otherwise>
</choose>
<if test='dongId!=null'>
and g.dong_id=#{dongId}
</if>
<choose>
<when test="sort==1">
order by likeCount desc, g.refreshed_at desc
</when>
<when test="sort==3">
order by price asc, g.refreshed_at desc
</when>
<when test="sort==4">
order by price desc, g.refreshed_at desc
</when>
<otherwise>
order by g.refreshed_at desc
</otherwise>
</choose>
)
list)
)list2 where list2.rn between #{startRownum} and #{endRownum}
</select>
조건 검색 JSP 로직(Click)
이전에 적용한 조건들이 유지되면서, 새로운 조건들을 추가해 나가기 위해
하나의 form 태그 안에 미리 input 태그를 준비해 놓은 후 새로운 값이 추가 될 때마다
input태그에 값을 추가해 submit 하고.
Controller에서부터 JSP로는 이전에 적용 된 조건값들을 다시 넘겨주었습니다.
주소 변경, 삭제, 추가를 위한 Mapper코드(Click)
<알고리즘 설명>1. 주소순서 1번을 대표주소로 본다.
2. 대표주소 변경시 모든 주소순서를 +1 한다.
3. 대표주소로 설정할 주소의 주소순서를 1로 설정한다.
4. 마지막으로 대표주소로 변경한 주소의 주소순서보다 큰 주소에 대해서 -1을 적용한다.
<!-------------------------------------------대표주소 변경을 위한 mapper--------------------------------------->
<!-- 대표주소를 변경하기위해 주소순서를 하나씩 뒤로 민다 -->
<update id="updateMainAddressNo" parameterType="string">
update user_address set address_no=address_no+1 where user_id=#{userId}
</update>
<update id="updateMainAddress" parameterType="map"> <!--주소순서를 1로 바꿈으로써 대표주소 변경 -->
update user_address set address_no=1 where user_id=#{userId} and address_no=#{addressNo}+1
</update>
<update id="updateMainAddress2" parameterType="map"> <!-- 대표주소가 된 주소보다 높은순서들을 다시 1씩 내려주기. -->
update user_address set address_no=address_no-1 where user_id=#{userId} and address_no > #{addressNo}
</update>
<!------------------------------------------------주소 추가 mapper -------------------------------------------->
<!-- 새로입력된 주소를 count+1 주소순서에 추가한다 -->
<insert id="insertAddress" parameterType="UserInsertAddressDto">
insert into user_address values(#{detailAddress},#{postCode},#{userId},#{roadAddress},#{address},#{addressNickname},(select count(*) from user_address where user_id=#{userId})+1)
</insert>
<!-----------------------------------------------주소 삭제 mapper -------------------------------------------->
<!-- 주소삭제 1단계 작업(주소삭제)-->
<delete id="deleteAddress1" parameterType="map">
delete from user_address where address_no=#{addressNo} and user_id=#{userId}
</delete>
<!-- 주소삭제 2단계 작업(삭제된 주소순서보다 작은 주소들 -1) -->
<update id="deleteAddress2" parameterType="map">
update user_address set Address_no=Address_no-1 where Address_no > #{addressNo} and user_id=#{userId}
</update>
안전거래 유효성 검사 코드(Click)
안전거래의 핵심은 유효성 검사
입니다. JSP를 통해 결제가 이루어지게 되면
클라이언트는 개발자 도구를 통해 상품가격과 같은 결제정보를 변조하여 결제를 시도 할 수 있습니다.
이를 방지하고자 JSP에서 아임포트 API를 통해 결제가 이루어지면 Controller
에서는 Database
에 저장된 정보와 API
에서 결제된 정보를 대조
합니다.
이 결과에 따라 Database
에 안전결제에 대한 데이터를 삽입한 후
삽입이 성공하였다면
최종적으로 안전결제 성공여부를 클라이언트
에게 전달합니다.
@PostMapping("payment/callback")
@ResponseBody
public IamportResponse<Payment> callback(String impUid,InsertSafeTradingDto insertSafeTradingDto,Integer goodsId,HttpServletRequest request,Principal principal) {
IamportResponse<Payment> result=null;
String userId=principal.getName();
try {
result= api.paymentByImpUid(impUid);
int amount =(int)Math.round(payServiceImpl.getAmount(goodsId) * 1.035);
String goodTitle=result.getResponse().getName();
if(result.getResponse().getStatus().equals("paid")&&amount==result.getResponse().getAmount().intValue()) { // 금액이 일치하고 지불이 완료되었다면.
insertSafeTradingDto.setTransactionId(impUid);
insertSafeTradingDto.setGoodsTitle(goodTitle);
insertSafeTradingDto.setPrice(amount);
insertSafeTradingDto.setPurchaseMethod(result.getResponse().getPayMethod());
insertSafeTradingDto.setBuyerId(userId);
int addResult = payServiceImpl.addSafeTrading(insertSafeTradingDto);
//데이터베이스에 안전거래에 대한 데이터를 넣음
if(addResult==1) { // 가지 데이터베이스에 값이 정상적으로 들어갔다면
Map<String, Object> map=new HashMap<String, Object>();
map.put("status", 2);
map.put("goodsId",goodsId);
if(payServiceImpl.updateStatus(map)==1) {
TitleBuyerDto titleBuyerDto=payServiceImpl.getIdFromTransactionId(impUid);
insertNotificationDto.setBuyerId(titleBuyerDto.getBuyerId());
insertNotificationDto.setSellerId(titleBuyerDto.getSellerId());
insertNotificationDto.setType(2);
insertNotificationDto.setReferenceId(impUid);
insertNotificationDto.setMessage(titleBuyerDto.getGoodsTitle()+"의 안전거래가 신청되었습니다.");
payServiceImpl.insertNoti(insertNotificationDto);
return result; // 거래정보 반환.
}
}
else // 가지 데이터베이스에 값이 입력되지 않았거나 거래중으로 변경이 안되었다면 거래취소함.
{
cancelData=new CancelData(impUid, true);
api.cancelPaymentByImpUid(cancelData);
return result; //거래 정보 반환
}
}
else {
cancelData=new CancelData(impUid, true); // imp_uid를 이용하여 거래취소함수에 인자가될 객체생성
api.cancelPaymentByImpUid(cancelData); // api의 취소함수에 cancelData를 인자로하여 거래취소.
return result; //거래 정보 반환
}
} catch (IamportResponseException | IOException e) {
e.printStackTrace();
}
return result;
}
리뷰작성 프로시저 코드(Click)
프로시저
를 활용하여리뷰작성
과유저평점 업데이트
를Transactional
하게 처리
리뷰작성이 발생하였을 경우 리뷰점수를 바탕으로 해당 유저의 평점도 항상 업데이트 됩니다.
떄문에transactional
또는프로시저
로 처리하는것이 적절합니다.
프로시저를 사용함으로써 spring 코드의 복잡성을 줄이고 한번의 입력으로 데이터베이스에서 여러가지 실행을 하도록 하여 네트워크 오버헤드를 줄일 수 있습니다.
CREATE OR REPLACE PROCEDURE updateRatingScore(
goodsId IN NUMBER,
userId IN VARCHAR,
message IN VARCHAR,
mannerPoint IN NUMBER,
timePoint IN NUMBER,
goodsPoint IN NUMBER,
result1 OUT NUMBER
) AS
BEGIN
BEGIN
INSERT INTO deal_review VALUES (goodsId, userId, message, mannerPoint, timePoint, goodsPoint);
result1 := 1; -- 성공
EXCEPTION
WHEN OTHERS THEN
result1 := 0; -- 실패
END;
-- rating_score 업데이트
IF result1 = 1 THEN
BEGIN
UPDATE users
SET rating_score = rating_score + (mannerPoint + timePoint + goodsPoint - 11) / 5
WHERE user_Id = (select user_Id from goods where goods_Id=goodsId);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
result1 := 0; -- 실패
ROLLBACK;
END;
END IF;
END;
알림 기능 설명(Click)
안전결제 신청, 수락, 취소, 운송장 등록, 결제확정 등에 대해서 알림이 생성되며ajax를 통해 주기적으로 알림갯수를 불러와 최신화 합니다.
알림 선택시 해당하는 안전거래 상세페이지로 이동하며
개별삭제 및 선택알림 일괄삭제가 가능합니다.
담당자 | 내용 |
---|---|
팀장 김종호 | Spring Framework 초기 셋팅 Spring Sercurity 적용 URL Mapping 요구사항 정의서 초안 작성 채팅 화면/기능 구현 |
팀원 천영준 | 유스케이스 다이어그램 작성 Controller Mapping작업 회원가입(이메일인증SMPT, 주소추가) 아이디찾기 비밀번호찾기(이메일인증SMPT) 마이페이지(회원정보변경) 관리자페이지(가입유저조회, 정지된 유저 리스트, 신고된 유저가 등록한 게시글 신고리스트, 신고상세페이지, 정지, 정지해제, 검토) 게시글 작성, 수정, 삭제(cloudinary파일업로드 추가 및 삭제, ckeditor5 ckfinder 내용 이미지업로드, kakoMap 약속위치지정, ipstack 사용자 기본 위치 지정) 게시글 상세보기(kakoMap 약속장소맵및링크) 회원 모아보기 추가, 모아보기 해제하기 상품 찜, 찜 해제 |
팀원 신정훈 | |
팀원 백의헌 | 노션 세팅 및 회의록 작성 ERD 다이어그램 작성 mapper, Service, Dao 초안 작성 API 아임포트 안전결제(결제, 결제취소, 결제수락, 결제확정) 거래상태 변경에 따른 상품상태 변경 안전거래 알림 생성, 삭제, 조회 상품 신고 주소(추가, 변경, 삭제) 리뷰 및 유저평점 업데이트 기능(프로시저 사용) 상품 리스트 페이지(각종 조회 조건 처리) 판매내역 조회 구매내역 조회 특정 유저 상품 조회 모아보기 페이지 찜 페이지 페이징 처리 |
- 여러가지 검색 조건 및 페이징을 적용한 상품리스트 조회
- 채팅을 통한 직거래
- 안전거래
- 사진업로드 및 텍스트 설정 가능한 글 작성