zhangchen 1 هفته پیش
والد
کامیت
02ab4e9405
41فایلهای تغییر یافته به همراه3478 افزوده شده و 348 حذف شده
  1. 86 0
      alien-entity/src/main/java/shop/alien/entity/store/BathFacilityService.java
  2. 86 0
      alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java
  3. 1 1
      alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java
  4. 19 3
      alien-entity/src/main/java/shop/alien/entity/store/StoreMenu.java
  5. 75 0
      alien-entity/src/main/java/shop/alien/entity/store/StorePersonnel.java
  6. 10 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java
  7. 39 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/BathFacilityServiceCategoryVo.java
  8. 66 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/BathFacilityServiceVo.java
  9. 39 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategoryVo.java
  10. 61 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityVo.java
  11. 53 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumDetailVo.java
  12. 25 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumNameVo.java
  13. 29 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreOfficialAlbumImgVo.java
  14. 34 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StorePersonnelVo.java
  15. 17 0
      alien-entity/src/main/java/shop/alien/mapper/BathFacilityServiceMapper.java
  16. 17 0
      alien-entity/src/main/java/shop/alien/mapper/SportsEquipmentFacilityMapper.java
  17. 26 0
      alien-entity/src/main/java/shop/alien/mapper/StoreInfoMapper.java
  18. 43 2
      alien-entity/src/main/java/shop/alien/mapper/StoreMenuMapper.java
  19. 44 0
      alien-entity/src/main/java/shop/alien/mapper/StorePersonnelMapper.java
  20. 168 0
      alien-store/src/main/java/shop/alien/store/controller/BathFacilityServiceController.java
  21. 194 0
      alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java
  22. 0 261
      alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java
  23. 84 5
      alien-store/src/main/java/shop/alien/store/controller/StoreMenuController.java
  24. 90 4
      alien-store/src/main/java/shop/alien/store/controller/StoreOfficialAlbumController.java
  25. 87 0
      alien-store/src/main/java/shop/alien/store/controller/StorePersonnelController.java
  26. 48 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffConfigController.java
  27. 82 0
      alien-store/src/main/java/shop/alien/store/service/BathFacilityServiceService.java
  28. 49 44
      alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java
  29. 86 0
      alien-store/src/main/java/shop/alien/store/service/SportsEquipmentFacilityService.java
  30. 14 0
      alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java
  31. 32 4
      alien-store/src/main/java/shop/alien/store/service/StoreMenuService.java
  32. 25 1
      alien-store/src/main/java/shop/alien/store/service/StoreOfficialAlbumService.java
  33. 58 0
      alien-store/src/main/java/shop/alien/store/service/StorePersonnelService.java
  34. 19 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffConfigService.java
  35. 258 0
      alien-store/src/main/java/shop/alien/store/service/impl/BathFacilityServiceServiceImpl.java
  36. 307 0
      alien-store/src/main/java/shop/alien/store/service/impl/SportsEquipmentFacilityServiceImpl.java
  37. 453 2
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  38. 279 20
      alien-store/src/main/java/shop/alien/store/service/impl/StoreMenuServiceImpl.java
  39. 182 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java
  40. 161 0
      alien-store/src/main/java/shop/alien/store/service/impl/StorePersonnelServiceImpl.java
  41. 32 1
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

+ 86 - 0
alien-entity/src/main/java/shop/alien/entity/store/BathFacilityService.java

@@ -0,0 +1,86 @@
+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.io.Serializable;
+import java.util.Date;
+
+/**
+ * 洗浴设施及服务表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("bath_facility_service")
+@ApiModel(value = "BathFacilityService对象", description = "洗浴设施及服务表")
+public class BathFacilityService implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)")
+    @TableField("facility_category")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "名称")
+    @TableField("facility_name")
+    private String facilityName;
+
+    @ApiModelProperty(value = "收费标准")
+    @TableField("charging_standard")
+    private String chargingStandard;
+
+    @ApiModelProperty(value = "使用时间类型(0:全天, 1:选择时间)")
+    @TableField("usage_time_type")
+    private Integer usageTimeType;
+
+    @ApiModelProperty(value = "使用开始时间(HH:mm格式)")
+    @TableField("usage_start_time")
+    private String usageStartTime;
+
+    @ApiModelProperty(value = "使用结束时间(HH:mm格式)")
+    @TableField("usage_end_time")
+    private String usageEndTime;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    @TableField("display_in_store_detail")
+    private Integer displayInStoreDetail;
+
+    @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(value = "created_user_id", fill = FieldFill.INSERT)
+    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(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+}
+

+ 86 - 0
alien-entity/src/main/java/shop/alien/entity/store/SportsEquipmentFacility.java

@@ -0,0 +1,86 @@
+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.io.Serializable;
+import java.util.Date;
+
+/**
+ * 运动器材设施表
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("sports_equipment_facility")
+@ApiModel(value = "SportsEquipmentFacility对象", description = "运动器材设施表")
+public class SportsEquipmentFacility implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    @TableField("facility_category")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施名称")
+    @TableField("facility_name")
+    private String facilityName;
+
+    @ApiModelProperty(value = "数量")
+    @TableField("quantity")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "品牌")
+    @TableField("brand")
+    private String brand;
+
+    @ApiModelProperty(value = "描述")
+    @TableField("description")
+    private String description;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    @TableField("display_in_store_detail")
+    private Integer displayInStoreDetail;
+
+    @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(value = "created_user_id", fill = FieldFill.INSERT)
+    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(value = "updated_user_id", fill = FieldFill.INSERT_UPDATE)
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "收费类型0-免费,1-收费")
+    @TableField("billing_type")
+    private Integer billingType;
+}
+

+ 1 - 1
alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java

@@ -30,7 +30,7 @@ public class StoreImg extends Model<StoreImg> {
     @TableField("store_id")
     private Integer storeId;
 
-    @ApiModelProperty(value = "图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉,10:商家头像,11:店铺轮播图,12:联名卡图片,13:动态折扣, 14:套餐图片,15:合同照片,17:打卡广场小人图片 18: 商品发布图片 19:二手商品与用户举报图片,20头图单图模式,21头图多图模式 , 22续签合同,23,二手商品记录图片类型, 24 食品经营许可证审核前状态 25.食品经营许可证审核后状态")
+    @ApiModelProperty(value = "图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉,10:商家头像,11:店铺轮播图,12:联名卡图片,13:动态折扣, 14:套餐图片,15:合同照片,17:打卡广场小人图片 18: 商品发布图片 19:二手商品与用户举报图片,20头图单图模式,21头图多图模式 , 22续签合同,23,二手商品记录图片类型, 24 食品经营许可证审核前状态 25.食品经营许可证审核后状态, 28:运动器材设施图片, 29:洗浴设施及服务图片 30 酒水")
     @TableField("img_type")
     private Integer imgType;
 

+ 19 - 3
alien-entity/src/main/java/shop/alien/entity/store/StoreMenu.java

@@ -34,15 +34,19 @@ public class StoreMenu {
     @TableField("img_id")
     private Integer imgId;
 
-    @ApiModelProperty(value = "菜品名称")
+    @ApiModelProperty(value = "菜单类型:1-菜单,2-酒水")
+    @TableField("dish_menu_type")
+    private String dishMenuType;
+
+    @ApiModelProperty(value = "名称")
     @TableField("dish_name")
     private String dishName;
 
-    @ApiModelProperty(value = "菜品价格")
+    @ApiModelProperty(value = "价格")
     @TableField("dish_price")
     private BigDecimal dishPrice;
 
-    @ApiModelProperty(value = "菜品类型, 0:菜单, 1:推荐")
+    @ApiModelProperty(value = "是否推荐, 0:非推荐, 1:推荐")
     @TableField("dish_type")
     private Integer dishType;
 
@@ -89,5 +93,17 @@ public class StoreMenu {
     @TableField("description")
     private String description;
 
+    @ApiModelProperty(value = "酒精度")
+    @TableField("alcoholVolume")
+    private String alcoholVolume;
+
+    @ApiModelProperty(value = "分类")
+    @TableField("category")
+    private String category;
+
+    @ApiModelProperty(value = "品味")
+    @TableField("flavor")
+    private String flavor;
+
 
 }

+ 75 - 0
alien-entity/src/main/java/shop/alien/entity/store/StorePersonnel.java

@@ -0,0 +1,75 @@
+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-15
+ */
+@Data
+@JsonInclude
+@TableName("store_personnel")
+@ApiModel(value = "StorePersonnel对象", description = "店铺人员")
+public class StorePersonnel {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "门店id")
+    @TableField("store_id")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "人员姓名")
+    @TableField("personnel_name")
+    private String personnelName;
+
+    @ApiModelProperty(value = "角色标签,1-歌手,2-技师,3-教练,4-美发师/美容师")
+    @TableField("role_tags")
+    private String roleTags;
+
+    @ApiModelProperty(value = "图片id,关联store_img表")
+    @TableField("img_id")
+    private Integer imgId;
+
+    @ApiModelProperty(value = "人员描述")
+    @TableField("description")
+    private String description;
+
+    @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;
+}
+

+ 10 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffConfig.java

@@ -27,33 +27,43 @@ public class StoreStaffConfig extends Model<StoreStaffConfig> {
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @ApiModelProperty(value = "员工职位")
     @TableField("staff_position")
     private String staffPosition;
 
+    @ApiModelProperty(value = "员工名称/昵称")
     @TableField("name")
     private String name;
 
+    @ApiModelProperty(value = "头像")
     @TableField("staff_image")
     private String staffImage;
 
+    @ApiModelProperty(value = "擅长项目")
     @TableField("proficient_projects")
     private String proficientProjects;
 
+    @ApiModelProperty(value = "标签")
     @TableField("tag")
     private String tag;
 
+    @ApiModelProperty(value = "个人简介")
     @TableField("personal_introduction")
     private String personalIntroduction;
 
+    @ApiModelProperty(value = "人员状态0-待审核 1-审核通过 2-审核拒绝")
     @TableField("status")
     private String status;
 
+    @ApiModelProperty(value = "店铺ID")
     @TableField("store_id")
     private Integer storeId;
 
+    @ApiModelProperty(value = "店铺名称")
     @TableField("store_name")
     private String storeName;
 
+    @ApiModelProperty(value = "拒绝原因")
     @TableField("rejection_reason")
     private String rejectionReason;
 

+ 39 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/BathFacilityServiceCategoryVo.java

@@ -0,0 +1,39 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 洗浴设施及服务分类汇总视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "BathFacilityServiceCategoryVo对象", description = "洗浴设施及服务分类汇总视图对象")
+public class BathFacilityServiceCategoryVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施分类名称")
+    private String facilityCategoryName;
+
+    @ApiModelProperty(value = "该分类下的设备数量")
+    private Integer facilityCount;
+
+    @ApiModelProperty(value = "该分类的代表图片(从该分类下第一个设备的第一张图片获取)")
+    private String categoryImage;
+
+    @ApiModelProperty(value = "该分类下的设备列表(包含设备信息和图片)")
+    private List<BathFacilityServiceVo> facilityList;
+}
+

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

@@ -0,0 +1,66 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 洗浴设施及服务视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "BathFacilityServiceVo对象", description = "洗浴设施及服务视图对象")
+public class BathFacilityServiceVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施分类名称")
+    private String facilityCategoryName;
+
+    @ApiModelProperty(value = "名称")
+    private String facilityName;
+
+    @ApiModelProperty(value = "收费标准")
+    private String chargingStandard;
+
+    @ApiModelProperty(value = "使用时间类型(0:全天, 1:选择时间)")
+    private Integer usageTimeType;
+
+    @ApiModelProperty(value = "使用时间类型文本")
+    private String usageTimeTypeText;
+
+    @ApiModelProperty(value = "使用开始时间(HH:mm格式)")
+    private String usageStartTime;
+
+    @ApiModelProperty(value = "使用结束时间(HH:mm格式)")
+    private String usageEndTime;
+
+    @ApiModelProperty(value = "使用时间范围(如: 08:00-20:00)")
+    private String usageTimeRange;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    private Integer displayInStoreDetail;
+
+    @ApiModelProperty(value = "是否显示在店铺详情文本")
+    private String displayInStoreDetailText;
+
+    @ApiModelProperty(value = "图片列表")
+    private List<String> imageList;
+}
+

+ 39 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityCategoryVo.java

@@ -0,0 +1,39 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 运动器材设施分类汇总视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "SportsEquipmentFacilityCategoryVo对象", description = "运动器材设施分类汇总视图对象")
+public class SportsEquipmentFacilityCategoryVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施分类名称")
+    private String facilityCategoryName;
+
+    @ApiModelProperty(value = "该分类下的设备数量")
+    private Integer facilityCount;
+
+    @ApiModelProperty(value = "该分类下的图片列表")
+    private List<String> imageList;
+
+    @ApiModelProperty(value = "该分类下的设备列表")
+    private List<SportsEquipmentFacilityVo> facilityList;
+}
+

+ 61 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/SportsEquipmentFacilityVo.java

@@ -0,0 +1,61 @@
+package shop.alien.entity.store.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 运动器材设施视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "SportsEquipmentFacilityVo对象", description = "运动器材设施视图对象")
+public class SportsEquipmentFacilityVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "门店ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)")
+    private Integer facilityCategory;
+
+    @ApiModelProperty(value = "设施分类名称")
+    private String facilityCategoryName;
+
+    @ApiModelProperty(value = "设施名称")
+    private String facilityName;
+
+    @ApiModelProperty(value = "数量")
+    private Integer quantity;
+
+    @ApiModelProperty(value = "品牌")
+    private String brand;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "是否显示在店铺详情(0:隐藏, 1:显示)")
+    private Integer displayInStoreDetail;
+
+    @ApiModelProperty(value = "是否显示在店铺详情文本")
+    private String displayInStoreDetailText;
+
+    @ApiModelProperty(value = "收费类型0-免费,1-收费")
+    private Integer billingType;
+
+    @ApiModelProperty(value = "图片列表")
+    private List<String> imageList;
+}
+

+ 53 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumDetailVo.java

@@ -0,0 +1,53 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 官方相册详情VO(包含相册名称、图片列表和数量)
+ *
+ * @author zhangchen
+ * @since 2025-01-16
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreAlbumDetailVo", description = "官方相册详情VO")
+public class StoreAlbumDetailVo {
+
+    @ApiModelProperty(value = "相册名称")
+    private String albumName;
+
+    @ApiModelProperty(value = "该相册下的图片数量")
+    private Integer imgCount;
+
+    @ApiModelProperty(value = "该相册下的图片列表")
+    private List<StoreImgInfo> imgList;
+
+    /**
+     * 图片信息
+     */
+    @Data
+    @JsonInclude
+    @ApiModel(value = "StoreImgInfo", description = "图片信息")
+    public static class StoreImgInfo {
+        @ApiModelProperty(value = "图片ID")
+        private Integer id;
+
+        @ApiModelProperty(value = "图片链接")
+        private String imgUrl;
+
+        @ApiModelProperty(value = "图片描述")
+        private String imgDescription;
+
+        @ApiModelProperty(value = "图片排序")
+        private Integer imgSort;
+
+        @ApiModelProperty(value = "图片类型")
+        private Integer imgType;
+    }
+}
+

+ 25 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreAlbumNameVo.java

@@ -0,0 +1,25 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 官方相册名称VO
+ *
+ * @author zhangchen
+ * @since 2025-01-16
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreAlbumNameVo", description = "官方相册名称VO")
+public class StoreAlbumNameVo {
+
+    @ApiModelProperty(value = "相册名称")
+    private String albumName;
+
+    @ApiModelProperty(value = "该相册下的图片数量")
+    private Integer imgCount;
+}
+

+ 29 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreOfficialAlbumImgVo.java

@@ -0,0 +1,29 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.store.StoreImg;
+
+import java.util.List;
+
+/**
+ * 官方相册图片列表返回VO
+ *
+ * @author zhangchen
+ * @since 2025-01-16
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "StoreOfficialAlbumImgVo", description = "官方相册图片列表返回VO")
+public class StoreOfficialAlbumImgVo {
+
+    @ApiModelProperty(value = "图片列表")
+    private List<StoreImg> imgList;
+
+    @ApiModelProperty(value = "图片总数")
+    private Integer totalCount;
+
+}
+

+ 34 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StorePersonnelVo.java

@@ -0,0 +1,34 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import shop.alien.entity.store.StorePersonnel;
+
+import java.util.List;
+
+/**
+ * 店铺人员视图对象
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude
+public class StorePersonnelVo extends StorePersonnel {
+
+    @ApiModelProperty(value = "图片排序")
+    private Integer imgSort;
+
+    @ApiModelProperty(value = "图片链接")
+    private String imgUrl;
+
+    @ApiModelProperty(value = "图片描述")
+    private String imgDescription;
+
+    @ApiModelProperty(value = "人员排序列表")
+    private List<StorePersonnelVo> sortList;
+}
+

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/BathFacilityServiceMapper.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.BathFacilityService;
+
+/**
+ * 洗浴设施及服务 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface BathFacilityServiceMapper extends BaseMapper<BathFacilityService> {
+
+}
+

+ 17 - 0
alien-entity/src/main/java/shop/alien/mapper/SportsEquipmentFacilityMapper.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.SportsEquipmentFacility;
+
+/**
+ * 运动器材设施 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface SportsEquipmentFacilityMapper extends BaseMapper<SportsEquipmentFacility> {
+
+}
+

+ 26 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreInfoMapper.java

@@ -158,4 +158,30 @@ public interface StoreInfoMapper extends BaseMapper<StoreInfo> {
             "from store_info where id = #{storeId}"
             )
     Double getStoreDistance(@Param("position") String position,@Param("storeId") Integer storeId);
+
+
+    /**
+     * 更多推荐(用户端)
+     *
+     * @param queryWrapper 查询条件
+     * @param position 经纬度位置(格式:经度,纬度)
+     * @return List<StoreInfoVo>
+     */
+    @Select("select a.*,b.name store_contact, b.phone store_phone, b.id_card idCard, b.password, c.dict_detail store_status_str, d.dict_detail business_status_str, e.dict_detail store_type_str,  " +
+            "( " +
+            " select ifnull(round(avg(score), 1), 0) " +
+            " from store_evaluation eval " +
+            " where eval.store_id = a.id and eval.delete_flag = 0 " +
+            ") score, " +
+            "ROUND(ST_Distance_Sphere(ST_GeomFromText(CONCAT('POINT(', REPLACE(#{position}, ',', ' '), ')' )), ST_GeomFromText(CONCAT('POINT(', REPLACE(a.store_position, ',', ' '), ')' )))) distance3 " +
+            "from store_info a " +
+            "left join store_user b on a.id = b.store_id and a.delete_flag = 0 and b.delete_flag = 0 " +
+            "left join store_dictionary c on a.store_status = c.dict_id and c.type_name = 'storeState' and c.delete_flag = 0 " +
+            "left join store_dictionary d on a.business_status = d.dict_id and d.type_name = 'businessStatus' and d.delete_flag = 0 " +
+            "left join store_dictionary e on e.type_name = 'storeType' and e.dict_id = a.store_type and e.delete_flag = 0 " +
+            "${ew.customSqlSegment} " +
+            "and a.store_position is not null and a.store_position != '' " +
+            "and ROUND(ST_Distance_Sphere(ST_GeomFromText(CONCAT('POINT(', REPLACE(#{position}, ',', ' '), ')' )), ST_GeomFromText(CONCAT('POINT(', REPLACE(a.store_position, ',', ' '), ')' ))) / 1000, 2) <= 1 " +
+            "order by distance3 asc limit 20")
+    List<StoreInfoVo> getMoreRecommendedStores(@Param(Constants.WRAPPER) QueryWrapper<StoreInfoVo> queryWrapper, @Param("position") String position);
 }

+ 43 - 2
alien-entity/src/main/java/shop/alien/mapper/StoreMenuMapper.java

@@ -28,11 +28,52 @@ public interface StoreMenuMapper extends BaseMapper<StoreMenu> {
     //"图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉, 10:商家头像, 11:店铺轮播图"
     @Select("select a.*, b.img_url, b.img_sort, b.img_description from store_menu a " +
             "left join store_img b on a.img_id = b.id where a.delete_flag = 0 and b.delete_flag = 0 " +
-            "and a.store_id = #{storeId} AND (if(#{dishType} is null, 1 = 1, a.dish_type = #{dishType}))")
-    List<StoreMenuVo> getStoreMenuList(@Param("storeId") Integer storeId, @Param("dishType") Integer dishType);
+            "and a.store_id = #{storeId} " +
+            "and (if(#{dishType} is null, 1 = 1, a.dish_type = #{dishType})) " +
+            "and (if(#{dishMenuType} is null, 1 = 1, a.dish_menu_type = #{dishMenuType}))")
+    List<StoreMenuVo> getStoreMenuList(@Param("storeId") Integer storeId, @Param("dishType") Integer dishType, @Param("dishMenuType") Integer dishMenuType);
 
     @Select("select a.*, b.img_url, b.img_sort, b.img_description from store_menu a " +
             "left join store_img b on a.img_id = b.id where a.delete_flag = 0 and b.delete_flag = 0 " +
             "and a.id = #{id}")
     StoreMenuVo getMenuInfo(@Param("id") Integer id);
+
+    /**
+     * 获取菜单(客户端)
+     * <p>
+     * 根据门店ID查询菜单列表,支持按菜品类型和菜单类型筛选。
+     * 查询未删除的菜单及其关联的图片信息。
+     * </p>
+     *
+     * @param storeId      门店ID,必填
+     * @param dishType     菜品类型,可选,0:非推荐, 1:推荐。当为null时,不限制菜品类型
+     * @param dishMenuType 菜单类型,可选,1-菜单,2-酒水。当为null时,不限制菜单类型
+     * @return 菜单列表,包含菜单信息和关联的图片信息
+     */
+    @Select("<script>" +
+            "SELECT " +
+            "    a.*, " +
+            "    b.img_url, " +
+            "    b.img_sort, " +
+            "    b.img_description " +
+            "FROM store_menu a " +
+            "LEFT JOIN store_img b ON a.img_id = b.id " +
+            "WHERE a.delete_flag = 0 " +
+            "    AND (b.delete_flag = 0 OR b.delete_flag IS NULL) " +
+            "    AND a.store_id = #{storeId} " +
+            "<if test='dishType != null'>" +
+            "    AND a.dish_type = #{dishType} " +
+            "</if>" +
+            "<if test='dishMenuType != null'>" +
+            "    AND a.dish_menu_type = #{dishMenuType} " +
+            "</if>" +
+            "</script>")
+    List<StoreMenuVo> getClientMenuByStoreId(@Param("storeId") Integer storeId,
+                                             @Param("dishType") Integer dishType,
+                                             @Param("dishMenuType") Integer dishMenuType);
+
+    @Select("select a.*, b.img_url, b.img_sort, b.img_description from store_menu a " +
+            "left join store_img b on a.img_id = b.id where a.delete_flag = 0 and b.delete_flag = 0 " +
+            "and a.id = #{id}")
+    StoreMenuVo getClientMenuInfoById(@Param("id") Integer id);
 }

+ 44 - 0
alien-entity/src/main/java/shop/alien/mapper/StorePersonnelMapper.java

@@ -0,0 +1,44 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import shop.alien.entity.store.StorePersonnel;
+import shop.alien.entity.store.vo.StorePersonnelVo;
+
+import java.util.List;
+
+/**
+ * 店铺人员 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+@Mapper
+public interface StorePersonnelMapper extends BaseMapper<StorePersonnel> {
+
+    /**
+     * 获取店铺人员列表
+     *
+     * @param storeId 门店id
+     * @return List<StorePersonnelVo>
+     */
+    @Select("select a.*, b.img_url, b.img_sort, b.img_description from store_personnel a " +
+            "left join store_img b on a.img_id = b.id where a.delete_flag = 0 and b.delete_flag = 0 " +
+            "and a.store_id = #{storeId} " +
+            "order by a.sort asc")
+    List<StorePersonnelVo> getStorePersonnelList(@Param("storeId") Integer storeId);
+
+    /**
+     * 获取人员详情
+     *
+     * @param id 人员id
+     * @return StorePersonnelVo
+     */
+    @Select("select a.*, b.img_url, b.img_sort, b.img_description from store_personnel a " +
+            "left join store_img b on a.img_id = b.id where a.delete_flag = 0 and b.delete_flag = 0 " +
+            "and a.id = #{id}")
+    StorePersonnelVo getPersonnelInfo(@Param("id") Integer id);
+}
+

+ 168 - 0
alien-store/src/main/java/shop/alien/store/controller/BathFacilityServiceController.java

@@ -0,0 +1,168 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+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.BathFacilityService;
+import shop.alien.entity.store.vo.BathFacilityServiceCategoryVo;
+import shop.alien.entity.store.vo.BathFacilityServiceVo;
+import shop.alien.store.service.BathFacilityServiceService;
+
+import java.util.List;
+
+/**
+ * 洗浴设施及服务Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"洗浴设施及服务管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/bath/facility/service")
+@RequiredArgsConstructor
+public class BathFacilityServiceController {
+
+    private final BathFacilityServiceService facilityServiceService;
+
+    @ApiOperation("分页查询洗浴设施及服务列表(用户端)")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true, defaultValue = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "int", paramType = "query", required = true, defaultValue = "10"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<BathFacilityServiceVo>> getPageList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize,
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer facilityCategory) {
+        log.info("BathFacilityServiceController.getPageList?pageNum={},pageSize={},storeId={},facilityCategory={}",
+                pageNum, pageSize, storeId, facilityCategory);
+        return R.data(facilityServiceService.getPageList(pageNum, pageSize, storeId, facilityCategory));
+    }
+
+    @ApiOperation("查询洗浴设施及服务列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list")
+    public R<List<BathFacilityServiceVo>> getList(
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer facilityCategory) {
+        log.info("BathFacilityServiceController.getList?storeId={},facilityCategory={}", storeId, facilityCategory);
+        return R.data(facilityServiceService.getList(storeId, facilityCategory));
+    }
+
+    @ApiOperation("根据ID查询洗浴设施及服务详情(用户端)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<BathFacilityServiceVo> getDetail(@RequestParam Integer id) {
+        log.info("BathFacilityServiceController.getDetail?id={}", id);
+        BathFacilityServiceVo vo = facilityServiceService.getDetail(id);
+        if (vo == null) {
+            return R.fail("数据不存在");
+        }
+        return R.data(vo);
+    }
+
+    @ApiOperation("新增洗浴设施及服务")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/save")
+    public R<Boolean> saveFacilityService(@RequestBody BathFacilityServiceVo vo) {
+        log.info("BathFacilityServiceController.saveFacilityService?vo={}", vo);
+        try {
+            BathFacilityService facilityService = new BathFacilityService();
+            facilityService.setStoreId(vo.getStoreId());
+            facilityService.setFacilityCategory(vo.getFacilityCategory());
+            facilityService.setFacilityName(vo.getFacilityName());
+            facilityService.setChargingStandard(vo.getChargingStandard());
+            facilityService.setUsageTimeType(vo.getUsageTimeType());
+            facilityService.setUsageStartTime(vo.getUsageStartTime());
+            facilityService.setUsageEndTime(vo.getUsageEndTime());
+            facilityService.setDisplayInStoreDetail(vo.getDisplayInStoreDetail());
+            boolean result = facilityServiceService.saveFacilityService(facilityService, vo.getImageList());
+            if (result) {
+                return R.success("新增成功");
+            }
+            return R.fail("新增失败");
+        } catch (Exception e) {
+            log.error("BathFacilityServiceController.saveFacilityService异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("修改洗浴设施及服务")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/update")
+    public R<Boolean> updateFacilityService(@RequestBody BathFacilityServiceVo vo) {
+        log.info("BathFacilityServiceController.updateFacilityService?vo={}", vo);
+        try {
+            if (vo.getId() == null) {
+                return R.fail("主键ID不能为空");
+            }
+            BathFacilityService facilityService = new BathFacilityService();
+            facilityService.setId(vo.getId());
+            facilityService.setStoreId(vo.getStoreId());
+            facilityService.setFacilityCategory(vo.getFacilityCategory());
+            facilityService.setFacilityName(vo.getFacilityName());
+            facilityService.setChargingStandard(vo.getChargingStandard());
+            facilityService.setUsageTimeType(vo.getUsageTimeType());
+            facilityService.setUsageStartTime(vo.getUsageStartTime());
+            facilityService.setUsageEndTime(vo.getUsageEndTime());
+            facilityService.setDisplayInStoreDetail(vo.getDisplayInStoreDetail());
+            boolean result = facilityServiceService.updateFacilityService(facilityService, vo.getImageList());
+            if (result) {
+                return R.success("修改成功");
+            }
+            return R.fail("修改失败");
+        } catch (Exception e) {
+            log.error("BathFacilityServiceController.updateFacilityService异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("删除洗浴设施及服务")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/delete")
+    public R<Boolean> deleteFacilityService(@RequestParam Integer id) {
+        log.info("BathFacilityServiceController.deleteFacilityService?id={}", id);
+        try {
+            boolean result = facilityServiceService.deleteFacilityService(id);
+            if (result) {
+                return R.success("删除成功");
+            }
+            return R.fail("删除失败");
+        } catch (Exception e) {
+            log.error("BathFacilityServiceController.deleteFacilityService异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、设备列表和图片)(用户端)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/categorySummary")
+    public R<List<BathFacilityServiceCategoryVo>> getCategorySummary(@RequestParam Integer storeId) {
+        log.info("BathFacilityServiceController.getCategorySummary?storeId={}", storeId);
+        return R.data(facilityServiceService.getCategorySummary(storeId));
+    }
+}
+

+ 194 - 0
alien-store/src/main/java/shop/alien/store/controller/SportsEquipmentFacilityController.java

@@ -0,0 +1,194 @@
+package shop.alien.store.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+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.SportsEquipmentFacility;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategoryVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityVo;
+import shop.alien.store.service.SportsEquipmentFacilityService;
+
+import java.util.List;
+
+/**
+ * 运动器材设施Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"运动器材设施管理"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/sports/equipment/facility")
+@RequiredArgsConstructor
+public class SportsEquipmentFacilityController {
+
+    private final SportsEquipmentFacilityService facilityService;
+
+    @ApiOperation("分页查询运动器材设施列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true, defaultValue = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "页大小", dataType = "int", paramType = "query", required = true, defaultValue = "10"),
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/page")
+    public R<IPage<SportsEquipmentFacilityVo>> getPageList(
+            @RequestParam(defaultValue = "1") Integer pageNum,
+            @RequestParam(defaultValue = "10") Integer pageSize,
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer facilityCategory) {
+        log.info("SportsEquipmentFacilityController.getPageList?pageNum={},pageSize={},storeId={},facilityCategory={}",
+                pageNum, pageSize, storeId, facilityCategory);
+        return R.data(facilityService.getPageList(pageNum, pageSize, storeId, facilityCategory));
+    }
+
+    @ApiOperation("查询运动器材设施列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "facilityCategory", value = "设施分类(1:有氧区, 2:力量区, 3:单功能机械区)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list")
+    public R<List<SportsEquipmentFacilityVo>> getList(
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer facilityCategory) {
+        log.info("SportsEquipmentFacilityController.getList?storeId={},facilityCategory={}", storeId, facilityCategory);
+        return R.data(facilityService.getList(storeId, facilityCategory));
+    }
+
+    @ApiOperation("根据ID查询运动器材设施详情(用户端)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<SportsEquipmentFacilityVo> getDetail(@RequestParam Integer id) {
+        log.info("SportsEquipmentFacilityController.getDetail?id={}", id);
+        SportsEquipmentFacilityVo vo = facilityService.getDetail(id);
+        if (vo == null) {
+            return R.fail("数据不存在");
+        }
+        return R.data(vo);
+    }
+
+    @ApiOperation("新增运动器材设施")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/save")
+    public R<Boolean> saveFacility(@RequestBody SportsEquipmentFacilityVo vo) {
+        log.info("SportsEquipmentFacilityController.saveFacility?vo={}", vo);
+        try {
+            SportsEquipmentFacility facility = new SportsEquipmentFacility();
+            facility.setStoreId(vo.getStoreId());
+            facility.setFacilityCategory(vo.getFacilityCategory());
+            facility.setFacilityName(vo.getFacilityName());
+            facility.setQuantity(vo.getQuantity());
+            facility.setBrand(vo.getBrand());
+            facility.setDescription(vo.getDescription());
+            facility.setDisplayInStoreDetail(vo.getDisplayInStoreDetail());
+            boolean result = facilityService.saveFacility(facility, vo.getImageList());
+            if (result) {
+                return R.success("新增成功");
+            }
+            return R.fail("新增失败");
+        } catch (Exception e) {
+            log.error("SportsEquipmentFacilityController.saveFacility异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("修改运动器材设施")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/update")
+    public R<Boolean> updateFacility(@RequestBody SportsEquipmentFacilityVo vo) {
+        log.info("SportsEquipmentFacilityController.updateFacility?vo={}", vo);
+        try {
+            if (vo.getId() == null) {
+                return R.fail("主键ID不能为空");
+            }
+            SportsEquipmentFacility facility = new SportsEquipmentFacility();
+            facility.setId(vo.getId());
+            facility.setStoreId(vo.getStoreId());
+            facility.setFacilityCategory(vo.getFacilityCategory());
+            facility.setFacilityName(vo.getFacilityName());
+            facility.setQuantity(vo.getQuantity());
+            facility.setBrand(vo.getBrand());
+            facility.setDescription(vo.getDescription());
+            facility.setDisplayInStoreDetail(vo.getDisplayInStoreDetail());
+            boolean result = facilityService.updateFacility(facility, vo.getImageList());
+            if (result) {
+                return R.success("修改成功");
+            }
+            return R.fail("修改失败");
+        } catch (Exception e) {
+            log.error("SportsEquipmentFacilityController.updateFacility异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("删除运动器材设施")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/delete")
+    public R<Boolean> deleteFacility(@RequestParam Integer id) {
+        log.info("SportsEquipmentFacilityController.deleteFacility?id={}", id);
+        try {
+            boolean result = facilityService.deleteFacility(id);
+            if (result) {
+                return R.success("删除成功");
+            }
+            return R.fail("删除失败");
+        } catch (Exception e) {
+            log.error("SportsEquipmentFacilityController.deleteFacility异常", e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、图片列表和设备列表)(用户端)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/categorySummary")
+    public R<List<SportsEquipmentFacilityCategoryVo>> getCategorySummary(@RequestParam Integer storeId) {
+        log.info("SportsEquipmentFacilityController.getCategorySummary?storeId={}", storeId);
+        return R.data(facilityService.getCategorySummary(storeId));
+    }
+
+
+
+    @ApiOperation("根据ID查询运动器材设施详情(商户端)")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/storeDetail")
+    public R<SportsEquipmentFacilityVo> getStoreDetail(@RequestParam Integer id) {
+        log.info("SportsEquipmentFacilityController.getStoreDetail?id={}", id);
+        SportsEquipmentFacilityVo vo = facilityService.getStoreDetail(id);
+        if (vo == null) {
+            return R.fail("数据不存在");
+        }
+        return R.data(vo);
+    }
+
+    @ApiOperation("查询指定店铺按分类汇总的设备信息(包含设备数量、图片列表和设备列表)(商户端)")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/storeCategorySummary")
+    public R<List<SportsEquipmentFacilityCategoryVo>> getstoreCategorySummary(@RequestParam Integer storeId) {
+        log.info("SportsEquipmentFacilityController.getstoreCategorySummary?storeId={}", storeId);
+        return R.data(facilityService.getstoreCategorySummary(storeId));
+    }
+}
+

+ 0 - 261
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -782,265 +782,4 @@ public class StoreInfoController {
         }
         return R.data(ocrData);
     }
-
-    /**
-     * 查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选
-     *
-     * @param lon         经度
-     * @param lat         纬度
-     * @param distance    距离范围(单位:公里)
-     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
-     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧需要查询字典表),可选,如果指定则只查询该类型
-     * @param pageNum     页码
-     * @param pageSize    页容
-     * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
-     */
-    @ApiOperation("查询四种类型店铺(酒吧、ktv、洗浴汗蒸、按摩足浴)并按距离筛选")
-    @ApiOperationSupport(order = 16)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
-            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query", required = true),
-            @ApiImplicitParam(name = "distance", value = "距离范围(单位:公里)", dataType = "Double", paramType = "query"),
-            @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
-            @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
-            @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
-    })
-    @GetMapping("/getSpecialTypeStoresByDistance")
-    public R<IPage<StoreInfoVo>> getSpecialTypeStoresByDistance(
-            @RequestParam("lon") Double lon,
-            @RequestParam("lat") Double lat,
-            @RequestParam(value = "distance", required = false) Double distance,
-            @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
-            @RequestParam(value = "businessType", required = false) Integer businessType,
-            @RequestParam(value = "categoryId", required = false) Integer categoryId,
-            @RequestParam(defaultValue = "1") int pageNum,
-            @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getSpecialTypeStoresByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
-
-        try {
-            // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
-            if (lon == null || lat == null) {
-                return R.fail("经纬度参数不能为空");
-            }
-
-            // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
-            if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
-                return R.fail(String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat));
-            }
-
-            if (lon < -180 || lon > 180) {
-                return R.fail("经度参数超出有效范围 [-180, 180],当前值: " + lon);
-            }
-            if (lat < -90 || lat > 90) {
-                return R.fail("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
-            }
-
-            // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getSpecialTypeStoresByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
-
-            // 记录响应日志
-            log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
-                    result.getTotal(), result.getCurrent(), result.getSize());
-
-            return R.data(result);
-        } catch (IllegalArgumentException e) {
-            log.warn("四种类型店铺距离筛选参数错误: {}", e.getMessage());
-            return R.fail(e.getMessage());
-        } catch (Exception e) {
-            log.error("四种类型店铺距离筛选异常", e);
-            return R.fail("筛选失败,请稍后重试");
-        }
-    }
-
-
-
-    /**
-     * 获取休闲娱乐分类数据(主分类和子分类)
-     * 根据图片需求,返回层级结构的分类数据
-     *
-     * @return R<List<StoreDictionaryVo>> 分类列表,包含主分类和子分类
-     */
-    @ApiOperation("获取休闲娱乐分类数据(主分类和子分类)")
-    @ApiOperationSupport(order = 18)
-    @GetMapping("/getLeisureEntertainmentCategories")
-    public R<List<StoreDictionaryVo>> getLeisureEntertainmentCategories() {
-        log.info("StoreInfoController.getLeisureEntertainmentCategories");
-        try {
-            List<StoreDictionaryVo> result = storeInfoService.getLeisureEntertainmentCategories();
-            return R.data(result);
-        } catch (Exception e) {
-            log.error("获取休闲娱乐分类数据异常", e);
-            return R.fail("获取分类数据失败,请稍后重试");
-        }
-    }
-
-    /**
-     * 获取休闲娱乐分类数据(主分类和子分类)
-     * 根据图片需求,返回层级结构的分类数据
-     *getAllBusinessSection
-     * @return R<List<StoreDictionary>> 分类列表,包含主分类和子分类
-     */
-
-
-    @ApiOperation("获取所有经营板块以及子类")
-    @ApiOperationSupport(order = 18)
-    @GetMapping("/getAllBusinessSection")
-    public R<List<StoreDictionary>> queryBusinessSectionTree(
-            @ApiParam(value = "一级分类dictId,可选,如果传入则只返回该一级分类下的二级和三级分类", required = false)
-            @RequestParam(required = false) String businessSection) {
-        log.info("platformBusinessSection.queryBusinessSectionTree, businessSection={}", businessSection);
-
-        try {
-            List<StoreDictionary> result = storeInfoService.getAllBusinessSection(businessSection);
-            return R.data(result);
-        }catch (Exception e){
-            log.error("获取休闲娱乐分类数据异常", e);
-            return R.fail("获取分类数据失败,请稍后重试");
-        }
-
-    }
-
-
-    @ApiOperation(value = "获取banner图详情")
-    @ApiOperationSupport(order = 7)
-    @GetMapping("/getBannerUrlInfo")
-    @ResponseBody
-    public R getBannerUrlInfo(@RequestParam("storeId") String storeId, @RequestParam("businessId") Integer businessId) {
-        log.info("StoreInfoController.getBannerUrlInfo?storeId={},businessId{}", storeId, businessId);
-
-        try {
-
-            return R.data(storeInfoService.getBannerUrlInfo(storeId, businessId));
-
-        } catch (Exception e) {
-
-            log.error("StoreInfoController.getBannerUrlInfo ERROR Msg={}", e.getMessage(), e);
-
-            return R.fail("查询失败");
-        }
-
-    }
-
-
-    /**
-     * 查询两种类型店铺(丽人美发、运动健身)并按距离筛选
-     *
-     * @param lon         经度
-     * @param lat         纬度
-     * @param distance    距离范围(单位:公里)
-     * @param sortType    排序模式(1:智能排序,2:好评优先,3:距离优先)
-     * @param businessType 店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7),可选,如果指定则只查询该类型
-     * @param pageNum     页码
-     * @param pageSize    页容
-     * @return R<IPage<StoreInfoVo>> 分页的门店信息列表
-     */
-    @ApiOperation("查询四种类型店铺(丽人美发、运动健身)并按距离筛选")
-    @ApiOperationSupport(order = 16)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query", required = true),
-            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query", required = true),
-            @ApiImplicitParam(name = "distance", value = "距离范围(单位:公里)", dataType = "Double", paramType = "query"),
-            @ApiImplicitParam(name = "sortType", value = "排序模式(1:智能排序,2:好评优先,3:距离优先)", dataType = "int", paramType = "query"),
-            @ApiImplicitParam(name = "businessType", value = "店铺类型(KTV=3、洗浴汗蒸=4、按摩足浴=5,丽人美发=6,运动健身=7,酒吧=11)", dataType = "int", paramType = "query"),
-            @ApiImplicitParam(name = "categoryId", value = "字典表id,根据此id查询经营板块、经营种类、分类并匹配店铺", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "int", paramType = "query", required = true),
-            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "int", paramType = "query", required = true)
-    })
-    @GetMapping("/getLifeServicesByDistance")
-    public R<IPage<StoreInfoVo>> getLifeServicesByDistance(
-            @RequestParam("lon") Double lon,
-            @RequestParam("lat") Double lat,
-            @RequestParam(value = "distance", required = false) Double distance,
-            @RequestParam(value = "sortType", required = false, defaultValue = "1") Integer sortType,
-            @RequestParam(value = "businessType", required = false) Integer businessType,
-            @RequestParam(value = "categoryId", required = false) Integer categoryId,
-            @RequestParam(defaultValue = "1") int pageNum,
-            @RequestParam(defaultValue = "10") int pageSize) {
-        log.info("StoreInfoController.getLifeServicesByDistance?lon={},lat={},distance={},sortType={},businessType={},categoryId={},pageNum={},pageSize={}",
-                lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
-
-        try {
-            // 参数校验:经度范围 [-180, 180],纬度范围 [-90, 90]
-            if (lon == null || lat == null) {
-                return R.fail("经纬度参数不能为空");
-            }
-
-            // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
-            if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
-                return R.fail(String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat));
-            }
-
-            if (lon < -180 || lon > 180) {
-                return R.fail("经度参数超出有效范围 [-180, 180],当前值: " + lon);
-            }
-            if (lat < -90 || lat > 90) {
-                return R.fail("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
-            }
-
-            // 调用服务层获取筛选结果
-            IPage<StoreInfoVo> result = storeInfoService.getLifeServicesByDistance(lon, lat, distance, sortType, businessType, categoryId, pageNum, pageSize);
-
-            // 记录响应日志
-            log.info("四种类型店铺距离筛选响应 - 总记录数: {}, 当前页: {}, 页大小: {}",
-                    result.getTotal(), result.getCurrent(), result.getSize());
-
-            return R.data(result);
-        } catch (IllegalArgumentException e) {
-            log.warn("四种类型店铺距离筛选参数错误: {}", e.getMessage());
-            return R.fail(e.getMessage());
-        } catch (Exception e) {
-            log.error("四种类型店铺距离筛选异常", e);
-            return R.fail("筛选失败,请稍后重试");
-        }
-    }
-
-
-
-    /**
-     * 你可能还喜欢(推荐店铺)
-     * 根据一级分类、二级分类、三级分类进行店铺筛选
-     * 筛选顺序:先三级,然后二级,最后一级
-     *
-     * @param businessSection 一级分类(经营板块)
-     * @param businessTypes 二级分类(经营种类)
-     * @param businessClassify 三级分类(分类)
-     * @param lon 经度
-     * @param lat 纬度
-     * @return R<List<StoreInfoVo>> 店铺信息列表
-     */
-    @ApiOperation("你可能还喜欢(推荐店铺)")
-    @ApiOperationSupport(order = 19)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "businessSection", value = "一级分类(经营板块)", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "businessTypes", value = "二级分类(经营种类)", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "businessClassify", value = "三级分类(分类)", dataType = "String", paramType = "query"),
-            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query"),
-            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query")
-    })
-    @GetMapping("/getRecommendedStores")
-    public R<List<StoreInfoVo>> getRecommendedStores(
-            @RequestParam(value = "businessSection", required = true) String businessSection,
-            @RequestParam(value = "businessTypes", required = false) String businessTypes,
-            @RequestParam(value = "businessClassify", required = false) String businessClassify,
-            @RequestParam(value = "lon", required = false) Double lon,
-            @RequestParam(value = "lat", required = false) Double lat) {
-        log.info("StoreInfoController.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}",
-                businessSection, businessTypes, businessClassify, lon, lat);
-        try {
-            List<StoreInfoVo> result = storeInfoService.getRecommendedStores(businessSection, businessTypes, businessClassify, lon, lat);
-            return R.data(result);
-        } catch (Exception e) {
-            log.error("获取推荐店铺异常", e);
-            return R.fail("获取推荐店铺失败,请稍后重试");
-        }
-    }
-
-
-
-
-
-
 }

+ 84 - 5
alien-store/src/main/java/shop/alien/store/controller/StoreMenuController.java

@@ -10,6 +10,7 @@ import shop.alien.entity.store.vo.StoreMenuVo;
 import shop.alien.store.service.StoreMenuService;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 二期-门店菜单Controller
@@ -32,13 +33,19 @@ public class StoreMenuController {
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({
             @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "dishType", value = "菜品类型, 0:菜单, 1:推荐", dataType = "Integer", paramType = "query"),
-            @ApiImplicitParam(name = "phoneId", value = "消息标识", dataType = "Integer", paramType = "query")
+            @ApiImplicitParam(name = "dishType", value = "菜品类型, 0:非推荐, 1:推荐", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "用户手机号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "dishMenuType", value = "菜单类型:1-菜单,2-酒水", dataType = "String", paramType = "query")
     })
     @GetMapping("/getMenuByStoreId")
-    public R<List<StoreMenuVo>> getMenuByStoreId(Integer storeId, Integer dishType, String phoneId) {
-        log.info("StoreRecommendController.getByStoreId?id={}&dishType={}&phoneId={}", storeId, dishType, phoneId);
-        return R.data(storeMenuService.getStoreMenu(storeId, dishType, phoneId));
+    public R<List<StoreMenuVo>> getMenuByStoreId(
+            @RequestParam(value = "storeId", required = true) Integer storeId,
+            @RequestParam(value = "dishType", required = false) Integer dishType,
+            @RequestParam(value = "phoneId", required = false) String phoneId,
+            @RequestParam(value = "dishMenuType", required = false) Integer dishMenuType) {
+        log.info("StoreMenuController.getMenuByStoreId?storeId={}&dishType={}&phoneId={}&dishMenuType={}",
+                storeId, dishType, phoneId, dishMenuType);
+        return R.data(storeMenuService.getStoreMenu(storeId, dishType, phoneId, dishMenuType));
     }
 
     @ApiOperation("新增或修改门店菜单")
@@ -116,4 +123,76 @@ public class StoreMenuController {
         log.info("StoreRecommendController.getMenuLikeStatus?userId={}&menuId={}", userId, menuId);
         return R.data(storeMenuService.getMenuLikeStatus(userId, menuId));
     }
+
+
+    /**
+     * 获取菜单(用户端)
+     * <p>
+     * 根据门店ID获取菜单列表,支持按菜品类型、菜单类型筛选,并可查询用户点赞状态
+     * 返回菜单列表以及按四种组合统计的数量:
+     * 1. menu_all: dish_menu_type=1(菜单),dish_type=0(全部)
+     * 2. menu_recommend: dish_menu_type=1(菜单),dish_type=1(推荐)
+     * 3. drink_all: dish_menu_type=2(酒水),dish_type=0(全部)
+     * 4. drink_recommend: dish_menu_type=2(酒水),dish_type=1(推荐)
+     * </p>
+     *
+     * @param storeId      门店ID,必填,必须大于0
+     * @param dishType     菜品类型,可选,0:非推荐, 1:推荐
+     * @param phoneId      用户手机号,可选,用于查询点赞状态
+     * @param dishMenuType 菜单类型,可选,1-菜单,2-酒水
+     * @return 统一返回结果,包含菜单列表和统计信息(list:菜单列表,count:按四种组合统计的数量)
+     * @throws IllegalArgumentException 当门店ID为空或小于等于0时抛出
+     */
+    @ApiOperation("获取菜单(用户端)")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "dishType", value = "菜品类型, 0:(推荐+非推荐), 1:推荐", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "用户手机号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "dishMenuType", value = "菜单类型:1-菜单,2-酒水", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getClientMenuByStoreId")
+    public R<Map<String, Object>> getClientMenuByStoreId(
+            @RequestParam(value = "storeId", required = true) Integer storeId,
+            @RequestParam(value = "dishType", required = false) Integer dishType,
+            @RequestParam(value = "phoneId", required = true) String phoneId,
+            @RequestParam(value = "dishMenuType", required = false) Integer dishMenuType) {
+        // 记录请求入参
+        log.info("获取用户端菜单,入参:storeId={}, dishType={}, phoneId={}, dishMenuType={}",
+                storeId, dishType, phoneId, dishMenuType);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取用户端菜单失败,门店ID无效:storeId={}", storeId);
+            return R.fail("门店ID不能为空且必须大于0");
+        }
+
+        try {
+            // 调用服务层获取菜单列表和统计信息
+            Map<String, Object> result = storeMenuService.getClientMenuByStoreId(
+                    storeId, dishType, phoneId, dishMenuType);
+
+            // 记录返回结果
+            @SuppressWarnings("unchecked")
+            List<StoreMenuVo> menuList = (List<StoreMenuVo>) result.get("list");
+            log.info("获取用户端菜单成功,门店ID:{},返回菜单数量:{},统计信息:{}",
+                    storeId, menuList != null ? menuList.size() : 0, result.get("count"));
+
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取用户端菜单异常,门店ID:{},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("获取菜单失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("获取门店菜单详情(用户端)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "菜单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "用户手机号", dataType = "String", paramType = "query")})
+    @GetMapping("/getClientMenuInfoById")
+    public R<StoreMenuVo> getClientMenuInfoById(@RequestParam(value = "id", required = true) Integer id,
+                                                @RequestParam(value = "phoneId", required = true) String phoneId) {
+        log.info("StoreRecommendController.getClientMenuInfoById?id={},phoneId={}", id, phoneId);
+        return R.data(storeMenuService.getClientMenuInfoById(id, phoneId));
+    }
 }

+ 90 - 4
alien-store/src/main/java/shop/alien/store/controller/StoreOfficialAlbumController.java

@@ -1,9 +1,6 @@
 package shop.alien.store.controller;
 
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiOperationSupport;
-import io.swagger.annotations.ApiSort;
+import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
@@ -11,6 +8,8 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreOfficialAlbum;
 import shop.alien.entity.store.vo.StoreOfficialAlbumVo;
 import shop.alien.store.service.StoreOfficialAlbumService;
+import shop.alien.entity.store.vo.StoreAlbumNameVo;
+import shop.alien.entity.store.vo.StoreOfficialAlbumImgVo;
 
 import java.util.List;
 
@@ -59,4 +58,91 @@ public class StoreOfficialAlbumController {
         }
         return R.fail("失败");
     }
+    /**
+     * 获取官方相册图片列表(客户端)
+     * <p>
+     * 根据门店ID和相册名称查询官方相册中的图片列表
+     * 查询条件:imgType = 2(官方相册),通过 business_id 关联到 store_official_album 表,按 albumName 筛选
+     * </p>
+     *
+     * @param storeId   门店ID,必填,必须大于0
+     * @param albumName 相册名称,可选。例如:酒水、餐食、环境、全部等。当为null或空字符串时,查询全部
+     * @return 统一返回结果,包含图片列表和总数
+     * @throws IllegalArgumentException 当门店ID为空或小于等于0时抛出
+     */
+    @ApiOperation("获取官方相册图片列表(客户端)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "albumName", value = "相册名称,例如:酒水、餐食、环境、全部等。不传或传空字符串时查询全部", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getOfficialAlbumImgList")
+    public R<StoreOfficialAlbumImgVo> getOfficialAlbumImgList(
+            @RequestParam(value = "storeId", required = true) Integer storeId,
+            @RequestParam(value = "albumName", required = false) String albumName) {
+        // 记录请求入参
+        log.info("获取官方相册图片列表,入参:storeId={}, albumName={}", storeId, albumName);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取官方相册图片列表失败,门店ID无效:storeId={}", storeId);
+            return R.fail("门店ID不能为空且必须大于0");
+        }
+
+        try {
+            // 调用服务层获取图片列表
+            StoreOfficialAlbumImgVo result = storeOfficialAlbumService.getOfficialAlbumImgList(storeId, albumName);
+
+            // 记录返回结果
+            log.info("获取官方相册图片列表成功,门店ID:{},相册名称:{},返回图片数量:{}",
+                    storeId, albumName, result.getTotalCount());
+
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取官方相册图片列表异常,门店ID:{},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("获取图片列表失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取官方相册名称列表(客户端)
+     * <p>
+     * 根据门店ID查询所有可用的相册名称列表,用于前端展示筛选选项
+     * 返回每个相册名称及其对应的图片数量
+     * </p>
+     *
+     * @param storeId 门店ID,必填,必须大于0
+     * @return 统一返回结果,包含相册名称列表和每个相册的图片数量
+     * @throws IllegalArgumentException 当门店ID为空或小于等于0时抛出
+     */
+    @ApiOperation("获取官方相册名称列表(客户端)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getAlbumNameList")
+    public R<List<StoreAlbumNameVo>> getAlbumNameList(
+            @RequestParam(value = "storeId", required = true) Integer storeId) {
+        // 记录请求入参
+        log.info("获取官方相册名称列表,入参:storeId={}", storeId);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取官方相册名称列表失败,门店ID无效:storeId={}", storeId);
+            return R.fail("门店ID不能为空且必须大于0");
+        }
+
+        try {
+            // 调用服务层获取相册名称列表
+            List<StoreAlbumNameVo> result = storeOfficialAlbumService.getAlbumNameList(storeId);
+
+            // 记录返回结果
+            log.info("获取官方相册名称列表成功,门店ID:{},返回相册数量:{}", storeId, result.size());
+
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取官方相册名称列表异常,门店ID:{},异常信息:{}", storeId, e.getMessage(), e);
+            return R.fail("获取相册名称列表失败:" + e.getMessage());
+        }
+    }
 }

+ 87 - 0
alien-store/src/main/java/shop/alien/store/controller/StorePersonnelController.java

@@ -0,0 +1,87 @@
+package shop.alien.store.controller;
+
+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.StorePersonnel;
+import shop.alien.entity.store.vo.StorePersonnelVo;
+import shop.alien.store.service.StorePersonnelService;
+
+import java.util.List;
+
+/**
+ * 店铺人员Controller
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+@Slf4j
+@Api(tags = {"店铺人员"})
+@ApiSort(5)
+@CrossOrigin
+@RestController
+@RequestMapping("/personnel")
+@RequiredArgsConstructor
+public class StorePersonnelController {
+
+    private final StorePersonnelService storePersonnelService;
+
+    @ApiOperation("获取店铺人员列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getPersonnelList")
+    public R<List<StorePersonnelVo>> getPersonnelList(
+            @RequestParam(value = "storeId", required = true) Integer storeId) {
+        log.info("StorePersonnelController.getPersonnelList?storeId={}", storeId);
+        return R.data(storePersonnelService.getStorePersonnelList(storeId));
+    }
+
+    @ApiOperation("获取人员详情")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "人员id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getPersonnelInfo")
+    public R<StorePersonnelVo> getPersonnelInfo(
+            @RequestParam(value = "id", required = true) Integer id) {
+        log.info("StorePersonnelController.getPersonnelInfo?id={}", id);
+        return R.data(storePersonnelService.getPersonnelInfo(id));
+    }
+
+    @ApiOperation("新增或修改店铺人员")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/saveOrUpdate")
+    public R<String> saveOrUpdate(@RequestBody StorePersonnelVo storePersonnelVo) {
+        log.info("StorePersonnelController.saveOrUpdate?storePersonnelVo={}", storePersonnelVo);
+        return storePersonnelService.saveOrUpdatePersonnel(storePersonnelVo);
+    }
+
+    @ApiOperation("删除店铺人员")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "ids", value = "人员id列表", dataType = "List<Integer>", paramType = "query", required = true)
+    })
+    @GetMapping("/delete")
+    public R<String> delete(@RequestParam(value = "ids") List<Integer> ids) {
+        log.info("StorePersonnelController.delete?ids={}", ids);
+        return storePersonnelService.deletePersonnel(ids);
+    }
+
+    @ApiOperation("保存人员排序")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/savePersonnelSort")
+    public R<Boolean> savePersonnelSort(@RequestBody List<StorePersonnel> storePersonnelList) {
+        log.info("StorePersonnelController.savePersonnelSort?storePersonnelList={}", storePersonnelList);
+        Boolean flag = storePersonnelService.savePersonnelSort(storePersonnelList);
+        if (flag) {
+            return R.success("已更新排序");
+        } else {
+            return R.fail("排序失败");
+        }
+    }
+}
+

+ 48 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffConfigController.java

@@ -78,4 +78,52 @@ public class StoreStaffConfigController {
         return R.data(s);
     }
 
+    /**
+     * 员工列表查询接口(用户端)
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param storeId 店铺ID
+     * @param status  员工状态
+     * @return 员工列表
+     */
+    @ApiOperation("员工列表查询(用户端)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "分页页数", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "size", value = "分页条数", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "storeId", value = "店铺ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "员工状态(0-待审核 1-审核通过 2-审核拒绝)", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/queryStaffList")
+    public R<IPage<StoreStaffConfig>> queryStaffList(
+            @RequestParam(value = "page", defaultValue = "1") Integer page,
+            @RequestParam(value = "size", defaultValue = "100") Integer size,
+            @RequestParam(value = "storeId", required = true) Integer storeId,
+            @RequestParam(value = "status", required = false) String status) {
+        log.info("StoreStaffConfigController.queryStaffList?page={}&size={}&storeId={}&status={}", page, size, storeId, status);
+        IPage<StoreStaffConfig> result = storeStaffConfigService.queryStaffList(page, size, storeId, status);
+        return R.data(result);
+    }
+
+    /**
+     * 员工详情查询接口(用户端)
+     *
+     * @param id 员工主键id
+     * @return 员工详情
+     */
+    @ApiOperation("员工详情查询(用户端)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "员工主键id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/queryStaffDetail")
+    public R<StoreStaffConfig> queryStaffDetail(@RequestParam(value = "id", required = true) Integer id) {
+        log.info("StoreStaffConfigController.queryStaffDetail?id={}", id);
+        StoreStaffConfig result = storeStaffConfigService.queryStaffDetail(id);
+        if (result == null) {
+            return R.fail("员工不存在");
+        }
+        return R.data(result);
+    }
 }

+ 82 - 0
alien-store/src/main/java/shop/alien/store/service/BathFacilityServiceService.java

@@ -0,0 +1,82 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.BathFacilityService;
+import shop.alien.entity.store.vo.BathFacilityServiceCategoryVo;
+import shop.alien.entity.store.vo.BathFacilityServiceVo;
+
+import java.util.List;
+
+/**
+ * 洗浴设施及服务服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface BathFacilityServiceService extends IService<BathFacilityService> {
+
+    /**
+     * 分页查询洗浴设施及服务列表
+     *
+     * @param pageNum          页码
+     * @param pageSize         页大小
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类
+     * @return IPage<BathFacilityServiceVo>
+     */
+    IPage<BathFacilityServiceVo> getPageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory);
+
+    /**
+     * 查询洗浴设施及服务列表
+     *
+     * @param storeId          门店ID
+     * @param facilityCategory 设施分类
+     * @return List<BathFacilityServiceVo>
+     */
+    List<BathFacilityServiceVo> getList(Integer storeId, Integer facilityCategory);
+
+    /**
+     * 根据ID查询详情
+     *
+     * @param id 主键ID
+     * @return BathFacilityServiceVo
+     */
+    BathFacilityServiceVo getDetail(Integer id);
+
+    /**
+     * 新增洗浴设施及服务
+     *
+     * @param facilityService 设施服务信息
+     * @param imageList       图片列表
+     * @return boolean
+     */
+    boolean saveFacilityService(BathFacilityService facilityService, List<String> imageList);
+
+    /**
+     * 修改洗浴设施及服务
+     *
+     * @param facilityService 设施服务信息
+     * @param imageList       图片列表
+     * @return boolean
+     */
+    boolean updateFacilityService(BathFacilityService facilityService, List<String> imageList);
+
+    /**
+     * 删除洗浴设施及服务
+     *
+     * @param id 主键ID
+     * @return boolean
+     */
+    boolean deleteFacilityService(Integer id);
+
+    /**
+     * 查询指定店铺按分类汇总的设备信息
+     * 包含每个分类的设备数量和设备列表(包括设备信息和图片)
+     *
+     * @param storeId 门店ID
+     * @return List<BathFacilityServiceCategoryVo>
+     */
+    List<BathFacilityServiceCategoryVo> getCategorySummary(Integer storeId);
+}
+

+ 49 - 44
alien-store/src/main/java/shop/alien/store/service/LifeCommentService.java

@@ -54,6 +54,7 @@ public class LifeCommentService {
         updateWrapper.eq(LifeLikeRecord::getType, type);
         updateWrapper.eq(LifeLikeRecord::getDianzanId, userId);
         updateWrapper.eq(LifeLikeRecord::getHuifuId, huifuId);
+        updateWrapper.eq(LifeLikeRecord::getDeleteFlag, 0);
         List<LifeLikeRecord> record = lifeLikeRecordMapper.selectList(updateWrapper);
         if (CollectionUtils.isEmpty(record)) {
             LifeLikeRecord lifeLikeRecord = new LifeLikeRecord();
@@ -62,39 +63,40 @@ public class LifeCommentService {
             lifeLikeRecord.setDianzanId(userId);
             lifeLikeRecord.setType(type);
             lifeLikeRecordMapper.insert(lifeLikeRecord);
-        }
-        if ("1".equals(type)) {
-            LambdaUpdateWrapper<StoreComment> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(StoreComment::getId, huifuId);
-            lambdaUpdateWrapper.setSql("like_count = like_count + 1");
-            return storeCommentMapper.update(null, lambdaUpdateWrapper);
-        } else if ("2".equals(type)) {
-            LambdaUpdateWrapper<LifeUserDynamics> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(LifeUserDynamics::getId, huifuId);
-            lambdaUpdateWrapper.setSql("dianzan_count = dianzan_count + 1");
-            int num = lifeUserDynamicsMapper.update(null, lambdaUpdateWrapper);
-            if (num > 0) insertNotice(userId, huifuId, type);
-            return num;
-        } else if ("3".equals(type)) {
-            LambdaUpdateWrapper<LifeActivity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(LifeActivity::getId, huifuId);
-            lambdaUpdateWrapper.setSql("dianzan_count = dianzan_count + 1");
-            return lifeActivityMapper.update(null, lambdaUpdateWrapper);
-        } else if ("4".equals(type)) {
-            LambdaUpdateWrapper<StoreMenu> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(StoreMenu::getId, huifuId);
-            lambdaUpdateWrapper.setSql("like_count = like_count + 1");
-            return storeRecommendMapper.update(null, lambdaUpdateWrapper);
-        } else if ("5".equals(type)) {
-            LambdaUpdateWrapper<StoreClockIn> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(StoreClockIn::getId, huifuId);
-            lambdaUpdateWrapper.setSql("like_count = like_count + 1");
-            return storeClockInMapper.update(null, lambdaUpdateWrapper);
-        } else if ("6".equals(type)) {
-            LambdaUpdateWrapper<SecondGoods> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(SecondGoods::getId, huifuId);
-            lambdaUpdateWrapper.setSql("like_count = like_count + 1");
-            return secondGoodsMapper.update(null, lambdaUpdateWrapper);
+
+            if ("1".equals(type)) {
+                LambdaUpdateWrapper<StoreComment> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(StoreComment::getId, huifuId);
+                lambdaUpdateWrapper.setSql("like_count = like_count + 1");
+                return storeCommentMapper.update(null, lambdaUpdateWrapper);
+            } else if ("2".equals(type)) {
+                LambdaUpdateWrapper<LifeUserDynamics> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(LifeUserDynamics::getId, huifuId);
+                lambdaUpdateWrapper.setSql("dianzan_count = dianzan_count + 1");
+                int num = lifeUserDynamicsMapper.update(null, lambdaUpdateWrapper);
+                if (num > 0) insertNotice(userId, huifuId, type);
+                return num;
+            } else if ("3".equals(type)) {
+                LambdaUpdateWrapper<LifeActivity> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(LifeActivity::getId, huifuId);
+                lambdaUpdateWrapper.setSql("dianzan_count = dianzan_count + 1");
+                return lifeActivityMapper.update(null, lambdaUpdateWrapper);
+            } else if ("4".equals(type)) {
+                LambdaUpdateWrapper<StoreMenu> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(StoreMenu::getId, huifuId);
+                lambdaUpdateWrapper.setSql("like_count = like_count + 1");
+                return storeRecommendMapper.update(null, lambdaUpdateWrapper);
+            } else if ("5".equals(type)) {
+                LambdaUpdateWrapper<StoreClockIn> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(StoreClockIn::getId, huifuId);
+                lambdaUpdateWrapper.setSql("like_count = like_count + 1");
+                return storeClockInMapper.update(null, lambdaUpdateWrapper);
+            } else if ("6".equals(type)) {
+                LambdaUpdateWrapper<SecondGoods> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(SecondGoods::getId, huifuId);
+                lambdaUpdateWrapper.setSql("like_count = like_count + 1");
+                return secondGoodsMapper.update(null, lambdaUpdateWrapper);
+            }
         }
         return 0;
     }
@@ -117,17 +119,19 @@ public class LifeCommentService {
     }
 
     public int cancelLike(String userId, String huifuId, String type) {
-        LambdaUpdateWrapper<LifeLikeRecord> updateWrapper = new LambdaUpdateWrapper<>();
-        updateWrapper.eq(LifeLikeRecord::getDianzanId, userId);
-        updateWrapper.eq(LifeLikeRecord::getHuifuId, huifuId);
-        List<LifeLikeRecord> record = lifeLikeRecordMapper.selectList(updateWrapper);
+        LambdaQueryWrapper<LifeLikeRecord> lifeLikeRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lifeLikeRecordLambdaQueryWrapper.eq(LifeLikeRecord::getDianzanId, userId);
+        lifeLikeRecordLambdaQueryWrapper.eq(LifeLikeRecord::getHuifuId, huifuId);
+        lifeLikeRecordLambdaQueryWrapper.eq(LifeLikeRecord::getType, type);
+        lifeLikeRecordLambdaQueryWrapper.eq(LifeLikeRecord::getDeleteFlag, 0);
+        List<LifeLikeRecord> record = lifeLikeRecordMapper.selectList(lifeLikeRecordLambdaQueryWrapper);
         if (!CollectionUtils.isEmpty(record)) {
-            LambdaUpdateWrapper<LifeLikeRecord> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
-            lambdaUpdateWrapper.eq(LifeLikeRecord::getHuifuId, huifuId);
-            lambdaUpdateWrapper.eq(LifeLikeRecord::getDianzanId, userId);
-            lambdaUpdateWrapper.eq(LifeLikeRecord::getType, type);
-            lifeLikeRecordMapper.delete(lambdaUpdateWrapper);
-        }
+            LambdaUpdateWrapper<LifeLikeRecord> lifeLikeRecordLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+            lifeLikeRecordLambdaUpdateWrapper.eq(LifeLikeRecord::getHuifuId, huifuId);
+            lifeLikeRecordLambdaUpdateWrapper.eq(LifeLikeRecord::getDianzanId, userId);
+            lifeLikeRecordLambdaUpdateWrapper.eq(LifeLikeRecord::getType, type);
+            lifeLikeRecordMapper.delete(lifeLikeRecordLambdaUpdateWrapper);
+
         if ("1".equals(type)) {
             LambdaUpdateWrapper<StoreComment> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
             lambdaUpdateWrapper.eq(StoreComment::getId, huifuId).gt(StoreComment::getLikeCount, 0);
@@ -159,8 +163,9 @@ public class LifeCommentService {
             lambdaUpdateWrapper.setSql("like_count = like_count - 1");
             return secondGoodsMapper.update(null, lambdaUpdateWrapper);
         }
-        return 0;
     }
+        return 0;
+}
 
     public int addOrUpdateStore(LifeComment store) {
         if (StringUtils.isEmpty(store.getId())) {

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

@@ -0,0 +1,86 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.SportsEquipmentFacility;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategoryVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityVo;
+
+import java.util.List;
+
+/**
+ * 运动器材设施服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface SportsEquipmentFacilityService extends IService<SportsEquipmentFacility> {
+
+    /**
+     * 分页查询运动器材设施列表
+     *
+     * @param pageNum          页码
+     * @param pageSize         页大小
+     * @param storeId           门店ID
+     * @param facilityCategory 设施分类
+     * @return IPage<SportsEquipmentFacilityVo>
+     */
+    IPage<SportsEquipmentFacilityVo> getPageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory);
+
+    /**
+     * 查询运动器材设施列表
+     *
+     * @param storeId           门店ID
+     * @param facilityCategory 设施分类
+     * @return List<SportsEquipmentFacilityVo>
+     */
+    List<SportsEquipmentFacilityVo> getList(Integer storeId, Integer facilityCategory);
+
+    /**
+     * 根据ID查询详情
+     *
+     * @param id 主键ID
+     * @return SportsEquipmentFacilityVo
+     */
+    SportsEquipmentFacilityVo getDetail(Integer id);
+
+    /**
+     * 新增运动器材设施
+     *
+     * @param facility 设施信息
+     * @param imageList 图片列表
+     * @return boolean
+     */
+    boolean saveFacility(SportsEquipmentFacility facility, List<String> imageList);
+
+    /**
+     * 修改运动器材设施
+     *
+     * @param facility 设施信息
+     * @param imageList 图片列表
+     * @return boolean
+     */
+    boolean updateFacility(SportsEquipmentFacility facility, List<String> imageList);
+
+    /**
+     * 删除运动器材设施
+     *
+     * @param id 主键ID
+     * @return boolean
+     */
+    boolean deleteFacility(Integer id);
+
+    /**
+     * 查询指定店铺按分类汇总的设备信息
+     * 包含每个分类的设备数量、图片列表和设备列表
+     *
+     * @param storeId 门店ID
+     * @return List<SportsEquipmentFacilityCategoryVo>
+     */
+    List<SportsEquipmentFacilityCategoryVo> getCategorySummary(Integer storeId);
+
+    SportsEquipmentFacilityVo getStoreDetail(Integer id);
+
+    List<SportsEquipmentFacilityCategoryVo> getstoreCategorySummary(Integer storeId);
+}
+

+ 14 - 0
alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java

@@ -381,4 +381,18 @@ public interface StoreInfoService extends IService<StoreInfo> {
 
     List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId);
 
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify);
+
+
+    /**
+     * web端查询门店明细
+     */
+    StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu);
+
 }

+ 32 - 4
alien-store/src/main/java/shop/alien/store/service/StoreMenuService.java

@@ -19,12 +19,14 @@ public interface StoreMenuService extends IService<StoreMenu> {
     /**
      * 获取门店菜单
      *
-     * @param storeId  门店id
-     * @param dishType 菜品类型, 0:菜单, 1:推荐
-     * @param phoneId  消息标识
+     * @param storeId      门店id
+     * @param dishType     菜品类型, 0:非推荐, 1:推荐
+     * @param phoneId      用户手机号
+     * @param dishMenuType 菜单类型:1-菜单,2-酒水
      * @return list
      */
-    List<StoreMenuVo> getStoreMenu(Integer storeId, Integer dishType, String phoneId);
+    List<StoreMenuVo> getStoreMenu(Integer storeId, Integer dishType, String phoneId, Integer dishMenuType);
+
 
     /**
      * 获取菜品详情
@@ -76,5 +78,31 @@ public interface StoreMenuService extends IService<StoreMenu> {
      * @return boolean
      */
     boolean getMenuLikeStatus(String userId, Integer menuId);
+
+
+    /**
+     * 获取门店菜单(客户端)
+     * <p>
+     * 根据门店ID查询菜单列表,支持按菜品类型和菜单类型筛选。
+     * 当查询推荐菜且提供用户手机号时,会批量查询并设置用户的点赞状态。
+     * 最终结果按排序字段升序排列。
+     * </p>
+     *
+     * @param storeId      门店ID,必填,必须大于0
+     * @param dishType     菜品类型,可选,0:非推荐, 1:推荐。当为0时,查询条件中不包含菜品类型限制
+     * @param phoneId      用户手机号,可选,用于查询用户对推荐菜的点赞状态
+     * @param dishMenuType 菜单类型,可选,1-菜单,2-酒水
+     * @return Map包含菜单列表和统计信息:list(菜单列表)、count(按四种组合统计:menu_all、menu_recommend、drink_all、drink_recommend)
+     * @throws IllegalArgumentException 当门店ID为空或小于等于0时抛出
+     */
+    java.util.Map<String, Object> getClientMenuByStoreId(Integer storeId, Integer dishType, String phoneId, Integer dishMenuType);
+
+    /**
+     * 获取菜品详情
+     *
+     * @param id 菜品id
+     * @return StoreMenuVo
+     */
+    StoreMenuVo getClientMenuInfoById(Integer id, String phoneId);
 }
 

+ 25 - 1
alien-store/src/main/java/shop/alien/store/service/StoreOfficialAlbumService.java

@@ -13,4 +13,28 @@ public interface StoreOfficialAlbumService extends IService<StoreOfficialAlbum>
     StoreOfficialAlbum createOrUpdateOfficialAlbum(StoreOfficialAlbum storeOfficialAlbum);
     List<StoreOfficialAlbumVo> getOfficialAlbumList(String storeId);
     int deleteOfficialAlbum(List<StoreOfficialAlbum> storeOfficialAlbumList);
-}
+
+    /**
+     * 获取官方相册图片列表(客户端)
+     * <p>
+     * 根据门店ID和相册名称查询官方相册中的图片列表
+     * 查询条件:imgType = 2(官方相册),通过 business_id 关联到 store_official_album 表,按 albumName 筛选
+     * </p>
+     *
+     * @param storeId   门店ID,必填
+     * @param albumName 相册名称,可选。例如:酒水、餐食、环境、全部等。当为null或空字符串时,查询全部
+     * @return 图片列表和总数
+     */
+    shop.alien.entity.store.vo.StoreOfficialAlbumImgVo getOfficialAlbumImgList(Integer storeId, String albumName);
+
+    /**
+     * 获取官方相册名称列表(客户端)
+     * <p>
+     * 根据门店ID查询所有可用的相册名称列表,用于前端展示筛选选项
+     * </p>
+     *
+     * @param storeId 门店ID,必填
+     * @return 相册名称列表,包含相册名称和图片数量
+     */
+    List<shop.alien.entity.store.vo.StoreAlbumNameVo> getAlbumNameList(Integer storeId);
+}

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

@@ -0,0 +1,58 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StorePersonnel;
+import shop.alien.entity.store.vo.StorePersonnelVo;
+
+import java.util.List;
+
+/**
+ * 店铺人员服务类
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+public interface StorePersonnelService extends IService<StorePersonnel> {
+
+    /**
+     * 获取店铺人员列表
+     *
+     * @param storeId 门店id
+     * @return List<StorePersonnelVo>
+     */
+    List<StorePersonnelVo> getStorePersonnelList(Integer storeId);
+
+    /**
+     * 获取人员详情
+     *
+     * @param id 人员id
+     * @return StorePersonnelVo
+     */
+    StorePersonnelVo getPersonnelInfo(Integer id);
+
+    /**
+     * 新增或修改店铺人员
+     *
+     * @param storePersonnelVo 人员信息
+     * @return R<String>
+     */
+    R<String> saveOrUpdatePersonnel(StorePersonnelVo storePersonnelVo);
+
+    /**
+     * 删除店铺人员
+     *
+     * @param ids 人员id列表
+     * @return R<String>
+     */
+    R<String> deletePersonnel(List<Integer> ids);
+
+    /**
+     * 保存人员排序
+     *
+     * @param storePersonnelList 人员列表
+     * @return Boolean
+     */
+    Boolean savePersonnelSort(List<StorePersonnel> storePersonnelList);
+}
+

+ 19 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffConfigService.java

@@ -17,4 +17,23 @@ public interface StoreStaffConfigService {
     StoreStaffConfig getStaffConfigDeatail(Integer id);
 
     String staffConfigExport(String status) throws IOException;
+
+    /**
+     * 员工列表查询
+     *
+     * @param page    分页页数
+     * @param size    分页条数
+     * @param storeId 店铺ID
+     * @param status  员工状态
+     * @return 员工列表
+     */
+    IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status);
+
+    /**
+     * 员工详情查询
+     *
+     * @param id 员工主键id
+     * @return 员工详情
+     */
+    StoreStaffConfig queryStaffDetail(Integer id);
 }

+ 258 - 0
alien-store/src/main/java/shop/alien/store/service/impl/BathFacilityServiceServiceImpl.java

@@ -0,0 +1,258 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.store.BathFacilityService;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.vo.BathFacilityServiceCategoryVo;
+import shop.alien.entity.store.vo.BathFacilityServiceVo;
+import shop.alien.mapper.BathFacilityServiceMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.store.service.BathFacilityServiceService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 洗浴设施及服务服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class BathFacilityServiceServiceImpl extends ServiceImpl<BathFacilityServiceMapper, BathFacilityService>
+        implements BathFacilityServiceService {
+
+    private final BathFacilityServiceMapper facilityServiceMapper;
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 设施分类名称映射
+     */
+    private static final String[] FACILITY_CATEGORY_NAMES = {"", "洗浴区", "汗蒸区", "休闲区", "餐饮区"};
+
+    /**
+     * 洗浴设施及服务图片类型
+     */
+    private static final Integer IMG_TYPE_BATH_FACILITY = 29;
+
+    @Override
+    public IPage<BathFacilityServiceVo> getPageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory) {
+        Page<BathFacilityService> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<BathFacilityService> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BathFacilityService::getStoreId, storeId);
+        if (facilityCategory != null) {
+            queryWrapper.eq(BathFacilityService::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByDesc(BathFacilityService::getCreatedTime);
+        IPage<BathFacilityService> facilityServicePage = facilityServiceMapper.selectPage(page, queryWrapper);
+        return facilityServicePage.convert(this::convertToVo);
+    }
+
+    @Override
+    public List<BathFacilityServiceVo> getList(Integer storeId, Integer facilityCategory) {
+        LambdaQueryWrapper<BathFacilityService> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BathFacilityService::getStoreId, storeId);
+        if (facilityCategory != null) {
+            queryWrapper.eq(BathFacilityService::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByDesc(BathFacilityService::getCreatedTime);
+        List<BathFacilityService> facilityServiceList = facilityServiceMapper.selectList(queryWrapper);
+        return facilityServiceList.stream().map(this::convertToVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public BathFacilityServiceVo getDetail(Integer id) {
+        BathFacilityService facilityService = facilityServiceMapper.selectById(id);
+        if (facilityService == null) {
+            return null;
+        }
+        return convertToVo(facilityService);
+    }
+
+    @Override
+    public boolean saveFacilityService(BathFacilityService facilityService, List<String> imageList) {
+        // 校验使用时间
+        validateUsageTime(facilityService);
+        // 保存设施服务信息
+        boolean result = this.save(facilityService);
+        if (result && !CollectionUtils.isEmpty(imageList)) {
+            // 保存图片
+            saveImages(facilityService.getId(), facilityService.getStoreId(), imageList);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean updateFacilityService(BathFacilityService facilityService, List<String> imageList) {
+        // 校验使用时间
+        validateUsageTime(facilityService);
+        // 更新设施服务信息
+        boolean result = this.updateById(facilityService);
+        if (result && !CollectionUtils.isEmpty(imageList)) {
+            // 删除旧图片
+            LambdaQueryWrapper<StoreImg> deleteWrapper = new LambdaQueryWrapper<>();
+            deleteWrapper.eq(StoreImg::getBusinessId, facilityService.getId())
+                    .eq(StoreImg::getImgType, IMG_TYPE_BATH_FACILITY);
+            storeImgMapper.delete(deleteWrapper);
+            // 保存新图片
+            saveImages(facilityService.getId(), facilityService.getStoreId(), imageList);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean deleteFacilityService(Integer id) {
+        // 删除图片
+        LambdaQueryWrapper<StoreImg> deleteWrapper = new LambdaQueryWrapper<>();
+        deleteWrapper.eq(StoreImg::getBusinessId, id)
+                .eq(StoreImg::getImgType, IMG_TYPE_BATH_FACILITY);
+        storeImgMapper.delete(deleteWrapper);
+        // 删除设施服务
+        return this.removeById(id);
+    }
+
+    /**
+     * 校验使用时间
+     */
+    private void validateUsageTime(BathFacilityService facilityService) {
+        if (facilityService.getUsageTimeType() == null) {
+            throw new RuntimeException("使用时间类型不能为空");
+        }
+        // 如果选择时间,需要填写开始时间和结束时间
+        if (facilityService.getUsageTimeType() == 1) {
+            if (!StringUtils.hasText(facilityService.getUsageStartTime())
+                    || !StringUtils.hasText(facilityService.getUsageEndTime())) {
+                throw new RuntimeException("选择时间时,开始时间和结束时间不能为空");
+            }
+            // 校验时间格式
+            if (!facilityService.getUsageStartTime().matches("^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$")
+                    || !facilityService.getUsageEndTime().matches("^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$")) {
+                throw new RuntimeException("时间格式错误,请使用HH:mm格式");
+            }
+        }
+    }
+
+    /**
+     * 保存图片
+     */
+    private void saveImages(Integer facilityServiceId, Integer storeId, List<String> imageList) {
+        for (int i = 0; i < imageList.size(); i++) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(IMG_TYPE_BATH_FACILITY);
+            storeImg.setBusinessId(facilityServiceId);
+            storeImg.setImgUrl(imageList.get(i));
+            storeImg.setImgSort(i + 1);
+            storeImg.setImgDescription("洗浴设施及服务图片");
+            storeImgMapper.insert(storeImg);
+        }
+    }
+
+    /**
+     * 转换为VO对象
+     */
+    private BathFacilityServiceVo convertToVo(BathFacilityService facilityService) {
+        BathFacilityServiceVo vo = new BathFacilityServiceVo();
+        BeanUtils.copyProperties(facilityService, vo);
+        // 设置分类名称
+        if (facilityService.getFacilityCategory() != null && facilityService.getFacilityCategory() > 0
+                && facilityService.getFacilityCategory() < FACILITY_CATEGORY_NAMES.length) {
+            vo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[facilityService.getFacilityCategory()]);
+        }
+        // 设置使用时间类型文本
+        if (facilityService.getUsageTimeType() != null) {
+            vo.setUsageTimeTypeText(facilityService.getUsageTimeType() == 0 ? "全天" : "选择时间");
+            // 设置使用时间范围
+            if (facilityService.getUsageTimeType() == 1
+                    && StringUtils.hasText(facilityService.getUsageStartTime())
+                    && StringUtils.hasText(facilityService.getUsageEndTime())) {
+                vo.setUsageTimeRange(facilityService.getUsageStartTime() + "-" + facilityService.getUsageEndTime());
+            }
+        }
+        // 设置显示状态文本
+        if (facilityService.getDisplayInStoreDetail() != null) {
+            vo.setDisplayInStoreDetailText(facilityService.getDisplayInStoreDetail() == 1 ? "显示" : "隐藏");
+        }
+        // 查询图片列表
+        LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+        imageWrapper.eq(StoreImg::getBusinessId, facilityService.getId())
+                .eq(StoreImg::getImgType, IMG_TYPE_BATH_FACILITY);
+        imageWrapper.orderByAsc(StoreImg::getImgSort);
+        List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+        if (!CollectionUtils.isEmpty(imageList)) {
+            vo.setImageList(imageList.stream().map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList()));
+        } else {
+            vo.setImageList(new ArrayList<>());
+        }
+        return vo;
+    }
+
+    @Override
+    public List<BathFacilityServiceCategoryVo> getCategorySummary(Integer storeId) {
+        List<BathFacilityServiceCategoryVo> result = new ArrayList<>();
+        
+        // 遍历所有分类(1:洗浴区, 2:汗蒸区, 3:休闲区, 4:餐饮区)
+        for (int category = 1; category < FACILITY_CATEGORY_NAMES.length; category++) {
+            BathFacilityServiceCategoryVo categoryVo = new BathFacilityServiceCategoryVo();
+            categoryVo.setFacilityCategory(category);
+            categoryVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+            
+            // 查询该分类下的设备列表(不分页)
+            LambdaQueryWrapper<BathFacilityService> facilityWrapper = new LambdaQueryWrapper<>();
+            facilityWrapper.eq(BathFacilityService::getStoreId, storeId)
+                    .eq(BathFacilityService::getFacilityCategory, category);
+            facilityWrapper.orderByDesc(BathFacilityService::getCreatedTime);
+            List<BathFacilityService> facilityServiceList = facilityServiceMapper.selectList(facilityWrapper);
+            
+            // 设置设备数量
+            categoryVo.setFacilityCount(facilityServiceList != null ? facilityServiceList.size() : 0);
+            
+            // 查询该分类的代表图片(从第一个设备的第一张图片获取)
+            if (!CollectionUtils.isEmpty(facilityServiceList)) {
+                BathFacilityService firstFacility = facilityServiceList.get(0);
+                // 查询该设备的第一张图片
+                LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+                imageWrapper.eq(StoreImg::getStoreId, storeId)
+                        .eq(StoreImg::getBusinessId, firstFacility.getId())
+                        .eq(StoreImg::getImgType, IMG_TYPE_BATH_FACILITY);
+                imageWrapper.orderByAsc(StoreImg::getImgSort);
+                imageWrapper.last("LIMIT 1");
+                StoreImg firstImage = storeImgMapper.selectOne(imageWrapper);
+                if (firstImage != null) {
+                    categoryVo.setCategoryImage(firstImage.getImgUrl());
+                }
+            }
+            
+            // 转换为VO列表(包含设备信息和图片)
+            List<BathFacilityServiceVo> facilityVoList = new ArrayList<>();
+            if (!CollectionUtils.isEmpty(facilityServiceList)) {
+                for (BathFacilityService facilityService : facilityServiceList) {
+                    // 使用现有的convertToVo方法,它会自动查询图片
+                    BathFacilityServiceVo vo = convertToVo(facilityService);
+                    facilityVoList.add(vo);
+                }
+            }
+            categoryVo.setFacilityList(facilityVoList);
+            
+            result.add(categoryVo);
+        }
+        
+        return result;
+    }
+}
+

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

@@ -0,0 +1,307 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.store.SportsEquipmentFacility;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityCategoryVo;
+import shop.alien.entity.store.vo.SportsEquipmentFacilityVo;
+import shop.alien.mapper.SportsEquipmentFacilityMapper;
+import shop.alien.mapper.StoreImgMapper;
+import shop.alien.store.service.SportsEquipmentFacilityService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 运动器材设施服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class SportsEquipmentFacilityServiceImpl extends ServiceImpl<SportsEquipmentFacilityMapper, SportsEquipmentFacility>
+        implements SportsEquipmentFacilityService {
+
+    private final SportsEquipmentFacilityMapper facilityMapper;
+    private final StoreImgMapper storeImgMapper;
+
+    /**
+     * 运动器材设施图片类型
+     */
+    private static final Integer IMG_TYPE_SPORTS_EQUIPMENT = 28;
+
+    /**
+     * 设施分类名称映射
+     */
+    private static final String[] FACILITY_CATEGORY_NAMES = {"", "有氧区", "力量区", "单功能机械区"};
+
+    @Override
+    public IPage<SportsEquipmentFacilityVo> getPageList(Integer pageNum, Integer pageSize, Integer storeId, Integer facilityCategory) {
+        Page<SportsEquipmentFacility> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<SportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SportsEquipmentFacility::getStoreId, storeId);
+        if (facilityCategory != null) {
+            queryWrapper.eq(SportsEquipmentFacility::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByDesc(SportsEquipmentFacility::getCreatedTime);
+        IPage<SportsEquipmentFacility> facilityPage = facilityMapper.selectPage(page, queryWrapper);
+        return facilityPage.convert(this::convertToVo);
+    }
+
+    @Override
+    public List<SportsEquipmentFacilityVo> getList(Integer storeId, Integer facilityCategory) {
+        LambdaQueryWrapper<SportsEquipmentFacility> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SportsEquipmentFacility::getStoreId, storeId);
+        if (facilityCategory != null) {
+            queryWrapper.eq(SportsEquipmentFacility::getFacilityCategory, facilityCategory);
+        }
+        queryWrapper.orderByDesc(SportsEquipmentFacility::getCreatedTime);
+        List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(queryWrapper);
+        return facilityList.stream().map(this::convertToVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public SportsEquipmentFacilityVo getDetail(Integer id) {
+        SportsEquipmentFacility facility = facilityMapper.selectById(id);
+        if (facility == null) {
+            return null;
+        }
+        return convertToVo(facility);
+    }
+
+    @Override
+    public SportsEquipmentFacilityVo getStoreDetail(Integer id) {
+        SportsEquipmentFacility facility = facilityMapper.selectById(id);
+        if (facility == null) {
+            return null;
+        }
+        return convertToVo(facility);
+    }
+
+    @Override
+    public boolean saveFacility(SportsEquipmentFacility facility, List<String> imageList) {
+        // 校验图片数量(最多20张)
+        if (!CollectionUtils.isEmpty(imageList) && imageList.size() > 20) {
+            throw new RuntimeException("实景图片最多上传20张");
+        }
+        // 保存设施信息
+        boolean result = this.save(facility);
+        if (result && !CollectionUtils.isEmpty(imageList) && facility.getFacilityCategory() != null) {
+            // 删除该分类下的旧图片(如果存在)
+            LambdaQueryWrapper<StoreImg> deleteWrapper = new LambdaQueryWrapper<>();
+            deleteWrapper.eq(StoreImg::getStoreId, facility.getStoreId())
+                    .eq(StoreImg::getBusinessId, facility.getFacilityCategory())
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+            storeImgMapper.delete(deleteWrapper);
+            // 保存新图片,business_id存储facility_category
+            saveImages(facility.getFacilityCategory(), facility.getStoreId(), imageList);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean updateFacility(SportsEquipmentFacility facility, List<String> imageList) {
+        // 校验图片数量(最多20张)
+        if (!CollectionUtils.isEmpty(imageList) && imageList.size() > 20) {
+            throw new RuntimeException("实景图片最多上传20张");
+        }
+        // 更新设施信息
+        boolean result = this.updateById(facility);
+        if (result && !CollectionUtils.isEmpty(imageList) && facility.getFacilityCategory() != null) {
+            // 删除该分类下的旧图片
+            LambdaQueryWrapper<StoreImg> deleteWrapper = new LambdaQueryWrapper<>();
+            deleteWrapper.eq(StoreImg::getStoreId, facility.getStoreId())
+                    .eq(StoreImg::getBusinessId, facility.getFacilityCategory())
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+            storeImgMapper.delete(deleteWrapper);
+            // 保存新图片,business_id存储facility_category
+            saveImages(facility.getFacilityCategory(), facility.getStoreId(), imageList);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean deleteFacility(Integer id) {
+        // 注意:图片和设施没有关系,只和facility_category有关系,所以删除设施时不删除图片
+        // 删除设施
+        return this.removeById(id);
+    }
+
+    @Override
+    public List<SportsEquipmentFacilityCategoryVo> getCategorySummary(Integer storeId) {
+        List<SportsEquipmentFacilityCategoryVo> result = new ArrayList<>();
+        
+        // 遍历所有分类(1:有氧区, 2:力量区, 3:单功能机械区)
+        for (int category = 1; category < FACILITY_CATEGORY_NAMES.length; category++) {
+            SportsEquipmentFacilityCategoryVo categoryVo = new SportsEquipmentFacilityCategoryVo();
+            categoryVo.setFacilityCategory(category);
+            categoryVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+            
+            // 查询该分类下的设备列表(不分页)
+            LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
+            facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                    .eq(SportsEquipmentFacility::getFacilityCategory, category);
+            facilityWrapper.orderByDesc(SportsEquipmentFacility::getCreatedTime);
+            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(facilityWrapper);
+            
+            // 设置设备数量
+            categoryVo.setFacilityCount(facilityList != null ? facilityList.size() : 0);
+            
+            // 转换为VO列表(不包含图片,避免重复查询)
+            List<SportsEquipmentFacilityVo> facilityVoList = new ArrayList<>();
+            if (!CollectionUtils.isEmpty(facilityList)) {
+                for (SportsEquipmentFacility facility : facilityList) {
+                    SportsEquipmentFacilityVo vo = new SportsEquipmentFacilityVo();
+                    BeanUtils.copyProperties(facility, vo);
+                    // 设置分类名称
+                    vo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+                    // 设置显示状态文本
+                    if (facility.getDisplayInStoreDetail() != null) {
+                        vo.setDisplayInStoreDetailText(facility.getDisplayInStoreDetail() == 1 ? "显示" : "隐藏");
+                    }
+                    facilityVoList.add(vo);
+                }
+            }
+            categoryVo.setFacilityList(facilityVoList);
+            
+            // 查询该分类下的图片列表
+            LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+            imageWrapper.eq(StoreImg::getStoreId, storeId)
+                    .eq(StoreImg::getBusinessId, category)
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+            imageWrapper.orderByAsc(StoreImg::getImgSort);
+            List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+            if (!CollectionUtils.isEmpty(imageList)) {
+                categoryVo.setImageList(imageList.stream().map(StoreImg::getImgUrl)
+                        .collect(Collectors.toList()));
+            } else {
+                categoryVo.setImageList(new ArrayList<>());
+            }
+            
+            result.add(categoryVo);
+        }
+        
+        return result;
+    }
+
+    /**
+     * 保存图片
+     * 
+     * @param facilityCategory 设施分类
+     * @param storeId 门店ID
+     * @param imageList 图片列表
+     */
+    private void saveImages(Integer facilityCategory, Integer storeId, List<String> imageList) {
+        for (int i = 0; i < imageList.size(); i++) {
+            StoreImg storeImg = new StoreImg();
+            storeImg.setStoreId(storeId);
+            storeImg.setImgType(IMG_TYPE_SPORTS_EQUIPMENT);
+            storeImg.setBusinessId(facilityCategory); // business_id存储facility_category
+            storeImg.setImgUrl(imageList.get(i));
+            storeImg.setImgSort(i + 1);
+            storeImg.setImgDescription("运动器材设施实景图片");
+            storeImgMapper.insert(storeImg);
+        }
+    }
+
+    /**
+     * 转换为VO对象
+     */
+    private SportsEquipmentFacilityVo convertToVo(SportsEquipmentFacility facility) {
+        SportsEquipmentFacilityVo vo = new SportsEquipmentFacilityVo();
+        BeanUtils.copyProperties(facility, vo);
+        // 设置分类名称
+        if (facility.getFacilityCategory() != null && facility.getFacilityCategory() > 0
+                && facility.getFacilityCategory() < FACILITY_CATEGORY_NAMES.length) {
+            vo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[facility.getFacilityCategory()]);
+        }
+        // 设置显示状态文本
+        if (facility.getDisplayInStoreDetail() != null) {
+            vo.setDisplayInStoreDetailText(facility.getDisplayInStoreDetail() == 1 ? "显示" : "隐藏");
+        }
+        // 查询图片列表,根据store_id和facility_category查询
+        LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+        imageWrapper.eq(StoreImg::getStoreId, facility.getStoreId())
+                .eq(StoreImg::getBusinessId, facility.getFacilityCategory())
+                .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+        imageWrapper.orderByAsc(StoreImg::getImgSort);
+        List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+        if (!CollectionUtils.isEmpty(imageList)) {
+            vo.setImageList(imageList.stream().map(StoreImg::getImgUrl)
+                    .collect(Collectors.toList()));
+        }
+        return vo;
+    }
+
+
+    @Override
+    public List<SportsEquipmentFacilityCategoryVo> getstoreCategorySummary(Integer storeId) {
+        List<SportsEquipmentFacilityCategoryVo> result = new ArrayList<>();
+
+        // 遍历所有分类(1:有氧区, 2:力量区, 3:单功能机械区)
+        for (int category = 1; category < FACILITY_CATEGORY_NAMES.length; category++) {
+            SportsEquipmentFacilityCategoryVo categoryVo = new SportsEquipmentFacilityCategoryVo();
+            categoryVo.setFacilityCategory(category);
+            categoryVo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+
+            // 查询该分类下的设备列表(不分页)
+            LambdaQueryWrapper<SportsEquipmentFacility> facilityWrapper = new LambdaQueryWrapper<>();
+            facilityWrapper.eq(SportsEquipmentFacility::getStoreId, storeId)
+                    .eq(SportsEquipmentFacility::getFacilityCategory, category);
+            facilityWrapper.orderByDesc(SportsEquipmentFacility::getCreatedTime);
+            List<SportsEquipmentFacility> facilityList = facilityMapper.selectList(facilityWrapper);
+
+            // 设置设备数量
+            categoryVo.setFacilityCount(facilityList != null ? facilityList.size() : 0);
+
+            // 转换为VO列表(不包含图片,避免重复查询)
+            List<SportsEquipmentFacilityVo> facilityVoList = new ArrayList<>();
+            if (!CollectionUtils.isEmpty(facilityList)) {
+                for (SportsEquipmentFacility facility : facilityList) {
+                    SportsEquipmentFacilityVo vo = new SportsEquipmentFacilityVo();
+                    BeanUtils.copyProperties(facility, vo);
+                    // 设置分类名称
+                    vo.setFacilityCategoryName(FACILITY_CATEGORY_NAMES[category]);
+                    // 设置显示状态文本
+                    if (facility.getDisplayInStoreDetail() != null) {
+                        vo.setDisplayInStoreDetailText(facility.getDisplayInStoreDetail() == 1 ? "显示" : "隐藏");
+                    }
+                    facilityVoList.add(vo);
+                }
+            }
+            categoryVo.setFacilityList(facilityVoList);
+
+            // 查询该分类下的图片列表
+            LambdaQueryWrapper<StoreImg> imageWrapper = new LambdaQueryWrapper<>();
+            imageWrapper.eq(StoreImg::getStoreId, storeId)
+                    .eq(StoreImg::getBusinessId, category)
+                    .eq(StoreImg::getImgType, IMG_TYPE_SPORTS_EQUIPMENT);
+            imageWrapper.orderByAsc(StoreImg::getImgSort);
+            List<StoreImg> imageList = storeImgMapper.selectList(imageWrapper);
+            if (!CollectionUtils.isEmpty(imageList)) {
+                categoryVo.setImageList(imageList.stream().map(StoreImg::getImgUrl)
+                        .collect(Collectors.toList()));
+            } else {
+                categoryVo.setImageList(new ArrayList<>());
+            }
+
+            result.add(categoryVo);
+        }
+
+        return result;
+    }
+}
+

+ 453 - 2
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.geo.Point;
@@ -70,10 +71,13 @@ import java.util.stream.Collectors;
  * @author ssk
  * @since 2024-12-05
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 @Transactional
 public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo> implements StoreInfoService {
+    private static final int DEFAULT_DISTANCE_METER = 1000;
+
 
     private final String DEFAULT_PASSWORD = "123456";
 
@@ -145,6 +149,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final RestTemplate restTemplate;
 
+    private final StoreImgService storeImgService;
+
+
     /** 商户证照历史记录数据访问对象 */
     private final StoreLicenseHistoryMapper licenseHistoryMapper;
 
@@ -210,9 +217,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         List<StoreImg> albumUrlList = storeImgMapper.selectList(albumUrlQueryWrapper);
         storeMainInfoVo.setAlbumUrl(albumUrlList.stream().sorted(Comparator.comparing(StoreImg::getImgSort)).map(StoreImg::getImgUrl).collect(Collectors.toList()));
         //推荐菜
-        storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
         //菜单
-        storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+
+        // todo 需要根据门店类型来判断
+        // 推荐酒水
+        storeMainInfoVo.setRecommendBeverageUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+        //酒单
+        storeMainInfoVo.setBeverageUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
+
         //门店标签
         storeMainInfoVo.setStoreLabel(storeLabelMapper.selectOne(new LambdaQueryWrapper<StoreLabel>().eq(StoreLabel::getStoreId, id)));
         //营业时间
@@ -3966,5 +3980,442 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     }
 
 
+    /**
+     * web-分页查询店铺信息
+     *
+
+     * @return IPage<StoreInfoVo>
+     */
+    @Override
+    public List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify) {
+        // 参数校验
+        if (lon == null || lat == null) {
+            log.warn("获取更多推荐店铺失败,经纬度为空");
+            return Collections.emptyList();
+        }
+
+        QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("a.delete_flag", 0).eq("b.delete_flag", 0);
+        //如果查询未过期
+        // 获取当前时刻
+        Date currentDate = new Date();
+        // 获取当前日期和时间
+        Calendar calendar = Calendar.getInstance();
+        // 将时间设置为 0 点
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        // 加上 30 天
+        calendar.add(Calendar.DAY_OF_MONTH, 30);
+        // 如果 expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
+        queryWrapper.and(w -> w.isNull("a.expiration_time")
+                .or()
+                .gt("a.expiration_time", currentDate));
+
+        // 如果 food_licence_expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
+        queryWrapper.and(w -> w.isNull("a.food_licence_expiration_time")
+                .or()
+                .gt("a.food_licence_expiration_time", currentDate));
+
+        // 构建一级分类
+        if(StringUtils.isNotEmpty(businessSection)){
+            queryWrapper.eq("a.business_section", businessSection);
+            // 构建二级分类
+            if(StringUtils.isNotEmpty(businessTypes)){
+                queryWrapper.eq("a.business_types", businessTypes);
+                // 构建三级分类
+                if(StringUtils.isNotEmpty(businessClassify)){
+                    // 解析businessClassify参数(格式:1,2,3)
+                    String[] classifyArray = businessClassify.split(",");
+                    // 使用FIND_IN_SET函数检查数据库字段是否包含参数中的任何一个值
+                    queryWrapper.and(wrapper -> {
+                        for (int i = 0; i < classifyArray.length; i++) {
+                            String classify = classifyArray[i].trim();
+                            if (StringUtils.isNotEmpty(classify)) {
+                                if (i == 0) {
+                                    wrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                                } else {
+                                    wrapper.or().apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
+                                }
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        // 构建position参数(格式:经度,纬度)
+        String position = lon + "," + lat;
+        List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getMoreRecommendedStores(queryWrapper, position);
+        if (CollectionUtils.isEmpty(storeInfoVoList)) {
+            return Collections.emptyList();
+        }
+        // 提前查询所有需要的字典数据
+        List<StoreInfoVo> collect = storeInfoVoList.stream().filter(record -> StringUtils.isNotEmpty(record.getStoreType())).collect(Collectors.toList());
+        Set<String> allTypes = collect.stream().map(StoreInfoVo::getStoreType).flatMap(type -> Arrays.stream(type.split(","))).collect(Collectors.toSet());
+
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getTypeName, "storeType").isNull(StoreDictionary::getParentId).in(!allTypes.isEmpty(), StoreDictionary::getDictId, allTypes));
+        Map<String, String> typeMap = storeDictionaries.stream().collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail));
+
+
+        // 计算平均分和评价
+        Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
+        Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
+
+        // 注意:需要将store_id转换为String类型,与后续containsKey判断保持一致
+        avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
+        commentMap = storeCommentMapper.selectList(new QueryWrapper<StoreComment>().eq("business_type", "5").eq("delete_flag", 0)).stream().collect(Collectors.groupingBy(StoreComment::getStoreId));
+
+
+        // 查询入口头图
+        List<Integer> storeIds = storeInfoVoList.stream()
+                .map(StoreInfoVo::getId)  // 假设 StoreImg 有 getStoreUrl() 方法
+                .collect(Collectors.toList());
+
+        List<StoreImg> storeImgList = storeImgMapper.selectList(new QueryWrapper<StoreImg>().in("store_id", storeIds).eq("img_type", 1));
+        Map<Integer, List<StoreImg>>  storeCollect = storeImgList.stream()
+                .collect(Collectors.groupingBy(StoreImg::getStoreId));
+
+
+        for (StoreInfoVo record : storeInfoVoList) {
+            //处理类型
+            if (StringUtils.isNotEmpty(record.getStoreType())) {
+                String[] types = record.getStoreType().split(",");
+                List<String> typeDetails = Arrays.stream(types).map(typeMap::get).filter(Objects::nonNull).collect(Collectors.toList());
+                record.setStoreTypeStr(String.join(",", typeDetails));
+                record.setStoreTypeList(Arrays.asList(types));
+            }
+
+
+            // 加入头图
+            if(!CollectionUtils.isEmpty(storeCollect) && storeCollect.containsKey(record.getId())){
+                List<StoreImg> storeImgs = storeCollect.get(record.getId());
+                if(!CollectionUtils.isEmpty(storeImgs)){
+                    record.setEntranceImage(storeImgs.get(0).getImgUrl());
+                }
+            }
+
+            //写经纬度
+            String[] split = record.getStorePosition().split(",");
+            record.setStorePositionLongitude(split[0]);
+            record.setStorePositionLatitude(split[1]);
+            // 格式化距离,移除无意义的小数位
+            if (!StringUtils.isEmpty(record.getDistance3())) {
+                try {
+                    BigDecimal distanceValue = new BigDecimal(record.getDistance3());
+                    record.setDistance3(distanceValue.stripTrailingZeros().toPlainString());
+                } catch (NumberFormatException ex) {
+                    log.warn("店铺距离格式化失败, storeId: {}, distance3: {}", record.getId(), record.getDistance3(), ex);
+                }
+            }
+            //处理一下到期状态
+            Date expirationTime = record.getExpirationTime();
+            if (expirationTime != null) {
+                // 获取当前时间
+                Calendar now = Calendar.getInstance();
+                Date nowCurrentDate = now.getTime();
+                // 计算 30 天后的时间
+                now.add(Calendar.DAY_OF_YEAR, 30);
+                Date thirtyDaysLater = now.getTime();
+                // 比较两个日期
+                if (expirationTime.after(currentDate)) {
+                    record.setExpiredState("0");
+                    if ((expirationTime.after(nowCurrentDate) || expirationTime.equals(nowCurrentDate)) && expirationTime.before(thirtyDaysLater)) {
+                        record.setExpiredState("1");
+                    }
+                } else {
+                    record.setExpiredState("2");
+                }
+
+                // 获取当前时间
+                LocalDate nowLocal = LocalDate.now();
+                // 将 expirationTime 转换为 LocalDate
+                LocalDate expDate = expirationTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+                // 计算距离到期的天数
+                long daysToExpire = ChronoUnit.DAYS.between(nowLocal, expDate);
+                record.setDaysToExpire(daysToExpire);
+            }
+
+            // 设置店铺得分,设置店铺人均消费,设置总评论数
+            if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
+                record.setAvgScore(String.valueOf(avgScoreMap.get(String.valueOf(record.getId())).get(0).get("avg_score")));
+            }
+            // 设置店铺得分,设置店铺人均消费,设置总评论数
+            if (commentMap.containsKey(record.getId())) {
+                record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
+            }
+
+        }
+
+        // SQL已经实现了距离过滤和排序,直接返回结果
+        return storeInfoVoList;
+    }
+
+
+    @Override
+    public StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu) {
+        StoreInfoVo result = new StoreInfoVo();
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+        BeanUtils.copyProperties(storeInfo, result);
+        //将经营板块和种类拆分成集合
+        String businessTypes = storeInfo.getBusinessTypes();
+        if (StringUtils.isNotEmpty(businessTypes)) {
+            String[] split = businessTypes.split(",");
+            List<String> list = Arrays.asList(split);
+            result.setBusinessTypesList(list);
+        }
+        //存入用户账户
+        StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId));
+        if (storeUser != null) {
+            result.setUserAccount(storeUser.getId().toString());
+            result.setStorePhone(storeUser.getPhone());
+            result.setStoreUserName(storeUser.getName());
+            result.setIdCard(storeUser.getIdCard());
+        }
+//        //存入执照图片地址
+//        List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 14));
+//        List<String> storeImgPaths = new ArrayList<>();
+//        for (StoreImg storeImg : storeImgs) {
+//            storeImgPaths.add(storeImg.getImgUrl());
+//        }
+//        result.setBusinessLicenseAddress(storeImgPaths);
+//        //存入合同图片地址
+//        List<StoreImg> storeContractImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 15));
+//        List<String> storeContractImagePathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : storeContractImageImgs) {
+//            storeContractImagePathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setContractImageList(storeContractImagePathImgs);
+//        //存入续签合同地址
+//        List<StoreImg> renewContractImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 22));
+//        List<String> renewContractImagePathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : renewContractImgs) {
+//            renewContractImagePathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setRenewContractImageList(renewContractImagePathImgs);
+//        //存入经营许可证通过地址
+//        List<StoreImg> foodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 25));
+//        List<String> foodLicenceImgsPathImgs = new ArrayList<>();
+//        for (StoreImg storeImg : foodLicenceImgs) {
+//            foodLicenceImgsPathImgs.add(storeImg.getImgUrl());
+//        }
+//        result.setFoodLicenceImageList(foodLicenceImgsPathImgs);
+//        //存入经营许可证未通过地址
+//        List<StoreImg> notPassFoodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 24));
+//        List<String> notPassFoodLicenceList = new ArrayList<>();
+//        for (StoreImg storeImg : notPassFoodLicenceImgs) {
+//            notPassFoodLicenceList.add(storeImg.getImgUrl());
+//        }
+//        result.setNotPassFoodLicenceImageList(notPassFoodLicenceList);
+        // 存放商家入口图
+        List<StoreImg> storeEntranceImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 1));
+        if (!storeEntranceImageImgs.isEmpty()) {
+            result.setEntranceImage(storeEntranceImageImgs.get(0).getImgUrl());
+        } else {
+            result.setEntranceImage("null");
+        }
+        // 存放商家头像
+        List<StoreImg> storeImgs1 = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 10));
+        if (!storeImgs1.isEmpty()) {
+            result.setImgUrl(storeImgs1.get(0).getImgUrl());
+        } else {
+            result.setImgUrl("null");
+        }
+
+        // 获取店铺相册
+        List<StoreImg> storeAlbumList = new ArrayList<>();
+        if(storeInfo.getImgMode() != null && storeInfo.getImgMode() == 0){
+            storeAlbumList =  storeImgService.getStoreImg(Integer.parseInt(storeId), 20);
+        } else {
+            storeAlbumList =  storeImgService.getStoreImg(Integer.parseInt(storeId), 21);
+        }
+
+        if(!CollectionUtils.isEmpty(storeAlbumList)){
+            List<String> storeAlbumUrlList = storeAlbumList.stream().map(StoreImg::getImgUrl)  // 假设 StoreImg 有 getStoreUrl() 方法
+                    .filter(url -> url != null && !url.trim().isEmpty())  // 过滤空值
+                    .collect(Collectors.toList());
+            result.setStoreAlbumUrlList(storeAlbumUrlList);
+        } else {
+            result.setStoreAlbumUrlList(new ArrayList<>());
+        }
+
+        // 设置经纬度
+        result.setStorePositionLongitude(result.getStorePosition().split(",")[0]);
+        result.setStorePositionLatitude(result.getStorePosition().split(",")[1]);
+        // 设置距离
+        if ((jingdu != null && !jingdu.isEmpty()) && (weidu != null && !weidu.isEmpty())) {
+            /*double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
+            double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
+            double storeDistance = DistanceUtil.haversineCalculateDistance(Double.parseDouble(jingdu), Double.parseDouble(weidu), storeJing, storeWei);*/
+
+            Double distance = storeInfoMapper.getStoreDistance(jingdu + "," + weidu,result.getId());
+
+            result.setDistance(distance);
+        }
+        // 计算店铺到最近地铁站的距离
+        JSONObject nearbySubway = gaoDeMapUtil.getNearbySubway(result.getStorePosition().split(",")[0], result.getStorePosition().split(",")[1]);
+        // 地铁名
+        String subWayName = nearbySubway.getString("name");
+        result.setSubwayName(subWayName);
+        // 地铁站经纬度
+        String subWayJing = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[0];
+        String subWayWei = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[1];
+        if ((subWayJing != null && !subWayJing.isEmpty()) && (subWayWei != null && !subWayWei.isEmpty())) {
+            double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
+            double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
+            double storeDistance2 = DistanceUtil.haversineCalculateDistance(Double.parseDouble(subWayJing), Double.parseDouble(subWayWei), storeJing, storeWei);
+            result.setDistance2(storeDistance2);
+        } else {
+            result.setDistance2(0);
+        }
+        // 当前登录用户是否收藏
+        LambdaUpdateWrapper<LifeCollect> shouCangWrapper = new LambdaUpdateWrapper<>();
+        shouCangWrapper.eq(LifeCollect::getUserId, userId).eq(LifeCollect::getStoreId, storeId);
+        List<LifeCollect> shouCangList = lifeCollectMapper.selectList(shouCangWrapper);
+        if (null == shouCangList || shouCangList.isEmpty()) {
+            result.setCollection(0);
+        } else {
+            result.setCollection(1);
+        }
+
+        // 该用户的打卡记录
+        LambdaQueryWrapper<StoreClockIn> clockInWrapper = new LambdaQueryWrapper<>();
+        clockInWrapper.eq(StoreClockIn::getUserId, userId);
+        List<StoreClockIn> clockInList = storeClockInMapper.selectList(clockInWrapper);
+
+        List<StoreClockIn> clockStoreList = clockInList.stream().filter(item -> item.getStoreId() == Integer.parseInt(storeId)).collect(Collectors.toList());
+        // 该用户是否在该店铺打过卡
+        if (!clockStoreList.isEmpty()) {
+            result.setClockInStore(1);
+        } else {
+            result.setClockInStore(0);
+        }
+
+        // 今天在该店铺是否打过卡
+        int today = (int) clockStoreList.stream().filter(item -> item.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(LocalDate.now())).count();
+        if (today > 0) {
+            result.setClockInStoreToday(1);
+        } else {
+            result.setClockInStoreToday(0);
+        }
+
+
+        Map<String, Object> commitCountAndScore = storeCommentService.getCommitCountAndScore(null, 5, Integer.parseInt(storeId), null, null);
+        result.setScore(Double.parseDouble(commitCountAndScore.get("score").toString()));
+        result.setCommitCount(commitCountAndScore.get("commitCount").toString());
+
+
+        // 在该店铺的打卡次数
+        result.setClockInStoreNum(clockStoreList.size());
+
+        // 该用户打卡的所有店铺次数(一个店铺只算一次)
+        int clockInNum = (int) clockInList.stream().map(StoreClockIn::getStoreId).distinct().count();
+        result.setClockInNum(clockInNum);
+
+//        // 获取店铺动态列表
+//        QueryWrapper<LifeUserDynamics> dynamicsWrapper = new QueryWrapper<>();
+//        dynamicsWrapper.eq("phone_id", "store_" + result.getStorePhone()).orderByDesc("lud.created_time");
+//        dynamicsWrapper.eq("lud.delete_flag", 0);
+//
+//        LambdaQueryWrapper<LifeBlacklist> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+//        lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockerId, userId);
+//        lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockedPhoneId, "store_" + result.getStorePhone());
+//        LifeBlacklist blacklist = lifeBlacklistMapper.selectOne(lambdaQueryWrapper1);
+//        List<LifeUserDynamicsVo> storeDynamicslist = new ArrayList<>();
+
+//        //判断没有拉黑当前门店账户 查出门店动态
+//        if(blacklist == null){
+//            storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, "store_" + result.getStorePhone());
+//        }
+//
+//        List<String> followList = new ArrayList<>();
+//        List<String> fansList = new ArrayList<>();
+
+//        if (StringUtils.isNotEmpty(userId)) {
+//            LifeUser lifeUser = lifeUserMapper.selectById(userId);
+//            if (lifeUser != null && StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
+//                // 查询我的关注信息,构建关注者ID列表
+//                LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
+//                lifeFansWrapper.eq(LifeFans::getFansId, "user_" + result.getStorePhone());
+//                List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
+//                if (!CollectionUtils.isEmpty(lifeFansList)) {
+//                    followList = lifeFansList.stream().map(LifeFans::getFollowedId).collect(Collectors.toList());
+//                }
+//
+//                // 查询我的粉丝信息,构建粉丝ID列表
+//                lifeFansWrapper = new LambdaQueryWrapper<>();
+//                lifeFansWrapper.eq(LifeFans::getFollowedId, "user_" + result.getStorePhone());
+//                lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
+//                if (!CollectionUtils.isEmpty(lifeFansList)) {
+//                    fansList = lifeFansList.stream().map(LifeFans::getFansId).collect(Collectors.toList());
+//                }
+//            }
+//        }
+
+//        for (LifeUserDynamicsVo vo : storeDynamicslist) {
+//            if (followList.contains(vo.getPhoneId())) {
+//                vo.setIsFollowThis("1");
+//            } else {
+//                vo.setIsFollowThis("0");
+//            }
+//            if (fansList.contains(vo.getPhoneId())) {
+//                vo.setIsFollowMe("1");
+//            } else {
+//                vo.setIsFollowMe("0");
+//            }
+//        }
+
+//        // 返回动态最新的5条
+//        List<LifeUserDynamicsVo> storeDynamicslist2 = storeDynamicslist.stream()
+//                .limit(5).collect(Collectors.toList());
+//        result.setDynamicsList(storeDynamicslist2);
+//        //设置动态条数
+//        Integer dynamicsNum = storeDynamicslist2.size();
+//        result.setDynamicsNum(dynamicsNum);
+//
+//        // 获取店铺动态总数
+//        result.setTotalDynamicsNum(storeDynamicslist.size());
+
+        //营业时间
+        List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().eq(StoreBusinessInfo::getStoreId, storeId).eq(StoreBusinessInfo::getDeleteFlag, 0));
+        if (ObjectUtils.isNotEmpty(storeBusinessInfos)) {
+            result.setStoreBusinessInfo(storeBusinessInfos.get(0));
+            result.setStoreBusinessInfos(storeBusinessInfos);
+            StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfos().stream().filter(item -> item.getBusinessType() == 1).findFirst().orElse(null);
+            if (ObjectUtils.isNotEmpty(storeBusinessInfo)) {
+
+                Calendar calendar = Calendar.getInstance(); // 获取Calendar实例
+                int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 获取星期几,注意Calendar中的DAY_OF_WEEK是从1(代表星期天)开始的
+                String[] days = {"7", "1", "2", "3", "4", "5", "6"};
+                String day = days[dayOfWeek - 1];
+                if (storeBusinessInfo.getBusinessDate().contains(day)) {
+                    if (StringUtils.isNotEmpty(storeBusinessInfo.getStartTime()) && StringUtils.isNotEmpty(storeBusinessInfo.getEndTime())) {
+                        LocalTime now = LocalTime.now();
+                        List<String> startList = Arrays.asList(storeBusinessInfo.getStartTime().split(":"));
+                        List<String> endList = Arrays.asList(storeBusinessInfo.getEndTime().split(":"));
+                        LocalTime start = LocalTime.of(Integer.parseInt(startList.get(0)), Integer.parseInt(startList.get(1)));
+                        LocalTime end = LocalTime.of(Integer.parseInt(endList.get(0)), Integer.parseInt(startList.get(1)));
+                        if (now.isAfter(start) && now.isBefore(end)) {
+                            result.setYyFlag(1);
+                        } else {
+                            result.setYyFlag(0);
+                        }
+                    }
+                } else {
+                    result.setYyFlag(0);
+                }
+            }
+        }
+        LambdaQueryWrapper<StoreDictionary> storeDictionaryLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        storeDictionaryLambdaQueryWrapper.eq(StoreDictionary::getTypeName, "businessStatus")
+                .eq(StringUtils.isNotEmpty(result.getBusinessStatus().toString()), StoreDictionary::getDictId, result.getBusinessStatus());
+        List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(storeDictionaryLambdaQueryWrapper);
+        if (!storeDictionaries.isEmpty()) {
+            result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
+        }
+        return result;
+    }
+
 
 }

+ 279 - 20
alien-store/src/main/java/shop/alien/store/service/impl/StoreMenuServiceImpl.java

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import shop.alien.entity.result.R;
@@ -22,9 +23,12 @@ import shop.alien.mapper.StoreMenuMapper;
 import shop.alien.store.service.StoreImgService;
 import shop.alien.store.service.StoreMenuService;
 
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -33,6 +37,7 @@ import java.util.stream.Collectors;
  * @author ssk
  * @since 2024-12-05
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class StoreMenuServiceImpl extends ServiceImpl<StoreMenuMapper, StoreMenu> implements StoreMenuService {
@@ -46,35 +51,74 @@ public class StoreMenuServiceImpl extends ServiceImpl<StoreMenuMapper, StoreMenu
     private final LifeGroupBuyThaliMapper lifeGroupBuyThaliMapper;
 
     /**
+     * 菜品类型:非推荐
+     */
+    private static final Integer DISH_TYPE_NON_RECOMMEND = 0;
+
+    /**
+     * 菜品类型:推荐
+     */
+    private static final Integer DISH_TYPE_RECOMMEND = 1;
+
+    /**
+     * 点赞状态:未点赞
+     */
+    private static final Integer LIKE_STATUS_NO = 0;
+
+    /**
+     * 点赞状态:已点赞
+     */
+    private static final Integer LIKE_STATUS_YES = 1;
+
+    /**
      * 获取门店菜单
      *
-     * @param storeId  门店id
-     * @param dishType 菜品类型, 0:菜单, 1:推荐
-     * @param phoneId  消息标识
+     * @param storeId      门店id
+     * @param dishType     菜品类型, 0:非推荐, 1:推荐
+     * @param phoneId      消息标识
+     * @param dishMenuType 菜单类型:1-菜单,2-酒水
      * @return list
      */
     @Override
-    public List<StoreMenuVo> getStoreMenu(Integer storeId, Integer dishType, String phoneId) {
+    public List<StoreMenuVo> getStoreMenu(Integer storeId, Integer dishType, String phoneId, Integer dishMenuType) {
+        // 查询菜单列表
+        Integer queryDishType = (dishType != null && dishType == 0) ? null : dishType;
+        List<StoreMenuVo> menuList = storeMenuMapper.getStoreMenuList(storeId, queryDishType, dishMenuType);
 
-        if (dishType == 0) {
-            List<StoreMenuVo> collect = storeMenuMapper.getStoreMenuList(storeId, null);
-            return collect.stream().sorted(Comparator.comparing(StoreMenuVo::getSort)).collect(Collectors.toList());
-        } else {
-            List<StoreMenuVo> collect = storeMenuMapper.getStoreMenuList(storeId, dishType);
-            collect.forEach(item -> {
-                if (StringUtils.isNotEmpty(phoneId)) {
-                    LambdaQueryWrapper<LifeLikeRecord> query = new LambdaQueryWrapper<>();
-                    query.eq(LifeLikeRecord::getDianzanId, phoneId).eq(LifeLikeRecord::getHuifuId, item.getId());
-                    Integer i = lifeLikeRecordMapper.selectCount(query);
-                    if (i > 0) {
-                        item.setIsLike(1);
-                    } else {
-                        item.setIsLike(0);
-                    }
+        // 如果是推荐菜且有用户标识,批量查询点赞状态
+        if (dishType != null && dishType == 1 && StringUtils.isNotEmpty(phoneId)
+                && CollectionUtils.isNotEmpty(menuList)) {
+            // 将菜单ID转换为String集合,用于查询点赞记录
+            Set<String> menuIdStrSet = menuList.stream()
+                    .map(item -> String.valueOf(item.getId()))
+                    .collect(Collectors.toSet());
+
+            // 批量查询点赞记录
+            LambdaQueryWrapper<LifeLikeRecord> likeQuery = new LambdaQueryWrapper<>();
+            likeQuery.eq(LifeLikeRecord::getDianzanId, phoneId)
+                    .in(LifeLikeRecord::getHuifuId, menuIdStrSet);
+            List<LifeLikeRecord> likeRecordList = lifeLikeRecordMapper.selectList(likeQuery);
+
+            // 构建已点赞的菜单ID集合
+            Set<String> likedMenuIdSet = likeRecordList.stream()
+                    .map(LifeLikeRecord::getHuifuId)
+                    .collect(Collectors.toSet());
+
+            // 设置点赞状态
+            menuList.forEach(item -> {
+                String menuIdStr = String.valueOf(item.getId());
+                if (likedMenuIdSet.contains(menuIdStr)) {
+                    item.setIsLike(1);
+                } else {
+                    item.setIsLike(0);
                 }
             });
-            return collect.stream().sorted(Comparator.comparing(StoreMenuVo::getSort)).collect(Collectors.toList());
         }
+
+        // 按排序字段排序
+        return menuList.stream()
+                .sorted(Comparator.comparing(StoreMenuVo::getSort))
+                .collect(Collectors.toList());
     }
 
     /**
@@ -280,4 +324,219 @@ public class StoreMenuServiceImpl extends ServiceImpl<StoreMenuMapper, StoreMenu
     public boolean getMenuLikeStatus(String userId, Integer menuId) {
         return lifeLikeRecordMapper.selectCount(new QueryWrapper<LifeLikeRecord>().eq("dianzan_id", userId).eq("huifu_id", menuId).eq("delete_flag", 0)) > 0;
     }
+
+
+    /**
+     * 获取门店菜单(客户端)
+     * <p>
+     * 根据门店ID查询菜单列表,支持按菜品类型和菜单类型筛选。
+     * 当查询推荐菜且提供用户手机号时,会批量查询并设置用户的点赞状态。
+     * 最终结果按排序字段升序排列。
+     * </p>
+     *
+     * @param storeId      门店ID,必填,必须大于0
+     * @param dishType     菜品类型,可选,0:全部, 1:推荐
+     * @param phoneId      用户手机号,可选,用于查询用户对推荐菜的点赞状态
+     * @param dishMenuType 菜单类型,可选,1-菜单,2-酒水
+     * @return 菜单列表,按排序字段升序排列,如果查询结果为空则返回空列表
+     */
+    @Override
+    public Map<String, Object> getClientMenuByStoreId(Integer storeId, Integer dishType, String phoneId, Integer dishMenuType) {
+        log.info("开始获取用户端菜单,门店ID:{},菜品类型:{},用户手机号:{},菜单类型:{}",
+                storeId, dishType, phoneId, dishMenuType);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取用户端菜单失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        // 先查询该门店的所有菜单数据(不应用筛选条件),用于统计
+        List<StoreMenuVo> allMenuList = storeMenuMapper.getClientMenuByStoreId(storeId, null, null);
+
+        // 按照六种组合进行分组统计(基于所有菜单数据,不受筛选条件影响):
+        // 1. dish_menu_type = 1(菜单),dish_type = 0(全部)
+        // 2. dish_menu_type = 1(菜单),dish_type = 1(推荐)
+        // 3. dish_menu_type = 2(酒水),dish_type = 0(全部)
+        // 4. dish_menu_type = 2(酒水),dish_type = 1(推荐)
+        // 5. dish_menu_type = 1和2(全部),dish_type = 0(全部)
+        // 6. dish_menu_type = 1和2(全部),dish_type = 1(推荐)
+        Map<String, Integer> countMap = new HashMap<>();
+
+        if (CollectionUtils.isNotEmpty(allMenuList)) {
+            // 统计:dish_menu_type = 1(菜单),dish_type = 1(推荐)
+            long menuRecommendCount = allMenuList.stream()
+                    .filter(item -> "1".equals(item.getDishMenuType())
+                            && DISH_TYPE_RECOMMEND.equals(item.getDishType()))
+                    .count();
+            countMap.put("menu_recommend", (int) menuRecommendCount);
+
+            // 统计:dish_menu_type = 1(菜单),dish_type = 0(全部)
+            long menuAllCount = allMenuList.stream()
+                    .filter(item -> "1".equals(item.getDishMenuType())
+                            && (item.getDishType() == null || DISH_TYPE_NON_RECOMMEND.equals(item.getDishType())))
+                    .count();
+            countMap.put("menu_all", (int) menuAllCount + (int) menuRecommendCount);
+
+            // 统计:dish_menu_type = 2(酒水),dish_type = 1(推荐)
+            long drinkRecommendCount = allMenuList.stream()
+                    .filter(item -> "2".equals(item.getDishMenuType())
+                            && DISH_TYPE_RECOMMEND.equals(item.getDishType()))
+                    .count();
+            countMap.put("drink_recommend", (int) drinkRecommendCount);
+
+            // 统计:dish_menu_type = 2(酒水),dish_type = 0(全部)
+            long drinkAllCount = allMenuList.stream()
+                    .filter(item -> "2".equals(item.getDishMenuType())
+                            && (item.getDishType() == null || DISH_TYPE_NON_RECOMMEND.equals(item.getDishType())))
+                    .count();
+            countMap.put("drink_all", (int) drinkAllCount + (int) drinkRecommendCount);
+
+            // 新增统计:dist_type 为全部,dish_menu_type 为 1 和 2(菜单和酒水都包含)
+            int allMenuAllCount = countMap.get("menu_all") + countMap.get("drink_all");
+            countMap.put("all_menu_all", allMenuAllCount);
+
+            // 新增统计:dist_type 为 1(推荐),dish_menu_type 为 1 和 2(菜单和酒水都包含)
+            int allMenuRecommendCount = countMap.get("menu_recommend") + countMap.get("drink_recommend");
+            countMap.put("all_menu_recommend", allMenuRecommendCount);
+        } else {
+            // 如果没有数据,返回0
+            countMap.put("menu_all", 0);
+            countMap.put("menu_recommend", 0);
+            countMap.put("drink_all", 0);
+            countMap.put("drink_recommend", 0);
+            countMap.put("all_menu_all", 0);
+            countMap.put("all_menu_recommend", 0);
+        }
+
+        // 处理菜品类型参数:当dishType为0时,转换为null以查询所有类型
+        Integer queryDishType = (dishType != null && DISH_TYPE_NON_RECOMMEND.equals(dishType)) ? null : dishType;
+
+        // 根据筛选条件查询菜单列表(用于返回给前端)
+        List<StoreMenuVo> menuList = storeMenuMapper.getClientMenuByStoreId(storeId, queryDishType, dishMenuType);
+
+        // 如果查询结果为空,返回空列表但保留统计信息
+        if (CollectionUtils.isEmpty(menuList)) {
+            log.info("获取用户端菜单完成,门店ID:{},未查询到符合条件的菜单数据", storeId);
+            Map<String, Object> result = new HashMap<>();
+            result.put("list", Collections.emptyList());
+            result.put("count", countMap);
+            return result;
+        }
+
+        log.info("获取用户端菜单,门店ID:{},查询到符合条件的菜单数量:{}", storeId, menuList.size());
+
+        // 如果是推荐菜且有用户标识,批量查询并设置点赞状态
+        if (DISH_TYPE_RECOMMEND.equals(dishType) && StringUtils.isNotEmpty(phoneId)) {
+            setLikeStatusForMenuList(menuList, phoneId);
+        } else {
+            // 非推荐菜或未提供用户手机号时,设置默认未点赞状态
+            menuList.forEach(item -> item.setIsLike(LIKE_STATUS_NO));
+        }
+
+        // 按排序字段升序排序
+        List<StoreMenuVo> sortedMenuList = menuList.stream()
+                .sorted(Comparator.comparing(StoreMenuVo::getSort, Comparator.nullsLast(Integer::compareTo)))
+                .collect(Collectors.toList());
+
+        // 构建返回结果
+        Map<String, Object> result = new HashMap<>();
+        result.put("list", sortedMenuList);
+        result.put("count", countMap);
+
+        log.info("获取用户端菜单完成,门店ID:{},返回菜单数量:{},统计信息:菜单-全部={},菜单-推荐={},酒水-全部={},酒水-推荐={},全部-全部={},全部-推荐={}",
+                storeId, sortedMenuList.size(),
+                countMap.get("menu_all"), countMap.get("menu_recommend"),
+                countMap.get("drink_all"), countMap.get("drink_recommend"),
+                countMap.get("all_menu_all"), countMap.get("all_menu_recommend"));
+
+        return result;
+    }
+
+    /**
+     * 批量设置菜单的点赞状态
+     * <p>
+     * 根据用户手机号和菜单ID列表,批量查询点赞记录,并设置每个菜单的点赞状态
+     * </p>
+     *
+     * @param menuList 菜单列表
+     * @param phoneId  用户手机号
+     */
+    private void setLikeStatusForMenuList(List<StoreMenuVo> menuList, String phoneId) {
+        if (CollectionUtils.isEmpty(menuList) || StringUtils.isEmpty(phoneId)) {
+            return;
+        }
+
+        log.debug("开始批量查询点赞状态,用户手机号:{},菜单数量:{}", phoneId, menuList.size());
+
+        // 将菜单ID转换为String集合,用于查询点赞记录
+        Set<String> menuIdStrSet = menuList.stream()
+                .map(item -> String.valueOf(item.getId()))
+                .filter(id -> id != null && !"null".equals(id))
+                .collect(Collectors.toSet());
+
+        if (menuIdStrSet.isEmpty()) {
+            log.warn("菜单ID集合为空,无法查询点赞状态");
+            menuList.forEach(item -> item.setIsLike(LIKE_STATUS_NO));
+            return;
+        }
+
+        // 批量查询点赞记录
+        LambdaQueryWrapper<LifeLikeRecord> likeQuery = new LambdaQueryWrapper<>();
+        likeQuery.eq(LifeLikeRecord::getDianzanId, phoneId)
+                .eq(LifeLikeRecord::getDeleteFlag, 0)
+                .in(LifeLikeRecord::getHuifuId, menuIdStrSet);
+        List<LifeLikeRecord> likeRecordList = lifeLikeRecordMapper.selectList(likeQuery);
+
+        // 构建已点赞的菜单ID集合
+        Set<String> likedMenuIdSet = likeRecordList.stream()
+                .map(LifeLikeRecord::getHuifuId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+
+        log.debug("查询到点赞记录数量:{},已点赞菜单数量:{}",
+                likeRecordList.size(), likedMenuIdSet.size());
+
+        // 设置点赞状态
+        menuList.forEach(item -> {
+            if (item.getId() == null) {
+                item.setIsLike(LIKE_STATUS_NO);
+                return;
+            }
+
+            String menuIdStr = String.valueOf(item.getId());
+            if (likedMenuIdSet.contains(menuIdStr)) {
+                item.setIsLike(LIKE_STATUS_YES);
+            } else {
+                item.setIsLike(LIKE_STATUS_NO);
+            }
+        });
+    }
+
+    /**
+     * 获取菜品详情
+     *
+     * @param id 菜品id
+     * @return StoreMenuVo
+     */
+    @Override
+    public StoreMenuVo getClientMenuInfoById(Integer id, String phoneId) {
+        StoreMenuVo storeMenuVo = storeMenuMapper.getClientMenuInfoById(id);
+        if(StringUtils.isNotEmpty(phoneId)){
+            // 批量查询点赞记录
+            LambdaQueryWrapper<LifeLikeRecord> likeQuery = new LambdaQueryWrapper<>();
+            likeQuery.eq(LifeLikeRecord::getDianzanId, phoneId)
+                    .eq(LifeLikeRecord::getDeleteFlag, 0)
+                    .eq(LifeLikeRecord::getHuifuId, storeMenuVo.getId());
+            int likeCount = lifeLikeRecordMapper.selectCount(likeQuery);
+            if(likeCount > 0){
+                storeMenuVo.setIsLike(1);
+            }
+        } else {
+            storeMenuVo.setLikeCount(0);
+            storeMenuVo.setIsLike(0);
+        }
+
+        return storeMenuVo;
+    }
 }

+ 182 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java

@@ -1,14 +1,19 @@
 package shop.alien.store.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.entity.store.StoreOfficialAlbum;
+import shop.alien.entity.store.vo.StoreAlbumDetailVo;
+import shop.alien.entity.store.vo.StoreAlbumNameVo;
+import shop.alien.entity.store.vo.StoreOfficialAlbumImgVo;
 import shop.alien.entity.store.vo.StoreOfficialAlbumVo;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreOfficialAlbumMapper;
@@ -16,8 +21,10 @@ import shop.alien.store.service.StoreOfficialAlbumService;
 import shop.alien.store.util.CommonConstant;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Transactional
 @Service
 @RequiredArgsConstructor
@@ -97,4 +104,179 @@ public class StoreOfficialAlbumServiceImpl extends ServiceImpl<StoreOfficialAlbu
         storeImgMapper.delete(wrapper);
         return CommonConstant.ERROR_CODE_VALID_PARAMS;
     }
+
+
+    /**
+     * 获取官方相册图片列表(客户端)
+     * <p>
+     * 根据门店ID和相册名称查询官方相册中的图片列表
+     * 查询条件:imgType = 2(官方相册),通过 business_id 关联到 store_official_album 表,按 albumName 筛选
+     * </p>
+     *
+     * @param storeId   门店ID,必填
+     * @param albumName 相册名称,可选。例如:酒水、餐食、环境、全部等。当为null或空字符串时,查询全部
+     * @return 图片列表和总数
+     */
+    @Override
+    public StoreOfficialAlbumImgVo getOfficialAlbumImgList(Integer storeId, String albumName) {
+        log.info("开始获取官方相册图片列表,门店ID:{},相册名称:{}", storeId, albumName);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取官方相册图片列表失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        // 先查询符合条件的官方相册ID列表
+        LambdaQueryWrapper<StoreOfficialAlbum> albumQueryWrapper = new LambdaQueryWrapper<>();
+        albumQueryWrapper.eq(StoreOfficialAlbum::getStoreId, storeId)
+                .eq(StoreOfficialAlbum::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+
+        // 如果指定了相册名称,添加筛选条件
+        if (StringUtils.isNotBlank(albumName)) {
+            albumQueryWrapper.eq(StoreOfficialAlbum::getAlbumName, albumName);
+        }
+
+        List<StoreOfficialAlbum> albumList = storeOfficialAlbumMapper.selectList(albumQueryWrapper);
+
+        // 如果相册列表为空,直接返回空结果
+        if (CollectionUtils.isEmpty(albumList)) {
+            log.info("获取官方相册图片列表,门店ID:{},未查询到符合条件的相册", storeId);
+            StoreOfficialAlbumImgVo result = new StoreOfficialAlbumImgVo();
+            result.setImgList(Collections.emptyList());
+            result.setTotalCount(0);
+            return result;
+        }
+
+        // 提取相册ID列表
+        List<Integer> albumIds = albumList.stream()
+                .map(StoreOfficialAlbum::getId)
+                .collect(Collectors.toList());
+
+        log.debug("查询到符合条件的相册数量:{},相册ID列表:{}", albumList.size(), albumIds);
+
+        // 查询这些相册下的所有图片(imgType = 2 表示官方相册)
+        LambdaQueryWrapper<StoreImg> imgQueryWrapper = new LambdaQueryWrapper<>();
+        imgQueryWrapper.eq(StoreImg::getStoreId, storeId)
+                .eq(StoreImg::getImgType, CommonConstant.STORE_IMG_ALBUM) // imgType = 2 表示官方相册
+                .in(StoreImg::getBusinessId, albumIds) // business_id 关联到相册ID
+                .eq(StoreImg::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                .orderByAsc(StoreImg::getImgSort);
+
+        List<StoreImg> imgList = storeImgMapper.selectList(imgQueryWrapper);
+
+        // 构建返回结果
+        StoreOfficialAlbumImgVo result = new StoreOfficialAlbumImgVo();
+        result.setImgList(imgList);
+        result.setTotalCount(imgList.size());
+
+        log.info("获取官方相册图片列表完成,门店ID:{},相册名称:{},返回图片数量:{}",
+                storeId, albumName, result.getTotalCount());
+
+        return result;
+    }
+
+    /**
+     * 获取官方相册名称列表(客户端)
+     * <p>
+     * 根据门店ID查询所有可用的相册名称列表,用于前端展示筛选选项
+     * 返回每个相册名称及其对应的图片数量
+     * </p>
+     *
+     * @param storeId 门店ID,必填
+     * @return 相册名称列表,包含相册名称和图片数量
+     */
+    @Override
+    public List<StoreAlbumNameVo> getAlbumNameList(Integer storeId) {
+        log.info("开始获取官方相册名称列表,门店ID:{}", storeId);
+
+        // 参数校验
+        if (storeId == null || storeId <= 0) {
+            log.warn("获取官方相册名称列表失败,门店ID无效:{}", storeId);
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+
+        // 查询该门店下所有未删除的官方相册
+        LambdaQueryWrapper<StoreOfficialAlbum> albumQueryWrapper = new LambdaQueryWrapper<>();
+        albumQueryWrapper.eq(StoreOfficialAlbum::getStoreId, storeId)
+                .eq(StoreOfficialAlbum::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                .isNotNull(StoreOfficialAlbum::getAlbumName)
+                .ne(StoreOfficialAlbum::getAlbumName, "");
+
+        List<StoreOfficialAlbum> albumList = storeOfficialAlbumMapper.selectList(albumQueryWrapper);
+
+        // 如果相册列表为空,直接返回空列表
+        if (CollectionUtils.isEmpty(albumList)) {
+            log.info("获取官方相册名称列表,门店ID:{},未查询到相册", storeId);
+            return Collections.emptyList();
+        }
+
+        // 提取相册ID列表
+        List<Integer> albumIds = albumList.stream()
+                .map(StoreOfficialAlbum::getId)
+                .collect(Collectors.toList());
+
+        // 查询这些相册下的所有图片(imgType = 2 表示官方相册)
+        LambdaQueryWrapper<StoreImg> imgQueryWrapper = new LambdaQueryWrapper<>();
+        imgQueryWrapper.eq(StoreImg::getStoreId, storeId)
+                .eq(StoreImg::getImgType, CommonConstant.STORE_IMG_ALBUM) // imgType = 2 表示官方相册
+                .in(StoreImg::getBusinessId, albumIds) // business_id 关联到相册ID
+                .eq(StoreImg::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+
+        List<StoreImg> imgList = storeImgMapper.selectList(imgQueryWrapper);
+
+        // 构建相册ID到相册名称的映射
+        Map<Integer, String> albumIdToNameMap = albumList.stream()
+                .collect(Collectors.toMap(
+                        StoreOfficialAlbum::getId,
+                        StoreOfficialAlbum::getAlbumName,
+                        (existing, replacement) -> existing // 如果有重复的ID,保留第一个
+                ));
+
+        // 按相册名称分组统计图片数量
+        Map<String, Long> albumNameCountMap = imgList.stream()
+                .filter(img -> img.getBusinessId() != null && albumIdToNameMap.containsKey(img.getBusinessId()))
+                .collect(Collectors.groupingBy(
+                        img -> albumIdToNameMap.get(img.getBusinessId()),
+                        Collectors.counting()
+                ));
+
+        // 转换为返回VO列表
+        List<StoreAlbumNameVo> result = albumNameCountMap.entrySet().stream()
+                .map(entry -> {
+                    StoreAlbumNameVo vo = new StoreAlbumNameVo();
+                    vo.setAlbumName(entry.getKey());
+                    vo.setImgCount(entry.getValue().intValue());
+                    return vo;
+                })
+                .sorted((a, b) -> {
+                    // 按图片数量降序排序,如果数量相同则按名称排序
+                    int countCompare = Integer.compare(b.getImgCount(), a.getImgCount());
+                    if (countCompare != 0) {
+                        return countCompare;
+                    }
+                    return a.getAlbumName().compareTo(b.getAlbumName());
+                })
+                .collect(Collectors.toList());
+
+        log.info("获取官方相册名称列表完成,门店ID:{},返回相册数量:{}", storeId, result.size());
+
+        return result;
+    }
+
+    /**
+     * 将StoreImg转换为StoreImgInfo
+     *
+     * @param img 图片实体
+     * @return 图片信息VO
+     */
+    private StoreAlbumDetailVo.StoreImgInfo convertToImgInfo(StoreImg img) {
+        StoreAlbumDetailVo.StoreImgInfo info = new StoreAlbumDetailVo.StoreImgInfo();
+        info.setId(img.getId());
+        info.setImgUrl(img.getImgUrl());
+        info.setImgDescription(img.getImgDescription());
+        info.setImgSort(img.getImgSort());
+        info.setImgType(img.getImgType());
+        return info;
+    }
 }

+ 161 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StorePersonnelServiceImpl.java

@@ -0,0 +1,161 @@
+package shop.alien.store.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.StorePersonnel;
+import shop.alien.entity.store.vo.StorePersonnelVo;
+import shop.alien.mapper.StorePersonnelMapper;
+import shop.alien.store.service.StoreImgService;
+import shop.alien.store.service.StorePersonnelService;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 店铺人员服务实现类
+ *
+ * @author system
+ * @since 2025-01-15
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class StorePersonnelServiceImpl extends ServiceImpl<StorePersonnelMapper, StorePersonnel> implements StorePersonnelService {
+
+    private final StorePersonnelMapper storePersonnelMapper;
+
+    private final StoreImgService storeImgService;
+
+    /**
+     * 获取店铺人员列表
+     *
+     * @param storeId 门店id
+     * @return List<StorePersonnelVo>
+     */
+    @Override
+    public List<StorePersonnelVo> getStorePersonnelList(Integer storeId) {
+        List<StorePersonnelVo> personnelList = storePersonnelMapper.getStorePersonnelList(storeId);
+        // 按排序字段排序
+        return personnelList.stream()
+                .sorted(Comparator.comparing(StorePersonnelVo::getSort, Comparator.nullsLast(Integer::compareTo)))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 获取人员详情
+     *
+     * @param id 人员id
+     * @return StorePersonnelVo
+     */
+    @Override
+    public StorePersonnelVo getPersonnelInfo(Integer id) {
+        return storePersonnelMapper.getPersonnelInfo(id);
+    }
+
+    /**
+     * 新增或修改店铺人员
+     *
+     * @param storePersonnelVo 人员信息
+     * @return R<String>
+     */
+    @Override
+    public R<String> saveOrUpdatePersonnel(StorePersonnelVo storePersonnelVo) {
+        boolean flag = false;
+        LambdaQueryWrapper<StorePersonnel> queryWrapper = new LambdaQueryWrapper<>();
+        Integer imgId = 0;
+
+        // 处理图片信息
+        if (storePersonnelVo.getImgId() == null || storePersonnelVo.getImgId() == 0) {
+            if (StringUtils.isNotEmpty(storePersonnelVo.getImgUrl())) {
+                StoreImg storeImg = new StoreImg();
+                storeImg.setStoreId(storePersonnelVo.getStoreId());
+                // 图片类型:12-人员头像(根据实际业务调整)
+                storeImg.setImgType(12);
+                storeImg.setImgUrl(storePersonnelVo.getImgUrl());
+                storeImg.setImgDescription(storePersonnelVo.getPersonnelName());
+                storeImgService.saveOrUpdate(storeImg);
+                imgId = storeImg.getId();
+            }
+        } else {
+            imgId = storePersonnelVo.getImgId();
+        }
+
+        // 封装StorePersonnel参数
+        StorePersonnel storePersonnel = new StorePersonnel();
+        BeanUtils.copyProperties(storePersonnelVo, storePersonnel);
+        storePersonnel.setImgId(imgId);
+
+        // 修改人员
+        if (storePersonnel.getId() != null) {
+            flag = this.updateById(storePersonnel);
+            if (!flag) {
+                log.error("人员修改失败");
+                return R.fail("人员修改失败");
+            }
+            return R.success("人员修改成功");
+        } else {
+            // 新增人员
+            // 校验人员参数
+            if (StringUtils.isEmpty(storePersonnel.getPersonnelName())) {
+                return R.fail("请输入人员姓名");
+            }
+
+            // 计算排序值
+            queryWrapper.eq(StorePersonnel::getStoreId, storePersonnel.getStoreId());
+            List<StorePersonnel> personnelList = this.list(queryWrapper);
+            if (CollectionUtil.isNotEmpty(personnelList)) {
+                Integer maxSort = personnelList.stream()
+                        .map(StorePersonnel::getSort)
+                        .filter(sort -> sort != null)
+                        .max(Integer::compareTo)
+                        .orElse(0);
+                storePersonnel.setSort(maxSort + 1);
+            } else {
+                storePersonnel.setSort(1);
+            }
+
+            // 保存人员
+            flag = this.save(storePersonnel);
+            if (!flag) {
+                return R.fail("人员新增失败");
+            }
+        }
+        return R.success("新增人员成功");
+    }
+
+    /**
+     * 删除店铺人员
+     *
+     * @param ids 人员id列表
+     * @return R<String>
+     */
+    @Override
+    public R<String> deletePersonnel(List<Integer> ids) {
+        boolean flag = this.removeByIds(ids);
+        if (!flag) {
+            return R.fail("删除失败");
+        }
+        return R.success("删除成功");
+    }
+
+    /**
+     * 保存人员排序
+     *
+     * @param storePersonnelList 人员列表
+     * @return Boolean
+     */
+    @Override
+    public Boolean savePersonnelSort(List<StorePersonnel> storePersonnelList) {
+        return this.updateBatchById(storePersonnelList);
+    }
+}
+

+ 32 - 1
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffConfigServiceImpl.java

@@ -110,4 +110,35 @@ public class StoreStaffConfigServiceImpl implements StoreStaffConfigService {
         return aliOSSUtil.uploadFile(new File(filePath), "excel/" + fileName + ".xlsx");
     }
 
-}
+
+
+    @Override
+    public IPage<StoreStaffConfig> queryStaffList(Integer page, Integer size, Integer storeId, String status) {
+        IPage<StoreStaffConfig> storePage = new Page<>(page, size);
+        QueryWrapper<StoreStaffConfig> queryWrapper = new QueryWrapper<>();
+        // 按照店铺ID查询
+        if (storeId != null && storeId > 0) {
+            queryWrapper.eq("store_id", storeId);
+        }
+        // 如果状态不为空,则进行精确匹配查询
+        if (StringUtils.isNotEmpty(status)) {
+            queryWrapper.eq("status", status);
+        }
+        // 只查询未删除的记录
+        queryWrapper.eq("delete_flag", 0);
+        queryWrapper.orderByDesc("created_time");
+        return storeStaffConfigMapper.selectPage(storePage, queryWrapper);
+    }
+
+    @Override
+    public StoreStaffConfig queryStaffDetail(Integer id) {
+        if (id == null || id <= 0) {
+            return null;
+        }
+        QueryWrapper<StoreStaffConfig> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("id", id);
+        queryWrapper.eq("delete_flag", 0);
+        return storeStaffConfigMapper.selectOne(queryWrapper);
+
+
+    }