Files
1818web-hoduan/docs/banner-api-bug-fix.md
2025-11-14 17:41:15 +08:00

6.8 KiB
Raw Permalink Blame History

Banner管理API Bug修复报告

🐛 问题描述

错误1: 批量排序验证失败

PUT /admin/banners/batch-sort
HandlerMethodValidationException: 400 BAD_REQUEST "Validation failure"

根本原因:

  • 前端发送的数据格式与BannerUpdateDto的验证要求不匹配
  • BannerUpdateDto包含过多必填字段,而批量排序只需要idsortOrder

错误2: 状态切换接口不存在

PUT /admin/banners/status
HttpRequestMethodNotSupportedException: Request method 'PUT' is not supported

根本原因:

  • 控制器中缺少/status接口
  • 前端调用的接口在后端没有实现

修复方案

1. 创建专用DTO

新增 BannerSortDto.java

@Data
@Schema(description = "Banner排序请求")
public class BannerSortDto {
    @NotNull(message = "Banner ID不能为空")
    private Long id;
    
    @NotNull(message = "排序值不能为空")
    @Min(value = 0, message = "排序值不能小于0")
    private Integer sortOrder;
}

新增 BannerStatusDto.java

@Data
@Schema(description = "Banner状态请求")
public class BannerStatusDto {
    @NotNull(message = "Banner ID不能为空")
    private Long id;
    
    @NotNull(message = "状态不能为空")
    private Boolean isEnabled;
}

2. 更新控制器接口

修改批量排序接口

@PutMapping("/batch-sort")
public Result<Void> batchUpdateSortOrder(@Valid @RequestBody List<BannerSortDto> sortDtos) {
    log.info("管理员批量更新Banner排序: size={}", sortDtos.size());
    bannerService.batchUpdateSortOrder(sortDtos);
    return Result.success(null);
}

新增状态切换接口

@PutMapping("/status")
public Result<Void> updateBannerStatus(@Valid @RequestBody BannerStatusDto statusDto) {
    log.info("管理员更新Banner状态: id={}, enabled={}", statusDto.getId(), statusDto.getIsEnabled());
    bannerService.updateBannerStatus(statusDto.getId(), statusDto.getIsEnabled());
    return Result.success(null);
}

3. 更新服务层

修改服务接口

// 修改批量排序方法签名
void batchUpdateSortOrder(List<BannerSortDto> sortDtos);

// 新增状态更新方法
void updateBannerStatus(Long id, Boolean isEnabled);

更新服务实现

@Override
@Transactional
public void batchUpdateSortOrder(List<BannerSortDto> sortDtos) {
    if (sortDtos == null || sortDtos.isEmpty()) {
        throw new BusinessException("批量更新数据不能为空");
    }
    
    List<Banner> banners = sortDtos.stream().map(sortDto -> {
        Banner banner = new Banner();
        banner.setId(sortDto.getId());
        banner.setSortOrder(sortDto.getSortOrder());
        banner.setUpdateTime(LocalDateTime.now());
        return banner;
    }).toList();
    
    int result = bannerMapper.batchUpdateSortOrder(banners);
    if (result <= 0) {
        throw new BusinessException("批量更新排序失败");
    }
    
    log.info("批量更新Banner排序成功: size={}", sortDtos.size());
}

@Override
@Transactional
public void updateBannerStatus(Long id, Boolean isEnabled) {
    Banner existingBanner = bannerMapper.selectById(id);
    if (existingBanner == null) {
        throw new BusinessException("Banner不存在");
    }
    
    Banner banner = new Banner();
    banner.setId(id);
    banner.setIsEnabled(Boolean.TRUE.equals(isEnabled) ? 1 : 0);
    banner.setUpdateTime(LocalDateTime.now());
    
    int result = bannerMapper.updateById(banner);
    if (result <= 0) {
        throw new BusinessException("更新Banner状态失败");
    }
    
    log.info("更新Banner状态成功: id={}, enabled={}", id, isEnabled);
}

🧪 测试验证

测试页面

创建了 test_banner_admin.html 测试页面,包含:

  • 📄 Banner列表加载
  • 🔢 批量排序测试
  • 🔄 状态切换测试
  • 📊 实时结果显示

测试用例

1. 批量排序测试

// 请求数据格式
PUT /admin/banners/batch-sort
[
  {"id": 1, "sortOrder": 1},
  {"id": 2, "sortOrder": 2},
  {"id": 3, "sortOrder": 3}
]

// 预期响应
{
  "code": 200,
  "message": "操作成功",
  "data": null
}

2. 状态切换测试

// 请求数据格式
PUT /admin/banners/status
{
  "id": 1,
  "isEnabled": false
}

// 预期响应
{
  "code": 200,
  "message": "操作成功",
  "data": null
}

📊 修复前后对比

修复前

接口路径 状态 问题
PUT /admin/banners/batch-sort 失败 参数验证失败
PUT /admin/banners/status 失败 接口不存在

修复后

接口路径 状态 功能
PUT /admin/banners/batch-sort 正常 批量更新排序
PUT /admin/banners/status 正常 状态切换

🛡️ 安全性

权限验证

  • 所有接口都使用 @RequireAdminOrStaff 注解
  • 需要有效的管理员JWT Token
  • 自动记录操作日志

数据验证

  • 使用专用DTO进行参数验证
  • 数据库操作前检查记录存在性
  • 事务保护确保数据一致性

🚀 使用指南

前端调用示例

批量排序

const sortData = [
  {id: 1, sortOrder: 1},
  {id: 2, sortOrder: 2}
];

const response = await fetch('/admin/banners/batch-sort', {
  method: 'PUT',
  headers: {
    'Authorization': `Bearer ${adminToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(sortData)
});

状态切换

const statusData = {
  id: 1,
  isEnabled: false
};

const response = await fetch('/admin/banners/status', {
  method: 'PUT',
  headers: {
    'Authorization': `Bearer ${adminToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(statusData)
});

📋 文件清单

新增文件

  • src/main/java/com/dora/dto/BannerSortDto.java
  • src/main/java/com/dora/dto/BannerStatusDto.java
  • src/main/resources/static/test_banner_admin.html

修改文件

  • src/main/java/com/dora/controller/AdminBannerController.java
  • src/main/java/com/dora/service/BannerService.java
  • src/main/java/com/dora/service/impl/BannerServiceImpl.java

🎯 总结

修复成果

  1. 参数验证优化: 创建专用DTO避免过度验证
  2. 接口完整性: 补充缺失的状态切换接口
  3. 错误处理: 增强异常处理和日志记录
  4. 测试支持: 提供完整的测试页面

🚨 注意事项

  1. 参数格式: 确保前端发送的数据格式与DTO要求一致
  2. 权限验证: 所有操作都需要管理员权限
  3. 数据一致性: 批量操作使用事务保护
  4. 错误处理: 详细的错误信息便于问题排查

修复状态: 已完成
测试状态: 已验证
风险等级: 低(不影响现有功能)
部署要求: 重启服务器使修改生效