Просмотр исходного кода

基础设施与服务重构1.0

panzhilin 3 месяцев назад
Родитель
Сommit
61e71a8700

+ 42 - 9
alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java

@@ -65,9 +65,42 @@ public class SportsEquipmentFacilityController {
         return R.data(facilityService.getList(storeId, facilityCategory));
     }
 
-    @ApiOperation("根据ID查询运动器材设施详情(用户端)")
+    @ApiOperation("根据区域ID查询设备列表")
     @ApiOperationSupport(order = 3)
     @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "areaId", value = "区域ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/listByArea")
+    public R<List<SportsEquipmentFacilityVo>> getListByArea(
+            @RequestParam Integer storeId,
+            @RequestParam Integer areaId) {
+        log.info("SportsEquipmentFacilityController.getListByArea?storeId={},areaId={}", storeId, areaId);
+        try {
+            if (storeId == null || storeId <= 0) {
+                log.warn("根据区域ID查询设备列表失败,门店ID无效:{}", storeId);
+                return R.fail("门店ID不能为空且必须大于0");
+            }
+            if (areaId == null || areaId <= 0) {
+                log.warn("根据区域ID查询设备列表失败,区域ID无效:{}", areaId);
+                return R.fail("区域ID不能为空且必须大于0");
+            }
+            List<SportsEquipmentFacilityVo> result = facilityService.getListByAreaId(storeId, areaId);
+            log.info("根据区域ID查询设备列表成功,storeId={},areaId={},设备数量:{}", storeId, areaId, result.size());
+            return R.data(result);
+        } catch (IllegalArgumentException e) {
+            log.warn("根据区域ID查询设备列表失败,参数验证失败:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("根据区域ID查询设备列表异常,storeId={},areaId={},异常信息:{}", 
+                    storeId, areaId, e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据ID查询运动器材设施详情(用户端)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
             @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
     })
     @GetMapping("/detail")
@@ -81,7 +114,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("新增运动器材设施")
-    @ApiOperationSupport(order = 4)
+    @ApiOperationSupport(order = 5)
     @PostMapping("/save")
     public R<Boolean> saveFacility(@RequestBody SportsEquipmentFacilityVo vo) {
         log.info("SportsEquipmentFacilityController.saveFacility?vo={}", vo);
@@ -106,7 +139,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("修改运动器材设施")
-    @ApiOperationSupport(order = 5)
+    @ApiOperationSupport(order = 6)
     @PostMapping("/update")
     public R<Boolean> updateFacility(@RequestBody SportsEquipmentFacilityVo vo) {
         log.info("SportsEquipmentFacilityController.updateFacility?vo={}", vo);
@@ -135,7 +168,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("删除运动器材设施")
-    @ApiOperationSupport(order = 6)
+    @ApiOperationSupport(order = 7)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
     })
@@ -155,7 +188,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、图片列表和设备列表)(用户端)")
-    @ApiOperationSupport(order = 7)
+    @ApiOperationSupport(order = 8)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
     })
@@ -179,7 +212,7 @@ public class SportsEquipmentFacilityController {
 
 
     @ApiOperation("根据ID查询运动器材设施详情(商户端)")
-    @ApiOperationSupport(order = 8)
+    @ApiOperationSupport(order = 9)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
     })
@@ -194,7 +227,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、图片列表和设备列表)(商户端)")
-    @ApiOperationSupport(order = 9)
+    @ApiOperationSupport(order = 10)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
     })
@@ -205,7 +238,7 @@ public class SportsEquipmentFacilityController {
     }
 
     @ApiOperation("保存分类下的实景图片(商户端)- 设施列表通过添加接口已保存,此接口仅保存图片")
-    @ApiOperationSupport(order = 10)
+    @ApiOperationSupport(order = 11)
     @PostMapping("/saveCategoryImages")
     public R<Boolean> saveCategoryImages(@RequestBody SportsEquipmentFacilityCategoryVo vo) {
         log.info("SportsEquipmentFacilityController.saveCategoryImages?storeId={},facilityCategory={},imageList={}", 
@@ -238,7 +271,7 @@ public class SportsEquipmentFacilityController {
     private static final int MIN_VALID_VALUE = 1;
     
     @ApiOperation("查询分类的设备详情(用户端)- 包含图片列表、各子分类的设备数量汇总、设备列表。facilityCategoryName为空时返回所有分类,equipmentType为空时返回所有类型")
-    @ApiOperationSupport(order = 11)
+    @ApiOperationSupport(order = 12)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
             @ApiImplicitParam(name = "facilityCategoryName", value = "设施分类名称(商户自定义),可选,为空时返回所有分类", dataType = "String", paramType = "query", required = false),

+ 17 - 13
alien-store/src/main/java/shop/alien/store/controller/SportsFacilityAreaController.java

@@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.SportsFacilityArea;
+import shop.alien.entity.store.vo.BatchDeleteAreaRequestVo;
 import shop.alien.entity.store.vo.CreateAreaRequestVo;
 import shop.alien.entity.store.vo.DeleteAreaRequestVo;
 import shop.alien.store.service.SportsFacilityAreaService;
@@ -179,29 +180,32 @@ public class SportsFacilityAreaController {
     @ApiOperation("批量删除区域(逻辑删除,同时删除这些区域下的所有设施)")
     @ApiOperationSupport(order = 5)
     @ApiImplicitParams({
-            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "areaIds", value = "区域ID列表", dataType = "List<Integer>", paramType = "body", required = true)
+            @ApiImplicitParam(name = "request", value = "批量删除区域请求参数", dataType = "BatchDeleteAreaRequestVo", paramType = "body", required = true)
     })
     @PostMapping("/batchDelete")
-    public R<Boolean> batchDeleteAreas(
-            @RequestParam("storeId") Integer storeId,
-            @RequestBody List<Integer> areaIds) {
-        log.info("批量删除区域,storeId={},areaIds={}", storeId, areaIds);
+    public R<Boolean> batchDeleteAreas(@RequestBody BatchDeleteAreaRequestVo request) {
+        log.info("批量删除区域,request={}", request);
         try {
             // 参数验证
-            if (storeId == null || storeId < MIN_VALID_VALUE) {
-                log.warn("批量删除区域失败,门店ID无效:{}", storeId);
+            if (request == null) {
+                log.warn("批量删除区域失败,请求参数为空");
+                return R.fail("请求参数不能为空");
+            }
+
+            if (request.getStoreId() == null || request.getStoreId() < MIN_VALID_VALUE) {
+                log.warn("批量删除区域失败,门店ID无效:{}", request.getStoreId());
                 return R.fail(INVALID_STORE_ID_MSG);
             }
 
-            if (areaIds == null || areaIds.isEmpty()) {
+            if (request.getAreaIds() == null || request.getAreaIds().isEmpty()) {
                 log.warn("批量删除区域失败,区域ID列表为空");
                 return R.fail("区域ID列表不能为空");
             }
 
-            boolean result = areaService.batchDeleteAreas(storeId, areaIds);
+            boolean result = areaService.batchDeleteAreas(request.getStoreId(), request.getAreaIds());
             if (result) {
-                log.info("批量删除区域成功,storeId={},区域数量:{}", storeId, areaIds.size());
+                log.info("批量删除区域成功,storeId={},区域数量:{}", 
+                        request.getStoreId(), request.getAreaIds().size());
                 return R.success("批量删除区域成功");
             }
             return R.fail("批量删除区域失败");
@@ -209,8 +213,8 @@ public class SportsFacilityAreaController {
             log.warn("批量删除区域失败,参数验证失败:{}", e.getMessage());
             return R.fail(e.getMessage());
         } catch (Exception e) {
-            log.error("批量删除区域异常,storeId={},areaIds={},异常信息:{}", 
-                    storeId, areaIds, e.getMessage(), e);
+            log.error("批量删除区域异常,request={},异常信息:{}", 
+                    request, e.getMessage(), e);
             return R.fail("批量删除区域失败:" + e.getMessage());
         }
     }

+ 9 - 0
alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java

@@ -39,6 +39,15 @@ public interface SportsEquipmentFacilityService extends IService<SportsEquipment
     List<SportsEquipmentFacilityVo> getList(Integer storeId, Integer facilityCategory);
 
     /**
+     * 根据区域ID查询设备列表
+     *
+     * @param storeId 门店ID
+     * @param areaId  区域ID
+     * @return List<SportsEquipmentFacilityVo>
+     */
+    List<SportsEquipmentFacilityVo> getListByAreaId(Integer storeId, Integer areaId);
+
+    /**
      * 根据ID查询详情
      *
      * @param id 主键ID

+ 60 - 0
alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java

@@ -25,6 +25,8 @@ import shop.alien.mapper.SportsFacilityAreaMapper;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.store.service.FitnessEquipmentInfoService;
 import shop.alien.store.service.SportsEquipmentFacilityService;
+import shop.alien.store.service.SportsFacilityAreaService;
+import shop.alien.entity.store.SportsFacilityArea;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +49,7 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
     private final SportsFacilityAreaMapper sportsFacilityAreaMapper;
     private final StoreImgMapper storeImgMapper;
     private final FitnessEquipmentInfoService fitnessEquipmentInfoService;
+    private final SportsFacilityAreaService areaService;
 
     /**
      * 运动器材设施图片类型
@@ -134,6 +137,43 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
     }
 
     @Override
+    public List<SportsEquipmentFacilityVo> getListByAreaId(Integer storeId, Integer areaId) {
+        log.info("根据区域ID查询设备列表,storeId={},areaId={}", storeId, areaId);
+        
+        // 参数验证
+        if (storeId == null || storeId <= 0) {
+            log.warn("根据区域ID查询设备列表失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+        
+        if (areaId == null || areaId <= 0) {
+            log.warn("根据区域ID查询设备列表失败,区域ID无效:{}", areaId);
+            throw new IllegalArgumentException("区域ID不能为空且必须大于0");
+        }
+        
+        try {
+            LambdaQueryWrapper<SportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                    .eq(SportsEquipmentFacility::getAreaId, areaId)
+                    .eq(SportsEquipmentFacility::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .orderByDesc(SportsEquipmentFacility::getCreatedTime);
+            
+            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(queryWrapper);
+            List<SportsEquipmentFacilityVo> voList = facilityList.stream()
+                    .map(this::convertToVo)
+                    .collect(Collectors.toList());
+            
+            log.info("根据区域ID查询设备列表成功,storeId={},areaId={},设备数量:{}", 
+                    storeId, areaId, voList.size());
+            return voList;
+        } catch (Exception e) {
+            log.error("根据区域ID查询设备列表异常,storeId={},areaId={},异常信息:{}", 
+                    storeId, areaId, e.getMessage(), e);
+            throw new RuntimeException("查询设备列表失败:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
     public SportsEquipmentFacilityVo getDetail(Integer id) {
         SportsEquipmentFacility facility = facilityMapper.selectById(id);
         if (facility == null) {
@@ -157,6 +197,16 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
         if (!CollectionUtils.isEmpty(imageList) && imageList.size() > 20) {
             throw new RuntimeException("实景图片最多上传20张");
         }
+        
+        // 如果提供了areaId,验证区域是否存在且未删除
+        if (facility.getAreaId() != null && facility.getAreaId() > 0) {
+            SportsFacilityArea area = areaService.getById(facility.getAreaId());
+            if (area == null || area.getDeleteFlag() == null || area.getDeleteFlag() != DELETE_FLAG_NOT_DELETED) {
+                log.warn("新增设备失败,区域不存在或已删除:areaId={}", facility.getAreaId());
+                throw new IllegalArgumentException("区域不存在或已删除");
+            }
+        }
+        
         // 保存设施信息
         boolean result = this.save(facility);
         if (result && !CollectionUtils.isEmpty(imageList) && facility.getFacilityCategory() != null) {
@@ -178,6 +228,16 @@ public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipm
         if (!CollectionUtils.isEmpty(imageList) && imageList.size() > 20) {
             throw new RuntimeException("实景图片最多上传20张");
         }
+        
+        // 如果提供了areaId,验证区域是否存在且未删除
+        if (facility.getAreaId() != null && facility.getAreaId() > 0) {
+            SportsFacilityArea area = areaService.getById(facility.getAreaId());
+            if (area == null || area.getDeleteFlag() == null || area.getDeleteFlag() != DELETE_FLAG_NOT_DELETED) {
+                log.warn("修改设备失败,区域不存在或已删除:areaId={}", facility.getAreaId());
+                throw new IllegalArgumentException("区域不存在或已删除");
+            }
+        }
+        
         // 更新设施信息
         boolean result = this.updateById(facility);
         if (result && !CollectionUtils.isEmpty(imageList) && facility.getFacilityCategory() != null) {

+ 51 - 23
alien-store/src/main/java/shop/alien/store/service/impl/SportsFacilityAreaServiceImpl.java

@@ -18,6 +18,8 @@ import shop.alien.mapper.StoreImgMapper;
 import shop.alien.store.service.SportsFacilityAreaService;
 
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * 运动设施区域服务实现类
@@ -78,18 +80,7 @@ public class SportsFacilityAreaServiceImpl extends ServiceImpl<SportsFacilityAre
             throw new IllegalArgumentException("区域名称长度不能超过10个字符");
         }
 
-        // 3. 检查该门店下是否已存在相同名称的区域(未删除的)
-        LambdaQueryWrapper<SportsFacilityArea> checkWrapper = new LambdaQueryWrapper<>();
-        checkWrapper.eq(SportsFacilityArea::getStoreId, storeId)
-                .eq(SportsFacilityArea::getAreaName, trimmedAreaName)
-                .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED);
-        long existCount = this.count(checkWrapper);
-        if (existCount > 0) {
-            log.warn("新建区域失败,该门店下已存在相同名称的区域:storeId={},areaName={}", storeId, trimmedAreaName);
-            throw new IllegalArgumentException("该门店下已存在相同名称的区域");
-        }
-
-        // 4. 创建区域记录
+        // 3. 创建区域记录(区域名称允许重复,无需检查)
         SportsFacilityArea area = new SportsFacilityArea();
         area.setStoreId(storeId);
         area.setAreaName(trimmedAreaName);
@@ -158,17 +149,7 @@ public class SportsFacilityAreaServiceImpl extends ServiceImpl<SportsFacilityAre
                 log.warn("更新区域失败,区域名称长度超过10字:{},长度:{}", trimmedAreaName, trimmedAreaName.length());
                 throw new IllegalArgumentException("区域名称长度不能超过10个字符");
             }
-            // 检查是否与其他区域重复(排除自己)
-            LambdaQueryWrapper<SportsFacilityArea> checkWrapper = new LambdaQueryWrapper<>();
-            checkWrapper.eq(SportsFacilityArea::getStoreId, existingArea.getStoreId())
-                    .eq(SportsFacilityArea::getAreaName, trimmedAreaName)
-                    .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
-                    .ne(SportsFacilityArea::getId, areaId);
-            long existCount = this.count(checkWrapper);
-            if (existCount > 0) {
-                log.warn("更新区域失败,该门店下已存在相同名称的区域:storeId={},areaName={}", existingArea.getStoreId(), trimmedAreaName);
-                throw new IllegalArgumentException("该门店下已存在相同名称的区域");
-            }
+            // 区域名称允许重复,无需检查
             updateWrapper.set(SportsFacilityArea::getAreaName, trimmedAreaName);
         }
 
@@ -200,6 +181,24 @@ public class SportsFacilityAreaServiceImpl extends ServiceImpl<SportsFacilityAre
             throw new IllegalArgumentException("区域不存在或已删除");
         }
 
+        // 处理唯一索引冲突:如果存在同名且已删除的记录,先物理删除它们
+        LambdaQueryWrapper<SportsFacilityArea> conflictWrapper = new LambdaQueryWrapper<>();
+        conflictWrapper.eq(SportsFacilityArea::getStoreId, area.getStoreId())
+                .eq(SportsFacilityArea::getAreaName, area.getAreaName())
+                .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_DELETED)
+                .ne(SportsFacilityArea::getId, areaId); // 排除当前要删除的记录
+        List<SportsFacilityArea> conflictAreas = this.list(conflictWrapper);
+        
+        if (!CollectionUtils.isEmpty(conflictAreas)) {
+            // 物理删除冲突的记录(已删除的重复记录)
+            List<Integer> conflictIds = conflictAreas.stream()
+                    .map(SportsFacilityArea::getId)
+                    .collect(Collectors.toList());
+            this.removeByIds(conflictIds);
+            log.info("物理删除冲突的已删除区域记录,storeId={},areaName={},冲突记录ID={}", 
+                    area.getStoreId(), area.getAreaName(), conflictIds);
+        }
+
         // 1. 逻辑删除区域
         LambdaUpdateWrapper<SportsFacilityArea> areaUpdateWrapper = new LambdaUpdateWrapper<>();
         areaUpdateWrapper.eq(SportsFacilityArea::getId, areaId)
@@ -250,6 +249,35 @@ public class SportsFacilityAreaServiceImpl extends ServiceImpl<SportsFacilityAre
             throw new IllegalArgumentException("部分区域不存在或不属于该门店");
         }
 
+        // 查询要删除的区域信息,用于检查唯一索引冲突
+        List<SportsFacilityArea> areasToDelete = this.list(checkWrapper);
+        
+        // 处理唯一索引冲突:在逻辑删除之前,先物理删除所有已删除的同名记录
+        // 这样可以避免将当前记录设置为已删除时,与已存在的已删除同名记录冲突
+        Set<String> areaNames = areasToDelete.stream()
+                .map(SportsFacilityArea::getAreaName)
+                .collect(Collectors.toSet());
+        
+        for (String areaName : areaNames) {
+            // 查询所有已删除的同名记录(包括当前要删除的记录之外的同名已删除记录)
+            LambdaQueryWrapper<SportsFacilityArea> conflictWrapper = new LambdaQueryWrapper<>();
+            conflictWrapper.eq(SportsFacilityArea::getStoreId, storeId)
+                    .eq(SportsFacilityArea::getAreaName, areaName)
+                    .eq(SportsFacilityArea::getDeleteFlag, DELETE_FLAG_DELETED)
+                    .notIn(SportsFacilityArea::getId, areaIds); // 排除当前要删除的记录(它们还未删除)
+            List<SportsFacilityArea> conflictAreas = this.list(conflictWrapper);
+            
+            if (!CollectionUtils.isEmpty(conflictAreas)) {
+                // 物理删除冲突的记录(已删除的重复记录)
+                List<Integer> conflictIds = conflictAreas.stream()
+                        .map(SportsFacilityArea::getId)
+                        .collect(Collectors.toList());
+                this.removeByIds(conflictIds);
+                log.info("物理删除冲突的已删除区域记录,storeId={},areaName={},冲突记录ID={}", 
+                        storeId, areaName, conflictIds);
+            }
+        }
+
         // 1. 批量逻辑删除区域
         LambdaUpdateWrapper<SportsFacilityArea> areaUpdateWrapper = new LambdaUpdateWrapper<>();
         areaUpdateWrapper.eq(SportsFacilityArea::getStoreId, storeId)