diff --git a/backend/database/cabi_local.sql b/backend/database/cabi_local.sql index 668508da5..e0861ae15 100644 --- a/backend/database/cabi_local.sql +++ b/backend/database/cabi_local.sql @@ -121,22 +121,22 @@ LOCK TABLES `cabinet` WRITE; /*!40000 ALTER TABLE `cabinet` DISABLE KEYS */; INSERT INTO `cabinet` -VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), - (2, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 90, 5, '', '', 1), - (3, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 91, 5, '', '', 1), - (4, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 92, 5, '', '', 1), - (5, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 93, 5, '', '', 1), - (6, 5, 0, 'SHARE', 3, 'AVAILABLE', '', 94, 5, '', '', 1), - (7, 6, 0, 'SHARE', 3, 'AVAILABLE', '', 95, 5, '', '', 1), - (8, 7, 0, 'SHARE', 3, 'AVAILABLE', '', 96, 5, '', '', 1), - (9, 8, 0, 'SHARE', 3, 'AVAILABLE', '', 97, 5, '', '', 1), - (10, 9, 0, 'SHARE', 3, 'AVAILABLE', '', 98, 5, '', '', 1), - (11, 10, 0, 'SHARE', 3, 'AVAILABLE', '', 99, 5, '', '', 1), - (12, 11, 0, 'SHARE', 3, 'AVAILABLE', '', 100, 5, '', '', 1), - (13, 12, 0, 'SHARE', 3, 'AVAILABLE', '', 101, 5, '', '', 1), - (14, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 102, 5, '', '', 1), - (15, 1, 1, 'SHARE', 3, 'AVAILABLE', '', 103, 5, '', '', 1), - (16, 2, 1, 'SHARE', 3, 'AVAILABLE', '', 104, 5, '', '', 1), +VALUES (1, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 89, 5, '', '', 1), + (2, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 90, 5, '', '', 1), + (3, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 91, 5, '', '', 1), + (4, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 92, 5, '', '', 1), + (5, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 93, 5, '', '', 1), + (6, 5, 0, 'SHARE', 4, 'AVAILABLE', '', 94, 5, '', '', 1), + (7, 6, 0, 'SHARE', 4, 'AVAILABLE', '', 95, 5, '', '', 1), + (8, 7, 0, 'SHARE', 4, 'AVAILABLE', '', 96, 5, '', '', 1), + (9, 8, 0, 'SHARE', 4, 'AVAILABLE', '', 97, 5, '', '', 1), + (10, 9, 0, 'SHARE', 4, 'AVAILABLE', '', 98, 5, '', '', 1), + (11, 10, 0, 'SHARE', 4, 'AVAILABLE', '', 99, 5, '', '', 1), + (12, 11, 0, 'SHARE', 4, 'AVAILABLE', '', 100, 5, '', '', 1), + (13, 12, 0, 'SHARE', 4, 'AVAILABLE', '', 101, 5, '', '', 1), + (14, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 102, 5, '', '', 1), + (15, 1, 1, 'SHARE', 4, 'AVAILABLE', '', 103, 5, '', '', 1), + (16, 2, 1, 'SHARE', 4, 'AVAILABLE', '', 104, 5, '', '', 1), (17, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 105, 5, '', '', 1), (18, 4, 1, 'PRIVATE', 1, 'AVAILABLE', '', 106, 5, '', '', 1), (19, 5, 1, 'PRIVATE', 1, 'AVAILABLE', '', 107, 5, '', '', 1), @@ -173,9 +173,9 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (50, 10, 3, 'PRIVATE', 1, 'AVAILABLE', '', 138, 5, '', '', 1), (51, 11, 3, 'PRIVATE', 1, 'AVAILABLE', '', 139, 5, '', '', 1), (52, 12, 3, 'PRIVATE', 1, 'AVAILABLE', '', 140, 5, '', '', 1), - (53, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 141, 6, '', '', 1), - (54, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 142, 6, '', '', 1), - (55, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 143, 6, '', '', 1), + (53, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 141, 6, '', '', 1), + (54, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 142, 6, '', '', 1), + (55, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 143, 6, '', '', 1), (56, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 144, 6, '', '', 1), (57, 0, 2, 'PRIVATE', 1, 'AVAILABLE', '', 145, 6, '', '', 1), (58, 1, 2, 'PRIVATE', 1, 'AVAILABLE', '', 146, 6, '', '', 1), @@ -201,11 +201,11 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (78, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 34, 2, '', '', 1), (79, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 35, 2, '', '', 1), (80, 4, 3, 'PRIVATE', 1, 'AVAILABLE', '', 36, 2, '', '', 1), - (81, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 1, 1, '', '', 1), - (82, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 2, 1, '', '', 1), - (83, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 3, 1, '', '', 1), - (84, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 4, 1, '', '', 1), - (85, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 5, 1, '', '', 1), + (81, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 1, 1, '', '', 1), + (82, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 2, 1, '', '', 1), + (83, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 3, 1, '', '', 1), + (84, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 4, 1, '', '', 1), + (85, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 5, 1, '', '', 1), (86, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 6, 1, '', '', 1), (87, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 7, 1, '', '', 1), (88, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 8, 1, '', '', 1), @@ -217,22 +217,22 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (94, 1, 3, 'PRIVATE', 1, 'AVAILABLE', '', 14, 1, '', '', 1), (95, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 15, 1, '', '', 1), (96, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 16, 1, '', '', 1), - (97, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 37, 3, '', '', 1), - (98, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 38, 3, '', '', 1), - (99, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 39, 3, '', '', 1), - (100, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 40, 3, '', '', 1), - (101, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 41, 3, '', '', 1), - (102, 5, 0, 'SHARE', 3, 'AVAILABLE', '', 42, 3, '', '', 1), - (103, 6, 0, 'SHARE', 3, 'AVAILABLE', '', 43, 3, '', '', 1), - (104, 7, 0, 'SHARE', 3, 'AVAILABLE', '', 44, 3, '', '', 1), - (105, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 45, 3, '', '', 1), - (106, 1, 1, 'SHARE', 3, 'AVAILABLE', '', 46, 3, '', '', 1), - (107, 2, 1, 'SHARE', 3, 'AVAILABLE', '', 47, 3, '', '', 1), - (108, 3, 1, 'SHARE', 3, 'AVAILABLE', '', 48, 3, '', '', 1), - (109, 4, 1, 'SHARE', 3, 'AVAILABLE', '', 49, 3, '', '', 1), - (110, 5, 1, 'SHARE', 3, 'AVAILABLE', '', 50, 3, '', '', 1), - (111, 6, 1, 'SHARE', 3, 'AVAILABLE', '', 51, 3, '', '', 1), - (112, 7, 1, 'SHARE', 3, 'AVAILABLE', '', 52, 3, '', '', 1), + (97, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 37, 3, '', '', 1), + (98, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 38, 3, '', '', 1), + (99, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 39, 3, '', '', 1), + (100, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 40, 3, '', '', 1), + (101, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 41, 3, '', '', 1), + (102, 5, 0, 'SHARE', 4, 'AVAILABLE', '', 42, 3, '', '', 1), + (103, 6, 0, 'SHARE', 4, 'AVAILABLE', '', 43, 3, '', '', 1), + (104, 7, 0, 'SHARE', 4, 'AVAILABLE', '', 44, 3, '', '', 1), + (105, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 45, 3, '', '', 1), + (106, 1, 1, 'SHARE', 4, 'AVAILABLE', '', 46, 3, '', '', 1), + (107, 2, 1, 'SHARE', 4, 'AVAILABLE', '', 47, 3, '', '', 1), + (108, 3, 1, 'SHARE', 4, 'AVAILABLE', '', 48, 3, '', '', 1), + (109, 4, 1, 'SHARE', 4, 'AVAILABLE', '', 49, 3, '', '', 1), + (110, 5, 1, 'SHARE', 4, 'AVAILABLE', '', 50, 3, '', '', 1), + (111, 6, 1, 'SHARE', 4, 'AVAILABLE', '', 51, 3, '', '', 1), + (112, 7, 1, 'SHARE', 4, 'AVAILABLE', '', 52, 3, '', '', 1), (113, 0, 2, 'PRIVATE', 1, 'AVAILABLE', '', 53, 3, '', '', 1), (114, 1, 2, 'PRIVATE', 1, 'AVAILABLE', '', 54, 3, '', '', 1), (115, 2, 2, 'PRIVATE', 1, 'AVAILABLE', '', 55, 3, '', '', 1), @@ -269,22 +269,22 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (146, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 86, 4, '', '', 1), (147, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 87, 4, '', '', 1), (148, 4, 3, 'PRIVATE', 1, 'AVAILABLE', '', 88, 4, '', '', 1), - (149, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 37, 16, '', '', 1), - (150, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 38, 16, '', '', 1), - (151, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 39, 16, '', '', 1), - (152, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 40, 16, '', '', 1), - (153, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 41, 16, '', '', 1), - (154, 5, 0, 'SHARE', 3, 'AVAILABLE', '', 42, 16, '', '', 1), - (155, 6, 0, 'SHARE', 3, 'AVAILABLE', '', 43, 16, '', '', 1), - (156, 7, 0, 'SHARE', 3, 'AVAILABLE', '', 44, 16, '', '', 1), - (157, 8, 0, 'SHARE', 3, 'AVAILABLE', '', 45, 16, '', '', 1), - (158, 9, 0, 'SHARE', 3, 'AVAILABLE', '', 46, 16, '', '', 1), - (159, 10, 0, 'SHARE', 3, 'AVAILABLE', '', 47, 16, '', '', 1), - (160, 11, 0, 'SHARE', 3, 'AVAILABLE', '', 48, 16, '', '', 1), - (161, 12, 0, 'SHARE', 3, 'AVAILABLE', '', 49, 16, '', '', 1), - (162, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 50, 16, '', '', 1), - (163, 1, 1, 'SHARE', 3, 'AVAILABLE', '', 51, 16, '', '', 1), - (164, 2, 1, 'SHARE', 3, 'AVAILABLE', '', 52, 16, '', '', 1), + (149, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 37, 16, '', '', 1), + (150, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 38, 16, '', '', 1), + (151, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 39, 16, '', '', 1), + (152, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 40, 16, '', '', 1), + (153, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 41, 16, '', '', 1), + (154, 5, 0, 'SHARE', 4, 'AVAILABLE', '', 42, 16, '', '', 1), + (155, 6, 0, 'SHARE', 4, 'AVAILABLE', '', 43, 16, '', '', 1), + (156, 7, 0, 'SHARE', 4, 'AVAILABLE', '', 44, 16, '', '', 1), + (157, 8, 0, 'SHARE', 4, 'AVAILABLE', '', 45, 16, '', '', 1), + (158, 9, 0, 'SHARE', 4, 'AVAILABLE', '', 46, 16, '', '', 1), + (159, 10, 0, 'SHARE', 4, 'AVAILABLE', '', 47, 16, '', '', 1), + (160, 11, 0, 'SHARE', 4, 'AVAILABLE', '', 48, 16, '', '', 1), + (161, 12, 0, 'SHARE', 4, 'AVAILABLE', '', 49, 16, '', '', 1), + (162, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 50, 16, '', '', 1), + (163, 1, 1, 'SHARE', 4, 'AVAILABLE', '', 51, 16, '', '', 1), + (164, 2, 1, 'SHARE', 4, 'AVAILABLE', '', 52, 16, '', '', 1), (165, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 53, 16, '', '', 1), (166, 4, 1, 'PRIVATE', 1, 'AVAILABLE', '', 54, 16, '', '', 1), (167, 5, 1, 'PRIVATE', 1, 'AVAILABLE', '', 55, 16, '', '', 1), @@ -321,10 +321,10 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (198, 10, 3, 'PRIVATE', 1, 'AVAILABLE', '', 86, 16, '', '', 1), (199, 11, 3, 'PRIVATE', 1, 'AVAILABLE', '', 87, 16, '', '', 1), (200, 12, 3, 'PRIVATE', 1, 'AVAILABLE', '', 88, 16, '', '', 1), - (201, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 17, '', '', 1), - (202, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 90, 17, '', '', 1), - (203, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 91, 17, '', '', 1), - (204, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 92, 17, '', '', 1), + (201, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 89, 17, '', '', 1), + (202, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 90, 17, '', '', 1), + (203, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 91, 17, '', '', 1), + (204, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 92, 17, '', '', 1), (205, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 93, 17, '', '', 1), (206, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 94, 17, '', '', 1), (207, 0, 2, 'PRIVATE', 1, 'AVAILABLE', '', 95, 17, '', '', 1), @@ -333,12 +333,12 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (210, 0, 3, 'PRIVATE', 1, 'AVAILABLE', '', 98, 17, '', '', 1), (211, 1, 3, 'PRIVATE', 1, 'AVAILABLE', '', 99, 17, '', '', 1), (212, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 100, 17, '', '', 1), - (213, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 17, 15, '', '', 1), - (214, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 18, 15, '', '', 1), - (215, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 19, 15, '', '', 1), - (216, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 20, 15, '', '', 1), - (217, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 21, 15, '', '', 1), - (218, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 22, 15, '', '', 1), + (213, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 17, 15, '', '', 1), + (214, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 18, 15, '', '', 1), + (215, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 19, 15, '', '', 1), + (216, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 20, 15, '', '', 1), + (217, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 21, 15, '', '', 1), + (218, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 22, 15, '', '', 1), (219, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 23, 15, '', '', 1), (220, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 24, 15, '', '', 1), (221, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 25, 15, '', '', 1), @@ -353,11 +353,11 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (230, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 34, 15, '', '', 1), (231, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 35, 15, '', '', 1), (232, 4, 3, 'PRIVATE', 1, 'AVAILABLE', '', 36, 15, '', '', 1), - (233, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 1, 14, '', '', 1), - (234, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 2, 14, '', '', 1), - (235, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 3, 14, '', '', 1), - (236, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 4, 14, '', '', 1), - (237, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 5, 14, '', '', 1), + (233, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 1, 14, '', '', 1), + (234, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 2, 14, '', '', 1), + (235, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 3, 14, '', '', 1), + (236, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 4, 14, '', '', 1), + (237, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 5, 14, '', '', 1), (238, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 6, 14, '', '', 1), (239, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 7, 14, '', '', 1), (240, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 8, 14, '', '', 1), @@ -369,22 +369,22 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (246, 1, 3, 'PRIVATE', 1, 'AVAILABLE', '', 14, 14, '', '', 1), (247, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 15, 14, '', '', 1), (248, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 16, 14, '', '', 1), - (249, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 37, 20, '', '', 1), - (250, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 38, 20, '', '', 1), - (251, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 39, 20, '', '', 1), - (252, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 40, 20, '', '', 1), - (253, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 41, 20, '', '', 1), - (254, 5, 0, 'SHARE', 3, 'AVAILABLE', '', 42, 20, '', '', 1), - (255, 6, 0, 'SHARE', 3, 'AVAILABLE', '', 43, 20, '', '', 1), - (256, 7, 0, 'SHARE', 3, 'AVAILABLE', '', 44, 20, '', '', 1), - (257, 8, 0, 'SHARE', 3, 'AVAILABLE', '', 45, 20, '', '', 1), - (258, 9, 0, 'SHARE', 3, 'AVAILABLE', '', 46, 20, '', '', 1), - (259, 10, 0, 'SHARE', 3, 'AVAILABLE', '', 47, 20, '', '', 1), - (260, 11, 0, 'SHARE', 3, 'AVAILABLE', '', 48, 20, '', '', 1), - (261, 12, 0, 'SHARE', 3, 'AVAILABLE', '', 49, 20, '', '', 1), - (262, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 50, 20, '', '', 1), - (263, 1, 1, 'SHARE', 3, 'AVAILABLE', '', 51, 20, '', '', 1), - (264, 2, 1, 'SHARE', 3, 'AVAILABLE', '', 52, 20, '', '', 1), + (249, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 37, 20, '', '', 1), + (250, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 38, 20, '', '', 1), + (251, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 39, 20, '', '', 1), + (252, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 40, 20, '', '', 1), + (253, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 41, 20, '', '', 1), + (254, 5, 0, 'SHARE', 4, 'AVAILABLE', '', 42, 20, '', '', 1), + (255, 6, 0, 'SHARE', 4, 'AVAILABLE', '', 43, 20, '', '', 1), + (256, 7, 0, 'SHARE', 4, 'AVAILABLE', '', 44, 20, '', '', 1), + (257, 8, 0, 'SHARE', 4, 'AVAILABLE', '', 45, 20, '', '', 1), + (258, 9, 0, 'SHARE', 4, 'AVAILABLE', '', 46, 20, '', '', 1), + (259, 10, 0, 'SHARE', 4, 'AVAILABLE', '', 47, 20, '', '', 1), + (260, 11, 0, 'SHARE', 4, 'AVAILABLE', '', 48, 20, '', '', 1), + (261, 12, 0, 'SHARE', 4, 'AVAILABLE', '', 49, 20, '', '', 1), + (262, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 50, 20, '', '', 1), + (263, 1, 1, 'SHARE', 4, 'AVAILABLE', '', 51, 20, '', '', 1), + (264, 2, 1, 'SHARE', 4, 'AVAILABLE', '', 52, 20, '', '', 1), (265, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 53, 20, '', '', 1), (266, 4, 1, 'PRIVATE', 1, 'AVAILABLE', '', 54, 20, '', '', 1), (267, 5, 1, 'PRIVATE', 1, 'AVAILABLE', '', 55, 20, '', '', 1), @@ -421,12 +421,12 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (298, 10, 3, 'PRIVATE', 1, 'AVAILABLE', '', 86, 20, '', '', 1), (299, 11, 3, 'PRIVATE', 1, 'AVAILABLE', '', 87, 20, '', '', 1), (300, 12, 3, 'PRIVATE', 1, 'AVAILABLE', '', 88, 20, '', '', 1), - (301, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 17, 19, '', '', 1), - (302, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 18, 19, '', '', 1), - (303, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 19, 19, '', '', 1), - (304, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 20, 19, '', '', 1), - (305, 4, 0, 'SHARE', 3, 'AVAILABLE', '', 21, 19, '', '', 1), - (306, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 22, 19, '', '', 1), + (301, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 17, 19, '', '', 1), + (302, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 18, 19, '', '', 1), + (303, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 19, 19, '', '', 1), + (304, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 20, 19, '', '', 1), + (305, 4, 0, 'SHARE', 4, 'AVAILABLE', '', 21, 19, '', '', 1), + (306, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 22, 19, '', '', 1), (307, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 23, 19, '', '', 1), (308, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 24, 19, '', '', 1), (309, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 25, 19, '', '', 1), @@ -441,11 +441,11 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (318, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 34, 19, '', '', 1), (319, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 35, 19, '', '', 1), (320, 4, 3, 'PRIVATE', 1, 'AVAILABLE', '', 36, 19, '', '', 1), - (321, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 1, 18, '', '', 1), - (322, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 2, 18, '', '', 1), - (323, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 3, 18, '', '', 1), - (324, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 4, 18, '', '', 1), - (325, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 5, 18, '', '', 1), + (321, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 1, 18, '', '', 1), + (322, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 2, 18, '', '', 1), + (323, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 3, 18, '', '', 1), + (324, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 4, 18, '', '', 1), + (325, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 5, 18, '', '', 1), (326, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 6, 18, '', '', 1), (327, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 7, 18, '', '', 1), (328, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 8, 18, '', '', 1), @@ -457,10 +457,10 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (334, 1, 3, 'PRIVATE', 1, 'AVAILABLE', '', 14, 18, '', '', 1), (335, 2, 3, 'PRIVATE', 1, 'AVAILABLE', '', 15, 18, '', '', 1), (336, 3, 3, 'PRIVATE', 1, 'AVAILABLE', '', 16, 18, '', '', 1), - (337, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 21, '', '', 1), - (338, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 90, 21, '', '', 1), - (339, 0, 1, 'SHARE', 3, 'AVAILABLE', '', 91, 21, '', '', 1), - (340, 1, 1, 'SHARE', 3, 'AVAILABLE', '', 92, 21, '', '', 1), + (337, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 89, 21, '', '', 1), + (338, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 90, 21, '', '', 1), + (339, 0, 1, 'SHARE', 4, 'AVAILABLE', '', 91, 21, '', '', 1), + (340, 1, 1, 'SHARE', 4, 'AVAILABLE', '', 92, 21, '', '', 1), (341, 0, 2, 'PRIVATE', 1, 'AVAILABLE', '', 93, 21, '', '', 1), (342, 1, 2, 'PRIVATE', 1, 'AVAILABLE', '', 94, 21, '', '', 1), (343, 0, 3, 'PRIVATE', 1, 'AVAILABLE', '', 95, 21, '', '', 1), @@ -490,32 +490,32 @@ VALUES (1, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 89, 5, '', '', 1), (367, 2, 1, 'CLUB', 1, 'AVAILABLE', '', 23, 9, '', '', 1), (368, 3, 1, 'CLUB', 1, 'AVAILABLE', '', 24, 9, '', '', 1), (369, 0, 0, 'CLUB', 1, 'AVAILABLE', '', 25, 10, '', '', 1), - (370, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 26, 10, '', '', 1), - (371, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 27, 10, '', '', 1), - (372, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 28, 10, '', '', 1), + (370, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 26, 10, '', '', 1), + (371, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 27, 10, '', '', 1), + (372, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 28, 10, '', '', 1), (373, 0, 1, 'CLUB', 1, 'AVAILABLE', '', 29, 10, '', '', 1), (374, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 30, 10, '', '', 1), (375, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 31, 10, '', '', 1), (376, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 32, 10, '', '', 1), - (377, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 33, 11, '', '', 1), - (378, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 34, 11, '', '', 1), - (379, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 35, 11, '', '', 1), - (380, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 36, 11, '', '', 1), + (377, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 33, 11, '', '', 1), + (378, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 34, 11, '', '', 1), + (379, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 35, 11, '', '', 1), + (380, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 36, 11, '', '', 1), (381, 0, 1, 'PRIVATE', 1, 'AVAILABLE', '', 37, 11, '', '', 1), (382, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 38, 11, '', '', 1), (383, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 39, 11, '', '', 1), (384, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 40, 11, '', '', 1), - (385, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 41, 12, '', '', 1), - (386, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 42, 12, '', '', 1), - (387, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 43, 12, '', '', 1), - (388, 3, 0, 'SHARE', 3, 'AVAILABLE', '', 44, 12, '', '', 1), + (385, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 41, 12, '', '', 1), + (386, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 42, 12, '', '', 1), + (387, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 43, 12, '', '', 1), + (388, 3, 0, 'SHARE', 4, 'AVAILABLE', '', 44, 12, '', '', 1), (389, 0, 1, 'PRIVATE', 1, 'AVAILABLE', '', 45, 12, '', '', 1), (390, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 46, 12, '', '', 1), (391, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 47, 12, '', '', 1), (392, 3, 1, 'PRIVATE', 1, 'AVAILABLE', '', 48, 12, '', '', 1), - (393, 0, 0, 'SHARE', 3, 'AVAILABLE', '', 49, 13, '', '', 1), - (394, 1, 0, 'SHARE', 3, 'AVAILABLE', '', 50, 13, '', '', 1), - (395, 2, 0, 'SHARE', 3, 'AVAILABLE', '', 51, 13, '', '', 1), + (393, 0, 0, 'SHARE', 4, 'AVAILABLE', '', 49, 13, '', '', 1), + (394, 1, 0, 'SHARE', 4, 'AVAILABLE', '', 50, 13, '', '', 1), + (395, 2, 0, 'SHARE', 4, 'AVAILABLE', '', 51, 13, '', '', 1), (396, 0, 1, 'PRIVATE', 1, 'AVAILABLE', '', 52, 13, '', '', 1), (397, 1, 1, 'PRIVATE', 1, 'AVAILABLE', '', 53, 13, '', '', 1), (398, 2, 1, 'PRIVATE', 1, 'AVAILABLE', '', 54, 13, '', '', 1); @@ -742,7 +742,8 @@ VALUES (1, 0, 'EXTENSION_PREV', 'EXTENSION'), (17, 200, 'ADMIN_REWARD_200', 'ADMIN_REWARD'), (18, 500, 'ADMIN_REWARD_500', 'ADMIN_REWARD'), (19, 1000, 'ADMIN_REWARD_1000', 'ADMIN_REWARD'), - (20, 2000, 'ADMIN_REWARD_2000', 'ADMIN_REWARD'); + (20, 2000, 'ADMIN_REWARD_2000', 'ADMIN_REWARD'), + (21, 10, 'ADMIN_REWARD_COIN', 'ADMIN_REWARD'); /*!40000 ALTER TABLE `item` ENABLE KEYS */; UNLOCK TABLES; @@ -758,6 +759,7 @@ CREATE TABLE `item_history` `user_id` bigint(20) NOT NULL, `purchase_at` datetime(6) NOT NULL, `used_at` datetime(6) DEFAULT NULL, + `amount` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `item_history_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`), CONSTRAINT `item_history_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) diff --git a/backend/src/main/java/org/ftclub/cabinet/admin/dto/AdminCoinAssignRequestDto.java b/backend/src/main/java/org/ftclub/cabinet/admin/dto/AdminCoinAssignRequestDto.java new file mode 100644 index 000000000..beb456125 --- /dev/null +++ b/backend/src/main/java/org/ftclub/cabinet/admin/dto/AdminCoinAssignRequestDto.java @@ -0,0 +1,15 @@ +package org.ftclub.cabinet.admin.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.ftclub.cabinet.item.domain.Sku; + +@Getter +@AllArgsConstructor +public class AdminCoinAssignRequestDto { + + private List userIds; + private Sku itemSku; + private Long amount; +} diff --git a/backend/src/main/java/org/ftclub/cabinet/admin/item/controller/AdminItemController.java b/backend/src/main/java/org/ftclub/cabinet/admin/item/controller/AdminItemController.java index f3932160e..46035ef85 100644 --- a/backend/src/main/java/org/ftclub/cabinet/admin/item/controller/AdminItemController.java +++ b/backend/src/main/java/org/ftclub/cabinet/admin/item/controller/AdminItemController.java @@ -1,14 +1,26 @@ package org.ftclub.cabinet.admin.item.controller; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.ftclub.cabinet.admin.dto.AdminItemHistoryPaginationDto; import org.ftclub.cabinet.admin.item.service.AdminItemFacadeService; -import org.ftclub.cabinet.admin.statistics.service.AdminStatisticsFacadeService; import org.ftclub.cabinet.auth.domain.AuthGuard; import org.ftclub.cabinet.auth.domain.AuthLevel; import org.ftclub.cabinet.dto.ItemAssignRequestDto; import org.ftclub.cabinet.dto.ItemCreateDto; +import org.ftclub.cabinet.dto.ItemDetailsDto; +import org.ftclub.cabinet.dto.ItemStoreDto; +import org.ftclub.cabinet.dto.ItemStoreResponseDto; +import org.ftclub.cabinet.item.domain.Item; +import org.ftclub.cabinet.item.domain.ItemType; +import org.ftclub.cabinet.item.service.ItemQueryService; import org.ftclub.cabinet.log.Logging; +import org.ftclub.cabinet.mapper.ItemMapper; import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -24,8 +36,8 @@ public class AdminItemController { private final AdminItemFacadeService adminItemFacadeService; - private final AdminStatisticsFacadeService adminStatisticsFacadeService; - + private final ItemQueryService itemQueryService; + private final ItemMapper itemMapper; @PostMapping("") @AuthGuard(level = AuthLevel.ADMIN_ONLY) @@ -34,11 +46,29 @@ public void createItem(@RequestBody ItemCreateDto itemCreateDto) { itemCreateDto.getSku(), itemCreateDto.getType()); } + @GetMapping("") + @AuthGuard(level = AuthLevel.ADMIN_ONLY) + public ItemStoreResponseDto getAllItems() { + List allItems = itemQueryService.getAllItems(); + Map> itemMap = allItems.stream() + .collect(groupingBy(Item::getType, + mapping(itemMapper::toItemDetailsDto, Collectors.toList()))); + List result = itemMap.entrySet().stream() + .map(entry -> { + ItemStoreDto itemStoreDto = itemMapper.toItemStoreDto(entry.getKey(), + entry.getValue()); + itemStoreDto.sortBySkuASC(); + return itemStoreDto; + }) + .collect(Collectors.toList()); + return new ItemStoreResponseDto(result); + } + @PostMapping("/assign") @AuthGuard(level = AuthLevel.ADMIN_ONLY) public void assignItem(@RequestBody ItemAssignRequestDto itemAssignRequestDto) { adminItemFacadeService.assignItem(itemAssignRequestDto.getUserIds(), - itemAssignRequestDto.getItemSku()); + itemAssignRequestDto.getItemSku(), itemAssignRequestDto.getAmount()); } /** diff --git a/backend/src/main/java/org/ftclub/cabinet/admin/item/service/AdminItemFacadeService.java b/backend/src/main/java/org/ftclub/cabinet/admin/item/service/AdminItemFacadeService.java index d8c456f03..dda0e8fed 100644 --- a/backend/src/main/java/org/ftclub/cabinet/admin/item/service/AdminItemFacadeService.java +++ b/backend/src/main/java/org/ftclub/cabinet/admin/item/service/AdminItemFacadeService.java @@ -15,11 +15,10 @@ import org.ftclub.cabinet.item.service.ItemCommandService; import org.ftclub.cabinet.item.service.ItemHistoryCommandService; import org.ftclub.cabinet.item.service.ItemHistoryQueryService; +import org.ftclub.cabinet.item.service.ItemPolicyService; import org.ftclub.cabinet.item.service.ItemQueryService; -import org.ftclub.cabinet.item.service.ItemRedisService; import org.ftclub.cabinet.mapper.ItemMapper; import org.ftclub.cabinet.user.service.UserCommandService; -import org.ftclub.cabinet.user.service.UserQueryService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -33,11 +32,10 @@ public class AdminItemFacadeService { private final ItemCommandService itemCommandService; private final ItemHistoryQueryService itemHistoryQueryService; private final ItemHistoryCommandService itemHistoryCommandService; + private final ItemPolicyService itemPolicyService; private final ItemMapper itemMapper; - private final ItemRedisService itemRedisService; private final UserCommandService userCommandService; - private final UserQueryService userQueryService; @Transactional public void createItem(Integer Price, Sku sku, ItemType type) { @@ -45,22 +43,19 @@ public void createItem(Integer Price, Sku sku, ItemType type) { } @Transactional - public void assignItem(List userIds, Sku sku) { + public void assignItem(List userIds, Sku sku, Long amount) { Item item = itemQueryService.getBySku(sku); - Long price = item.getPrice(); LocalDateTime now = null; - if (price > 0) { + Long coinAmount = item.getPrice(); + if (sku.equals(Sku.ADMIN_REWARD_COIN)) { + itemPolicyService.verifyCoinAmount(amount); + coinAmount = amount; + } + if (coinAmount > 0) { now = LocalDateTime.now(); - userIds.forEach(userId -> { - long coinAmount = userQueryService.getUser(userId).getCoin(); - itemRedisService.saveCoinCount(userId, coinAmount + item.getPrice()); - - long totalCoinSupply = itemRedisService.getTotalCoinSupply(); - itemRedisService.saveTotalCoinSupply(totalCoinSupply + item.getPrice()); - userCommandService.updateCoinAmount(userId, price); - }); + userCommandService.addBulkCoin(userIds, coinAmount); } - itemHistoryCommandService.createItemHistories(userIds, item.getId(), now); + itemHistoryCommandService.createItemHistories(userIds, item.getId(), now, coinAmount); } @Transactional(readOnly = true) diff --git a/backend/src/main/java/org/ftclub/cabinet/dto/CoinHistoryDto.java b/backend/src/main/java/org/ftclub/cabinet/dto/CoinHistoryDto.java index 023469920..ef2b1afa3 100644 --- a/backend/src/main/java/org/ftclub/cabinet/dto/CoinHistoryDto.java +++ b/backend/src/main/java/org/ftclub/cabinet/dto/CoinHistoryDto.java @@ -13,7 +13,7 @@ public class CoinHistoryDto { private LocalDateTime date; - private Integer amount; + private Long amount; private String history; private String itemDetails; } diff --git a/backend/src/main/java/org/ftclub/cabinet/dto/ItemAssignRequestDto.java b/backend/src/main/java/org/ftclub/cabinet/dto/ItemAssignRequestDto.java index 20770c617..e8708378f 100644 --- a/backend/src/main/java/org/ftclub/cabinet/dto/ItemAssignRequestDto.java +++ b/backend/src/main/java/org/ftclub/cabinet/dto/ItemAssignRequestDto.java @@ -15,4 +15,5 @@ public class ItemAssignRequestDto { private Sku itemSku; private List userIds; + private Long amount; } diff --git a/backend/src/main/java/org/ftclub/cabinet/exception/ExceptionStatus.java b/backend/src/main/java/org/ftclub/cabinet/exception/ExceptionStatus.java index 39c570da2..f5047d2c3 100644 --- a/backend/src/main/java/org/ftclub/cabinet/exception/ExceptionStatus.java +++ b/backend/src/main/java/org/ftclub/cabinet/exception/ExceptionStatus.java @@ -84,7 +84,8 @@ public enum ExceptionStatus { INVALID_JWT_TOKEN(HttpStatus.BAD_REQUEST, "토큰이 없거나, 유효하지 않은 JWT 토큰입니다."), NOT_FOUND_SECTION(HttpStatus.BAD_REQUEST, "사물함 구역 정보를 찾을 수 없습니다."), ITEM_NOT_OWNED(HttpStatus.BAD_REQUEST, "해당 아이템을 보유하고 있지 않습니다"), - ITEM_USE_DUPLICATED(HttpStatus.FORBIDDEN, "아이템이 중복 사용되었습니다."); + ITEM_USE_DUPLICATED(HttpStatus.FORBIDDEN, "아이템이 중복 사용되었습니다."), + INVALID_AMOUNT(HttpStatus.BAD_REQUEST, "코인 지급양은 비어있을 수 없습니다."); final private int statusCode; final private String message; diff --git a/backend/src/main/java/org/ftclub/cabinet/item/domain/ItemHistory.java b/backend/src/main/java/org/ftclub/cabinet/item/domain/ItemHistory.java index 2784a5f72..8f85c32c4 100644 --- a/backend/src/main/java/org/ftclub/cabinet/item/domain/ItemHistory.java +++ b/backend/src/main/java/org/ftclub/cabinet/item/domain/ItemHistory.java @@ -75,11 +75,14 @@ public class ItemHistory { @Column(name = "USER_ID", nullable = false) private Long userId; + @Column(name = "AMOUNT", nullable = true) + private Long amount; - protected ItemHistory(long userId, long itemId, LocalDateTime usedAt) { + protected ItemHistory(Long userId, Long itemId, LocalDateTime assignedAt, Long amount) { this.userId = userId; this.itemId = itemId; - this.usedAt = usedAt; + this.usedAt = assignedAt; + this.amount = amount; } /** @@ -88,8 +91,8 @@ protected ItemHistory(long userId, long itemId, LocalDateTime usedAt) { * @param usedAt 아이템 사용일자 * @return 아이템 히스토리 객체 {@link ItemHistory} */ - public static ItemHistory of(long userId, long itemId, LocalDateTime usedAt) { - ItemHistory itemHistory = new ItemHistory(userId, itemId, usedAt); + public static ItemHistory of(Long userId, Long itemId, LocalDateTime usedAt, Long amount) { + ItemHistory itemHistory = new ItemHistory(userId, itemId, usedAt, amount); if (!itemHistory.isValid()) { throw ExceptionStatus.INVALID_ARGUMENT.asDomainException(); } diff --git a/backend/src/main/java/org/ftclub/cabinet/item/domain/Sku.java b/backend/src/main/java/org/ftclub/cabinet/item/domain/Sku.java index bda3b9beb..64b383a7a 100644 --- a/backend/src/main/java/org/ftclub/cabinet/item/domain/Sku.java +++ b/backend/src/main/java/org/ftclub/cabinet/item/domain/Sku.java @@ -25,11 +25,12 @@ public enum Sku { COIN_REWARD_1000("동전 줍기 20일 보상"), COIN_REWARD_2000("동전 줍기 20일 보상"), - ADMIN_REWARD_100("보상"), - ADMIN_REWARD_200("보상"), - ADMIN_REWARD_500("보상"), - ADMIN_REWARD_1000("보상"), - ADMIN_REWARD_2000("보상"), + ADMIN_REWARD_100("100"), + ADMIN_REWARD_200("200"), + ADMIN_REWARD_500("500"), + ADMIN_REWARD_1000("1000"), + ADMIN_REWARD_2000("2000"), + ADMIN_REWARD_COIN("지정 보상"), ; private final String details; diff --git a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemFacadeService.java b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemFacadeService.java index b6f0e5546..384977313 100644 --- a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemFacadeService.java +++ b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemFacadeService.java @@ -195,7 +195,8 @@ public CoinCollectionRewardResponseDto collectCoinAndIssueReward(Long userId) { // DB에 코인 저장 Item coinCollect = itemQueryService.getBySku(Sku.COIN_COLLECT); int reward = (int) (coinCollect.getPrice().longValue()); - itemHistoryCommandService.createCoinItemHistory(userId, coinCollect.getId()); + itemHistoryCommandService.createCoinItemHistory(userId, coinCollect.getId(), + coinCollect.getPrice()); // 출석 일자에 따른 랜덤 리워드 지급 Long coinCollectionCountInMonth = @@ -206,7 +207,8 @@ public CoinCollectionRewardResponseDto collectCoinAndIssueReward(Long userId) { Sku coinSku = itemPolicyService.getRewardSku(randomPercentage); Item coinReward = itemQueryService.getBySku(coinSku); - itemHistoryCommandService.createCoinItemHistory(userId, coinReward.getId()); + itemHistoryCommandService.createCoinItemHistory(userId, coinReward.getId(), + coinReward.getPrice()); reward += coinReward.getPrice(); } @@ -310,7 +312,7 @@ public void purchaseItem(Long userId, Sku sku) { itemPolicyService.verifyIsAffordable(userCoin, price); // 아이템 구매 처리 - itemHistoryCommandService.createItemHistory(user.getId(), item.getId()); + itemHistoryCommandService.createItemHistory(user.getId(), item.getId(), price); LockUtil.lockRedisCoin(userId, () -> { // 코인 차감 diff --git a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemHistoryCommandService.java b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemHistoryCommandService.java index 2863043c4..f76562608 100644 --- a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemHistoryCommandService.java +++ b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemHistoryCommandService.java @@ -17,20 +17,39 @@ public class ItemHistoryCommandService { private final ItemHistoryRepository itemHistoryRepository; - public void createItemHistory(Long userId, Long itemId) { - ItemHistory itemHistory = ItemHistory.of(userId, itemId, null); + public void createItemHistory(Long userId, Long itemId, Long amount) { + ItemHistory itemHistory = ItemHistory.of(userId, itemId, null, amount); itemHistoryRepository.save(itemHistory); } - public void createItemHistories(List userIds, Long itemId, LocalDateTime usedAt) { + public void createItemHistories(List userIds, Long itemId, LocalDateTime usedAt, + Long amount) { List itemHistories = userIds.stream() - .map(userId -> ItemHistory.of(userId, itemId, usedAt)) + .map(userId -> ItemHistory.of(userId, itemId, usedAt, amount)) .collect(Collectors.toList()); itemHistoryRepository.saveAll(itemHistories); } - public void createCoinItemHistory(Long userId, Long itemId) { - ItemHistory coinCollectItemHistory = ItemHistory.of(userId, itemId, LocalDateTime.now()); + public void createCoinItemHistory(Long userId, Long itemId, Long amount) { + ItemHistory coinCollectItemHistory = ItemHistory.of(userId, itemId, LocalDateTime.now(), + amount); itemHistoryRepository.save(coinCollectItemHistory); } + + /** + * 관리자가 코인 지급 시 ItemHistory에 기록을 남깁니다 + * + * @param userIds + * @param itemId + * @param usedAt + * @param amount + */ + public void createCoinAssignHistory(List userIds, Long itemId, LocalDateTime usedAt, + Long amount) { + List itemHistories = userIds.stream() + .map(userId -> ItemHistory.of(userId, itemId, usedAt, amount)) + .collect(Collectors.toList()); + + itemHistoryRepository.saveAll(itemHistories); + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemPolicyService.java b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemPolicyService.java index f27d4bf75..49c7dd4e3 100644 --- a/backend/src/main/java/org/ftclub/cabinet/item/service/ItemPolicyService.java +++ b/backend/src/main/java/org/ftclub/cabinet/item/service/ItemPolicyService.java @@ -64,4 +64,10 @@ public Sku getRewardSku(int randomPercentage) { return Sku.COIN_REWARD_2000; } } + + public void verifyCoinAmount(Long amount) { + if (amount == null) { + throw ExceptionStatus.INVALID_AMOUNT.asServiceException(); + } + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/mapper/ItemMapper.java b/backend/src/main/java/org/ftclub/cabinet/mapper/ItemMapper.java index 71939ee98..2649d97d4 100644 --- a/backend/src/main/java/org/ftclub/cabinet/mapper/ItemMapper.java +++ b/backend/src/main/java/org/ftclub/cabinet/mapper/ItemMapper.java @@ -37,9 +37,9 @@ public interface ItemMapper { ItemMapper INSTANCE = Mappers.getMapper(ItemMapper.class); @Mapping(target = "date", source = "itemHistory.purchaseAt") - @Mapping(target = "amount", source = "item.price") @Mapping(target = "history", source = "item.type.name") @Mapping(target = "itemDetails", source = "item.sku.details") + @Mapping(target = "amount", source = "itemHistory.amount") CoinHistoryDto toCoinHistoryDto(ItemHistory itemHistory, Item item); @Mapping(target = "itemSku", source = "item.sku") diff --git a/backend/src/main/java/org/ftclub/cabinet/user/repository/UserRepository.java b/backend/src/main/java/org/ftclub/cabinet/user/repository/UserRepository.java index 0a384bb73..cd495bac0 100644 --- a/backend/src/main/java/org/ftclub/cabinet/user/repository/UserRepository.java +++ b/backend/src/main/java/org/ftclub/cabinet/user/repository/UserRepository.java @@ -140,4 +140,10 @@ public interface UserRepository extends JpaRepository { @Query("SELECT u FROM User u WHERE u.deletedAt IS NULL") List findAllDeletedAtIsNull(); + + @Modifying(clearAutomatically = true) + @Query("UPDATE User u " + + "SET u.coin = u.coin + :amount " + + "WHERE u.id IN :userIds") + void updateBulkUserCoin(@Param("userIds") List userIds, @Param("amount") Long amount); } diff --git a/backend/src/main/java/org/ftclub/cabinet/user/service/LentExtensionManager.java b/backend/src/main/java/org/ftclub/cabinet/user/service/LentExtensionManager.java index cb569e574..380efefc8 100644 --- a/backend/src/main/java/org/ftclub/cabinet/user/service/LentExtensionManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/user/service/LentExtensionManager.java @@ -46,7 +46,8 @@ public void issueLentExtension() { users.forEach(user -> { Long userId = user.getId(); user.addCoin(coinRewardItem.getPrice()); - itemHistoryCommandService.createCoinItemHistory(userId, coinRewardItem.getId()); + itemHistoryCommandService.createCoinItemHistory(userId, coinRewardItem.getId(), + coinRewardItem.getPrice()); }); } diff --git a/backend/src/main/java/org/ftclub/cabinet/user/service/UserCommandService.java b/backend/src/main/java/org/ftclub/cabinet/user/service/UserCommandService.java index 994e9f7b8..c94c56710 100644 --- a/backend/src/main/java/org/ftclub/cabinet/user/service/UserCommandService.java +++ b/backend/src/main/java/org/ftclub/cabinet/user/service/UserCommandService.java @@ -1,6 +1,7 @@ package org.ftclub.cabinet.user.service; import java.time.LocalDateTime; +import java.util.List; import javax.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.ftclub.cabinet.auth.domain.FtProfile; @@ -115,4 +116,8 @@ public void updateCoinAmount(Long userId, Long reward) { .orElseThrow(ExceptionStatus.NOT_FOUND_USER::asServiceException); user.addCoin(reward); } + + public void addBulkCoin(List userIds, Long amount) { + userRepository.updateBulkUserCoin(userIds, amount); + } } diff --git a/frontend/src/Cabinet/api/axios/axios.custom.ts b/frontend/src/Cabinet/api/axios/axios.custom.ts index 17a4761dd..99f84ca47 100644 --- a/frontend/src/Cabinet/api/axios/axios.custom.ts +++ b/frontend/src/Cabinet/api/axios/axios.custom.ts @@ -376,6 +376,23 @@ export const axiosItems = async (): Promise => { const response = await instance.get(axiosItemsURL); return response; } catch (error) { + console.log(error); + logAxiosError( + error, + ErrorType.STORE, + "상점 아이템 목록 불러오는중 오류 발생" + ); + throw error; + } +}; + +const axiosAdminItemsURL = "/v5/admin/items"; +export const axiosAdminItems = async (): Promise => { + try { + const response = await instance.get(axiosAdminItemsURL); + return response; + } catch (error) { + console.log(error); logAxiosError( error, ErrorType.STORE, @@ -748,9 +765,9 @@ export const axiosLentClubCabinet = async ( try { const response = await instance.post( axiosLentClubCabinetURL + - clubId.toString() + - "/cabinets/" + - cabinetId.toString() + clubId.toString() + + "/cabinets/" + + cabinetId.toString() ); return response; } catch (error) { @@ -910,6 +927,7 @@ export const axiosSendSlackAlarmToChannel = async ( } }; +// TODO: 확인하고 필요없으면 지우기 const axiosItemAssignURL = "v5/admin/items/assign"; export const axiosItemAssign = async ( itemSku: string, @@ -927,6 +945,25 @@ export const axiosItemAssign = async ( } }; +const axiosCoinAssignURL = "v5/admin/items/assign"; +export const axiosCoinAssign = async ( + itemSku: string, + userIds: number[], + amount: number +): Promise => { + try { + const response = await instance.post(axiosCoinAssignURL, { + itemSku, + userIds, + amount, + }); + return response; + } catch (error) { + logAxiosError(error, ErrorType.STORE, "아이템 지급 중 오류 발생", true); + throw error; + } +}; + const axiosGetUserItemsURL = "/v5/admin/items/users/"; export const axiosGetUserItems = async ( userId: number, diff --git a/frontend/src/Cabinet/components/CabinetInfoArea/CabinetInfoArea.tsx b/frontend/src/Cabinet/components/CabinetInfoArea/CabinetInfoArea.tsx index 9159f504c..6830f6e62 100644 --- a/frontend/src/Cabinet/components/CabinetInfoArea/CabinetInfoArea.tsx +++ b/frontend/src/Cabinet/components/CabinetInfoArea/CabinetInfoArea.tsx @@ -51,8 +51,7 @@ const CabinetInfoArea: React.FC<{ closeModal, isSwappable, }) => { - const isExtensionVisible = isMine && selectedCabinetInfo; - // selectedCabinetInfo.status !== "IN_SESSION"; + const isExtensionVisible = isMine && selectedCabinetInfo && selectedCabinetInfo.status !== "IN_SESSION"; const isHoverBoxVisible = selectedCabinetInfo && selectedCabinetInfo.lentsLength <= 1 && diff --git a/frontend/src/Cabinet/components/Home/ServiceManual.tsx b/frontend/src/Cabinet/components/Home/ServiceManual.tsx index ccf8c876c..8416dd658 100644 --- a/frontend/src/Cabinet/components/Home/ServiceManual.tsx +++ b/frontend/src/Cabinet/components/Home/ServiceManual.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import styled from "styled-components"; import ManualContentBox from "@/Cabinet/components/Home/ManualContentBox"; import ManualModal from "@/Cabinet/components/Modals/ManualModal/ManualModal"; +import { ReactComponent as LinkImg } from "@/Cabinet/assets/images/link.svg"; import ContentStatus from "@/Cabinet/types/enum/content.status.enum"; const ServiceManual = ({ @@ -20,7 +21,7 @@ const ServiceManual = ({ }; const openNotionLink = () => { - window.open("https://cabi.oopy.io/0bbb08a2-241c-444b-8a96-6b33c3796451"); + window.open("https://cabi.oopy.io/115f29ec-ef8e-4748-a8a6-0d0341def33c"); }; return ( @@ -30,7 +31,8 @@ const ServiceManual = ({ Cabi 이용 안내서 - 상세보기 + 상세보기 + @@ -148,9 +150,30 @@ const NotionBtn = styled.button` color: var(--notion-btn-text-color); background: var(--bg-color); border: 1px solid var(--line-color); - :hover { - color: var(--normal-text-color); - font-weight: 400; + display: flex; + align-items: center; + justify-content: center; + @media (hover: hover) and (pointer: fine) { + &:hover { + color: var(--normal-text-color); + font-weight: 400; + + #linknImg > path { + stroke: var(--normal-text-color); + stroke-width: 1.2px; + } + } + } + + & > span { + line-height: 14px; + height: 16px; + } + + & > #linknImg { + width: 12px; + height: 12px; + margin-left: 4px; } `; diff --git a/frontend/src/Cabinet/components/Modals/StoreModal/AdminItemProvisionModal.tsx b/frontend/src/Cabinet/components/Modals/StoreModal/AdminItemProvisionModal.tsx index 667f0836d..4c70a6a83 100644 --- a/frontend/src/Cabinet/components/Modals/StoreModal/AdminItemProvisionModal.tsx +++ b/frontend/src/Cabinet/components/Modals/StoreModal/AdminItemProvisionModal.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useRecoilState } from "recoil"; import styled from "styled-components"; import { targetUserInfoState } from "@/Cabinet/recoil/atoms"; @@ -14,7 +14,10 @@ import { } from "@/Cabinet/components/Modals/ResponseModal/ResponseModal"; import { IItemDetail } from "@/Cabinet/types/dto/store.dto"; import { StoreItemType } from "@/Cabinet/types/enum/store.enum"; -import { axiosItemAssign, axiosItems } from "@/Cabinet/api/axios/axios.custom"; +import { + axiosCoinAssign, + axiosAdminItems, +} from "@/Cabinet/api/axios/axios.custom"; import { HttpStatusCode } from "axios"; interface IPenaltyModalProps { @@ -36,11 +39,17 @@ const AdminItemProvisionModal: React.FC = ({ onClose }) => { const [isItemDropdownOpen, setIsItemDropdownOpen] = useState(false); const [isItemTypeDropdownOpen, setIsItemTypeDropdownOpen] = useState(false); const [targetUserInfo] = useRecoilState(targetUserInfoState); - + const coinRef = useRef(null); const HandleItemProvisionBtn = async () => { + let coinRefVal = coinRef.current!.value; + coinRefVal = coinRefVal == "" ? "0" : String(coinRefVal); setIsLoading(true); try { - await axiosItemAssign(selectedItemSku, [targetUserInfo.userId!]); + await axiosCoinAssign( + selectedItemSku, + [targetUserInfo.userId!], + Number(coinRefVal) + ); setModalTitle("아이템 지급완료"); } catch (error: any) { setHasErrorOnResponse(true); @@ -73,7 +82,7 @@ const AdminItemProvisionModal: React.FC = ({ onClose }) => { const getItems = async () => { try { - const response = await axiosItems(); + const response = await axiosAdminItems(); setItems(response.data.items); } catch (error) { throw error; @@ -117,17 +126,19 @@ const AdminItemProvisionModal: React.FC = ({ onClose }) => { }, []); const getItemTypeOptions = (item: IItemDetail) => { + console.log(item.items); + return item.items.length === 1 ? [ - { - name: "타입이 없습니다", - value: item.items[0].itemSku, - hasNoOptions: true, - }, - ] + { + name: "타입이 없습니다", + value: item.items[0].itemSku, + hasNoOptions: true, + }, + ] : item.items.map((item) => { - return { name: item.itemDetails, value: item.itemSku }; - }); + return { name: item.itemDetails, value: item.itemSku }; + }); }; const modalContents: IModalContents = { @@ -153,6 +164,18 @@ const AdminItemProvisionModal: React.FC = ({ onClose }) => { 아이템 타입 + + + 코인 + { + if (e.key === "Enter") HandleItemProvisionBtn(); + }} + ref={coinRef} + maxLength={10} + id="input" + > + ), }; @@ -180,4 +203,19 @@ const ModalDropdownNameStyled = styled.div` font-size: 18px; `; +const CoinInputStyled = styled.input` + border: 1px solid var(--light-gray-line-btn-color); + width: 100%; + height: 60px; + border-radius: 10px; + text-align: start; + text-indent: 20px; + font-size: 1.125rem; + cursor: "input"; + color: "var(--normal-text-color)"; + &::placeholder { + color: "var(--line-color)"; + } +`; + export default AdminItemProvisionModal; diff --git a/frontend/src/Cabinet/pages/AvailablePage.tsx b/frontend/src/Cabinet/pages/AvailablePage.tsx index 82ebd53ff..9ec0be173 100644 --- a/frontend/src/Cabinet/pages/AvailablePage.tsx +++ b/frontend/src/Cabinet/pages/AvailablePage.tsx @@ -29,6 +29,9 @@ const toggleList: toggleItem[] = [ { name: "공유", key: AvailableCabinetsType.SHARE }, ]; +/* TODO: DISABLED_FLOOR 을 환경변수로 넣기 */ +export const DISABLED_FLOOR = ["4"]; + const AvailablePage = () => { const [toggleType, setToggleType] = useState( AvailableCabinetsType.ALL @@ -153,13 +156,15 @@ const AvailablePage = () => { {Object.keys(cabinets).length ? ( - Object.entries(cabinets).map(([key, value]) => ( - - )) + Object.entries(cabinets) + .filter(([key, _]) => !DISABLED_FLOOR.includes(key)) + .map(([key, value]) => ( + + )) ) : ( )} diff --git a/frontend/src/Cabinet/pages/MainPage.tsx b/frontend/src/Cabinet/pages/MainPage.tsx index dc73bc107..3550822bd 100644 --- a/frontend/src/Cabinet/pages/MainPage.tsx +++ b/frontend/src/Cabinet/pages/MainPage.tsx @@ -12,6 +12,7 @@ import { targetCabinetInfoState, } from "@/Cabinet/recoil/atoms"; import { currentFloorSectionState } from "@/Cabinet/recoil/selectors"; +import { DISABLED_FLOOR } from "@/Cabinet/pages/AvailablePage"; import CabinetListContainer from "@/Cabinet/components/CabinetList/CabinetList.container"; import LoadingAnimation from "@/Cabinet/components/Common/LoadingAnimation"; import SectionAlertModal from "@/Cabinet/components/Modals/StoreModal/SectionAlertModal"; @@ -74,11 +75,9 @@ const MainPage = () => { }, []); useEffect(() => { - const clubSection = clubSectionsData.find((section) => { + const clubSection = !!clubSectionsData.find((section) => { return section === currentSectionName; - }) - ? true - : false; + }); setIsClubSection(clubSection); }, [currentSectionName]); @@ -123,66 +122,58 @@ const MainPage = () => { setIsCurrentSectionRender(false); }; - return ( - <> - {isLoading && } - { - touchStartPosX.current = e.changedTouches[0].screenX; - touchStartPosY.current = e.changedTouches[0].screenY; - }} - onTouchEnd={(e: React.TouchEvent) => { - swipeSection( - e.changedTouches[0].screenX, - e.changedTouches[0].screenY - ); - }} - > - - {currentFloorSectionNames.includes(currentSectionName) && - !isClubSection && ( - - {sectionList[currentSectionIndex]?.alarmRegistered === true ? ( - - ) : ( - - )} - - )} - - - - - {currentSectionName !== SectionType.elevator && - currentSectionName !== SectionType.stairs && ( - - 새로고침 - - )} - - {showSectionAlertModal && ( - - )} - - + return isLoading ? ( + + ) : ( + { + touchStartPosX.current = e.changedTouches[0].screenX; + touchStartPosY.current = e.changedTouches[0].screenY; + }} + onTouchEnd={(e: React.TouchEvent) => { + swipeSection(e.changedTouches[0].screenX, e.changedTouches[0].screenY); + }} + > + + {currentFloorSectionNames.includes(currentSectionName) && + !isClubSection && ( + + {sectionList[currentSectionIndex]?.alarmRegistered === true ? ( + + ) : ( + + )} + + )} + + + + + {currentSectionName !== SectionType.elevator && + currentSectionName !== SectionType.stairs && ( + + 새로고침 + + )} + + {showSectionAlertModal && ( + + )} + ); }; @@ -229,7 +220,11 @@ const IconWrapperStyled = styled.div<{ disabled: boolean }>` } `; -const AlertStyled = styled.div` +const AlertStyled = styled.div<{ currentFloor: number | undefined }>` + visibility: ${({ currentFloor }) => + currentFloor && DISABLED_FLOOR.includes(currentFloor.toString()) + ? "hidden" + : "visible"}; height: 30px; display: flex; justify-content: end; diff --git a/frontend/src/Cabinet/pages/admin/AdminMainPage.tsx b/frontend/src/Cabinet/pages/admin/AdminMainPage.tsx index de410c157..adc495cb1 100644 --- a/frontend/src/Cabinet/pages/admin/AdminMainPage.tsx +++ b/frontend/src/Cabinet/pages/admin/AdminMainPage.tsx @@ -93,44 +93,43 @@ const AdminMainPage = () => { else moveToRightSection(); }; - return ( - <> - {isLoading && } - { - touchStartPosX.current = e.changedTouches[0].screenX; - touchStartPosY.current = e.changedTouches[0].screenY; - }} - onTouchEnd={(e: React.TouchEvent) => { - swipeSection( - e.changedTouches[0].screenX, - e.changedTouches[0].screenY - ); - }} - > - - - - - - + return isLoading ? ( + + ) : ( + { + touchStartPosX.current = e.changedTouches[0].screenX; + touchStartPosY.current = e.changedTouches[0].screenY; + }} + onTouchEnd={(e: React.TouchEvent) => { + swipeSection( + e.changedTouches[0].screenX, + e.changedTouches[0].screenY + ); + }} + > + + + + + + - - 새로고침 - - - - + + 새로고침 + + + ); }; diff --git a/frontend/src/Presentation/components/LeftNav/LeftMainNav/LeftMainNav.container.tsx b/frontend/src/Presentation/components/LeftNav/LeftMainNav/LeftMainNav.container.tsx index 367f83995..6bebb4647 100644 --- a/frontend/src/Presentation/components/LeftNav/LeftMainNav/LeftMainNav.container.tsx +++ b/frontend/src/Presentation/components/LeftNav/LeftMainNav/LeftMainNav.container.tsx @@ -30,9 +30,9 @@ const LeftMainNavContainer = ({ isAdmin }: { isAdmin?: boolean }) => { const onClickPresentationDetailButton = () => { if (isAdmin) { - navigator("/presentation/detail"); + navigator("/admin/presentation/detail"); } else { - navigator("detail"); + navigator("/presentation/detail"); } closeAll(); };