diff --git a/pom.xml b/pom.xml
index a6fb3a7..be6c37d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
1.18.30
3.5.5
4.3.1
- 8.0.33
+ 8.0.33
@@ -43,14 +43,14 @@
-
+
org.springframework.boot
spring-boot-starter-jdbc
-
+
com.mysql
mysql-connector-j
- ${mysql.version}
+ ${mysql.version}
com.baomidou
@@ -70,26 +70,26 @@
true
-
- io.jsonwebtoken
- jjwt-api
- 0.11.5
-
-
- io.jsonwebtoken
- jjwt-impl
- 0.11.5
-
-
- io.jsonwebtoken
- jjwt-jackson
- 0.11.5
-
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.springdoc
diff --git a/src/main/java/icu/sunway/ai_spring_example/Config/OpenApiConfig.java b/src/main/java/icu/sunway/ai_spring_example/Config/OpenApiConfig.java
new file mode 100644
index 0000000..5420e1d
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Config/OpenApiConfig.java
@@ -0,0 +1,22 @@
+package icu.sunway.ai_spring_example.Config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * OpenAPI配置类
+ */
+@Configuration
+public class OpenApiConfig {
+
+ @Bean
+ public OpenAPI customOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("Steam-like Game Platform API")
+ .version("1.0.0")
+ .description("类似Steam的游戏平台API文档,提供游戏推荐等功能"));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/GameController.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/GameController.java
new file mode 100644
index 0000000..b861089
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/GameController.java
@@ -0,0 +1,39 @@
+package icu.sunway.ai_spring_example.Controllers.GameController;
+
+import icu.sunway.ai_spring_example.Controllers.GameController.VO.GameVO;
+import icu.sunway.ai_spring_example.Service.IGameService;
+import icu.sunway.ai_spring_example.Utils.PageUtils;
+import icu.sunway.ai_spring_example.Utils.ResponseUtils;
+import icu.sunway.ai_spring_example.Utils.ResponseUtils.Response;
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/games")
+public class GameController {
+
+ @Resource
+ private IGameService gameService;
+
+ /**
+ * 获取游戏推荐列表
+ *
+ * @param page 当前页码
+ * @param limit 每页游戏数量
+ * @return 游戏推荐列表
+ */
+ @GetMapping("/recommendations")
+ public Response> getRecommendedGames(
+ @RequestParam(required = false) Integer page,
+ @RequestParam(required = false) Integer limit) {
+ try {
+ PageUtils.PaginationResult result = gameService.getRecommendedGames(page, limit);
+ return ResponseUtils.success("获取游戏推荐列表成功", result);
+ } catch (Exception e) {
+ return ResponseUtils.fail(ResponseUtils.INTERNAL_SERVER_ERROR, "获取游戏推荐列表失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/VO/GameVO.java b/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/VO/GameVO.java
new file mode 100644
index 0000000..e59fdc6
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Controllers/GameController/VO/GameVO.java
@@ -0,0 +1,34 @@
+package icu.sunway.ai_spring_example.Controllers.GameController.VO;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 游戏推荐列表VO,只返回指定的信息
+ */
+@Data
+@Builder
+public class GameVO {
+ private String name;
+ private String mainImage;
+ private String supportedPlatforms;
+ private List genres;
+ private BigDecimal price;
+ private Integer discountPercentage;
+ private LocalDateTime releaseDate;
+ private List screenshots;
+
+ /**
+ * 游戏截图VO
+ */
+ @Data
+ @Builder
+ public static class ScreenshotVO {
+ private String mediaUrl;
+ private String thumbnailUrl;
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Entity/Game.java b/src/main/java/icu/sunway/ai_spring_example/Entity/Game.java
new file mode 100644
index 0000000..d2e8daa
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Entity/Game.java
@@ -0,0 +1,64 @@
+package icu.sunway.ai_spring_example.Entity;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("games")
+public class Game {
+ private Integer id;
+ private Integer steamAppid;
+ private String name;
+ private String slug;
+ private String shortDescription;
+ private String detailedDescription;
+ private String aboutTheGame;
+ private LocalDateTime releaseDate;
+ private Boolean comingSoon;
+ private BigDecimal price;
+ private Integer discountPercentage;
+ private BigDecimal finalPrice;
+ private String currency;
+ private String paymentType;
+ private Integer metacriticScore;
+ private BigDecimal userRating;
+ private Integer ratingCount;
+ private Integer reviewScore;
+ private Integer positiveReviews;
+ private Integer negativeReviews;
+ private String supportedPlatforms;
+ private String gameEngine;
+ private String developerIds;
+ private String publisherIds;
+ private String genreIds;
+ private String tagIds;
+ private String headerImage;
+ private String capsuleImage;
+ private String website;
+ private Integer achievementsCount;
+ private Boolean controllerSupport;
+ private Boolean multiplayer;
+ private Boolean coOp;
+ private Boolean lobbies;
+ private String status;
+ private LocalDateTime createdAt;
+ private LocalDateTime updatedAt;
+
+ // 非数据库字段,用于返回API结果
+ @TableField(exist = false)
+ private String mainImage;
+ @TableField(exist = false)
+ private List genres;
+ @TableField(exist = false)
+ private List screenshots;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Entity/GameGenre.java b/src/main/java/icu/sunway/ai_spring_example/Entity/GameGenre.java
new file mode 100644
index 0000000..92d0c79
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Entity/GameGenre.java
@@ -0,0 +1,18 @@
+package icu.sunway.ai_spring_example.Entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("game_genres")
+public class GameGenre {
+ private Integer gameId;
+ private Integer genreId;
+ private LocalDateTime createdAt;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Entity/Genre.java b/src/main/java/icu/sunway/ai_spring_example/Entity/Genre.java
new file mode 100644
index 0000000..090d92d
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Entity/Genre.java
@@ -0,0 +1,21 @@
+package icu.sunway.ai_spring_example.Entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("genres")
+public class Genre {
+ private Integer id;
+ private String name;
+ private String slug;
+ private String description;
+ private LocalDateTime createdAt;
+ private LocalDateTime updatedAt;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Entity/Media.java b/src/main/java/icu/sunway/ai_spring_example/Entity/Media.java
new file mode 100644
index 0000000..eb52699
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Entity/Media.java
@@ -0,0 +1,25 @@
+package icu.sunway.ai_spring_example.Entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("media")
+public class Media {
+ private Integer id;
+ private Integer gameId;
+ private String mediaType;
+ private String mediaUrl;
+ private String thumbnailUrl;
+ private String description;
+ private Integer orderIndex;
+ private Boolean isPrimary;
+ private LocalDateTime createdAt;
+ private LocalDateTime updatedAt;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Entity/Screenshot.java b/src/main/java/icu/sunway/ai_spring_example/Entity/Screenshot.java
new file mode 100644
index 0000000..406dc8e
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Entity/Screenshot.java
@@ -0,0 +1,14 @@
+package icu.sunway.ai_spring_example.Entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Screenshot {
+ private Integer id;
+ private String mediaUrl;
+ private String thumbnailUrl;
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/GameGenreMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/GameGenreMapper.java
new file mode 100644
index 0000000..8aeb5f9
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/GameGenreMapper.java
@@ -0,0 +1,9 @@
+package icu.sunway.ai_spring_example.Mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import icu.sunway.ai_spring_example.Entity.GameGenre;
+
+@Mapper
+public interface GameGenreMapper extends BaseMapper {
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/GameMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/GameMapper.java
new file mode 100644
index 0000000..a0002e3
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/GameMapper.java
@@ -0,0 +1,10 @@
+package icu.sunway.ai_spring_example.Mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import icu.sunway.ai_spring_example.Entity.Game;
+
+@Mapper
+public interface GameMapper extends BaseMapper {
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/GenreMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/GenreMapper.java
new file mode 100644
index 0000000..944d38e
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/GenreMapper.java
@@ -0,0 +1,9 @@
+package icu.sunway.ai_spring_example.Mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import icu.sunway.ai_spring_example.Entity.Genre;
+
+@Mapper
+public interface GenreMapper extends BaseMapper {
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Mapper/MediaMapper.java b/src/main/java/icu/sunway/ai_spring_example/Mapper/MediaMapper.java
new file mode 100644
index 0000000..4a2c905
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Mapper/MediaMapper.java
@@ -0,0 +1,9 @@
+package icu.sunway.ai_spring_example.Mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import icu.sunway.ai_spring_example.Entity.Media;
+
+@Mapper
+public interface MediaMapper extends BaseMapper {
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/IGameService.java b/src/main/java/icu/sunway/ai_spring_example/Service/IGameService.java
new file mode 100644
index 0000000..4fbcf57
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Service/IGameService.java
@@ -0,0 +1,13 @@
+package icu.sunway.ai_spring_example.Service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import icu.sunway.ai_spring_example.Controllers.GameController.VO.GameVO;
+import icu.sunway.ai_spring_example.Entity.Game;
+import icu.sunway.ai_spring_example.Utils.PageUtils;
+
+public interface IGameService extends IService {
+
+ // 获取游戏推荐列表
+ PageUtils.PaginationResult getRecommendedGames(Integer page, Integer limit);
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Service/Implements/GameServiceImpl.java b/src/main/java/icu/sunway/ai_spring_example/Service/Implements/GameServiceImpl.java
new file mode 100644
index 0000000..296c92a
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Service/Implements/GameServiceImpl.java
@@ -0,0 +1,153 @@
+package icu.sunway.ai_spring_example.Service.Implements;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+import icu.sunway.ai_spring_example.Controllers.GameController.VO.GameVO;
+import icu.sunway.ai_spring_example.Entity.Game;
+import icu.sunway.ai_spring_example.Entity.GameGenre;
+import icu.sunway.ai_spring_example.Entity.Genre;
+import icu.sunway.ai_spring_example.Entity.Media;
+import icu.sunway.ai_spring_example.Mapper.GameGenreMapper;
+import icu.sunway.ai_spring_example.Mapper.GameMapper;
+import icu.sunway.ai_spring_example.Mapper.GenreMapper;
+import icu.sunway.ai_spring_example.Mapper.MediaMapper;
+import icu.sunway.ai_spring_example.Service.IGameService;
+import icu.sunway.ai_spring_example.Utils.PageUtils;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+
+@Service
+public class GameServiceImpl extends ServiceImpl
+ implements IGameService {
+
+ @Resource
+ private GameMapper gameMapper;
+
+ @Resource
+ private GameGenreMapper gameGenreMapper;
+
+ @Resource
+ private GenreMapper genreMapper;
+
+ @Resource
+ private MediaMapper mediaMapper;
+
+ @Override
+ public PageUtils.PaginationResult getRecommendedGames(Integer page, Integer limit) {
+ // 使用分页工具类创建Page对象
+ Page gamePage = PageUtils.createPage(page, limit);
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.orderByDesc("release_date");
+
+ // 获取游戏列表
+ Page pageResult = this.page(gamePage, queryWrapper);
+ List games = pageResult.getRecords();
+
+ if (games.isEmpty()) {
+ // 组装空结果返回
+ return PageUtils.createEmptyPaginationResult(page, limit);
+ }
+
+ // 获取游戏ID列表
+ List gameIds = games.stream()
+ .map(Game::getId)
+ .collect(Collectors.toList());
+
+ // 使用MyBatis Plus查询游戏类型
+ // 1. 查询游戏与类型的关联关系
+ QueryWrapper gameGenreWrapper = new QueryWrapper<>();
+ gameGenreWrapper.in("game_id", gameIds);
+ List gameGenres = gameGenreMapper.selectList(gameGenreWrapper);
+
+ // 2. 获取所有类型ID
+ List genreIds = gameGenres.stream()
+ .map(GameGenre::getGenreId)
+ .distinct()
+ .collect(Collectors.toList());
+
+ // 3. 查询类型信息
+ Map genreMap = new HashMap<>();
+ if (!genreIds.isEmpty()) {
+ QueryWrapper genreWrapper = new QueryWrapper<>();
+ genreWrapper.in("id", genreIds);
+ List genres = genreMapper.selectList(genreWrapper);
+ genreMap = genres.stream()
+ .collect(Collectors.toMap(Genre::getId, Genre::getName));
+ }
+
+ // 4. 组装游戏类型映射
+ Map> genresMap = new HashMap<>();
+ for (GameGenre gameGenre : gameGenres) {
+ Integer gameId = gameGenre.getGameId();
+ String genreName = genreMap.get(gameGenre.getGenreId());
+ if (genreName != null) {
+ genresMap.computeIfAbsent(gameId, k -> new ArrayList<>())
+ .add(genreName);
+ }
+ }
+
+ // 使用MyBatis Plus查询游戏主图
+ QueryWrapper mainImageWrapper = new QueryWrapper<>();
+ mainImageWrapper.in("game_id", gameIds)
+ .eq("is_primary", true)
+ .select("game_id", "media_url");
+ List mainImages = mediaMapper.selectList(mainImageWrapper);
+ Map mainImagesMap = mainImages.stream()
+ .collect(Collectors.toMap(Media::getGameId, Media::getMediaUrl));
+
+ // 使用MyBatis Plus查询游戏截图列表
+ QueryWrapper screenshotWrapper = new QueryWrapper<>();
+ screenshotWrapper.in("game_id", gameIds)
+ .in("media_type", "screenshot", "image")
+ .orderByAsc("order_index")
+ .select("id", "game_id", "media_url", "thumbnail_url");
+ List screenshots = mediaMapper.selectList(screenshotWrapper);
+
+ // 组装游戏截图映射
+ Map> screenshotsMap = new HashMap<>();
+ for (Media media : screenshots) {
+ Integer gameId = media.getGameId();
+ GameVO.ScreenshotVO screenshot = GameVO.ScreenshotVO.builder()
+ .mediaUrl(media.getMediaUrl())
+ .thumbnailUrl(media.getThumbnailUrl())
+ .build();
+ screenshotsMap.computeIfAbsent(gameId, k -> new ArrayList<>())
+ .add(screenshot);
+ }
+
+ // 将Game转换为GameVO
+ List gameVOs = games.stream()
+ .map(game -> {
+ Integer gameId = game.getId();
+ return GameVO.builder()
+ .name(game.getName())
+ .mainImage(mainImagesMap.getOrDefault(gameId, ""))
+ .supportedPlatforms(game.getSupportedPlatforms())
+ .genres(genresMap.getOrDefault(gameId, new ArrayList()))
+ .price(game.getPrice())
+ .discountPercentage(game.getDiscountPercentage())
+ .releaseDate(game.getReleaseDate())
+ .screenshots(screenshotsMap.getOrDefault(gameId, new ArrayList()))
+ .build();
+ })
+ .collect(Collectors.toList());
+
+ // 创建新的Page结果
+ Page voPageResult = new Page<>();
+ voPageResult.setRecords(gameVOs);
+ voPageResult.setTotal(pageResult.getTotal());
+ voPageResult.setCurrent(pageResult.getCurrent());
+ voPageResult.setSize(pageResult.getSize());
+ voPageResult.setPages(pageResult.getPages());
+
+ // 组装分页结果并返回
+ return PageUtils.createPaginationResult(voPageResult);
+ }
+}
diff --git a/src/main/java/icu/sunway/ai_spring_example/Utils/PageUtils.java b/src/main/java/icu/sunway/ai_spring_example/Utils/PageUtils.java
new file mode 100644
index 0000000..b2acda4
--- /dev/null
+++ b/src/main/java/icu/sunway/ai_spring_example/Utils/PageUtils.java
@@ -0,0 +1,111 @@
+package icu.sunway.ai_spring_example.Utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 分页工具类
+ */
+public class PageUtils {
+
+ /**
+ * 默认页码
+ */
+ public static final Integer DEFAULT_PAGE = 1;
+
+ /**
+ * 默认每页数量
+ */
+ public static final Integer DEFAULT_LIMIT = 10;
+
+ /**
+ * 最大每页数量
+ */
+ public static final Integer MAX_LIMIT = 100;
+
+ /**
+ * 分页结果类
+ *
+ * @param 数据类型
+ */
+ @Data
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class PaginationResult {
+ private Integer currentPage;
+ private Integer pageSize;
+ private Long totalItems;
+ private Integer totalPages;
+ private List list;
+ }
+
+ /**
+ * 验证和调整分页参数
+ *
+ * @param page 页码
+ * @param limit 每页数量
+ * @return 调整后的分页参数数组 [page, limit]
+ */
+ public static Integer[] validatePageParams(Integer page, Integer limit) {
+ // 验证页码
+ if (page == null || page < 1) {
+ page = DEFAULT_PAGE;
+ }
+
+ // 验证每页数量
+ if (limit == null || limit < 1 || limit > MAX_LIMIT) {
+ limit = DEFAULT_LIMIT;
+ }
+
+ return new Integer[] { page, limit };
+ }
+
+ /**
+ * 创建Page对象
+ *
+ * @param page 页码
+ * @param limit 每页数量
+ * @return Page对象
+ */
+ public static Page createPage(Integer page, Integer limit) {
+ Integer[] params = validatePageParams(page, limit);
+ return new Page<>(params[0], params[1]);
+ }
+
+ /**
+ * 将MyBatis Plus的Page结果转换为分页结果
+ *
+ * @param pageResult MyBatis Plus的Page结果
+ * @return 分页结果
+ */
+ public static PaginationResult createPaginationResult(IPage pageResult) {
+ return new PaginationResult<>(
+ (int) pageResult.getCurrent(),
+ (int) pageResult.getSize(),
+ pageResult.getTotal(),
+ (int) pageResult.getPages(),
+ pageResult.getRecords());
+ }
+
+ /**
+ * 创建空的分页结果
+ *
+ * @param page 页码
+ * @param limit 每页数量
+ * @return 空的分页结果
+ */
+ public static PaginationResult createEmptyPaginationResult(Integer page, Integer limit) {
+ Integer[] params = validatePageParams(page, limit);
+ return new PaginationResult<>(
+ params[0],
+ params[1],
+ 0L,
+ 0,
+ List.of());
+ }
+}