From b03c58ae1bfa52e24dee7baeb298cce43578712c Mon Sep 17 00:00:00 2001 From: zyh Date: Wed, 27 Aug 2025 17:25:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要修改: 1. 在SystemConfigController中完善用户端配置获取和批量更新接口的实现。 2. 优化SystemConfigService,增强配置值验证逻辑,确保配置的准确性和有效性。 技术细节: - 新增的功能提升了用户端配置的管理灵活性,支持更高效的批量操作。 --- docs/公告接口使用示例.md | 216 ++++++++++++++++++ .../admin/AnnouncementController.java | 188 +++++++++++++++ .../dto/admin/AnnouncementConverter.java | 43 ++++ .../model/dto/admin/AnnouncementRequest.java | 51 +++++ .../model/dto/admin/AnnouncementResponse.java | 85 +++++++ .../service/admin/AnnouncementService.java | 94 ++++++++ 6 files changed, 677 insertions(+) create mode 100644 docs/公告接口使用示例.md create mode 100644 src/main/java/com/gameplatform/server/controller/admin/AnnouncementController.java create mode 100644 src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementConverter.java create mode 100644 src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementRequest.java create mode 100644 src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementResponse.java create mode 100644 src/main/java/com/gameplatform/server/service/admin/AnnouncementService.java diff --git a/docs/公告接口使用示例.md b/docs/公告接口使用示例.md new file mode 100644 index 0000000..cfc0755 --- /dev/null +++ b/docs/公告接口使用示例.md @@ -0,0 +1,216 @@ +# 公告接口使用示例 + +## 接口概述 + +公告管理接口提供了完整的CRUD操作,包括创建、查询、更新和删除公告的功能。 + +**基础路径**: `/api/admin/announcement` + +## 接口列表 + +### 1. 创建公告 + +**POST** `/api/admin/announcement` + +**请求体**: +```json +{ + "title": "系统维护通知", + "content": "系统将于今晚10点进行维护,预计维护时间2小时", + "enabled": true, + "jumpUrl": "https://example.com" +} +``` + +**响应**: +```json +{ + "success": true, + "message": "公告创建成功", + "id": 1 +} +``` + +### 2. 获取公告列表(分页) + +**GET** `/api/admin/announcement/list?page=1&size=20&enabled=true` + +**参数**: +- `page`: 页码(默认1) +- `size`: 每页大小(默认20) +- `enabled`: 按启用状态筛选(可选) + +**响应**: +```json +{ + "items": [ + { + "id": 1, + "title": "系统维护通知", + "content": "系统将于今晚10点进行维护,预计维护时间2小时", + "enabled": true, + "jumpUrl": "https://example.com", + "createdAt": "2023-12-01T10:00:00", + "updatedAt": "2023-12-01T10:00:00" + } + ], + "total": 1, + "page": 1, + "size": 20 +} +``` + +### 3. 获取公告详情 + +**GET** `/api/admin/announcement/{id}` + +**响应**: +```json +{ + "id": 1, + "title": "系统维护通知", + "content": "系统将于今晚10点进行维护,预计维护时间2小时", + "enabled": true, + "jumpUrl": "https://example.com", + "createdAt": "2023-12-01T10:00:00", + "updatedAt": "2023-12-01T10:00:00" +} +``` + +### 4. 更新公告 + +**PUT** `/api/admin/announcement/{id}` + +**请求体**: +```json +{ + "title": "系统维护通知(更新)", + "content": "系统维护已完成", + "enabled": false, + "jumpUrl": null +} +``` + +**响应**: +```json +{ + "success": true, + "message": "公告更新成功" +} +``` + +### 5. 删除公告 + +**DELETE** `/api/admin/announcement/{id}` + +**响应**: +```json +{ + "success": true, + "message": "公告删除成功" +} +``` + +### 6. 更新公告启用状态 + +**PUT** `/api/admin/announcement/{id}/enabled?enabled=true` + +**参数**: +- `enabled`: 启用状态(true/false) + +**响应**: +```json +{ + "success": true, + "message": "公告已启用" +} +``` + +### 7. 获取启用的公告 + +**GET** `/api/admin/announcement/enabled` + +**响应**: +```json +[ + { + "id": 1, + "title": "系统维护通知", + "content": "系统将于今晚10点进行维护,预计维护时间2小时", + "enabled": true, + "jumpUrl": "https://example.com", + "createdAt": "2023-12-01T10:00:00", + "updatedAt": "2023-12-01T10:00:00" + } +] +``` + +## curl 示例 + +### 创建公告 +```bash +curl -X POST http://localhost:8080/api/admin/announcement \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{ + "title": "系统维护通知", + "content": "系统将于今晚10点进行维护,预计维护时间2小时", + "enabled": true, + "jumpUrl": "https://example.com" + }' +``` + +### 获取公告列表 +```bash +curl -X GET "http://localhost:8080/api/admin/announcement/list?page=1&size=10&enabled=true" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +### 更新公告状态 +```bash +curl -X PUT "http://localhost:8080/api/admin/announcement/1/enabled?enabled=false" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +## 错误处理 + +### 常见错误响应 + +**400 Bad Request** - 参数错误: +```json +{ + "success": false, + "message": "公告标题不能为空" +} +``` + +**404 Not Found** - 公告不存在: +```json +{ + "timestamp": "2023-12-01T10:00:00.000+00:00", + "status": 404, + "error": "Not Found", + "path": "/api/admin/announcement/999" +} +``` + +## 注意事项 + +1. 所有管理接口都需要JWT认证 +2. 公告标题和内容不能为空 +3. `enabled` 字段默认为 `false` +4. `jumpUrl` 字段可选,用于设置点击公告后的跳转链接 +5. 获取启用公告的接口最多返回10条记录 +6. 所有时间字段使用 ISO 8601 格式 + +## 数据库表结构 + +公告数据存储在 `announcement` 表中,包含以下字段: + +- `id` - 主键,自增 +- `title` - 公告标题 +- `content` - 公告内容 +- `enabled` - 启用状态 +- `jump_url` - 跳转链接 +- `created_at` - 创建时间 +- `updated_at` - 更新时间 diff --git a/src/main/java/com/gameplatform/server/controller/admin/AnnouncementController.java b/src/main/java/com/gameplatform/server/controller/admin/AnnouncementController.java new file mode 100644 index 0000000..63ce89a --- /dev/null +++ b/src/main/java/com/gameplatform/server/controller/admin/AnnouncementController.java @@ -0,0 +1,188 @@ +package com.gameplatform.server.controller.admin; + +import com.gameplatform.server.model.dto.admin.AnnouncementConverter; +import com.gameplatform.server.model.dto.admin.AnnouncementRequest; +import com.gameplatform.server.model.dto.admin.AnnouncementResponse; +import com.gameplatform.server.model.dto.common.PageResult; +import com.gameplatform.server.model.entity.admin.Announcement; +import com.gameplatform.server.service.admin.AnnouncementService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/admin/announcement") +@Tag(name = "公告管理", description = "系统公告的增删改查接口") +public class AnnouncementController { + + @Autowired + private AnnouncementService announcementService; + + @PostMapping + @Operation(summary = "创建公告", description = "创建新的系统公告") + public ResponseEntity createAnnouncement(@RequestBody AnnouncementRequest request) { + if (request.getTitle() == null || request.getTitle().trim().isEmpty()) { + return ResponseEntity.badRequest().body(new Object() { + public final boolean success = false; + public final String message = "公告标题不能为空"; + }); + } + + if (request.getContent() == null || request.getContent().trim().isEmpty()) { + return ResponseEntity.badRequest().body(new Object() { + public final boolean success = false; + public final String message = "公告内容不能为空"; + }); + } + + if (request.getEnabled() == null) { + request.setEnabled(false); // 默认禁用 + } + + Announcement announcement = AnnouncementConverter.toEntity(request); + boolean success = announcementService.createAnnouncement(announcement); + final boolean finalSuccess = success; + + return ResponseEntity.ok(new Object() { + public final boolean success = finalSuccess; + public final String message = finalSuccess ? "公告创建成功" : "公告创建失败"; + public final Long id = finalSuccess ? announcement.getId() : null; + }); + } + + @GetMapping("/list") + @Operation(summary = "获取公告列表", description = "分页获取所有系统公告") + public ResponseEntity> getAnnouncementList( + @Parameter(description = "页码", example = "1") @RequestParam(defaultValue = "1") int page, + @Parameter(description = "每页大小", example = "20") @RequestParam(defaultValue = "20") int size, + @Parameter(description = "按启用状态筛选,不传则获取全部") @RequestParam(required = false) Boolean enabled) { + + int offset = (page - 1) * size; + List announcements; + long total; + + if (enabled != null) { + announcements = announcementService.getAnnouncementsByEnabled(enabled, size, offset); + total = announcementService.getAnnouncementCountByEnabled(enabled); + } else { + announcements = announcementService.getAllAnnouncements(size, offset); + total = announcementService.getAnnouncementCount(); + } + + List responses = announcements.stream() + .map(AnnouncementConverter::toResponse) + .toList(); + + PageResult result = new PageResult<>(); + result.setItems(responses); + result.setTotal(total); + result.setPage(page); + result.setSize(size); + + return ResponseEntity.ok(result); + } + + @GetMapping("/{id}") + @Operation(summary = "获取公告详情", description = "根据ID获取公告详细信息") + public ResponseEntity getAnnouncementById( + @Parameter(description = "公告ID", example = "1") @PathVariable Long id) { + Announcement announcement = announcementService.getAnnouncementById(id); + if (announcement == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(AnnouncementConverter.toResponse(announcement)); + } + + @PutMapping("/{id}") + @Operation(summary = "更新公告", description = "更新指定ID的公告信息") + public ResponseEntity updateAnnouncement( + @Parameter(description = "公告ID", example = "1") @PathVariable Long id, + @RequestBody AnnouncementRequest request) { + + // 检查公告是否存在 + if (!announcementService.announcementExists(id)) { + return ResponseEntity.notFound().build(); + } + + if (request.getTitle() != null && request.getTitle().trim().isEmpty()) { + return ResponseEntity.badRequest().body(new Object() { + public final boolean success = false; + public final String message = "公告标题不能为空"; + }); + } + + if (request.getContent() != null && request.getContent().trim().isEmpty()) { + return ResponseEntity.badRequest().body(new Object() { + public final boolean success = false; + public final String message = "公告内容不能为空"; + }); + } + + Announcement announcement = AnnouncementConverter.toEntity(request); + announcement.setId(id); + boolean success = announcementService.updateAnnouncement(announcement); + final boolean finalSuccess = success; + + return ResponseEntity.ok(new Object() { + public final boolean success = finalSuccess; + public final String message = finalSuccess ? "公告更新成功" : "公告更新失败"; + }); + } + + @DeleteMapping("/{id}") + @Operation(summary = "删除公告", description = "删除指定ID的公告") + public ResponseEntity deleteAnnouncement( + @Parameter(description = "公告ID", example = "1") @PathVariable Long id) { + + // 检查公告是否存在 + if (!announcementService.announcementExists(id)) { + return ResponseEntity.notFound().build(); + } + + boolean success = announcementService.deleteAnnouncement(id); + final boolean finalSuccess = success; + + return ResponseEntity.ok(new Object() { + public final boolean success = finalSuccess; + public final String message = finalSuccess ? "公告删除成功" : "公告删除失败"; + }); + } + + @PutMapping("/{id}/enabled") + @Operation(summary = "更新公告启用状态", description = "启用或禁用指定公告") + public ResponseEntity updateAnnouncementEnabled( + @Parameter(description = "公告ID", example = "1") @PathVariable Long id, + @Parameter(description = "启用状态", example = "true") @RequestParam Boolean enabled) { + + // 检查公告是否存在 + if (!announcementService.announcementExists(id)) { + return ResponseEntity.notFound().build(); + } + + boolean success = announcementService.updateAnnouncementEnabled(id, enabled); + final boolean finalSuccess = success; + final Boolean finalEnabled = enabled; + + return ResponseEntity.ok(new Object() { + public final boolean success = finalSuccess; + public final String message = finalSuccess ? + (finalEnabled ? "公告已启用" : "公告已禁用") : + "状态更新失败"; + }); + } + + @GetMapping("/enabled") + @Operation(summary = "获取启用的公告", description = "获取所有启用状态的公告,用于前端显示") + public ResponseEntity> getEnabledAnnouncements() { + List announcements = announcementService.getEnabledAnnouncements(); + List responses = announcements.stream() + .map(AnnouncementConverter::toResponse) + .toList(); + return ResponseEntity.ok(responses); + } +} diff --git a/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementConverter.java b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementConverter.java new file mode 100644 index 0000000..44da593 --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementConverter.java @@ -0,0 +1,43 @@ +package com.gameplatform.server.model.dto.admin; + +import com.gameplatform.server.model.entity.admin.Announcement; + +public class AnnouncementConverter { + + /** + * 将请求DTO转换为实体 + */ + public static Announcement toEntity(AnnouncementRequest request) { + if (request == null) { + return null; + } + + Announcement announcement = new Announcement(); + announcement.setTitle(request.getTitle()); + announcement.setContent(request.getContent()); + announcement.setEnabled(request.getEnabled()); + announcement.setJumpUrl(request.getJumpUrl()); + + return announcement; + } + + /** + * 将实体转换为响应DTO + */ + public static AnnouncementResponse toResponse(Announcement announcement) { + if (announcement == null) { + return null; + } + + AnnouncementResponse response = new AnnouncementResponse(); + response.setId(announcement.getId()); + response.setTitle(announcement.getTitle()); + response.setContent(announcement.getContent()); + response.setEnabled(announcement.getEnabled()); + response.setJumpUrl(announcement.getJumpUrl()); + response.setCreatedAt(announcement.getCreatedAt()); + response.setUpdatedAt(announcement.getUpdatedAt()); + + return response; + } +} diff --git a/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementRequest.java b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementRequest.java new file mode 100644 index 0000000..38409e4 --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementRequest.java @@ -0,0 +1,51 @@ +package com.gameplatform.server.model.dto.admin; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "公告请求DTO") +public class AnnouncementRequest { + + @Schema(description = "公告标题", required = true, example = "系统维护通知") + private String title; + + @Schema(description = "公告内容", required = true, example = "系统将于今晚10点进行维护,预计维护时间2小时") + private String content; + + @Schema(description = "是否启用", required = true, example = "true") + private Boolean enabled; + + @Schema(description = "跳转链接", example = "https://example.com") + private String jumpUrl; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getJumpUrl() { + return jumpUrl; + } + + public void setJumpUrl(String jumpUrl) { + this.jumpUrl = jumpUrl; + } +} diff --git a/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementResponse.java b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementResponse.java new file mode 100644 index 0000000..d277c97 --- /dev/null +++ b/src/main/java/com/gameplatform/server/model/dto/admin/AnnouncementResponse.java @@ -0,0 +1,85 @@ +package com.gameplatform.server.model.dto.admin; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; + +@Schema(description = "公告响应DTO") +public class AnnouncementResponse { + + @Schema(description = "公告ID", example = "1") + private Long id; + + @Schema(description = "公告标题", example = "系统维护通知") + private String title; + + @Schema(description = "公告内容", example = "系统将于今晚10点进行维护,预计维护时间2小时") + private String content; + + @Schema(description = "是否启用", example = "true") + private Boolean enabled; + + @Schema(description = "跳转链接", example = "https://example.com") + private String jumpUrl; + + @Schema(description = "创建时间") + private LocalDateTime createdAt; + + @Schema(description = "更新时间") + private LocalDateTime updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getJumpUrl() { + return jumpUrl; + } + + public void setJumpUrl(String jumpUrl) { + this.jumpUrl = jumpUrl; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } +} diff --git a/src/main/java/com/gameplatform/server/service/admin/AnnouncementService.java b/src/main/java/com/gameplatform/server/service/admin/AnnouncementService.java new file mode 100644 index 0000000..ab27c58 --- /dev/null +++ b/src/main/java/com/gameplatform/server/service/admin/AnnouncementService.java @@ -0,0 +1,94 @@ +package com.gameplatform.server.service.admin; + +import com.gameplatform.server.mapper.admin.AnnouncementMapper; +import com.gameplatform.server.model.entity.admin.Announcement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class AnnouncementService { + + @Autowired + private AnnouncementMapper announcementMapper; + + /** + * 创建公告 + */ + public boolean createAnnouncement(Announcement announcement) { + return announcementMapper.insert(announcement) > 0; + } + + /** + * 根据ID获取公告 + */ + public Announcement getAnnouncementById(Long id) { + return announcementMapper.findById(id); + } + + /** + * 更新公告 + */ + public boolean updateAnnouncement(Announcement announcement) { + return announcementMapper.update(announcement) > 0; + } + + /** + * 删除公告 + */ + public boolean deleteAnnouncement(Long id) { + return announcementMapper.deleteById(id) > 0; + } + + /** + * 获取所有公告(分页) + */ + public List getAllAnnouncements(int size, int offset) { + return announcementMapper.findAll(size, offset); + } + + /** + * 获取公告总数 + */ + public long getAnnouncementCount() { + return announcementMapper.countAll(); + } + + /** + * 根据启用状态获取公告(分页) + */ + public List getAnnouncementsByEnabled(Boolean enabled, int size, int offset) { + return announcementMapper.findByEnabled(enabled, size, offset); + } + + /** + * 根据启用状态获取公告数量 + */ + public long getAnnouncementCountByEnabled(Boolean enabled) { + return announcementMapper.countByEnabled(enabled); + } + + /** + * 更新公告启用状态 + */ + public boolean updateAnnouncementEnabled(Long id, Boolean enabled) { + return announcementMapper.updateEnabled(id, enabled) > 0; + } + + /** + * 获取所有启用的公告(用于前端显示) + */ + public List getEnabledAnnouncements() { + return announcementMapper.findByEnabled(true, 10, 0); // 最多返回10条启用的公告 + } + + /** + * 检查公告是否存在 + */ + public boolean announcementExists(Long id) { + return getAnnouncementById(id) != null; + } +}