Inventory management added (admin)

This commit is contained in:
Michael Wain 2025-03-10 16:11:25 +03:00
parent 97ef45b9a7
commit efce5e9c11
16 changed files with 273 additions and 5 deletions

View File

@ -8,6 +8,7 @@ import lombok.Getter;
public enum RoleFlags { public enum RoleFlags {
NONE(0), NONE(0),
MEMBER(2), MEMBER(2),
BOSS(4),
SA(8), SA(8),
MODERATOR(131072), MODERATOR(131072),
ADMINISTRATOR(262144), ADMINISTRATOR(262144),

View File

@ -7,11 +7,14 @@ import com.alterdekim.game.component.game.ClubAccessType;
import com.alterdekim.game.component.game.PlayerProperties; import com.alterdekim.game.component.game.PlayerProperties;
import com.alterdekim.game.component.game.PlayerPropertyType; import com.alterdekim.game.component.game.PlayerPropertyType;
import com.alterdekim.game.component.game.RoleFlags; import com.alterdekim.game.component.game.RoleFlags;
import com.alterdekim.game.component.game.avatar.GoodClothType;
import com.alterdekim.game.component.game.inventory.InventoryItem;
import com.alterdekim.game.controller.result.api.ApiResult; import com.alterdekim.game.controller.result.api.ApiResult;
import com.alterdekim.game.controller.result.api.PreloaderResult; import com.alterdekim.game.controller.result.api.PreloaderResult;
import com.alterdekim.game.controller.result.api.PromotionBannerResult; import com.alterdekim.game.controller.result.api.PromotionBannerResult;
import com.alterdekim.game.controller.result.api.PromotionResult; import com.alterdekim.game.controller.result.api.PromotionResult;
import com.alterdekim.game.entity.*; import com.alterdekim.game.entity.*;
import com.alterdekim.game.repository.AvatarInventoryRepository;
import com.alterdekim.game.service.*; import com.alterdekim.game.service.*;
import com.alterdekim.game.utils.DateUtils; import com.alterdekim.game.utils.DateUtils;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -19,6 +22,7 @@ import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -69,6 +73,21 @@ public class ApiController {
@Autowired @Autowired
private LocationService locationService; private LocationService locationService;
@Autowired
private GoodsService goodsService;
@Autowired
private MRService mrService;
@Autowired
private MRTService mrtService;
@Autowired
private TRService trService;
@Autowired
private AvatarInventoryRepository avatarInventoryRepository;
@PostMapping("/delete_row") @PostMapping("/delete_row")
public ResponseEntity deleteRow(@RequestParam("table") APITable table, @RequestParam("row") Long rowId) { public ResponseEntity deleteRow(@RequestParam("table") APITable table, @RequestParam("row") Long rowId) {
switch (table) { switch (table) {
@ -205,6 +224,74 @@ public class ApiController {
return ResponseEntity.ok(results); return ResponseEntity.ok(results);
} }
@RequestMapping(value = "/get_goods", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<GoodResult>> getGoods() {
List<GoodResult> results = new LinkedList<>();
for( Good good : goodsService.getAll() ) {
GoodResult result = new GoodResult();
long goodId = good.getId();
result.setGoodId(goodId);
Integer iconId = -good.getMRId();
Integer trId = good.getTRId();
var icon = mrService.findById(iconId.longValue());
if( icon.isPresent() ) {
String url = mrtService.findById(icon.get().getTId().longValue()).getVal() + icon.get().getUrl();
result.setUrl(url);
}
var name = trService.findById(trId.longValue());
if( name.isPresent() ) {
String text = name.get().getH();
result.setName(text);
}
results.add(result);
}
return ResponseEntity.ok(results);
}
@RequestMapping(value = "/get_avatar_inventory", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<GoodResult>> getInventory(@RequestParam("id") Integer userId) {
List<GoodResult> results = new LinkedList<>();
List<InventoryItem> items = Arrays.stream(GoodClothType.values())
.map(type -> this.avatarInventoryRepository.findInventoryItems(userId, type))
.flatMap(Collection::stream)
.collect(Collectors.toList());
for( InventoryItem avatarInventory : items) {
var og = goodsService.findById(avatarInventory.getId());
if( og.isPresent() ) {
var good = og.get();
GoodResult result = new GoodResult();
long goodId = good.getId();
result.setGoodId(goodId);
Integer iconId = -good.getMRId();
Integer trId = good.getTRId();
var icon = mrService.findById(iconId.longValue());
if (icon.isPresent()) {
String url = mrtService.findById(icon.get().getTId().longValue()).getVal() + icon.get().getUrl();
result.setUrl(url);
}
var name = trService.findById(trId.longValue());
if (name.isPresent()) {
String text = name.get().getH();
result.setName(text);
}
results.add(result);
}
}
return ResponseEntity.ok(results);
}
@Setter
@NoArgsConstructor
public static class GoodResult {
@JsonProperty
private String url;
@JsonProperty
private Long goodId;
@JsonProperty
private String name;
}
@AllArgsConstructor @AllArgsConstructor
public static class UserPropertyResult { public static class UserPropertyResult {
@JsonProperty @JsonProperty

View File

@ -1,9 +1,13 @@
package com.alterdekim.game.controller; package com.alterdekim.game.controller;
import com.alterdekim.game.component.ReferenceLoader;
import com.alterdekim.game.component.game.AvatarInventoryType;
import com.alterdekim.game.config.ServerConfig; import com.alterdekim.game.config.ServerConfig;
import com.alterdekim.game.entity.AvatarInventory;
import com.alterdekim.game.entity.Role; import com.alterdekim.game.entity.Role;
import com.alterdekim.game.entity.User; import com.alterdekim.game.entity.User;
import com.alterdekim.game.security.AuthenticationUtil; import com.alterdekim.game.security.AuthenticationUtil;
import com.alterdekim.game.service.AvatarInventoryService;
import com.alterdekim.game.service.GoodsService; import com.alterdekim.game.service.GoodsService;
import com.alterdekim.game.service.UserService; import com.alterdekim.game.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -44,6 +48,15 @@ public class StaticController {
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired
private ReferenceLoader referenceLoader;
@Autowired
private AvatarInventoryService avatarInventoryService;
@Autowired
private GoodsService goodsService;
@GetMapping("/main") @GetMapping("/main")
public String mainPage(Model model) { public String mainPage(Model model) {
User user = AuthenticationUtil.authProfile(userService).get(); User user = AuthenticationUtil.authProfile(userService).get();
@ -72,9 +85,33 @@ public class StaticController {
public String panelUser(Model model, @RequestParam(value = "id", defaultValue = "1") Integer userId) { public String panelUser(Model model, @RequestParam(value = "id", defaultValue = "1") Integer userId) {
model.addAttribute("user_id", userId); model.addAttribute("user_id", userId);
model.addAttribute("user_name", userService.getUsernameById(userId).get()); model.addAttribute("user_name", userService.getUsernameById(userId).get());
model.addAttribute("inventory_link", "/panel_inventory?id="+userId);
return "panel_user"; return "panel_user";
} }
@GetMapping("/panel_inventory")
public String panelInventory(Model model, @RequestParam(value = "id", defaultValue = "1") Integer userId) {
model.addAttribute("user_id", userId);
model.addAttribute("user_name", "Inventory of " + userService.getUsernameById(userId).get());
return "panel_inventory";
}
@PostMapping("/panel_inventory")
public String panelInventory(Model model, @RequestParam(value = "id", defaultValue = "1") Integer userId, @RequestParam(value = "good_id") Long goodId) {
model.addAttribute("user_id", userId);
model.addAttribute("user_name", "Inventory of " + userService.getUsernameById(userId).get());
AvatarInventory av = new AvatarInventory(referenceLoader.getUserReference(userId), goodId, false, AvatarInventoryType.fromGoodId(goodsService, goodId), 0);
avatarInventoryService.addGoodToInventory(av);
return "redirect:/panel_inventory?id="+userId;
}
@GetMapping("/goods")
public String goodsPage() {
return "goods";
}
public enum PanelSection { public enum PanelSection {
Dashboard, Dashboard,
Locations, Locations,

View File

@ -64,6 +64,9 @@ public interface AvatarInventoryRepository extends JpaRepository<AvatarInventory
WHERE a.user_id = :uid AND a.type = :#{#goodTypeId.getVal().name()}""", nativeQuery = true) WHERE a.user_id = :uid AND a.type = :#{#goodTypeId.getVal().name()}""", nativeQuery = true)
List<InventoryItem> findInventoryItems(@Param("uid") Integer userId, @Param("goodTypeId") GoodClothType goodClothType); List<InventoryItem> findInventoryItems(@Param("uid") Integer userId, @Param("goodTypeId") GoodClothType goodClothType);
@Query(value = "SELECT a FROM AvatarInventory a WHERE a.user.id = :uid")
List<AvatarInventory> findAllByUserId(@Param("uid") Integer userId);
@Query(value = """ @Query(value = """
SELECT SELECT
b.id AS id, b.id AS id,

View File

@ -38,6 +38,8 @@ public class SpringSecurity {
.requestMatchers("/login").permitAll() .requestMatchers("/login").permitAll()
.requestMatchers("/panel").hasAuthority(Role.RoleType.RoleAdmin.name()) .requestMatchers("/panel").hasAuthority(Role.RoleType.RoleAdmin.name())
.requestMatchers("/panel_user").hasAuthority(Role.RoleType.RoleAdmin.name()) .requestMatchers("/panel_user").hasAuthority(Role.RoleType.RoleAdmin.name())
.requestMatchers("/panel_inventory").hasAuthority(Role.RoleType.RoleAdmin.name())
.requestMatchers("/goods").hasAuthority(Role.RoleType.RoleAdmin.name())
.requestMatchers("/api/**").hasAuthority(Role.RoleType.RoleAdmin.name()) .requestMatchers("/api/**").hasAuthority(Role.RoleType.RoleAdmin.name())
.requestMatchers("/main").hasAnyAuthority(Role.RoleType.RoleUser.name(), Role.RoleType.RoleAdmin.name()) .requestMatchers("/main").hasAnyAuthority(Role.RoleType.RoleUser.name(), Role.RoleType.RoleAdmin.name())
.requestMatchers("/"+ FileServerController.URL_PATH +"/**").permitAll() .requestMatchers("/"+ FileServerController.URL_PATH +"/**").permitAll()

View File

@ -7,6 +7,7 @@ import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -25,4 +26,8 @@ public class GoodsService implements OutputObjectCallback {
public Optional<Good> findById(Long id) { public Optional<Good> findById(Long id) {
return this.repository.findById(id); return this.repository.findById(id);
} }
public List<Good> getAll() {
return this.repository.findAll();
}
} }

View File

@ -28,4 +28,8 @@ public class MRTService implements OutputObjectCallback {
public List<MediaResourcePath> getAll() { public List<MediaResourcePath> getAll() {
return this.repository.findAll(); return this.repository.findAll();
} }
public MediaResourcePath findById(Long id) {
return this.repository.findById(id).get();
}
} }

View File

@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Optional;
@NoArgsConstructor @NoArgsConstructor
@Service @Service
@ -24,4 +25,8 @@ public class TRService implements OutputObjectCallback {
public List<TextResource> getAll() { public List<TextResource> getAll() {
return this.repository.findAll(); return this.repository.findAll();
} }
public Optional<TextResource> findById(Long id) {
return this.repository.findById(id);
}
} }

View File

@ -0,0 +1,35 @@
function initGoodTable(tag, data) {
if( data.length <= 0 ) return;
let keys = Object.keys(data[0]);
let header = "";
for( let i = 0; i < keys.length; i++ ) {
header += "<th>" + keys[i] + "</th>";
}
$($(tag).children("thead")[0]).html("<tr>"+header+"</tr>");
for( let i = 0; i < data.length; i++ ) {
header = "";
let row_id = data[i]["id"];
for( let u = 0; u < keys.length; u++ ) {
if( keys[u] == "url" ) {
header += "<td><img class=\"good-img\" src=\"/"+data[i][keys[u]]+"\"></td>";
} else {
header += "<td>" + data[i][keys[u]] + "</td>";
}
}
let tabName = tag.substring(1);
$($(tag).children("tbody")[0]).append("<tr data-rownum=\""+row_id+"\" data-table=\""+tabName[0].toUpperCase()+tabName.substring(1)+"\">"+header+"</tr>");
}
}
function getUserGoods() {
$.get( "/api/get_goods", function( data ) {
initGoodTable("#goods", data);
set_loading("#goods", false);
});
}
function set_loading(obj, state) {
$(obj).attr("aria-busy", state);
}
getUserGoods();

View File

@ -0,0 +1,35 @@
function initGoodTable(tag, data) {
if( data.length <= 0 ) return;
let keys = Object.keys(data[0]);
let header = "";
for( let i = 0; i < keys.length; i++ ) {
header += "<th>" + keys[i] + "</th>";
}
$($(tag).children("thead")[0]).html("<tr>"+header+"</tr>");
for( let i = 0; i < data.length; i++ ) {
header = "";
let row_id = data[i]["id"];
for( let u = 0; u < keys.length; u++ ) {
if( keys[u] == "url" ) {
header += "<td><img class=\"good-img\" src=\"/"+data[i][keys[u]]+"\"></td>";
} else {
header += "<td>" + data[i][keys[u]] + "</td>";
}
}
let tabName = tag.substring(1);
$($(tag).children("tbody")[0]).append("<tr data-rownum=\""+row_id+"\" data-table=\""+tabName[0].toUpperCase()+tabName.substring(1)+"\">"+header+"</tr>");
}
}
function getUserGoods() {
$.get( "/api/get_avatar_inventory?id="+$("#user_init_info").attr("data-id"), function( data ) {
initGoodTable("#inventory", data);
set_loading("#inventory", false);
});
}
function set_loading(obj, state) {
$(obj).attr("aria-busy", state);
}
getUserGoods();

View File

@ -1,6 +1,7 @@
const roleFlags = [ const roleFlags = [
"NONE", "NONE",
"MEMBER", "MEMBER",
"BOSS",
"SA", "SA",
"MODERATOR", "MODERATOR",
"ADMINISTRATOR", "ADMINISTRATOR",

View File

@ -18,3 +18,8 @@ td > img {
height: 28px; height: 28px;
cursor: pointer; cursor: pointer;
} }
.good-img {
width: 125px;
height: 100px;
}

View File

@ -7,13 +7,9 @@
<li><a href="/panel?section=Games">Games</a></li> <li><a href="/panel?section=Games">Games</a></li>
<li><a href="/panel?section=Preloaders">Preloaders</a></li> <li><a href="/panel?section=Preloaders">Preloaders</a></li>
<li><a href="/panel?section=Users">Users</a></li> <li><a href="/panel?section=Users">Users</a></li>
<li><a href="/panel?section=Banlist">Banlist</a></li> <li><a href="/goods">Goods</a></li>
<li><a href="/panel?section=Cache">Cache</a></li>
<li><a href="/panel?section=Goods">Goods</a></li>
<li><a href="/panel?section=Items">Items list</a></li> <li><a href="/panel?section=Items">Items list</a></li>
<li><a href="/panel?section=Actions">Actions</a></li> <li><a href="/panel?section=Actions">Actions</a></li>
<li><a href="/panel?section=Support">Support</a></li>
<li><a href="/panel?section=Settings">Settings</a></li>
</ul> </ul>
</nav> </nav>
</aside> </aside>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<th:block th:insert="~{fragments/head}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/header}"></th:block>
<main class="container grid" style="grid-template-columns: 1fr 6fr;">
<th:block th:insert="~{fragments/aside}"></th:block>
<div class="container-fluid">
<h2>Goods with names</h2>
<table id="goods" aria-busy="true">
<thead></thead>
<tbody></tbody>
</table>
</div>
</main>
</body>
<script type="text/javascript" src="/file/_js/goods.js"></script>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<th:block th:insert="~{fragments/head}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/header}"></th:block>
<main class="container grid" style="grid-template-columns: 1fr 6fr;">
<th:block th:insert="~{fragments/aside}"></th:block>
<div class="container-fluid">
<h2 id="user_init_info" th:data-id="${user_id}" th:text="${user_name}"></h2>
<form method="POST" action="/panel_inventory">
<fieldset role="group">
<input type="hidden" name="id" th:value="${user_id}">
<input
type="number"
name="good_id"
placeholder="Enter good id"
/>
<input type="submit" value="Add to inventory" />
</fieldset>
</form>
<table id="inventory" aria-busy="true">
<thead></thead>
<tbody></tbody>
</table>
</div>
</main>
</body>
<script type="text/javascript" src="/file/_js/inventory.js"></script>
</html>

View File

@ -9,6 +9,7 @@
<th:block th:insert="~{fragments/aside}"></th:block> <th:block th:insert="~{fragments/aside}"></th:block>
<div class="container-fluid"> <div class="container-fluid">
<h2 id="user_init_info" th:data-id="${user_id}" th:text="${user_name}"></h2> <h2 id="user_init_info" th:data-id="${user_id}" th:text="${user_name}"></h2>
<a th:href="${inventory_link}"><button class="primary">Inventory</button></a>
<table id="properties" aria-busy="true"> <table id="properties" aria-busy="true">
<thead></thead> <thead></thead>
<tbody></tbody> <tbody></tbody>