Skip to content

Commit

Permalink
[ALL] 5차 데모데이까지의 추가 작업물을 반영한다 (#744)
Browse files Browse the repository at this point in the history
* [FE] refactor: 사용자 유치 전 다양한 버그를 해결한다.  (#695)

* feat: 검색기능 삭제, 정렬기능 오류 수정 및 useQuery 훅 분리

* feat: 메인로고 클릭시 이동, 고객모드 TabBar에 따른 subHeader 로직 수정

* refactor: 고객 정렬 옵션 타입 단언 제거 및 리팩토링

(cherry picked from commit 58a6a07)

* [FE] fix: token 만료 시 만료된 토큰을 삭제한다. (#711)

* feat: 토큰이 만료되었을 시, 토큰을 비워주는 로직 작성

* feat: 토큰을 비운 이후 redirect 로직 수행

* fix: 잘못된 href 수정

(cherry picked from commit eddb635)

* [FE] fix: 버그 수정 (#721)

(cherry picked from commit fa759ac)

* [BE] feat: 개인정보 수집 및 이용 동의 개선 (#708)

(cherry picked from commit 0f3b475)

* fix: z-index를 주어 `CouponDetail`의 열렸을 시 탭바 안보이게 함 (#709)

(cherry picked from commit 12b63d3)

* fix: 잘못된 redirect url 변경 (#723)

(cherry picked from commit bc0a555)

* [BE] fix: 고객이 쿠폰만 있고 방문횟수가 없을때 에러 수정 (#727)

* fix: 고객 방문횟수 0일때 반환하지 않도록 수정

* fix: 리워드 사용 고객 조회 시 방문횟수 없으면 NotFoundException 으로 수정 (현재 UnAuthorized 에러 발생하면 로그인 페이지로 리다이렉트됨)

(cherry picked from commit 8ef3141)

* [BE] feat: submodule update (#728)

(cherry picked from commit aedc0f9)

* [FE] feat: 요청에 따른 access token 분기처리 (#726)

(cherry picked from commit 2fa0759)

* [FE] refactor: react-icons를 @react-icons/all-files와 svg 파일로 대체한다. (#717)

* chore: svg 파일 추가

* chore: svg 사용하기 위한 환경설정

* refactor: `@react-icons/all-files`와 `svg` 컴포넌트화 적용

* chore: `react-icons` 제거

* refactor: svg 경로 변경

* style: svg 파일 경로 재정리

* chore: webpack 원상복구

(cherry picked from commit 605fddc)

* fix: 배포 중 오류 (타입,tsx) 트러블 슈팅 (#729)

(cherry picked from commit 21aaa3e)

* feat: thread 로깅 추가 (#733)

(cherry picked from commit 9e35c22)

* [BE] feat: thread loggin 수정 (#734)

* feat: thread 로깅 추가

* feat: thread 로깅 추가

(cherry picked from commit c0e4fd9)

* [FE] fix: 사장모드 로고가 사라지는 오류 트러블 슈팅 (#736)

(cherry picked from commit cd73ec0)

* [FE] feat: 고객모드의 전화번호 등록페이지에서 추가된 플로우를 구현한다. (#714)

* refactor: get 요청 함수 header 종류에 따라 순서 조정

* feat: 고객의 전화번호로 회원상태를 파악하는 get 함수 작성, 필요한 타입 선언

* feat: 고객의 회원상태를 파악하는 hook 작성

* refactor: 전화번호 저장 post 함수 hook으로 분리

* feat: 전화번호로 고객상태 조회 api msw 구현

* chore: 전화번호로 고객 상태 조회 api mockData

* refactor: 전화번호 길이 상수화

* feat: 전화번호로 고객 데이터 연동 api 함수 및 훅 작성

* chore: mockData 수정

* feat: 전화번호로 고객 상태 조회, 데이터 연동 플로우 구현

* refactor: form 태그로 변경 및 로직 커스텀 훅 분리

* refactor: 함수 분리 및 상수화

* refactor: mutateAsync -> mutate로 변경

* fix: InputPhoneNumber 폴더 위치 Customer로 이동

* fix: 충돌 해결

(cherry picked from commit 718befc)

* [FE] fix: url 수정 (#740)

* fix: url 수정

* fix: 아이콘 로고 크기 수정

(cherry picked from commit eee11a2)

* [FE] fix: async로 수정 (#742)

(cherry picked from commit 3578d4f)

---------

Co-authored-by: 강영민 <[email protected]>
Co-authored-by: 2yunseong <[email protected]>
Co-authored-by: 박정규 <[email protected]>
Co-authored-by: youngh0 <[email protected]>
  • Loading branch information
5 people authored Sep 22, 2023
1 parent 4723f83 commit 8b31ccd
Show file tree
Hide file tree
Showing 67 changed files with 566 additions and 294 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public List<CafeCustomerFindResultDto> findCouponsByCafe(Long ownerId, Long cafe
for (CustomerCoupons customerCoupon : customerCoupons) {
Coupons coupons = new Coupons(customerCoupon.coupons);
VisitHistories visitHistories = findVisitHistories(cafe, customerCoupon);

if (visitHistories.getVisitCount() == 0) {
continue;
}

// TODO: CustomerCouponStatistics 없애도 될 것 같다.
CustomerCouponStatistics customerCouponStatistics = coupons.calculateStatistics();
cafeCustomerFindResultDtos.add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.stampcrush.backend.entity.visithistory.VisitHistory;
import com.stampcrush.backend.exception.CafeNotFoundException;
import com.stampcrush.backend.exception.OwnerNotFoundException;
import com.stampcrush.backend.exception.OwnerUnAuthorizationException;
import com.stampcrush.backend.exception.VisitHistoryNotFoundException;
import com.stampcrush.backend.repository.cafe.CafeRepository;
import com.stampcrush.backend.repository.reward.RewardRepository;
import com.stampcrush.backend.repository.user.OwnerRepository;
Expand All @@ -33,7 +33,7 @@ public List<RewardFindResultDto> findRewards(Long ownerId, RewardFindDto rewardF
List<VisitHistory> visitHistories = visitHistoryRepository.findByCafeIdAndCustomerId(rewardFindDto.getCafeId(), rewardFindDto.getCustomerId());

if (visitHistories.isEmpty()) {
throw new OwnerUnAuthorizationException("카페의 고객이 아닙니다");
throw new VisitHistoryNotFoundException("카페의 고객이 아닙니다");
}

Cafe cafe = cafeRepository.findById(rewardFindDto.getCafeId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,27 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.UUID;

@Slf4j
@RequiredArgsConstructor
@Component
public class RequestLoggingFilter extends OncePerRequestFilter {

private final ThreadPoolTaskExecutor executor;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("????");
log.info("thread-id : {}, thread-name: {}, pool-size: {}", Thread.currentThread().getId(), Thread.currentThread().getName(), executor.getPoolSize());
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("requestMethod", request.getMethod());
MDC.put("requestUrl", request.getRequestURI());
Expand Down
38 changes: 33 additions & 5 deletions backend/src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>

<property name="CONSOLE_LOG_PATTERN"
value="%d{HH:mm:ss.SSS} %clr([request-id: %X{requestId:-startup}]) %clr(%X{requestUrl} %X{requestMethod}){green} | %clr(authorization : %X{authorization}){faint} | [%t] %-5level %logger{36}%n %msg}"/>
value="%d{HH:mm:ss.SSS} %clr([request-id: %X{requestId:-startup}]) %clr(%X{requestUrl} %X{requestMethod}){green} | %clr(authorization : %X{authorization}){faint} | [%t] %-5level %logger%n %msg}"/>

<property name="SQL_LOG_PATTERN"
value="%d{HH:mm:ss.SSS} %clr([request-id: %X{requestId:-startup}]) %clr(%X{requestUrl} %X{requestMethod}){green} | [%t] %-5level %logger{36}%n"/>
Expand All @@ -22,6 +22,17 @@
</encoder>
</appender>

<appender name="CONSOLE_INFO" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>

<appender name="SQL" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
Expand Down Expand Up @@ -93,11 +104,24 @@

<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<mdc/>
<timestamp/>
<message/>
</providers>
<jsonGeneratorDecorator class="net.logstash.logback.decorate.CompositeJsonGeneratorDecorator">
<decorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator"/>
</jsonGeneratorDecorator>
<timestampPattern>yyyy-MM-dd' 'HH:mm:ss.SSS</timestampPattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./was-logs/%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<fileNamePattern>./was-info/warn.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
Expand All @@ -115,15 +139,19 @@
</springProfile>

<springProfile name="dev">
<root level="WARN">
<root level="INFO">
<appender-ref ref="FILE_WARN"/>
<appender-ref ref="FILE_ERROR"/>
<appender-ref ref="FILE_INFO"/>
</root>
<logger level="DEBUG" name="org.hibernate.SQL">
<appender-ref ref="SQL"/>
</logger>
<logger level="TRACE" name="org.hibernate.orm.jdbc.bind">
<appender-ref ref="SQL_PARAMETER"/>
</logger>
<logger name="com.stampcrush.backend.config.RequestLoggingFilter">
<appender-ref ref="FILE_INFO"/>
</logger>
</springProfile>
</configuration>
2 changes: 1 addition & 1 deletion backend/src/main/resources/security
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.stampcrush.backend.repository.user.OwnerRepository;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

Expand Down Expand Up @@ -72,7 +73,7 @@ public class ManagerRewardFindAcceptanceTest extends AcceptanceTest {
ExtractableResponse<Response> response = 리워드_목록_조회(notOwner, cafeId, customer.getId());

// then
assertThat(response.statusCode()).isEqualTo(401);
assertThat(response.statusCode()).isEqualTo(HttpStatus.SC_UNAUTHORIZED);
}

@Test
Expand All @@ -97,7 +98,7 @@ public class ManagerRewardFindAcceptanceTest extends AcceptanceTest {
ExtractableResponse<Response> response = 리워드_목록_조회(owner, cafeId, notMyCustomer.getId());

// then
assertThat(response.statusCode()).isEqualTo(401);
assertThat(response.statusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND);
}

// TODO 회원가입, 로그인 구현 후 API CAll 로 대체
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"@react-icons/all-files": "^4.1.0",
"@tanstack/react-query": "^4.29.25",
"@tanstack/react-query-devtools": "^4.29.25",
"react": "^18.2.0",
"react-daum-postcode": "^3.1.3",
"react-dom": "^18.2.0",
"react-icons": "^4.10.1",
"react-router-dom": "^6.14.1",
"styled-components": "^6.0.2"
},
Expand All @@ -32,6 +32,7 @@
"@storybook/react": "^7.0.27",
"@storybook/react-webpack5": "^7.0.27",
"@storybook/testing-library": "^0.0.14-next.2",
"@svgr/webpack": "^8.1.0",
"@tanstack/eslint-plugin-query": "^4.29.25",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { RouterProvider, createBrowserRouter, Outlet } from 'react-router-dom';
import { ROUTER_PATH } from './constants';

import CustomerList from './pages/Admin/CustomerList';
import ManageCafe from './pages/Admin/ManageCafe';
import CouponList from './pages/Customer/CouponList';
Expand All @@ -23,7 +22,7 @@ import Auth from './pages/Auth';
import AdminLogin from './pages/Admin/AdminLogin';
import AdminAuth from './pages/Admin/AdminAuth';
import TemplateCouponDesign from './pages/Admin/CouponDesign/TemplateCouponDesign';
import InputPhoneNumber from './pages/Admin/InputPhoneNumber';
import InputPhoneNumber from './pages/Customer/InputPhoneNumber';
import CustomerNotFound from './pages/NotFound/CustomerNotFound';
import PrivateProvider from './provider/PrivateProvider';

Expand Down
85 changes: 49 additions & 36 deletions frontend/src/api/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,34 @@ import {
OAuthJWTRes,
CustomerProfileRes,
RewardRes,
CustomerRegisterTypeRes,
} from '../types/api/response';
import { CouponDesign } from '../types/domain/coupon';

// 인증 header가 필요없는 api
export const getAdminOAuthToken = async (
{ params }: QueryReq<OAuthTokenParams>,
init: RequestInit = {},
) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<OAuthJWTRes>(
`/admin/login/${params.resourceServer}/token?code=${params.code}`,
init,
);
};

export const getOAuthToken = async (
{ params }: QueryReq<OAuthTokenParams>,
init: RequestInit = {},
) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<OAuthJWTRes>(
`/login/${params.resourceServer}/token?code=${params.code}`,
init,
);
};

// 사장모드 api
export const getCafe = async () => {
return await api.get<CafeRes>('/admin/cafes', ownerHeader());
};
Expand Down Expand Up @@ -67,6 +92,26 @@ export const getCouponSamples = async ({ params }: QueryReq<MaxStampCountParams>
);
};

export const getCouponDesign = async ({ params }: QueryReq<CafeIdParams>) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<CouponDesign>(
`/admin/coupon-setting?cafe-id=${params.cafeId}`,
ownerHeader(),
);
};

export const getCurrentCouponDesign = async ({
params,
}: QueryReq<CouponIdParams & CafeIdParams>) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<CouponDesign>(
`/admin/coupon-setting/${params.couponId}?cafe-id=${params.cafeId}`,
ownerHeader(),
);
};

// 고객모드 api

export const getCoupons = async () => {
return await api.get<CouponRes>('/coupons', customerHeader());
};
Expand All @@ -85,46 +130,14 @@ export const getStampHistories = async () => {
return await api.get<StampHistoryRes>('/stamp-histories', customerHeader());
};

export const getAdminOAuthToken = async (
{ params }: QueryReq<OAuthTokenParams>,
init: RequestInit = {},
) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<OAuthJWTRes>(
`/admin/login/${params.resourceServer}/token?code=${params.code}`,
init,
);
};

export const getOAuthToken = async (
{ params }: QueryReq<OAuthTokenParams>,
init: RequestInit = {},
) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<OAuthJWTRes>(
`/login/${params.resourceServer}/token?code=${params.code}`,
init,
);
};

export const getCustomerProfile = async () => {
return await api.get<CustomerProfileRes>('/profiles', customerHeader());
};

export const getCouponDesign = async ({ params }: QueryReq<CafeIdParams>) => {
export const getCustomerRegisterType = async ({ params }: QueryReq<PhoneNumberParams>) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<CouponDesign>(
`/admin/coupon-setting?cafe-id=${params.cafeId}`,
ownerHeader(),
);
};

export const getCurrentCouponDesign = async ({
params,
}: QueryReq<CouponIdParams & CafeIdParams>) => {
if (!params) throw new Error(PARAMS_ERROR_MESSAGE);
return await api.get<CouponDesign>(
`/admin/coupon-setting/${params.couponId}?cafe-id=${params.cafeId}`,
ownerHeader(),
return await api.get<CustomerRegisterTypeRes>(
`/profiles/search?phone-number=${params.phoneNumber}`,
customerHeader(),
);
};
14 changes: 13 additions & 1 deletion frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ const request = async (path: string, init?: RequestInit) => {
},
});

if (!response.ok) throw new Error(response.status.toString());
if (!response.ok) {
if (response.status === 401) {
const isAdminRequest = path.startsWith('/admin');
const expiredTokenKey = isAdminRequest ? 'admin-login-token' : 'login-token';
localStorage.setItem(expiredTokenKey, '');
const redirectUrl = `${location.origin}${isAdminRequest ? '/admin' : ''}`;
if (!location.pathname.endsWith('/login')) {
location.href = `${redirectUrl}/login`;
}
}

throw new Error(response.status.toString());
}
return response;
};

Expand Down
5 changes: 5 additions & 0 deletions frontend/src/api/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
CafeIdParams,
CafeRegisterReqBody,
IsFavoritesReqBody,
CustomerLinkDataReqBody,
} from '../types/api/request';

export const postEarnStamp = async ({
Expand Down Expand Up @@ -81,3 +82,7 @@ export const postUploadImage = async (file: File) => {
export const postCustomerPhoneNumber = async ({ body }: MutateReq<RegisterUserReqBody>) => {
return await api.post<RegisterUserReqBody>('/profiles/phone-number', customerHeader(), body);
};

export const postCustomerLinkData = async ({ body }: MutateReq<CustomerLinkDataReqBody>) => {
return await api.post<CustomerLinkDataReqBody>('/profiles/link-data', customerHeader(), body);
};
2 changes: 1 addition & 1 deletion frontend/src/assets/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/book_open_text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/buildings.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/ci_circle_alert.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/fa_location_dot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/gift.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/stamp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/icons/user_list.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8b31ccd

Please sign in to comment.