实现V4目录管理后台CP1后端
This commit is contained in:
@@ -78,6 +78,7 @@ class AuthControllerTest {
|
||||
"RESERVATION_AUDIT_READ",
|
||||
"RESERVATION_INVOICE_GENERATE",
|
||||
"RESERVATION_ROOMING_LIST_GENERATE",
|
||||
"RESERVATION_CATALOG_MANAGE",
|
||||
"HOTEL_SWITCH",
|
||||
"SYSTEM_AUTH_READ",
|
||||
"SYSTEM_USER_MANAGE",
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
package cn.nianxx.thhotel.workflows.reservation.control;
|
||||
|
||||
import static cn.nianxx.thhotel.support.MockMvcAuthTestSupport.loginToken;
|
||||
import static cn.nianxx.thhotel.support.MockMvcAuthTestSupport.performAuthorized;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import cn.nianxx.thhotel.ThHotelApplication;
|
||||
import cn.nianxx.thhotel.platform.hotel.repository.PlatformHotelRepository;
|
||||
import cn.nianxx.thhotel.platform.identity.common.enums.PlatformUserStatus;
|
||||
import cn.nianxx.thhotel.platform.identity.domain.PlatformUserEntity;
|
||||
import cn.nianxx.thhotel.platform.identity.repository.PlatformIdentityRepository;
|
||||
import cn.nianxx.thhotel.platform.identity.service.impl.AuthPasswordService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.time.LocalDateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
@SpringBootTest(
|
||||
classes = ThHotelApplication.class,
|
||||
properties = {
|
||||
"spring.datasource.url=jdbc:h2:mem:reservation_v4_catalog_admin_controller_test;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE",
|
||||
"auth.bootstrap.admin.username=v4-catalog-admin-cp1",
|
||||
"auth.bootstrap.admin.password=Admin@123456",
|
||||
"auth.bootstrap.admin.display-name=V4目录管理管理员",
|
||||
"auth.bootstrap.default-hotel-id=HOTEL-TEST",
|
||||
"auth.bootstrap.default-hotel-name=测试酒店",
|
||||
"auth.bootstrap.default-hotel-time-zone=Asia/Bangkok",
|
||||
"auth.session.ttl-minutes=720"
|
||||
})
|
||||
@AutoConfigureMockMvc
|
||||
@ActiveProfiles("test")
|
||||
class ReservationV4CatalogAdminControllerTest {
|
||||
|
||||
private static final String HOTEL_ID = "HOTEL-TEST";
|
||||
private static final String OTHER_HOTEL_ID = "HOTEL-OTHER";
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private PlatformIdentityRepository identityRepository;
|
||||
|
||||
@Autowired
|
||||
private PlatformHotelRepository hotelRepository;
|
||||
|
||||
@Autowired
|
||||
private AuthPasswordService passwordService;
|
||||
|
||||
private String adminToken;
|
||||
private String noPermissionToken;
|
||||
|
||||
@BeforeEach
|
||||
void ensureNoPermissionUser() {
|
||||
PlatformUserEntity user = identityRepository.findUserByUsername("v4-catalog-admin-no-permission")
|
||||
.orElseGet(() -> {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
PlatformUserEntity created = new PlatformUserEntity();
|
||||
created.setUsername("v4-catalog-admin-no-permission");
|
||||
created.setPasswordHash(passwordService.hash("NoPerm@123456"));
|
||||
created.setDisplayName("V4 目录管理无权限用户");
|
||||
created.setUserStatus(PlatformUserStatus.ACTIVE.name());
|
||||
created.setSuperAdmin(false);
|
||||
created.setPasswordChangedAt(now);
|
||||
created.setCreatedAt(now);
|
||||
created.setUpdatedAt(now);
|
||||
identityRepository.insertUser(created);
|
||||
return created;
|
||||
});
|
||||
hotelRepository.ensureUserHotel(user.getId(), HOTEL_ID, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListAndCreateAccountCatalog() throws Exception {
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/admin/reservation/catalogs/accounts")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "QBD")
|
||||
.param("status", "ACTIVE"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].account_code").value("QBD_TRAVEL"))
|
||||
.andExpect(jsonPath("$.items[0].status").value("ACTIVE"))
|
||||
.andExpect(jsonPath("$.page.page_num").value(1));
|
||||
|
||||
MvcResult created = performAuthorized(mockMvc, adminToken(), post("/api/admin/reservation/catalogs/accounts")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"hotel_id":"HOTEL-TEST",
|
||||
"account_code":"CP1_ACCOUNT",
|
||||
"account_name":"CP1 Managed Account",
|
||||
"market_code":"LEISURE",
|
||||
"source_code":"TRAVEL_AGENT",
|
||||
"metadata_json":"{\\"note\\":\\"manual\\"}"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.account_code").value("CP1_ACCOUNT"))
|
||||
.andExpect(jsonPath("$.account_name").value("CP1 Managed Account"))
|
||||
.andExpect(jsonPath("$.catalog_source").value("SYSTEM_MANAGED"))
|
||||
.andExpect(jsonPath("$.status").value("ACTIVE"))
|
||||
.andReturn();
|
||||
String accountId = objectMapper.readTree(created.getResponse().getContentAsString()).path("id").asText();
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/admin/reservation/catalogs/accounts")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_ACCOUNT"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].account_code").value("CP1_ACCOUNT"))
|
||||
.andExpect(jsonPath("$.items[0].market_code").value("LEISURE"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), post("/api/admin/reservation/catalogs/accounts")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"hotel_id":"HOTEL-TEST",
|
||||
"account_code":"CP1_ACCOUNT",
|
||||
"account_name":"Duplicate Account",
|
||||
"market_code":"LEISURE",
|
||||
"source_code":"TRAVEL_AGENT"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isConflict())
|
||||
.andExpect(jsonPath("$.error_code").value("RESERVATION_CATALOG_CONFLICT"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), put("/api/admin/reservation/catalogs/accounts/{accountId}/status", accountId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"status":"DISABLED"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("DISABLED"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/reservation/lookups/accounts")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_ACCOUNT"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateRoomTypeAndRateCodeCatalogs() throws Exception {
|
||||
performAuthorized(mockMvc, adminToken(), post("/api/admin/reservation/catalogs/room-types")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"hotel_id":"HOTEL-TEST",
|
||||
"code":"CP1_ROOM",
|
||||
"display_name":"CP1 Room",
|
||||
"sort_order":88,
|
||||
"metadata_json":"{\\"adult_capacity\\":2}"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.catalog_type").value("ROOM_TYPE"))
|
||||
.andExpect(jsonPath("$.code").value("CP1_ROOM"))
|
||||
.andExpect(jsonPath("$.catalog_source").value("SYSTEM_MANAGED"))
|
||||
.andExpect(jsonPath("$.status").value("ACTIVE"));
|
||||
|
||||
MvcResult rateCreated = performAuthorized(mockMvc, adminToken(), post("/api/admin/reservation/catalogs/rate-codes")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"hotel_id":"HOTEL-TEST",
|
||||
"code":"CP1_RATE",
|
||||
"display_name":"CP1 Rate",
|
||||
"sort_order":89,
|
||||
"metadata_json":"{\\"pricing_available\\":false}"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.catalog_type").value("RATE_CODE"))
|
||||
.andExpect(jsonPath("$.code").value("CP1_RATE"))
|
||||
.andExpect(jsonPath("$.catalog_source").value("SYSTEM_MANAGED"))
|
||||
.andExpect(jsonPath("$.status").value("ACTIVE"))
|
||||
.andReturn();
|
||||
String rateCodeId = objectMapper.readTree(rateCreated.getResponse().getContentAsString()).path("id").asText();
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), put("/api/admin/reservation/catalogs/rate-codes/{catalogId}/status", rateCodeId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"status":"DISABLED"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("DISABLED"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/reservation/lookups/rate-codes")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_RATE"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEnableAndDisableCatalogAndKeepLookupActiveOnly() throws Exception {
|
||||
MvcResult created = performAuthorized(mockMvc, adminToken(), post("/api/admin/reservation/catalogs/room-types")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"hotel_id":"HOTEL-TEST",
|
||||
"code":"CP1_DISABLE_ROOM",
|
||||
"display_name":"CP1 Disabled Room",
|
||||
"sort_order":91,
|
||||
"metadata_json":"{\\"adult_capacity\\":2}"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
String roomTypeId = objectMapper.readTree(created.getResponse().getContentAsString()).path("id").asText();
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), put("/api/admin/reservation/catalogs/room-types/{catalogId}/status", roomTypeId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"status":"DISABLED"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("DISABLED"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/admin/reservation/catalogs/room-types")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_DISABLE_ROOM")
|
||||
.param("status", "DISABLED"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].code").value("CP1_DISABLE_ROOM"))
|
||||
.andExpect(jsonPath("$.items[0].status").value("DISABLED"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/reservation/lookups/room-types")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_DISABLE_ROOM"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items").isEmpty());
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), put("/api/admin/reservation/catalogs/room-types/{catalogId}/status", roomTypeId)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"status":"ACTIVE"}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.status").value("ACTIVE"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/reservation/lookups/room-types")
|
||||
.param("hotel_id", HOTEL_ID)
|
||||
.param("keyword", "CP1_DISABLE_ROOM"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.items[0].code").value("CP1_DISABLE_ROOM"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectCatalogAdminWithoutPermissionOrHotelAccess() throws Exception {
|
||||
mockMvc.perform(get("/api/admin/reservation/catalogs/accounts")
|
||||
.param("hotel_id", HOTEL_ID))
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
performAuthorized(mockMvc, noPermissionToken(), get("/api/admin/reservation/catalogs/accounts")
|
||||
.param("hotel_id", HOTEL_ID))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath("$.error_code").value("ADMIN_PERMISSION_DENIED"));
|
||||
|
||||
performAuthorized(mockMvc, adminToken(), get("/api/admin/reservation/catalogs/accounts")
|
||||
.param("hotel_id", OTHER_HOTEL_ID))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath("$.error_code").value("HOTEL_ACCESS_DENIED"));
|
||||
}
|
||||
|
||||
private String adminToken() throws Exception {
|
||||
if (adminToken == null) {
|
||||
adminToken = loginToken(mockMvc, "v4-catalog-admin-cp1", "Admin@123456");
|
||||
}
|
||||
return adminToken;
|
||||
}
|
||||
|
||||
private String noPermissionToken() throws Exception {
|
||||
if (noPermissionToken == null) {
|
||||
noPermissionToken = loginToken(mockMvc, "v4-catalog-admin-no-permission", "NoPerm@123456");
|
||||
}
|
||||
return noPermissionToken;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user