Bladeren bron

feat: 商家端点餐管理菜品分类相关功能开发

penghao 2 maanden geleden
bovenliggende
commit
f4345f143b

+ 66 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreCuisineCategory.java

@@ -0,0 +1,66 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 菜品分类表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_cuisine_category")
+@ApiModel(value = "StoreCuisineCategory对象", description = "菜品分类表")
+public class StoreCuisineCategory {
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "分类名称")
+    @TableField("category_name")
+    private String categoryName;
+
+    @ApiModelProperty(value = "状态(0:禁用, 1:启用)")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "排序")
+    @TableField("sort")
+    private Integer sort;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+}

+ 32 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreCuisineCategoryDTO.java

@@ -0,0 +1,32 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 菜品分类管理DTO
+ * 用于批量创建和编辑菜品分类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreCuisineCategoryDTO对象", description = "菜品分类管理DTO")
+public class StoreCuisineCategoryDTO {
+
+    @ApiModelProperty(value = "分类ID", notes = "编辑分类时必填")
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID", notes = "批量创建分类时必填")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "分类名称", notes = "编辑分类时必填")
+    private String categoryName;
+
+    @ApiModelProperty(value = "分类名称列表", notes = "批量创建分类时必填,多个分类名称用英文逗号分隔,如:热菜,水果,甜品")
+    private String categoryNames;
+
+}

+ 24 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreCuisineCategorySortDTO.java

@@ -0,0 +1,24 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 菜品分类排序DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreCuisineCategorySortDTO对象", description = "菜品分类排序DTO")
+public class StoreCuisineCategorySortDTO {
+
+    @ApiModelProperty(value = "门店ID", required = true)
+    private Integer storeId;
+
+    @ApiModelProperty(value = "分类排序列表", required = true, notes = "按顺序排列的分类ID列表")
+    private List<Integer> categoryIds;
+}

+ 13 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreCuisineCategoryMapper.java

@@ -0,0 +1,13 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import shop.alien.entity.store.StoreCuisineCategory;
+
+/**
+ * 菜品分类表 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreCuisineCategoryMapper extends BaseMapper<StoreCuisineCategory> {
+}

+ 147 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreCuisineCategoryController.java

@@ -0,0 +1,147 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreCuisineCategory;
+import shop.alien.entity.store.dto.StoreCuisineCategoryDTO;
+import shop.alien.entity.store.dto.StoreCuisineCategorySortDTO;
+import shop.alien.store.service.StoreCuisineCategoryService;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 菜品分类管理 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商家端点餐管理-菜品分类管理"})
+@ApiSort(14)
+@CrossOrigin
+@RestController
+@RequestMapping("/storeCuisineCategory")
+@RequiredArgsConstructor
+public class StoreCuisineCategoryController {
+
+    private final StoreCuisineCategoryService storeCuisineCategoryService;
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("查询菜品分类列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getCategoryList")
+    public R<List<StoreCuisineCategory>> getCategoryList(@RequestParam Integer storeId) {
+        log.info("StoreCuisineCategoryController.getCategoryList?storeId={}", storeId);
+        return R.data(storeCuisineCategoryService.getCategoryList(storeId));
+    }
+
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("批量创建菜品分类")
+    @PostMapping("/batchCreateCategories")
+    public R<Boolean> batchCreateCategories(@RequestBody StoreCuisineCategoryDTO dto) {
+        log.info("StoreCuisineCategoryController.batchCreateCategories?dto={}", dto);
+        
+        if (dto.getStoreId() == null || !StringUtils.hasText(dto.getCategoryNames())) {
+            return R.fail("门店ID和分类名称列表不能为空");
+        }
+
+        // 解析分类名称列表
+        List<String> categoryNameList = Arrays.stream(dto.getCategoryNames().split(","))
+                .map(String::trim)
+                .filter(StringUtils::hasText)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (categoryNameList.isEmpty()) {
+            return R.fail("分类名称列表不能为空");
+        }
+
+        try {
+            boolean result = storeCuisineCategoryService.batchCreateCategories(dto.getStoreId(), categoryNameList);
+            if (result) {
+                return R.success("批量创建菜品分类成功");
+            } else {
+                return R.fail("批量创建菜品分类失败");
+            }
+        } catch (Exception e) {
+            log.error("批量创建菜品分类失败", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("编辑菜品分类")
+    @PostMapping("/updateCategory")
+    public R<Boolean> updateCategory(@RequestBody StoreCuisineCategoryDTO dto) {
+        log.info("StoreCuisineCategoryController.updateCategory?dto={}", dto);
+        
+        if (dto.getId() == null || !StringUtils.hasText(dto.getCategoryName())) {
+            return R.fail("分类ID和分类名称不能为空");
+        }
+        
+        try {
+            boolean result = storeCuisineCategoryService.updateCategory(dto.getId(), dto.getCategoryName());
+            if (result) {
+                return R.success("更新菜品分类成功");
+            } else {
+                return R.fail("更新菜品分类失败");
+            }
+        } catch (Exception e) {
+            log.error("更新菜品分类失败", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("删除菜品分类")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "分类ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/deleteCategory")
+    public R<Boolean> deleteCategory(@RequestParam Integer id) {
+        log.info("StoreCuisineCategoryController.deleteCategory?id={}", id);
+        
+        try {
+            boolean result = storeCuisineCategoryService.deleteCategory(id);
+            if (result) {
+                return R.success("删除菜品分类成功");
+            } else {
+                return R.fail("删除菜品分类失败");
+            }
+        } catch (Exception e) {
+            log.error("删除菜品分类失败", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 6)
+    @ApiOperation("更新菜品分类排序")
+    @PostMapping("/updateCategorySort")
+    public R<Boolean> updateCategorySort(@RequestBody StoreCuisineCategorySortDTO dto) {
+        log.info("StoreCuisineCategoryController.updateCategorySort?dto={}", dto);
+        
+        if (dto.getStoreId() == null || dto.getCategoryIds() == null || dto.getCategoryIds().isEmpty()) {
+            return R.fail("门店ID和分类ID列表不能为空");
+        }
+        
+        try {
+            boolean result = storeCuisineCategoryService.updateCategorySort(dto.getStoreId(), dto.getCategoryIds());
+            if (result) {
+                return R.success("更新菜品分类排序成功");
+            } else {
+                return R.fail("更新菜品分类排序失败");
+            }
+        } catch (Exception e) {
+            log.error("更新菜品分类排序失败", e);
+            return R.fail(e.getMessage());
+        }
+    }
+}

+ 58 - 0
alien-store/src/main/java/shop/alien/store/service/StoreCuisineCategoryService.java

@@ -0,0 +1,58 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreCuisineCategory;
+
+import java.util.List;
+
+/**
+ * 菜品分类表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreCuisineCategoryService extends IService<StoreCuisineCategory> {
+
+    /**
+     * 查询菜品分类列表(按排序字段排序)
+     *
+     * @param storeId 门店ID
+     * @return List<StoreCuisineCategory>
+     */
+    List<StoreCuisineCategory> getCategoryList(Integer storeId);
+
+    /**
+     * 批量创建菜品分类
+     *
+     * @param storeId        门店ID
+     * @param categoryNames  分类名称列表
+     * @return boolean
+     */
+    boolean batchCreateCategories(Integer storeId, List<String> categoryNames);
+
+    /**
+     * 更新菜品分类
+     *
+     * @param id           分类ID
+     * @param categoryName 分类名称
+     * @return boolean
+     */
+    boolean updateCategory(Integer id, String categoryName);
+
+    /**
+     * 删除菜品分类
+     *
+     * @param id 分类ID
+     * @return boolean
+     */
+    boolean deleteCategory(Integer id);
+
+    /**
+     * 更新菜品分类排序
+     *
+     * @param storeId     门店ID
+     * @param categoryIds 分类ID列表(按顺序排列)
+     * @return boolean
+     */
+    boolean updateCategorySort(Integer storeId, List<Integer> categoryIds);
+}

+ 200 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreCuisineCategoryServiceImpl.java

@@ -0,0 +1,200 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.StoreCuisineCategory;
+import shop.alien.mapper.StoreCuisineCategoryMapper;
+import shop.alien.store.service.StoreCuisineCategoryService;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * 菜品分类表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class StoreCuisineCategoryServiceImpl extends ServiceImpl<StoreCuisineCategoryMapper, StoreCuisineCategory> implements StoreCuisineCategoryService {
+
+    @Override
+    public List<StoreCuisineCategory> getCategoryList(Integer storeId) {
+        log.info("StoreCuisineCategoryServiceImpl.getCategoryList?storeId={}", storeId);
+        
+        LambdaQueryWrapper<StoreCuisineCategory> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StoreCuisineCategory::getStoreId, storeId)
+                .eq(StoreCuisineCategory::getStatus, 1) // 只查询启用的分类
+                .orderByAsc(StoreCuisineCategory::getSort) // 按排序字段升序
+                .orderByDesc(StoreCuisineCategory::getCreatedTime); // 如果排序字段相同,按创建时间倒序
+        
+        return this.list(wrapper);
+    }
+
+    @Override
+    public boolean batchCreateCategories(Integer storeId, List<String> categoryNames) {
+        log.info("StoreCuisineCategoryServiceImpl.batchCreateCategories?storeId={}&categoryNames={}", storeId, categoryNames);
+        
+        // 从JWT获取当前登录用户ID
+        Integer userId = getCurrentUserId();
+        
+        if (storeId == null || categoryNames == null || categoryNames.isEmpty()) {
+            log.warn("批量创建菜品分类失败:参数不完整");
+            return false;
+        }
+
+        // 检查分类名称是否已存在
+        LambdaQueryWrapper<StoreCuisineCategory> checkWrapper = new LambdaQueryWrapper<>();
+        checkWrapper.eq(StoreCuisineCategory::getStoreId, storeId)
+                .in(StoreCuisineCategory::getCategoryName, categoryNames);
+        List<StoreCuisineCategory> existingCategories = this.list(checkWrapper);
+        
+        if (!existingCategories.isEmpty()) {
+            List<String> existingNames = existingCategories.stream()
+                    .map(StoreCuisineCategory::getCategoryName)
+                    .collect(Collectors.toList());
+            log.warn("批量创建菜品分类失败:分类名称已存在,{}", existingNames);
+            throw new RuntimeException("分类名称已存在:" + String.join(",", existingNames));
+        }
+
+        // 查询当前最大的排序值
+        LambdaQueryWrapper<StoreCuisineCategory> maxSortWrapper = new LambdaQueryWrapper<>();
+        maxSortWrapper.eq(StoreCuisineCategory::getStoreId, storeId)
+                .orderByDesc(StoreCuisineCategory::getSort)
+                .last("LIMIT 1");
+        StoreCuisineCategory maxSortCategory = this.getOne(maxSortWrapper);
+        int maxSort = (maxSortCategory != null && maxSortCategory.getSort() != null) ? maxSortCategory.getSort() : 0;
+
+        // 使用 AtomicInteger 解决 lambda 表达式中的变量问题
+        AtomicInteger sortCounter = new AtomicInteger(maxSort);
+
+        // 批量创建
+        List<StoreCuisineCategory> categories = categoryNames.stream()
+                .map(categoryName -> {
+                    StoreCuisineCategory category = new StoreCuisineCategory();
+                    category.setStoreId(storeId);
+                    category.setCategoryName(categoryName);
+                    category.setStatus(1); // 默认启用
+                    category.setSort(sortCounter.incrementAndGet()); // 设置排序值
+                    category.setCreatedUserId(userId);
+                    return category;
+                })
+                .collect(Collectors.toList());
+
+        return this.saveBatch(categories);
+    }
+
+    @Override
+    public boolean updateCategory(Integer id, String categoryName) {
+        log.info("StoreCuisineCategoryServiceImpl.updateCategory?id={}&categoryName={}", id, categoryName);
+        
+        StoreCuisineCategory category = this.getById(id);
+        if (category == null) {
+            log.warn("更新菜品分类失败:分类不存在,id={}", id);
+            return false;
+        }
+
+        // 如果修改了分类名称,检查新分类名称是否已存在
+        if (!categoryName.equals(category.getCategoryName())) {
+            LambdaQueryWrapper<StoreCuisineCategory> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(StoreCuisineCategory::getStoreId, category.getStoreId())
+                    .eq(StoreCuisineCategory::getCategoryName, categoryName)
+                    .ne(StoreCuisineCategory::getId, id);
+            StoreCuisineCategory existingCategory = this.getOne(wrapper);
+            if (existingCategory != null) {
+                log.warn("更新菜品分类失败:分类名称已存在,categoryName={}", categoryName);
+                throw new RuntimeException("分类名称已存在:" + categoryName);
+            }
+        }
+
+        // 从JWT获取当前登录用户ID
+        Integer userId = getCurrentUserId();
+
+        LambdaUpdateWrapper<StoreCuisineCategory> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreCuisineCategory::getId, id)
+                .set(StoreCuisineCategory::getCategoryName, categoryName);
+        if (userId != null) {
+            updateWrapper.set(StoreCuisineCategory::getUpdatedUserId, userId);
+        }
+
+        return this.update(updateWrapper);
+    }
+
+    @Override
+    public boolean deleteCategory(Integer id) {
+        log.info("StoreCuisineCategoryServiceImpl.deleteCategory?id={}", id);
+        
+        StoreCuisineCategory category = this.getById(id);
+        if (category == null) {
+            log.warn("删除菜品分类失败:分类不存在,id={}", id);
+            return false;
+        }
+
+        // 逻辑删除
+        return this.removeById(id);
+    }
+
+    @Override
+    public boolean updateCategorySort(Integer storeId, List<Integer> categoryIds) {
+        log.info("StoreCuisineCategoryServiceImpl.updateCategorySort?storeId={}&categoryIds={}", storeId, categoryIds);
+        
+        if (storeId == null || categoryIds == null || categoryIds.isEmpty()) {
+            log.warn("更新菜品分类排序失败:参数不完整");
+            return false;
+        }
+
+        // 验证所有分类ID是否属于该门店
+        LambdaQueryWrapper<StoreCuisineCategory> checkWrapper = new LambdaQueryWrapper<>();
+        checkWrapper.eq(StoreCuisineCategory::getStoreId, storeId)
+                .in(StoreCuisineCategory::getId, categoryIds);
+        long count = this.count(checkWrapper);
+        if (count != categoryIds.size()) {
+            log.warn("更新菜品分类排序失败:部分分类ID不属于该门店");
+            throw new RuntimeException("部分分类ID不属于该门店");
+        }
+
+        // 从JWT获取当前登录用户ID
+        Integer userId = getCurrentUserId();
+
+        // 批量更新排序值
+        for (int i = 0; i < categoryIds.size(); i++) {
+            LambdaUpdateWrapper<StoreCuisineCategory> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreCuisineCategory::getId, categoryIds.get(i))
+                    .set(StoreCuisineCategory::getSort, i + 1);
+            if (userId != null) {
+                updateWrapper.set(StoreCuisineCategory::getUpdatedUserId, userId);
+            }
+            this.update(updateWrapper);
+        }
+
+        return true;
+    }
+
+    /**
+     * 从JWT获取当前登录用户ID
+     *
+     * @return 用户ID,如果未登录返回null
+     */
+    private Integer getCurrentUserId() {
+        try {
+            JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+            if (userInfo != null) {
+                return userInfo.getInteger("userId");
+            }
+        } catch (Exception e) {
+            log.warn("获取当前登录用户ID失败: {}", e.getMessage());
+        }
+        return null;
+    }
+}