[api] Add Chusan API

This commit is contained in:
Dom Eori
2022-03-19 21:48:12 +09:00
parent d15454c2cb
commit 27ff23c662
50 changed files with 28834 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
package icu.samnyan.aqua.api.controller.sega.game.chuni.v2;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameAvatarAccRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameCharacterRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameFrameRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameMapIconRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameMusicRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameTrophyRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameNamePlateRepository;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameSystemVoiceRepository;
import icu.samnyan.aqua.sega.chusan.model.gamedata.AvatarAcc;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Character;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Frame;
import icu.samnyan.aqua.sega.chusan.model.gamedata.MapIcon;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Music;
import icu.samnyan.aqua.sega.chusan.model.gamedata.NamePlate;
import icu.samnyan.aqua.sega.chusan.model.gamedata.SystemVoice;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Trophy;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@RestController
@RequestMapping("api/game/chuni/v2/data")
public class ApiChuniV2GameDataController {
private final GameMusicRepository gameMusicRepository;
private final GameCharacterRepository gameCharacterRepository;
private final GameTrophyRepository gameTrophyRepository;
private final GameNamePlateRepository gameNamePlateRepository;
private final GameSystemVoiceRepository gameSystemVoiceRepository;
private final GameMapIconRepository gameMapIconRepository;
private final GameFrameRepository gameFrameRepository;
private final GameAvatarAccRepository gameAvatarAccRepository;
public ApiChuniV2GameDataController(GameMusicRepository gameMusicRepository, GameCharacterRepository gameCharacterRepository, GameTrophyRepository gameTrophyRepository, GameNamePlateRepository gameNamePlateRepository, GameSystemVoiceRepository gameSystemVoiceRepository, GameMapIconRepository gameMapIconRepository, GameFrameRepository gameFrameRepository, GameAvatarAccRepository gameAvatarAccRepository) {
this.gameMusicRepository = gameMusicRepository;
this.gameCharacterRepository = gameCharacterRepository;
this.gameTrophyRepository = gameTrophyRepository;
this.gameNamePlateRepository = gameNamePlateRepository;
this.gameSystemVoiceRepository = gameSystemVoiceRepository;
this.gameMapIconRepository = gameMapIconRepository;
this.gameFrameRepository = gameFrameRepository;
this.gameAvatarAccRepository = gameAvatarAccRepository;
}
@GetMapping("music")
public List<Music> getMusic() {
return gameMusicRepository.findAll();
}
@GetMapping("character")
public List<Character> getCharacter() {
return gameCharacterRepository.findAll();
}
@GetMapping("trophy")
public List<Trophy> getTrophy() {
return gameTrophyRepository.findAll();
}
@GetMapping("nameplate")
public List<NamePlate> getNamePlate() {
return gameNamePlateRepository.findAll();
}
@GetMapping("sysvoice")
public List<SystemVoice> getSystemVoice() {
return gameSystemVoiceRepository.findAll();
}
@GetMapping("mapicon")
public List<MapIcon> getMapIcon() {
return gameMapIconRepository.findAll();
}
@GetMapping("frame")
public List<Frame> getFrame() {
return gameFrameRepository.findAll();
}
@GetMapping("avatar")
public List<AvatarAcc> getAvatarAcc() {
return gameAvatarAccRepository.findAll();
}
}

View File

@@ -0,0 +1,489 @@
package icu.samnyan.aqua.api.controller.sega.game.chuni.v2;
import com.fasterxml.jackson.core.type.TypeReference;
import icu.samnyan.aqua.api.model.MessageResponse;
import icu.samnyan.aqua.api.model.ReducedPageResponse;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.ProfileResp;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.RatingItem;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.RecentResp;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external.ChuniDataExport;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external.ChuniDataImport;
import icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external.ExternalUserData;
import icu.samnyan.aqua.api.util.ApiMapper;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Level;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Music;
import icu.samnyan.aqua.sega.chusan.model.userdata.*;
import icu.samnyan.aqua.sega.chusan.service.*;
import icu.samnyan.aqua.sega.general.model.Card;
import icu.samnyan.aqua.sega.general.service.CardService;
import icu.samnyan.aqua.sega.util.VersionInfo;
import icu.samnyan.aqua.sega.util.VersionUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
* For all aimeId parameter, should use String
* @author samnyan (privateamusement@protonmail.com)
*/
@RestController
@RequestMapping("api/game/chuni/v2")
public class ApiChuniV2PlayerDataController {
private static final Logger logger = LoggerFactory.getLogger(ApiChuniV2PlayerDataController.class);
private final ApiMapper mapper;
private final CardService cardService;
private final UserActivityService userActivityService;
private final UserCharacterService userCharacterService;
private final UserChargeService userChargeService;
private final UserCourseService userCourseService;
private final UserDataService userDataService;
private final UserDuelService userDuelService;
private final UserGameOptionService userGameOptionService;
private final UserItemService userItemService;
private final UserMapAreaService userMapAreaService;
private final UserMusicDetailService userMusicDetailService;
private final UserPlaylogService userPlaylogService;
private final UserGeneralDataService userGeneralDataService;
private final GameMusicService gameMusicService;
@Autowired
public ApiChuniV2PlayerDataController(ApiMapper mapper, CardService cardService, UserActivityService userActivityService, UserCharacterService userCharacterService, UserChargeService userChargeService, UserDataService userDataService, UserMapAreaService userMapAreaService, UserPlaylogService userPlaylogService, UserMusicDetailService userMusicDetailService, UserCourseService userCourseService, UserDuelService userDuelService, UserGameOptionService userGameOptionService, UserItemService userItemService, UserGeneralDataService userGeneralDataService, GameMusicService gameMusicService) {
this.mapper = mapper;
this.cardService = cardService;
this.userActivityService = userActivityService;
this.userCharacterService = userCharacterService;
this.userChargeService = userChargeService;
this.userDataService = userDataService;
this.userMapAreaService = userMapAreaService;
this.userPlaylogService = userPlaylogService;
this.userMusicDetailService = userMusicDetailService;
this.userCourseService = userCourseService;
this.userDuelService = userDuelService;
this.userGameOptionService = userGameOptionService;
this.userItemService = userItemService;
this.userGeneralDataService = userGeneralDataService;
this.gameMusicService = gameMusicService;
}
/*
// Keep it here for legacy
@GetMapping("music")
public List<Music> getMusicList() {
return gameMusicService.getAll();
}
*/
/**
* Get Basic info
*
* @return
*/
@GetMapping("profile")
public ProfileResp getProfile(@RequestParam String aimeId) {
ProfileResp resp = mapper.convert(userDataService.getUserByExtId(aimeId).orElseThrow(), new TypeReference<>() {
});
UserCourse course = userCourseService.getByUserId(aimeId)
.stream()
.filter(UserCourse::isClear)
.max(Comparator.comparingInt(UserCourse::getClassId))
.orElseGet(() -> new UserCourse(0));
resp.setCourseClass(course.getClassId());
return resp;
}
@PutMapping("profile/username")
public UserData updateName(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setUserName((String) request.get("userName"));
profile.setUserNameEx((String) request.get("userName"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/plate")
public UserData updatePlate(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setNameplateId((Integer) request.get("nameplateId"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/frame")
public UserData updateFrame(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setFrameId((Integer) request.get("frameId"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/trophy")
public UserData updateTrophy(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setTrophyId((Integer) request.get("trophyId"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/mapicon")
public UserData updateMapIcon(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setMapIconId((Integer) request.get("mapiconId"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/sysvoice")
public UserData updateSystemVoice(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
profile.setVoiceId((Integer) request.get("voiceId"));
return userDataService.saveUserData(profile);
}
@PutMapping("profile/avatar")
public UserData updateAvatar(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
int category = (Integer) request.get("category");
switch (category) {
case 1:
profile.setAvatarWear((Integer) request.get("accId"));
break;
case 2:
profile.setAvatarHead((Integer) request.get("accId"));
break;
case 3:
profile.setAvatarFace((Integer) request.get("accId"));
break;
case 4:
profile.setAvatarSkin((Integer) request.get("accId"));
break;
case 5:
profile.setAvatarItem((Integer) request.get("accId"));
break;
case 6:
profile.setAvatarFront((Integer) request.get("accId"));
break;
case 7:
profile.setAvatarBack((Integer) request.get("accId"));
break;
}
return userDataService.saveUserData(profile);
}
@PutMapping("profile/privacy")
public ResponseEntity<Object> updatePrivacy(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
UserGameOption option = userGameOptionService.getByUser(profile).orElseThrow();
int privacy = (Integer) request.get("privacy");
if (privacy != 1 && privacy != 0) {
return ResponseEntity.badRequest().body(new MessageResponse("Wrong data"));
}
option.setPrivacy(privacy);
return ResponseEntity.ok(userDataService.saveUserData(profile));
}
@GetMapping("recent")
public ReducedPageResponse<RecentResp> getRecentPlay(@RequestParam String aimeId,
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
Page<UserPlaylog> playLogs = userPlaylogService.getRecentPlays(aimeId, PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "userPlayDate")));
return new ReducedPageResponse<>(mapper.convert(playLogs.getContent(), new TypeReference<>() {
}), playLogs.getPageable().getPageNumber(), playLogs.getTotalPages(), playLogs.getTotalElements());
}
@GetMapping("rating")
public List<RatingItem> getRating(@RequestParam String aimeId) {
Map<Integer, Music> musicMap = gameMusicService.getIdMap();
List<UserMusicDetail> details = userMusicDetailService.getByUserId(aimeId);
var user = userDataService.getUserByExtId(aimeId).orElseThrow();
var version = VersionUtil.parseVersion(user.getLastRomVersion());
List<RatingItem> result = new ArrayList<>();
for (UserMusicDetail detail : details) {
Music music = musicMap.get(detail.getMusicId());
if (music != null) {
Level level = music.getLevels().get(detail.getLevel());
if (level != null) {
int levelBase = level.getLevel() * 100 + level.getLevelDecimal();
int score = detail.getScoreMax();
int rating = calculateRating(levelBase, score, version);
result.add(new RatingItem(music.getMusicId(), music.getName(), music.getArtistName(), level.getDiff(), score, levelBase, rating));
}
}
}
return result.stream()
.filter(detail -> detail.getLevel() != 4)
.sorted(Comparator.comparingInt(RatingItem::getRating).reversed())
.limit(30)
.collect(Collectors.toList());
}
@GetMapping("rating/recent")
public List<RatingItem> getRecentRating(@RequestParam String aimeId) {
Map<Integer, Music> musicMap = gameMusicService.getIdMap();
Optional<UserGeneralData> recentOptional = userGeneralDataService.getByUserIdAndKey(aimeId, "recent_rating_list");
var user = userDataService.getUserByExtId(aimeId).orElseThrow();
var version = VersionUtil.parseVersion(user.getLastRomVersion());
List<RatingItem> result = new LinkedList<>();
if (recentOptional.isPresent()) {
// Read from recent_rating_list
String val = recentOptional.get().getPropertyValue();
if (StringUtils.isNotBlank(val) && val.contains(",")) {
String[] records = val.split(",");
for (String record :
records) {
String[] value = record.split(":");
Music music = musicMap.get(Integer.parseInt(value[0]));
if (music != null) {
Level level = music.getLevels().get(Integer.parseInt(value[1]));
if (level != null) {
int levelBase = getLevelBase(level.getLevel(), level.getLevelDecimal());
int score = Integer.parseInt(value[2]);
int rating = calculateRating(levelBase, score, version);
result.add(new RatingItem(music.getMusicId(), music.getName(), music.getArtistName(), level.getDiff(), score, levelBase, rating));
}
}
}
}
} else {
// Use old method
List<UserPlaylog> logList = userPlaylogService.getRecent30Plays(aimeId);
for (UserPlaylog log : logList) {
Music music = musicMap.get(log.getMusicId());
if (music != null) {
Level level = music.getLevels().get(log.getLevel());
if (level != null) {
int levelBase = getLevelBase(level.getLevel(), level.getLevelDecimal());
int score = log.getScore();
int rating = calculateRating(levelBase, score, version);
result.add(new RatingItem(music.getMusicId(), music.getName(), music.getArtistName(), level.getDiff(), score, levelBase, rating));
}
}
}
}
return result.stream()
.filter(detail -> detail.getLevel() != 4)
.sorted(Comparator.comparingInt(RatingItem::getRating).reversed())
.limit(10)
.collect(Collectors.toList());
}
@GetMapping("song/{id}")
public List<UserMusicDetail> getSongDetail(@RequestParam String aimeId, @PathVariable int id) {
return userMusicDetailService.getByUserIdAndMusicId(aimeId, id);
}
@GetMapping("song/{id}/{level}")
public List<UserPlaylog> getLevelPlaylog(@RequestParam String aimeId, @PathVariable int id, @PathVariable int level) {
return userPlaylogService.getByUserIdAndMusicIdAndLevel(aimeId, id, level);
}
@GetMapping("character")
public ReducedPageResponse<UserCharacter> getCharacter(@RequestParam String aimeId,
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
Page<UserCharacter> characters = userCharacterService.getByUserId(aimeId, page, size);
return new ReducedPageResponse<>(characters.getContent(), characters.getPageable().getPageNumber(), characters.getTotalPages(), characters.getTotalElements());
}
@PostMapping("character")
public ResponseEntity<Object> updateCharacter(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
Integer characterId = (Integer) request.get("characterId");
Optional<UserCharacter> characterOptional = userCharacterService.getByUserAndCharacterId(profile, characterId);
UserCharacter character;
if(characterOptional.isPresent()) {
character = characterOptional.get();
} else {
character = new UserCharacter(profile);
character.setCharacterId(characterId);
}
if(request.containsKey("level")) {
character.setLevel((Integer) request.get("level"));
}
return ResponseEntity.ok(userCharacterService.save(character));
}
@GetMapping("course")
public List<UserCourse> getCourse(@RequestParam String aimeId) {
return userCourseService.getByUserId(aimeId);
}
@GetMapping("duel")
public List<UserDuel> getDuel(@RequestParam String aimeId) {
return userDuelService.getByUserId(aimeId);
}
@GetMapping("item")
public ReducedPageResponse<UserItem> getItem(@RequestParam String aimeId,
@RequestParam(required = false, defaultValue = "0") int page,
@RequestParam(required = false, defaultValue = "10") int size) {
Page<UserItem> items = userItemService.getByUserId(aimeId, page, size);
return new ReducedPageResponse<>(items.getContent(), items.getPageable().getPageNumber(), items.getTotalPages(), items.getTotalElements());
}
@GetMapping("item/{itemKind}")
public List<UserItem> getItemByKind(@RequestParam String aimeId, @PathVariable int itemKind) {
return userItemService.getByUserAndItemKind(aimeId, itemKind);
}
@PostMapping("item")
public ResponseEntity<Object> updateItem(@RequestBody Map<String, Object> request) {
UserData profile = userDataService.getUserByExtId((String) request.get("aimeId")).orElseThrow();
Integer itemId = (Integer) request.get("itemId");
Integer itemKind = (Integer) request.get("itemKind");
Optional<UserItem> itemOptional = userItemService.getByUserAndItemIdAndKind(profile, itemId,itemKind);
UserItem item;
if(itemOptional.isPresent()) {
item = itemOptional.get();
} else {
item = new UserItem(profile);
item.setItemId(itemId);
item.setItemKind(itemKind);
}
if(request.containsKey("stock")) {
item.setStock((Integer) request.get("stock"));
}
return ResponseEntity.ok(userItemService.save(item));
}
@GetMapping("general")
public ResponseEntity<Object> getGeneralData(@RequestParam String aimeId, @RequestParam String key) {
Optional<UserGeneralData> userGeneralDataOptional = userGeneralDataService.getByUserIdAndKey(aimeId,key);
return userGeneralDataOptional.<ResponseEntity<Object>>map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).body(new MessageResponse("User or value not found.")));
}
@GetMapping("export")
public ResponseEntity<Object> exportAllUserData(@RequestParam String aimeId) {
ChuniDataExport data = new ChuniDataExport();
try {
data.setGameId("SDHD");
data.setUserData(userDataService.getUserByExtId(aimeId).orElseThrow());
data.setUserActivityList(userActivityService.getByUserId(aimeId));
data.setUserCharacterList(userCharacterService.getByUserId(aimeId));
data.setUserChargeList(userChargeService.getByUserId(aimeId));
data.setUserCourseList(userCourseService.getByUserId(aimeId));
data.setUserDuelList(userDuelService.getByUserId(aimeId));
data.setUserGameOption(userGameOptionService.getByUserId(aimeId).orElseThrow());
data.setUserItemList(userItemService.getByUserId(aimeId));
data.setUserMapList(userMapAreaService.getByUserId(aimeId));
data.setUserMusicDetailList(userMusicDetailService.getByUserId(aimeId));
data.setUserPlaylogList(userPlaylogService.getByUserId(aimeId));
} catch (NoSuchElementException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new MessageResponse("User not found"));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new MessageResponse("Error during data export. Reason: " + e.getMessage()));
}
// Set filename
HttpHeaders headers = new HttpHeaders();
headers.set("content-disposition", "attachment; filename=chusan_" + aimeId + "_exported.json");
return new ResponseEntity<>(data, headers, HttpStatus.OK);
}
@PostMapping("import")
public ResponseEntity<Object> importAllUserData(@RequestBody ChuniDataImport data) {
if(!data.getGameId().equals("SDHD")) {
return ResponseEntity.unprocessableEntity().body(new MessageResponse("Wrong Game Profile, Expected 'SDHD', Get " + data.getGameId()));
}
ExternalUserData exUser = data.getUserData();
Optional<Card> cardOptional = cardService.getCardByAccessCode(exUser.getAccessCode());
Card card;
if (cardOptional.isPresent()) {
if (userDataService.getUserByCard(cardOptional.get()).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new MessageResponse("This card already has a chusan profile."));
} else {
card = cardOptional.get();
}
} else {
card = cardService.registerByAccessCode(exUser.getAccessCode());
}
UserData userData = mapper.convert(exUser, new TypeReference<>() {
});
userData.setCard(card);
userDataService.saveAndFlushUserData(userData);
List<UserActivity> userActivityList = data.getUserActivityList();
userActivityList.forEach(x -> x.setUser(userData));
userActivityService.saveAll(userActivityList);
List<UserCharacter> userCharacterList = data.getUserCharacterList();
userCharacterList.forEach(x -> x.setUser(userData));
userCharacterService.saveAll(userCharacterList);
List<UserCharge> userChargeList = data.getUserChargeList();
userCharacterList.forEach(x -> x.setUser(userData));
userChargeService.saveAll(userChargeList);
List<UserCourse> userCourseList = data.getUserCourseList();
userCourseList.forEach(x -> x.setUser(userData));
userCourseService.saveAll(userCourseList);
List<UserDuel> userDuelList = data.getUserDuelList();
userDuelList.forEach(x -> x.setUser(userData));
userDuelService.saveAll(userDuelList);
UserGameOption userGameOption = data.getUserGameOption();
userGameOption.setUser(userData);
userGameOptionService.save(userGameOption);
List<UserItem> userItemList = data.getUserItemList();
userItemList.forEach(x -> x.setUser(userData));
userItemService.saveAll(userItemList);
List<UserMapArea> userMapList = data.getUserMapList();
userMapList.forEach(x -> x.setUser(userData));
userMapAreaService.saveAll(userMapList);
List<UserMusicDetail> userMusicDetailList = data.getUserMusicDetailList();
userMusicDetailList.forEach(x -> x.setUser(userData));
userMusicDetailService.saveAll(userMusicDetailList);
List<UserPlaylog> userPlaylogList = data.getUserPlaylogList();
userPlaylogList.forEach(x -> x.setUser(userData));
userPlaylogService.saveAll(userPlaylogList);
return ResponseEntity.ok(new MessageResponse("Import successfully, aimeId: " + card.getExtId()));
}
private int getLevelBase(int level, int levelDecimal) {
return level * 100 + levelDecimal;
}
private int calculateRating(int levelBase, int score, VersionInfo version) {
if (score >= 1007500) return levelBase + 200;
if (score >= 1005000) return levelBase + 150 + (score - 1005000) * 10 / 500;
if (score >= 1000000) return levelBase + 100 + (score - 1000000) * 5 / 500;
if (score >= 975000) return levelBase + (score - 975000) * 2 / 500;
if (score >= 950000 && version.getMinorVersion() < 35) return levelBase - 150 + (score - 950000) * 3 / 500;
if (score >= 925000) return levelBase - 300 + (score - 925000) * 3 / 500;
if (score >= 900000) return levelBase - 500 + (score - 900000) * 4 / 500;
if (score >= 800000)
return ((levelBase - 500) / 2 + (score - 800000) * ((levelBase - 500) / 2) / (100000));
return 0;
}
}

View File

@@ -0,0 +1,84 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProfileResp {
private String userName;
private int level;
private String exp;
private long point;
private long totalPoint;
private int playCount;
private int playerRating;
private int highestRating;
private int nameplateId;
private int frameId;
private int characterId;
private int trophyId;
private int totalMapNum;
private long totalHiScore;
private long totalBasicHighScore;
private long totalAdvancedHighScore;
private long totalExpertHighScore;
private long totalMasterHighScore;
private long totalUltimaHighScore;
private int friendCount;
private LocalDateTime firstPlayDate;
private LocalDateTime lastPlayDate;
private int courseClass;
private int overPowerPoint;
private int overPowerRate;
private int mapIconId;
private int voiceId;
private int avatarWear;
private int avatarHead;
private int avatarFace;
private int avatarSkin;
private int avatarItem;
private int avatarFront;
private int avatarBack;
}

View File

@@ -0,0 +1,28 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RatingItem {
private int musicId;
private String musicName;
private String artistName;
private int level;
private int score;
private int ratingBase;
private int rating;
}

View File

@@ -0,0 +1,87 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RecentResp {
private LocalDateTime playDate;
private LocalDateTime userPlayDate;
private int musicId;
private int level;
private int customId;
private int playedCustom1;
private int playedCustom2;
private int playedCustom3;
private int track;
private int score;
private int rank;
private int maxCombo;
private int maxChain;
private int rateTap;
private int rateHold;
private int rateSlide;
private int rateAir;
private int rateFlick;
private int judgeGuilty;
private int judgeAttack;
private int judgeJustice;
private int judgeCritical;
private int playerRating;
@JsonProperty("isNewRecord")
private boolean isNewRecord;
@JsonProperty("isFullCombo")
private boolean isFullCombo;
private int fullChainKind;
@JsonProperty("isAllJustice")
private boolean isAllJustice;
private int characterId;
private int skillId;
private int playKind;
@JsonProperty("isClear")
private boolean isClear;
private int skillLevel;
private int skillEffect;
}

View File

@@ -0,0 +1,48 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ScoreResp {
private int musicId;
private int level;
private int playCount;
private int scoreMax;
private int resRequestCount;
private int resAcceptCount;
private int resSuccessCount;
private int missCount;
private int maxComboCount;
@JsonProperty("isFullCombo")
private boolean isFullCombo;
@JsonProperty("isAllJustice")
private boolean isAllJustice;
@JsonProperty("isSuccess")
private boolean isSuccess;
private int fullChain;
private int maxChain;
private int scoreRank;
}

View File

@@ -0,0 +1,30 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external;
import icu.samnyan.aqua.sega.chusan.model.userdata.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* This class is use for exporting chusan profile
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChuniDataExport {
private String gameId = "SDHD";
private UserData userData;
private List<UserActivity> userActivityList;
private List<UserCharacter> userCharacterList;
private List<UserCharge> userChargeList;
private List<UserCourse> userCourseList;
private List<UserDuel> userDuelList;
private UserGameOption userGameOption;
private List<UserItem> userItemList;
private List<UserMapArea> userMapList;
private List<UserMusicDetail> userMusicDetailList;
private List<UserPlaylog> userPlaylogList;
}

View File

@@ -0,0 +1,30 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external;
import icu.samnyan.aqua.sega.chusan.model.userdata.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* This class is use for importing chusan profile
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChuniDataImport {
private String gameId;
private ExternalUserData userData;
private List<UserActivity> userActivityList;
private List<UserCharacter> userCharacterList;
private List<UserCharge> userChargeList;
private List<UserCourse> userCourseList;
private List<UserDuel> userDuelList;
private UserGameOption userGameOption;
private List<UserItem> userItemList;
private List<UserMapArea> userMapList;
private List<UserMusicDetail> userMusicDetailList;
private List<UserPlaylog> userPlaylogList;
}

View File

@@ -0,0 +1,118 @@
package icu.samnyan.aqua.api.model.resp.sega.chuni.v2.external;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* This class is use for exported UserData class. Using access code as identifier
* @author samnyan (privateamusement@protonmail.com)
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExternalUserData {
// Access Code of the card
private String accessCode;
private String userName;
private LocalDateTime lastLoginDate;
private boolean isWebJoin;
private String webLimitDate;
private int level;
private int reincarnationNum;
private String exp;
private long point;
private long totalPoint;
private int playCount;
private int multiPlayCount;
private int multiWinCount;
private int requestResCount;
private int acceptResCount;
private int successResCount;
private int playerRating;
private int highestRating;
private int nameplateId;
private int frameId;
private int characterId;
private int trophyId;
private int playedTutorialBit;
private int firstTutorialCancelNum;
private int masterTutorialCancelNum;
private int totalRepertoireCount;
private int totalMapNum;
private long totalHiScore;
private long totalBasicHighScore;
private long totalAdvancedHighScore;
private long totalExpertHighScore;
private long totalMasterHighScore;
private LocalDateTime eventWatchedDate;
private int friendCount;
@JsonProperty("isMaimai")
private boolean isMaimai;
private String firstGameId;
private String firstRomVersion;
private String firstDataVersion;
private LocalDateTime firstPlayDate;
private String lastGameId;
private String lastRomVersion;
private String lastDataVersion;
private LocalDateTime lastPlayDate;
private int lastPlaceId;
private String lastPlaceName;
private String lastRegionId;
private String lastRegionName;
private String lastAllNetId;
private String lastClientId;
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.AvatarAcc;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameAvatarAccRepository")
public interface GameAvatarAccRepository extends JpaRepository<AvatarAcc, Long> {
Optional<AvatarAcc> findById(int trophyId);
}

View File

@@ -0,0 +1,12 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Character;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameCharacterRepository")
public interface GameCharacterRepository extends JpaRepository<Character, Long> {
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Frame;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameFrameRepository")
public interface GameFrameRepository extends JpaRepository<Frame, Long> {
Optional<Frame> findById(int trophyId);
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.MapIcon;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameMapIconRepository")
public interface GameMapIconRepository extends JpaRepository<MapIcon, Long> {
Optional<MapIcon> findById(int mapIconId);
}

View File

@@ -0,0 +1,16 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Music;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameMusicRepository")
public interface GameMusicRepository extends JpaRepository<Music, Long> {
Optional<Music> findByMusicId(int musicId);
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.NamePlate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameNamePlateRepository")
public interface GameNamePlateRepository extends JpaRepository<NamePlate, Long> {
Optional<NamePlate> findById(int trophyId);
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.SystemVoice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameSystemVoiceRepository")
public interface GameSystemVoiceRepository extends JpaRepository<SystemVoice, Long> {
Optional<SystemVoice> findById(int trophyId);
}

View File

@@ -0,0 +1,17 @@
package icu.samnyan.aqua.sega.chusan.dao.gamedata;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Trophy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Repository("ChusanGameTrophyRepository")
public interface GameTrophyRepository extends JpaRepository<Trophy, Long> {
Optional<Trophy> findById(int trophyId);
}

View File

@@ -22,5 +22,7 @@ public interface UserItemRepository extends JpaRepository<UserItem, Long> {
List<UserItem> findAllByUser_Card_ExtId(Long extId);
List<UserItem> findAllByUser_Card_ExtIdAndItemKind(Long extId, int itemKind);
Page<UserItem> findByUser_Card_ExtId(Long extId, Pageable pageable);
}

View File

@@ -0,0 +1,28 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "AvatarAcc")
@Table(name = "chusan_avatar")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AvatarAcc implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
private int category;
}

View File

@@ -0,0 +1,37 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanCharacter")
@Table(name = "chusan_game_character")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Character implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
private String releaseTag;
private String worksName;
private String illustratorName;
private String addImages;
}

View File

@@ -0,0 +1,26 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanFrame")
@Table(name = "chusan_frame")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Frame implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
}

View File

@@ -0,0 +1,32 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
public enum Genre {
POPS_ANIME("POPS & ANIME"),
GAME("GAME"),
NICONICO("niconico"),
TOUHOU("東方Project"),
RESERVE2("Reserve2"),
ORIGINAL("Original"),
VARIETY("Variety"),
IRODORI("イロドリミドリ"),
KOTONOHA("葉Project"),
GEKICHUMA("ゲキマイ");
private String displayName;
Genre(String displayName) {
this.displayName = displayName;
}
public String displayName() {
return displayName;
}
@Override
public String toString() {
return displayName;
}
}

View File

@@ -0,0 +1,40 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanMusicLevel")
@Table(name = "chusan_music_level")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Level implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonIgnore
private long id;
@JsonIgnore
@ManyToOne
@JoinColumn(name = "music_id")
private Music music;
private boolean enable;
private int level;
private int levelDecimal;
private int diff;
}

View File

@@ -0,0 +1,26 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanMapIcon")
@Table(name = "chusan_mapicon")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MapIcon implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
}

View File

@@ -0,0 +1,40 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Map;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanMusic")
@Table(name = "chusan_music")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Music implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private int musicId;
private String name;
private String sortName;
private String artistName;
private Genre genre;
private String releaseVersion;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "music")
@MapKey(name = "diff")
private Map<Integer, Level> levels;
}

View File

@@ -0,0 +1,26 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanNamePlate")
@Table(name = "chusan_nameplate")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NamePlate implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
}

View File

@@ -0,0 +1,26 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanSystemVoice")
@Table(name = "chusan_voice")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SystemVoice implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
}

View File

@@ -0,0 +1,26 @@
package icu.samnyan.aqua.sega.chusan.model.gamedata;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Entity(name = "ChusanTrophy")
@Table(name = "chusan_trophy")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Trophy implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private long id;
private String name;
}

View File

@@ -0,0 +1,53 @@
package icu.samnyan.aqua.sega.chusan.service;
import icu.samnyan.aqua.sega.chusan.dao.gamedata.GameMusicRepository;
import icu.samnyan.aqua.sega.chusan.model.gamedata.Music;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* @author samnyan (privateamusement@protonmail.com)
*/
@Service("ChusanGameMusicService")
public class GameMusicService {
private final GameMusicRepository gameMusicRepository;
@Autowired
public GameMusicService(GameMusicRepository gameMusicRepository) {
this.gameMusicRepository = gameMusicRepository;
}
public Music save(Music music) {
return gameMusicRepository.save(music);
}
public List<Music> saveAll(List<Music> musicList) {
return gameMusicRepository.saveAll(musicList);
}
@Cacheable("music")
public List<Music> getAll() {
return gameMusicRepository.findAll();
}
public Map<Integer, Music> getIdMap() {
Map<Integer, Music> musicMap = new LinkedHashMap<>();
getAll().forEach(music -> musicMap.put(music.getMusicId(), music));
return musicMap;
}
public Optional<Music> getById(String musicId) {
return getById(Integer.parseInt(musicId));
}
public Optional<Music> getById(int musicId) {
return gameMusicRepository.findByMusicId(musicId);
}
}

View File

@@ -44,6 +44,10 @@ public class UserItemService {
Pageable page = PageRequest.of(pageNumber, maxCount);
return userItemRepository.findAllByUser_Card_ExtIdAndItemKind(Long.parseLong(userId), kind, page);
}
public List<UserItem> getByUserAndItemKind(String userId, int kind) {
return userItemRepository.findAllByUser_Card_ExtIdAndItemKind(Long.parseLong(userId), kind);
}
public Page<UserItem> getByUserId(String userId, int page, int size) {
return userItemRepository.findByUser_Card_ExtId(Long.parseLong(userId), PageRequest.of(page, size));