Преглед изворни кода

商家端:美食价目表,通用价目表

liyafei пре 3 месеци
родитељ
комит
e9f3fe60bb
20 измењених фајлова са 1352 додато и 0 уклоњено
  1. 117 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreCuisine.java
  2. 69 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreCuisineCombo.java
  3. 113 0
      alien-entity/src/main/java/shop/alien/entity/store/StorePrice.java
  4. 30 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/CategoryGroupDto.java
  5. 74 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineComboDto.java
  6. 40 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineDetailDto.java
  7. 31 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineItemDto.java
  8. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreCuisineComboMapper.java
  9. 19 0
      alien-entity/src/main/java/shop/alien/mapper/StoreCuisineMapper.java
  10. 17 0
      alien-entity/src/main/java/shop/alien/mapper/StorePriceMapper.java
  11. 29 0
      alien-entity/src/main/resources/mapper/StoreCuisineComboMapper.xml
  12. 41 0
      alien-entity/src/main/resources/mapper/StoreCuisineMapper.xml
  13. 153 0
      alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java
  14. 228 0
      alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java
  15. 36 0
      alien-store/src/main/java/shop/alien/store/service/StoreCuisineComboService.java
  16. 46 0
      alien-store/src/main/java/shop/alien/store/service/StoreCuisineService.java
  17. 15 0
      alien-store/src/main/java/shop/alien/store/service/StorePriceService.java
  18. 46 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreCuisineComboServiceImpl.java
  19. 206 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreCuisineServiceImpl.java
  20. 23 0
      alien-store/src/main/java/shop/alien/store/service/impl/StorePriceServiceImpl.java

+ 117 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreCuisine.java

@@ -0,0 +1,117 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食价目表
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@JsonInclude
+@TableName("store_cuisine")
+@ApiModel(value = "StoreCuisine对象", description = "美食价目表")
+public class StoreCuisine {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商户id")
+    @TableField("stroe_id")
+    private Integer stroeId;
+
+    @ApiModelProperty(value = "菜名")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "总价")
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    @TableField("images")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    @TableField("image_content")
+    private String imageContent;
+
+    @ApiModelProperty(value = "菜品原料json(原料名称:name,所需重量:height,成本价:cost,推荐价格:suggest)")
+    @TableField("raw_json")
+    private String rawJson;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    @TableField("detail_content")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    @TableField("extra_note")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    @TableField("need_reserve")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    @TableField("reserve_rule")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    @TableField("people_limit")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    @TableField("usage_rule")
+    private String usageRule;
+
+    @ApiModelProperty(value = "状态:0-待审核 1-审核通过 2-审核拒绝")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "上下架状态:1-上架,2-下架")
+    @TableField("shelf_status")
+    private Integer shelfStatus;
+
+    @ApiModelProperty(value = "拒绝原因")
+    @TableField("rejection_reason")
+    private String rejectionReason;
+
+    @ApiModelProperty(value = "美食类型: 1-单品,2-套餐")
+    @TableField("cuisine_type")
+    private Integer cuisineType;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField(value = "updated_user_id", fill = FieldFill.UPDATE)
+    private Integer updatedUserId;
+
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+
+

+ 69 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreCuisineCombo.java

@@ -0,0 +1,69 @@
+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 auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@JsonInclude
+@TableName("store_cuisine_combo")
+@ApiModel(value = "StoreCuisineCombo对象", description = "美食套餐表(辅助详情查询)")
+public class StoreCuisineCombo {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "单品id")
+    @TableField("sid")
+    private Integer sid;
+
+    @ApiModelProperty(value = "套餐id")
+    @TableField("cid")
+    private Integer cid;
+
+    @ApiModelProperty(value = "套餐包含该单品的数量")
+    @TableField("snum")
+    private Integer snum;
+
+    @ApiModelProperty(value = "类别")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField(value = "updated_user_id", fill = FieldFill.UPDATE)
+    private Integer updatedUserId;
+
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+
+
+

+ 113 - 0
alien-entity/src/main/java/shop/alien/entity/store/StorePrice.java

@@ -0,0 +1,113 @@
+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.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 美食价目表
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@JsonInclude
+@TableName("store_price")
+@ApiModel(value = "StorePrice", description = "通用价目表")
+public class StorePrice {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "商户id")
+    @TableField("stroe_id")
+    private Integer stroeId;
+
+    @ApiModelProperty(value = "名字")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "总价")
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    @TableField("images")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    @TableField("image_content")
+    private String imageContent;
+
+    @ApiModelProperty(value = "服务项目json(服务名称:name,数量:num,单位:unit,服务说明:details)")
+    @TableField("service_json")
+    private String serviceJson;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    @TableField("detail_content")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    @TableField("extra_note")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    @TableField("need_reserve")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    @TableField("reserve_rule")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    @TableField("people_limit")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    @TableField("usage_rule")
+    private String usageRule;
+
+    @ApiModelProperty(value = "状态:0-待审核 1-审核通过 2-审核拒绝")
+    @TableField("status")
+    private Integer status;
+
+    @ApiModelProperty(value = "上下架状态:1-上架,2-下架")
+    @TableField("shelf_status")
+    private Integer shelfStatus;
+
+    @ApiModelProperty(value = "拒绝原因")
+    @TableField("rejection_reason")
+    private String rejectionReason;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建人")
+    @TableField(value = "created_user_id", fill = FieldFill.INSERT)
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "更新人")
+    @TableField(value = "updated_user_id", fill = FieldFill.UPDATE)
+    private Integer updatedUserId;
+
+    @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 = "更新时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+}
+
+

+ 30 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/CategoryGroupDto.java

@@ -0,0 +1,30 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 套餐分类组 DTO
+ * 用于解析前端传入的 groupJson
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "CategoryGroupDto", description = "套餐分类组")
+public class CategoryGroupDto {
+
+    @ApiModelProperty(value = "分类名称")
+    private String categoryName;
+
+    @ApiModelProperty(value = "该分类下的菜品列表")
+    private List<CuisineItemDto> items;
+}
+

+ 74 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineComboDto.java

@@ -0,0 +1,74 @@
+package shop.alien.entity.store.dto;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@ApiModel(value = "CuisineComboDto", description = "添加美食套餐dto")
+public class CuisineComboDto {
+
+    @ApiModelProperty(value = "id,修改时必传")
+    @TableField("id")
+    private Integer id;
+
+    @ApiModelProperty(value = "价目表内容的json数组,添加修改 套餐 时必传")
+    @TableField("group_json")
+    private String groupJson;
+
+    @ApiModelProperty(value = "菜品原料json(原料名称:name,所需重量:height,成本价:cost,推荐价格:suggest),添加修改 单品 时必传")
+    @TableField("raw_json")
+    private String rawJson;
+
+    @ApiModelProperty(value = "美食类型: 1-单品,2-套餐")
+    @TableField("cuisine_type")
+    private Integer cuisineType;
+
+    @ApiModelProperty(value = "商户id")
+    @TableField("stroe_id")
+    private Integer stroeId;
+
+    @ApiModelProperty(value = "菜名/套餐名")
+    @TableField("name")
+    private String name;
+
+    @ApiModelProperty(value = "总价")
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
+    @ApiModelProperty(value = "图片列表,最多 9 张 URL")
+    @TableField("images")
+    private String images;
+
+    @ApiModelProperty(value = "图文详情-图片")
+    @TableField("image_content")
+    private String imageContent;
+
+    @ApiModelProperty(value = "图文详情-文字")
+    @TableField("detail_content")
+    private String detailContent;
+
+    @ApiModelProperty(value = "补充说明")
+    @TableField("extra_note")
+    private String extraNote;
+
+    @ApiModelProperty(value = "是否需要预约:0=否,1=是")
+    @TableField("need_reserve")
+    private Integer needReserve;
+
+    @ApiModelProperty(value = "预约规则")
+    @TableField("reserve_rule")
+    private String reserveRule;
+
+    @ApiModelProperty(value = "适用人数")
+    @TableField("people_limit")
+    private Integer peopleLimit;
+
+    @ApiModelProperty(value = "使用规则")
+    @TableField("usage_rule")
+    private String usageRule;
+}

+ 40 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineDetailDto.java

@@ -0,0 +1,40 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.StoreCuisine;
+
+import java.util.List;
+
+/**
+ * 美食单品 / 套餐详情 DTO
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@ApiModel(value = "CuisineDetailDto", description = "美食单品或套餐详情")
+public class CuisineDetailDto {
+
+    @ApiModelProperty("主信息:单品或套餐(store_cuisine 表中的一条记录)")
+    private StoreCuisine baseInfo;
+
+    @ApiModelProperty("如果是套餐:包含的单品明细列表;如果是单品:为空列表")
+    private List<CuisineItemDetail> items;
+
+    @Data
+    @ApiModel(value = "CuisineItemDetail", description = "套餐中包含的单品明细")
+    public static class CuisineItemDetail {
+
+        @ApiModelProperty("单品信息")
+        private StoreCuisine single;
+
+        @ApiModelProperty("数量(中间表 snum)")
+        private Integer quantity;
+
+        @ApiModelProperty("分类名称(中间表 category)")
+        private String category;
+    }
+}
+

+ 31 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/CuisineItemDto.java

@@ -0,0 +1,31 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 套餐菜品项 DTO
+ * 用于解析前端传入的 groupJson 中的菜品信息
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "CuisineItemDto", description = "套餐菜品项")
+public class CuisineItemDto {
+
+    @ApiModelProperty(value = "菜品ID")
+    private Integer cuisineId;
+
+    @ApiModelProperty(value = "菜品名称")
+    private String cuisineName;
+
+    @ApiModelProperty(value = "数量")
+    private Integer quantity;
+}
+

+ 19 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreCuisineComboMapper.java

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreCuisineCombo;
+
+/**
+ * 美食套餐表(辅助详情查询) Mapper 接口
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Mapper
+public interface StoreCuisineComboMapper extends BaseMapper<StoreCuisineCombo> {
+
+}
+
+
+

+ 19 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreCuisineMapper.java

@@ -0,0 +1,19 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StoreCuisine;
+
+/**
+ * 美食价目表 Mapper 接口
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Mapper
+public interface StoreCuisineMapper extends BaseMapper<StoreCuisine> {
+
+}
+
+
+

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/StorePriceMapper.java

@@ -0,0 +1,17 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import shop.alien.entity.store.StorePrice;
+
+/**
+ * 通用价目表 Mapper 接口
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Mapper
+public interface StorePriceMapper extends BaseMapper<StorePrice> {
+
+}
+

+ 29 - 0
alien-entity/src/main/resources/mapper/StoreCuisineComboMapper.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.StoreCuisineComboMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.StoreCuisineCombo">
+        <id column="id" property="id" />
+        <result column="sid" property="sid" />
+        <result column="cid" property="cid" />
+        <result column="snum" property="snum" />
+        <result column="category" property="category" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_user_id" property="updatedUserId" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, sid, cid, snum, category, delete_flag, created_user_id, updated_user_id, created_time, updated_time
+    </sql>
+
+</mapper>
+
+
+

+ 41 - 0
alien-entity/src/main/resources/mapper/StoreCuisineMapper.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.StoreCuisineMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.StoreCuisine">
+        <id column="id" property="id" />
+        <result column="stroe_id" property="stroeId" />
+        <result column="name" property="name" />
+        <result column="total_price" property="totalPrice" />
+        <result column="images" property="images" />
+        <result column="image_content" property="imageContent" />
+        <result column="raw_json" property="rawJson" />
+        <result column="detail_content" property="detailContent" />
+        <result column="extra_note" property="extraNote" />
+        <result column="need_reserve" property="needReserve" />
+        <result column="reserve_rule" property="reserveRule" />
+        <result column="people_limit" property="peopleLimit" />
+        <result column="usage_rule" property="usageRule" />
+        <result column="status" property="status" />
+        <result column="rejection_reason" property="rejectionReason" />
+        <result column="shelf_status" property="shelfStatus" />
+        <result column="delete_flag" property="deleteFlag" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_user_id" property="updatedUserId" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, stroe_id, name, total_price, images, image_content, raw_json, detail_content,
+        extra_note, need_reserve, reserve_rule, people_limit, usage_rule, status, shelf_status,
+        rejection_reason, delete_flag, created_user_id, updated_user_id, created_time, updated_time
+    </sql>
+
+</mapper>
+
+

+ 153 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreCuisineController.java

@@ -0,0 +1,153 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreCuisine;
+import shop.alien.entity.store.dto.CuisineComboDto;
+import shop.alien.entity.store.dto.CuisineDetailDto;
+import shop.alien.store.service.StoreCuisineService;
+
+import java.util.List;
+
+/**
+ * 美食价目表
+ *
+ * @author auto-generated
+ * @since 2025-12-30
+ */
+@Slf4j
+@Api(tags = {"美食价目表"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/cuisine")
+@RequiredArgsConstructor
+public class StoreCuisineController {
+
+    private final StoreCuisineService storeCuisineService;
+
+    @ApiOperation("新增美食套餐或单品")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addCuisineCombo")
+    public R<StoreCuisine> addCuisineCombo(@RequestBody CuisineComboDto cuisineComboDto) {
+        log.info("StoreCuisineController.save?storeCuisine={}", cuisineComboDto);
+        if (storeCuisineService.addCuisineCombo(cuisineComboDto)) {
+            return R.success("新增成功");
+        }
+        return R.fail("新增失败");
+    }
+
+    @ApiOperation("修改美食套餐或单品")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/updateCuisineCombo")
+    public R<String> updateCuisineCombo(@RequestBody CuisineComboDto cuisineComboDto) {
+        log.info("StoreCuisineController.updateCuisineCombo?dto={}", cuisineComboDto);
+        if (storeCuisineService.updateCuisineCombo(cuisineComboDto)) {
+            return R.success("修改成功");
+        }
+        return R.fail("修改失败");
+    }
+
+    @ApiOperation("获取所有美食单品名称,用于添加套餐")
+    @ApiOperationSupport(order = 1)
+    @GetMapping("/getSingleName")
+    public R<List<StoreCuisine>> getSingleName() {
+        return R.data(storeCuisineService.getSingleName());
+    }
+
+    @ApiOperation("根据id与类型获取美食单品或套餐详情")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "美食/套餐 id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "cuisineType", value = "美食类型:1-单品,2-套餐", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getByCuisineType")
+    public R<CuisineDetailDto> getByCuisineType(Integer id, Integer cuisineType) {
+        return R.data(storeCuisineService.getByCuisineType(id, cuisineType));
+    }
+
+
+    @ApiOperation("删除美食价目")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "美食价目id", dataType = "Integer", paramType = "query", required = true)})
+    @PostMapping("/delete")
+    public R<String> delete(Integer id) {
+        log.info("StoreCuisineController.delete?id={}", id);
+        if (storeCuisineService.removeById(id)) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("批量删除美食价目")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StoreCuisineController.deleteBatch?ids={}", ids);
+        if (storeCuisineService.removeByIds(ids)) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("美食上下架:1-上架,2-下架")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "美食价目id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "shelfStatus", value = "上下架状态:1-上架,2-下架", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/changeShelfStatus")
+    public R<String> changeShelfStatus(Integer id, Integer shelfStatus) {
+        log.info("StoreCuisineController.changeShelfStatus?id={},shelfStatus={}", id, shelfStatus);
+        if (shelfStatus == null || (shelfStatus != 1 && shelfStatus != 2)) {
+            return R.fail("上下架状态不合法(只能为1或2)");
+        }
+        if (storeCuisineService.changeShelfStatus(id, shelfStatus)) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("分页查询美食价目")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "stroeId", value = "商户id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "菜名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态:0-待审核 1-审核通过 2-审核拒绝", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<StoreCuisine>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer stroeId,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) Integer status) {
+        log.info("StoreCuisineController.getPage?pageNum={},pageSize={},stroeId={},name={},status={}", pageNum, pageSize, stroeId, name, status);
+        Page<StoreCuisine> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<StoreCuisine> queryWrapper = new LambdaQueryWrapper<>();
+        // 默认只查询已上架的菜品/套餐(shelf_status = 1)
+        queryWrapper.eq(StoreCuisine::getShelfStatus, 1);
+        if (stroeId != null) {
+            queryWrapper.eq(StoreCuisine::getStroeId, stroeId);
+        }
+        if (name != null && !name.isEmpty()) {
+            queryWrapper.like(StoreCuisine::getName, name);
+        }
+        if (status != null) {
+            queryWrapper.eq(StoreCuisine::getStatus, status);
+        }
+        queryWrapper.orderByDesc(StoreCuisine::getCreatedTime);
+        IPage<StoreCuisine> result = storeCuisineService.page(page, queryWrapper);
+        return R.data(result);
+    }
+}
+
+

+ 228 - 0
alien-store/src/main/java/shop/alien/store/controller/StorePriceController.java

@@ -0,0 +1,228 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StorePrice;
+import shop.alien.store.service.StorePriceService;
+
+import java.util.List;
+
+/**
+ * 通用价目表
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Slf4j
+@Api(tags = {"通用价目表"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/price")
+@RequiredArgsConstructor
+public class StorePriceController {
+
+    private final StorePriceService storePriceService;
+
+    @ApiOperation("新增通用价目")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/save")
+    public R<StorePrice> save(@RequestBody StorePrice storePrice) {
+        log.info("StorePriceController.save?storePrice={}", storePrice);
+        if (storePriceService.save(storePrice)) {
+            return R.data(storePrice, "新增成功");
+        }
+        return R.fail("新增失败");
+    }
+
+    @ApiOperation("修改通用价目")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/update")
+    public R<String> update(@RequestBody StorePrice storePrice) {
+        log.info("StorePriceController.update?storePrice={}", storePrice);
+        
+        // 校验ID不能为空
+        if (storePrice.getId() == null) {
+            log.error("修改通用价目失败:ID不能为空");
+            return R.fail("ID不能为空");
+        }
+        
+        // 检查记录是否存在
+        StorePrice existingPrice = storePriceService.getById(storePrice.getId());
+        if (existingPrice == null) {
+            log.error("修改通用价目失败:ID={} 的记录不存在", storePrice.getId());
+            return R.fail("记录不存在,无法修改");
+        }
+        
+        // 执行更新
+        boolean result = storePriceService.updateById(storePrice);
+        if (result) {
+            return R.success("修改成功");
+        }
+        
+        log.error("修改通用价目失败:ID={},更新操作返回false", storePrice.getId());
+        return R.fail("修改失败,请检查数据是否正确");
+    }
+
+    @ApiOperation("根据ID查询通用价目")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "通用价目id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getById")
+    public R<StorePrice> getById(@RequestParam Integer id) {
+        log.info("StorePriceController.getById?id={}", id);
+        StorePrice storePrice = storePriceService.getById(id);
+        if (storePrice != null) {
+            return R.data(storePrice);
+        }
+        return R.fail("查询失败");
+    }
+
+    @ApiOperation("删除通用价目")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "通用价目id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/delete")
+    public R<String> delete(@RequestParam Integer id) {
+        log.info("StorePriceController.delete?id={}", id);
+        if (storePriceService.removeById(id)) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("批量删除通用价目")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/deleteBatch")
+    public R<String> deleteBatch(@RequestBody List<Integer> ids) {
+        log.info("StorePriceController.deleteBatch?ids={}", ids);
+        if (storePriceService.removeByIds(ids)) {
+            return R.success("批量删除成功");
+        }
+        return R.fail("批量删除失败");
+    }
+
+    @ApiOperation("分页查询通用价目")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "stroeId", value = "商户id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名字", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态:0-待审核 1-审核通过 2-审核拒绝", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "shelfStatus", value = "上下架状态:1-上架,2-下架", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<StorePrice>> getPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer stroeId,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer shelfStatus) {
+        log.info("StorePriceController.getPage?pageNum={},pageSize={},stroeId={},name={},status={},shelfStatus={}",
+                pageNum, pageSize, stroeId, name, status, shelfStatus);
+        Page<StorePrice> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<StorePrice> queryWrapper = new LambdaQueryWrapper<>();
+        if (stroeId != null) {
+            queryWrapper.eq(StorePrice::getStroeId, stroeId);
+        }
+        if (name != null && !name.isEmpty()) {
+            queryWrapper.like(StorePrice::getName, name);
+        }
+        if (status != null) {
+            queryWrapper.eq(StorePrice::getStatus, status);
+        }
+        if (shelfStatus != null) {
+            queryWrapper.eq(StorePrice::getShelfStatus, shelfStatus);
+        }
+        queryWrapper.orderByDesc(StorePrice::getCreatedTime);
+        IPage<StorePrice> result = storePriceService.page(page, queryWrapper);
+        return R.data(result);
+    }
+
+    @ApiOperation("查询所有通用价目")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "stroeId", value = "商户id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态:0-待审核 1-审核通过 2-审核拒绝", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "shelfStatus", value = "上下架状态:1-上架,2-下架", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/list")
+    public R<List<StorePrice>> list(
+            @RequestParam(required = false) Integer stroeId,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer shelfStatus) {
+        log.info("StorePriceController.list?stroeId={},status={},shelfStatus={}", stroeId, status, shelfStatus);
+        LambdaQueryWrapper<StorePrice> queryWrapper = new LambdaQueryWrapper<>();
+        if (stroeId != null) {
+            queryWrapper.eq(StorePrice::getStroeId, stroeId);
+        }
+        if (status != null) {
+            queryWrapper.eq(StorePrice::getStatus, status);
+        }
+        if (shelfStatus != null) {
+            queryWrapper.eq(StorePrice::getShelfStatus, shelfStatus);
+        }
+        queryWrapper.orderByDesc(StorePrice::getCreatedTime);
+        List<StorePrice> list = storePriceService.list(queryWrapper);
+        return R.data(list);
+    }
+
+    @ApiOperation("上下架操作:1-上架,2-下架")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "通用价目id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "shelfStatus", value = "上下架状态:1-上架,2-下架", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/changeShelfStatus")
+    public R<String> changeShelfStatus(@RequestParam Integer id, @RequestParam Integer shelfStatus) {
+        log.info("StorePriceController.changeShelfStatus?id={},shelfStatus={}", id, shelfStatus);
+        if (shelfStatus == null || (shelfStatus != 1 && shelfStatus != 2)) {
+            return R.fail("上下架状态不合法(只能为1或2)");
+        }
+        StorePrice storePrice = new StorePrice();
+        storePrice.setId(id);
+        storePrice.setShelfStatus(shelfStatus);
+        if (storePriceService.updateById(storePrice)) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("审核操作:0-待审核 1-审核通过 2-审核拒绝")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "通用价目id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "状态:0-待审核 1-审核通过 2-审核拒绝", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "rejectionReason", value = "拒绝原因", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/changeStatus")
+    public R<String> changeStatus(@RequestParam Integer id,
+                                   @RequestParam Integer status,
+                                   @RequestParam(required = false) String rejectionReason) {
+        log.info("StorePriceController.changeStatus?id={},status={},rejectionReason={}", id, status, rejectionReason);
+        if (status == null || (status != 0 && status != 1 && status != 2)) {
+            return R.fail("状态不合法(只能为0、1或2)");
+        }
+        StorePrice storePrice = new StorePrice();
+        storePrice.setId(id);
+        storePrice.setStatus(status);
+        if (status == 2 && rejectionReason != null) {
+            storePrice.setRejectionReason(rejectionReason);
+        }
+        if (storePriceService.updateById(storePrice)) {
+            return R.success("操作成功");
+        }
+        return R.fail("操作失败");
+    }
+}
+

+ 36 - 0
alien-store/src/main/java/shop/alien/store/service/StoreCuisineComboService.java

@@ -0,0 +1,36 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreCuisineCombo;
+import shop.alien.entity.store.dto.CuisineComboDto;
+
+import java.util.List;
+
+/**
+ * 美食套餐表(辅助详情查询) 服务类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+public interface StoreCuisineComboService extends IService<StoreCuisineCombo> {
+
+    /**
+     * 批量保存套餐单品关联关系
+     *
+     * @param comboList 套餐单品关联列表
+     * @return boolean
+     */
+    boolean saveBatch(List<StoreCuisineCombo> comboList);
+
+    /**
+     * 根据套餐id删除套餐单品关联关系
+     *
+     * @param cid 套餐id
+     * @return boolean
+     */
+    boolean deleteByComboId(Integer cid);
+
+}
+
+
+

+ 46 - 0
alien-store/src/main/java/shop/alien/store/service/StoreCuisineService.java

@@ -0,0 +1,46 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreCuisine;
+import shop.alien.entity.store.dto.CuisineComboDto;
+import shop.alien.entity.store.dto.CuisineDetailDto;
+
+import java.util.List;
+
+/**
+ * 美食价目表 服务类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+public interface StoreCuisineService extends IService<StoreCuisine> {
+
+    /**
+     * 新增美食(单品或套餐)
+     */
+    boolean addCuisineCombo(CuisineComboDto cuisineComboDto);
+
+    /**
+     * 修改美食(单品或套餐)
+     */
+    boolean updateCuisineCombo(CuisineComboDto cuisineComboDto);
+
+    /**
+     * 获取所有单品名称(cuisine_type = 1),用于添加套餐时选择
+     */
+    List<StoreCuisine> getSingleName();
+
+    /**
+     * 根据 id 与类型获取详情:
+     * - type = 1:返回单品详情
+     * - type = 2:返回套餐详情(主信息 + 套餐包含的单品列表)
+     */
+    CuisineDetailDto getByCuisineType(Integer id, Integer cuisineType);
+
+    /**
+     * 上下架操作:1-上架,2-下架
+     */
+    boolean changeShelfStatus(Integer id, Integer shelfStatus);
+}
+
+

+ 15 - 0
alien-store/src/main/java/shop/alien/store/service/StorePriceService.java

@@ -0,0 +1,15 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StorePrice;
+
+/**
+ * 通用价目表 服务类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+public interface StorePriceService extends IService<StorePrice> {
+
+}
+

+ 46 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreCuisineComboServiceImpl.java

@@ -0,0 +1,46 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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.StoreCuisineCombo;
+import shop.alien.entity.store.dto.CuisineComboDto;
+import shop.alien.mapper.StoreCuisineComboMapper;
+import shop.alien.store.service.StoreCuisineComboService;
+
+import java.util.List;
+
+/**
+ * 美食套餐表(辅助详情查询) 服务实现类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional
+public class StoreCuisineComboServiceImpl extends ServiceImpl<StoreCuisineComboMapper, StoreCuisineCombo> implements StoreCuisineComboService {
+
+    @Override
+    public boolean saveBatch(List<StoreCuisineCombo> comboList) {
+        log.info("StoreCuisineComboServiceImpl.saveBatch?comboList.size={}", comboList != null ? comboList.size() : 0);
+        if (comboList == null || comboList.isEmpty()) {
+            return false;
+        }
+        return super.saveBatch(comboList, comboList.size());
+    }
+
+    @Override
+    public boolean deleteByComboId(Integer cid) {
+        log.info("StoreCuisineComboServiceImpl.deleteByComboId?cid={}", cid);
+        LambdaQueryWrapper<StoreCuisineCombo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreCuisineCombo::getCid, cid);
+        return this.remove(queryWrapper);
+    }
+
+}
+

+ 206 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreCuisineServiceImpl.java

@@ -0,0 +1,206 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.StoreCuisine;
+import shop.alien.entity.store.StoreCuisineCombo;
+import shop.alien.entity.store.dto.CategoryGroupDto;
+import shop.alien.entity.store.dto.CuisineComboDto;
+import shop.alien.entity.store.dto.CuisineDetailDto;
+import shop.alien.entity.store.dto.CuisineItemDto;
+import shop.alien.mapper.StoreCuisineMapper;
+import shop.alien.store.service.StoreCuisineComboService;
+import shop.alien.store.service.StoreCuisineService;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 美食价目表 服务实现类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class StoreCuisineServiceImpl extends ServiceImpl<StoreCuisineMapper, StoreCuisine> implements StoreCuisineService {
+
+    private final StoreCuisineComboService storeCuisineComboService;
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    /**
+     * 新增美食(单品或套餐)
+     */
+    @Override
+    public boolean addCuisineCombo(CuisineComboDto cuisineComboDto) {
+        // 1. 保存主表信息到 store_cuisine
+        StoreCuisine cuisine = new StoreCuisine();
+        BeanUtils.copyProperties(cuisineComboDto, cuisine);
+        save(cuisine);
+
+        // 单品:只写主表,不写中间表
+        if (cuisineComboDto.getCuisineType() == 1) {
+            return true;
+        }
+
+        // 2. 套餐:解析 JSON,生成中间表记录并批量插入
+        Integer comboId = cuisine.getId();
+        List<StoreCuisineCombo> comboList = buildComboListFromJson(comboId, cuisineComboDto.getGroupJson());
+        return storeCuisineComboService.saveBatch(comboList);
+    }
+
+    /**
+     * 修改美食(单品或套餐)
+     */
+    @Override
+    public boolean updateCuisineCombo(CuisineComboDto cuisineComboDto) {
+        Integer comboId = cuisineComboDto.getId();
+
+        // 1. 更新主表信息
+        StoreCuisine cuisine = new StoreCuisine();
+        BeanUtils.copyProperties(cuisineComboDto, cuisine);
+        cuisine.setId(comboId);
+        updateById(cuisine);
+
+        // 单品:只更新主表即可
+        if (cuisineComboDto.getCuisineType() == 1) {
+            return true;
+        }
+
+        // 2. 套餐:删除旧的中间表记录(逻辑删除)
+        LambdaQueryWrapper<StoreCuisineCombo> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StoreCuisineCombo::getCid, comboId);
+        storeCuisineComboService.remove(wrapper);
+
+        // 3. 重新解析 JSON,生成新的中间表记录并批量插入
+        List<StoreCuisineCombo> comboList = buildComboListFromJson(comboId, cuisineComboDto.getGroupJson());
+        return storeCuisineComboService.saveBatch(comboList);
+    }
+
+    /**
+     * 获取所有单品名称(cuisine_type = 1),用于添加套餐
+     */
+    @Override
+    public List<StoreCuisine> getSingleName() {
+        return lambdaQuery()
+                .eq(StoreCuisine::getCuisineType, 1)
+                .list();
+    }
+
+    /**
+     * 上下架操作:1-上架,2-下架
+     */
+    @Override
+    public boolean changeShelfStatus(Integer id, Integer shelfStatus) {
+        StoreCuisine cuisine = new StoreCuisine();
+        cuisine.setId(id);
+        cuisine.setShelfStatus(shelfStatus);
+        return updateById(cuisine);
+    }
+
+    /**
+     * 根据 id 与类型获取详情:
+     * - type = 1:返回单品详情
+     * - type = 2:返回套餐详情(主信息 + 套餐包含的单品列表)
+     */
+    @Override
+    public CuisineDetailDto getByCuisineType(Integer id, Integer cuisineType) {
+        // 1. 先查主表记录
+        StoreCuisine base = getById(id);
+        if (base == null) {
+            return null;
+        }
+
+        CuisineDetailDto dto = new CuisineDetailDto();
+        dto.setBaseInfo(base);
+
+        // 单品:直接返回主信息,items 为空
+        if (cuisineType != null && cuisineType == 1) {
+            dto.setItems(Collections.emptyList());
+            return dto;
+        }
+
+        // 套餐:通过中间表查出包含哪些单品
+        LambdaQueryWrapper<StoreCuisineCombo> comboWrapper = new LambdaQueryWrapper<>();
+        comboWrapper.eq(StoreCuisineCombo::getCid, id);
+        List<StoreCuisineCombo> combos = storeCuisineComboService.list(comboWrapper);
+        if (combos.isEmpty()) {
+            dto.setItems(Collections.emptyList());
+            return dto;
+        }
+
+        // 收集所有单品 sid
+        List<Integer> sidList = combos.stream()
+                .map(StoreCuisineCombo::getSid)
+                .distinct()
+                .collect(Collectors.toList());
+
+        // 一次性查出所有单品
+        List<StoreCuisine> singles = lambdaQuery()
+                .in(StoreCuisine::getId, sidList)
+                .list();
+        Map<Integer, StoreCuisine> singleMap = singles.stream()
+                .collect(Collectors.toMap(StoreCuisine::getId, s -> s));
+
+        // 组装套餐明细
+        List<CuisineDetailDto.CuisineItemDetail> itemDetails = new ArrayList<>();
+        for (StoreCuisineCombo combo : combos) {
+            StoreCuisine single = singleMap.get(combo.getSid());
+            if (single == null) {
+                continue;
+            }
+            CuisineDetailDto.CuisineItemDetail detail = new CuisineDetailDto.CuisineItemDetail();
+            detail.setSingle(single);
+            detail.setQuantity(combo.getSnum());
+            detail.setCategory(combo.getCategory());
+            itemDetails.add(detail);
+        }
+
+        dto.setItems(itemDetails);
+        return dto;
+    }
+
+    /**
+     * 根据前端传入的 JSON,构建中间表记录列表
+     *
+     * @param comboId  套餐ID
+     * @param groupJson 前端传入的 JSON 字符串
+     * @return 中间表记录列表
+     */
+    private List<StoreCuisineCombo> buildComboListFromJson(Integer comboId, String groupJson) {
+        List<CategoryGroupDto> groups;
+        try {
+            groups = objectMapper.readValue(groupJson, new TypeReference<List<CategoryGroupDto>>() {});
+        } catch (IOException e) {
+            throw new RuntimeException("解析套餐组合 JSON 失败", e);
+        }
+
+        List<StoreCuisineCombo> comboList = new ArrayList<>();
+        for (CategoryGroupDto group : groups) {
+            String categoryName = group.getCategoryName();
+            for (CuisineItemDto item : group.getItems()) {
+                StoreCuisineCombo combo = new StoreCuisineCombo();
+                combo.setCid(comboId);
+                combo.setSid(item.getCuisineId());
+                combo.setSnum(item.getQuantity());
+                combo.setCategory(categoryName);
+                comboList.add(combo);
+            }
+        }
+        return comboList;
+    }
+}
+
+

+ 23 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StorePriceServiceImpl.java

@@ -0,0 +1,23 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.StorePrice;
+import shop.alien.mapper.StorePriceMapper;
+import shop.alien.store.service.StorePriceService;
+
+/**
+ * 通用价目表 服务实现类
+ *
+ * @author auto-generated
+ * @since 2025-01-01
+ */
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class StorePriceServiceImpl extends ServiceImpl<StorePriceMapper, StorePrice> implements StorePriceService {
+
+}
+